Further tweaking of parsetree & plantree representation of SubLinks.
authorTom Lane
Fri, 10 Jan 2003 21:08:15 +0000 (21:08 +0000)
committerTom Lane
Fri, 10 Jan 2003 21:08:15 +0000 (21:08 +0000)
Simplify SubLink by storing just a List of operator OIDs, instead of
a list of incomplete OpExprs --- that was a bizarre and bulky choice,
with no redeeming social value since we have to build new OpExprs
anyway when forming the plan tree.

17 files changed:
src/backend/catalog/dependency.c
src/backend/executor/execQual.c
src/backend/executor/nodeSubplan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/var.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/nodes/execnodes.h
src/include/nodes/primnodes.h

index bcc92bfab0f779fd5fdf835f2e3445841a3a3c87..688d4fb114b04b6b0d9e965d7b0be0e4d9d541bc 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.18 2002/12/12 15:49:21 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.19 2003/01/10 21:08:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -838,6 +838,18 @@ find_expr_references_walker(Node *node,
                           &context->addrs);
        /* fall through to examine arguments */
    }
+   if (IsA(node, SubLink))
+   {
+       SubLink    *sublink = (SubLink *) node;
+       List       *opid;
+
+       foreach(opid, sublink->operOids)
+       {
+           add_object_address(OCLASS_OPERATOR, (Oid) lfirsti(opid), 0,
+                              &context->addrs);
+       }
+       /* fall through to examine arguments */
+   }
    if (is_subplan(node))
    {
        /* Extra work needed here if we ever need this case */
index 971773b1212299b83a7a1872a76893de143172e3..49986de2748f2952a80f37bd4b8fbf2a499de913 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.121 2002/12/15 16:17:45 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.122 2003/01/10 21:08:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2023,8 +2023,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
                sstate->sub_estate = NULL;
                sstate->planstate = NULL;
 
-               sstate->oper = (List *)
-                   ExecInitExpr((Expr *) subplan->oper, parent);
+               sstate->exprs = (List *)
+                   ExecInitExpr((Expr *) subplan->exprs, parent);
                sstate->args = (List *)
                    ExecInitExpr((Expr *) subplan->args, parent);
 
@@ -2156,7 +2156,7 @@ ExecInitExprInitPlan(SubPlan *node, PlanState *parent)
    sstate->sub_estate = NULL;
    sstate->planstate = NULL;
 
-   sstate->oper = (List *) ExecInitExpr((Expr *) node->oper, parent);
+   sstate->exprs = (List *) ExecInitExpr((Expr *) node->exprs, parent);
    sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent);
 
    sstate->xprstate.expr = (Expr *) node;
index c9a02814bff2c1114ee8fb71e411bc9a3e23285e..40eca6749ec94f94ad56e9593d5439de3c622826 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.41 2003/01/09 20:50:50 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.42 2003/01/10 21:08:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,6 +110,7 @@ ExecSubPlan(SubPlanState *node,
        Datum       rowresult = BoolGetDatum(!useOr);
        bool        rownull = false;
        int         col = 1;
+       List       *plst;
 
        if (subLinkType == EXISTS_SUBLINK)
        {
@@ -155,45 +156,19 @@ ExecSubPlan(SubPlanState *node,
         * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
         * operators for columns of tuple.
         */
-       foreach(lst, node->oper)
+       plst = subplan->paramIds;
+       foreach(lst, node->exprs)
        {
            ExprState  *exprstate = (ExprState *) lfirst(lst);
-           OpExpr     *expr = (OpExpr *) exprstate->expr;
-           Param      *prm = lsecond(expr->args);
+           int         paramid = lfirsti(plst);
            ParamExecData *prmdata;
            Datum       expresult;
            bool        expnull;
 
            /*
-            * The righthand side of the expression should be either a
-            * Param or a function call or RelabelType node taking a Param
-            * as arg (these nodes represent run-time type coercions
-            * inserted by the parser to get to the input type needed by
-            * the operator). Find the Param node and insert the actual
-            * righthand-side value into the param's econtext slot.
-            *
-            * XXX possible improvement: could make a list of the ParamIDs
-            * at startup time, instead of repeating this check at each row.
+            * Load up the Param representing this column of the sub-select.
             */
-           if (!IsA(prm, Param))
-           {
-               switch (nodeTag(prm))
-               {
-                   case T_FuncExpr:
-                       prm = lfirst(((FuncExpr *) prm)->args);
-                       break;
-                   case T_RelabelType:
-                       prm = (Param *) (((RelabelType *) prm)->arg);
-                       break;
-                   default:
-                       /* will fail below */
-                       break;
-               }
-               if (!IsA(prm, Param))
-                   elog(ERROR, "ExecSubPlan: failed to find placeholder for subplan result");
-           }
-           Assert(prm->paramkind == PARAM_EXEC);
-           prmdata = &(econtext->ecxt_param_exec_vals[prm->paramid]);
+           prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
            Assert(prmdata->execPlan == NULL);
            prmdata->value = heap_getattr(tup, col, tdesc,
                                          &(prmdata->isnull));
@@ -236,6 +211,8 @@ ExecSubPlan(SubPlanState *node,
                    break;      /* needn't look at any more columns */
                }
            }
+
+           plst = lnext(plst);
            col++;
        }
 
@@ -312,6 +289,8 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
     */
    node->needShutdown = false;
    node->curTuple = NULL;
+   node->hashtable = NULL;
+   node->hashnulls = NULL;
 
    /*
     * create an EState for the subplan
index 4b326b75224eb11fc51770d09bd5d92a08e1f39b..e260bc6595077d21a6e21841aeae90412ec94a5a 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.234 2003/01/09 20:50:50 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.235 2003/01/10 21:08:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -809,10 +809,10 @@ _copySubLink(SubLink *from)
    SubLink    *newnode = makeNode(SubLink);
 
    COPY_SCALAR_FIELD(subLinkType);
-   COPY_SCALAR_FIELD(operIsEquals);
    COPY_SCALAR_FIELD(useOr);
    COPY_NODE_FIELD(lefthand);
-   COPY_NODE_FIELD(oper);
+   COPY_NODE_FIELD(operName);
+   COPY_INTLIST_FIELD(operOids);
    COPY_NODE_FIELD(subselect);
 
    return newnode;
@@ -828,10 +828,13 @@ _copySubPlan(SubPlan *from)
 
    COPY_SCALAR_FIELD(subLinkType);
    COPY_SCALAR_FIELD(useOr);
-   COPY_NODE_FIELD(oper);
+   COPY_NODE_FIELD(exprs);
+   COPY_INTLIST_FIELD(paramIds);
    COPY_NODE_FIELD(plan);
    COPY_SCALAR_FIELD(plan_id);
    COPY_NODE_FIELD(rtable);
+   COPY_SCALAR_FIELD(useHashTable);
+   COPY_SCALAR_FIELD(unknownEqFalse);
    COPY_INTLIST_FIELD(setParam);
    COPY_INTLIST_FIELD(parParam);
    COPY_NODE_FIELD(args);
index affa7f48a881d42ecb8e8e792684a927cba05595..0ef9b3fa2209a20c8d1afae7770c8208ec098a30 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.178 2003/01/09 20:50:50 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.179 2003/01/10 21:08:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -287,10 +287,10 @@ static bool
 _equalSubLink(SubLink *a, SubLink *b)
 {
    COMPARE_SCALAR_FIELD(subLinkType);
-   COMPARE_SCALAR_FIELD(operIsEquals);
    COMPARE_SCALAR_FIELD(useOr);
    COMPARE_NODE_FIELD(lefthand);
-   COMPARE_NODE_FIELD(oper);
+   COMPARE_NODE_FIELD(operName);
+   COMPARE_INTLIST_FIELD(operOids);
    COMPARE_NODE_FIELD(subselect);
 
    return true;
@@ -301,10 +301,13 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
 {
    COMPARE_SCALAR_FIELD(subLinkType);
    COMPARE_SCALAR_FIELD(useOr);
-   COMPARE_NODE_FIELD(oper);
+   COMPARE_NODE_FIELD(exprs);
+   COMPARE_INTLIST_FIELD(paramIds);
    /* should compare plans, but have to settle for comparing plan IDs */
    COMPARE_SCALAR_FIELD(plan_id);
    COMPARE_NODE_FIELD(rtable);
+   COMPARE_SCALAR_FIELD(useHashTable);
+   COMPARE_SCALAR_FIELD(unknownEqFalse);
    COMPARE_INTLIST_FIELD(setParam);
    COMPARE_INTLIST_FIELD(parParam);
    COMPARE_NODE_FIELD(args);
index 204c00ad674c36ca2c59c605515ab7d70658dfef..e7d8fa71ed78ea0b41c30affb6dce7ee9d13ca14 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.191 2003/01/09 20:50:50 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.192 2003/01/10 21:08:11 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -658,10 +658,10 @@ _outSubLink(StringInfo str, SubLink *node)
    WRITE_NODE_TYPE("SUBLINK");
 
    WRITE_ENUM_FIELD(subLinkType, SubLinkType);
-   WRITE_BOOL_FIELD(operIsEquals);
    WRITE_BOOL_FIELD(useOr);
    WRITE_NODE_FIELD(lefthand);
-   WRITE_NODE_FIELD(oper);
+   WRITE_NODE_FIELD(operName);
+   WRITE_INTLIST_FIELD(operOids);
    WRITE_NODE_FIELD(subselect);
 }
 
@@ -672,10 +672,13 @@ _outSubPlan(StringInfo str, SubPlan *node)
 
    WRITE_ENUM_FIELD(subLinkType, SubLinkType);
    WRITE_BOOL_FIELD(useOr);
-   WRITE_NODE_FIELD(oper);
+   WRITE_NODE_FIELD(exprs);
+   WRITE_INTLIST_FIELD(paramIds);
    WRITE_NODE_FIELD(plan);
    WRITE_INT_FIELD(plan_id);
    WRITE_NODE_FIELD(rtable);
+   WRITE_BOOL_FIELD(useHashTable);
+   WRITE_BOOL_FIELD(unknownEqFalse);
    WRITE_INTLIST_FIELD(setParam);
    WRITE_INTLIST_FIELD(parParam);
    WRITE_NODE_FIELD(args);
index 99afc0438c339ed7714eb9013c30dca3becf316b..457e04eb9f66daf1add4456a70c2ca55fe8d68ce 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.145 2003/01/09 20:50:51 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.146 2003/01/10 21:08:11 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -531,10 +531,10 @@ _readSubLink(void)
    READ_LOCALS(SubLink);
 
    READ_ENUM_FIELD(subLinkType, SubLinkType);
-   READ_BOOL_FIELD(operIsEquals);
    READ_BOOL_FIELD(useOr);
    READ_NODE_FIELD(lefthand);
-   READ_NODE_FIELD(oper);
+   READ_NODE_FIELD(operName);
+   READ_INTLIST_FIELD(operOids);
    READ_NODE_FIELD(subselect);
 
    READ_DONE();
index 5081f9c3401bc83b5b4e791c5d9a1b217793203b..c76d67b93e5191986b3db2125dc54b1a6cefd3f2 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.86 2002/12/14 00:17:55 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.87 2003/01/10 21:08:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,7 @@ typedef struct
 
 static void fix_expr_references(Plan *plan, Node *node);
 static bool fix_expr_references_walker(Node *node, void *context);
+static void mark_qual_expressions(List *quals);
 static void set_join_references(Join *join, List *rtable);
 static void set_uppernode_references(Plan *plan, Index subvarno);
 static Node *join_references_mutator(Node *node,
@@ -88,10 +89,12 @@ set_plan_references(Plan *plan, List *rtable)
        case T_SeqScan:
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            break;
        case T_IndexScan:
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            fix_expr_references(plan,
                                (Node *) ((IndexScan *) plan)->indxqual);
            fix_expr_references(plan,
@@ -100,6 +103,7 @@ set_plan_references(Plan *plan, List *rtable)
        case T_TidScan:
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            fix_expr_references(plan,
                                (Node *) ((TidScan *) plan)->tideval);
            break;
@@ -114,6 +118,7 @@ set_plan_references(Plan *plan, List *rtable)
                 */
                fix_expr_references(plan, (Node *) plan->targetlist);
                fix_expr_references(plan, (Node *) plan->qual);
+               mark_qual_expressions(plan->qual);
 
                /* Recurse into subplan too */
                rte = rt_fetch(((SubqueryScan *) plan)->scan.scanrelid,
@@ -129,6 +134,7 @@ set_plan_references(Plan *plan, List *rtable)
 
                fix_expr_references(plan, (Node *) plan->targetlist);
                fix_expr_references(plan, (Node *) plan->qual);
+               mark_qual_expressions(plan->qual);
                rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
                               rtable);
                Assert(rte->rtekind == RTE_FUNCTION);
@@ -139,13 +145,17 @@ set_plan_references(Plan *plan, List *rtable)
            set_join_references((Join *) plan, rtable);
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
+           mark_qual_expressions(((Join *) plan)->joinqual);
            break;
        case T_MergeJoin:
            set_join_references((Join *) plan, rtable);
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
+           mark_qual_expressions(((Join *) plan)->joinqual);
            fix_expr_references(plan,
                            (Node *) ((MergeJoin *) plan)->mergeclauses);
            break;
@@ -153,7 +163,9 @@ set_plan_references(Plan *plan, List *rtable)
            set_join_references((Join *) plan, rtable);
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
+           mark_qual_expressions(((Join *) plan)->joinqual);
            fix_expr_references(plan,
                              (Node *) ((HashJoin *) plan)->hashclauses);
            break;
@@ -180,6 +192,7 @@ set_plan_references(Plan *plan, List *rtable)
            set_uppernode_references(plan, (Index) 0);
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            break;
        case T_Result:
 
@@ -193,7 +206,9 @@ set_plan_references(Plan *plan, List *rtable)
                set_uppernode_references(plan, (Index) OUTER);
            fix_expr_references(plan, (Node *) plan->targetlist);
            fix_expr_references(plan, (Node *) plan->qual);
+           mark_qual_expressions(plan->qual);
            fix_expr_references(plan, ((Result *) plan)->resconstantqual);
+           mark_qual_expressions((List *) ((Result *) plan)->resconstantqual);
            break;
        case T_Append:
 
@@ -268,6 +283,28 @@ fix_expr_references_walker(Node *node, void *context)
    return expression_tree_walker(node, fix_expr_references_walker, context);
 }
 
+/*
+ * mark_qual_expressions
+ *   Do final cleanup on qualifier expressions (not targetlists!)
+ *
+ * SubPlans appearing at the top level of a qual expression are marked
+ * to indicate that they need not distinguish UNKNOWN (null) from FALSE
+ * results; this can save processing time in some cases.
+ */
+static void
+mark_qual_expressions(List *quals)
+{
+   List   *qual;
+
+   foreach(qual, quals)
+   {
+       Node   *node = lfirst(qual);
+
+       if (IsA(node, SubPlan))
+           ((SubPlan *) node)->unknownEqFalse = true;
+   }
+}
+
 /*
  * set_join_references
  *   Modifies the target list of a join node to reference its subplans,
index f8086d9ab6e2b9308b9740b0bfc76c57411ccb1f..460d5c388352a10169c9fef8203f4ea0785a91c2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.62 2003/01/09 20:50:51 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.63 2003/01/10 21:08:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/params.h"
 #include "optimizer/clauses.h"
@@ -25,6 +26,7 @@
 #include "parser/parsetree.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -59,8 +61,9 @@ typedef struct finalize_primnode_results
 } finalize_primnode_results;
 
 
-static List *convert_sublink_opers(List *operlist, List *lefthand,
-                                  List *targetlist, List **setParams);
+static List *convert_sublink_opers(List *lefthand, List *operOids,
+                                  List *targetlist, List **paramIds);
+static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
 static Node *replace_correlation_vars_mutator(Node *node, void *context);
 static Node *process_sublinks_mutator(Node *node, void *context);
 static bool finalize_primnode(Node *node, finalize_primnode_results *results);
@@ -222,11 +225,14 @@ make_subplan(SubLink *slink, List *lefthand)
    node->rtable = subquery->rtable;
 
    /*
-    * Fill in other fields of the SubPlan node.
+    * Initialize other fields of the SubPlan node.
     */
    node->subLinkType = slink->subLinkType;
    node->useOr = slink->useOr;
-   node->oper = NIL;
+   node->exprs = NIL;
+   node->paramIds = NIL;
+   node->useHashTable = false;
+   node->unknownEqFalse = false;
    node->setParam = NIL;
    node->parParam = NIL;
    node->args = NIL;
@@ -267,6 +273,7 @@ make_subplan(SubLink *slink, List *lefthand)
        TargetEntry *te = lfirst(plan->targetlist);
        Param      *prm;
 
+       Assert(!te->resdom->resjunk);
        prm = generate_new_param(te->resdom->restype, te->resdom->restypmod);
        node->setParam = lappendi(node->setParam, prm->paramid);
        PlannerInitPlan = lappend(PlannerInitPlan, node);
@@ -274,19 +281,25 @@ make_subplan(SubLink *slink, List *lefthand)
    }
    else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
    {
-       List   *oper;
-
-       /* Convert the oper list, but don't put it into the SubPlan node */
-       oper = convert_sublink_opers(slink->oper,
-                                    lefthand,
-                                    plan->targetlist,
-                                    &node->setParam);
+       List   *exprs;
+
+       /* Convert the lefthand exprs and oper OIDs into executable exprs */
+       exprs = convert_sublink_opers(lefthand,
+                                     slink->operOids,
+                                     plan->targetlist,
+                                     &node->paramIds);
+       node->setParam = nconc(node->setParam, listCopy(node->paramIds));
        PlannerInitPlan = lappend(PlannerInitPlan, node);
-       if (length(oper) > 1)
-           result = (Node *) (node->useOr ? make_orclause(oper) :
-                              make_andclause(oper));
+       /*
+        * The executable expressions are returned to become part of the
+        * outer plan's expression tree; they are not kept in the initplan
+        * node.
+        */
+       if (length(exprs) > 1)
+           result = (Node *) (node->useOr ? make_orclause(exprs) :
+                              make_andclause(exprs));
        else
-           result = (Node *) lfirst(oper);
+           result = (Node *) lfirst(exprs);
    }
    else
    {
@@ -296,13 +309,20 @@ make_subplan(SubLink *slink, List *lefthand)
         * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
         * to initPlans, even when they are uncorrelated or undirect
         * correlated, because we need to scan the output of the subplan
-        * for each outer tuple.  However, we have the option to tack a
-        * MATERIAL node onto the top of an uncorrelated/undirect
-        * correlated subplan, which lets us do the work of evaluating the
-        * subplan only once.  We do this if the subplan's top plan node
-        * is anything more complicated than a plain sequential scan, and
-        * we do it even for seqscan if the qual appears selective enough
-        * to eliminate many tuples.
+        * for each outer tuple.  But if it's an IN (= ANY) test, we might
+        * be able to use a hashtable to avoid comparing all the tuples.
+        */
+       if (subplan_is_hashable(slink, node))
+           node->useHashTable = true;
+       /*
+        * Otherwise, we have the option to tack a MATERIAL node onto the top
+        * of the subplan, to reduce the cost of reading it repeatedly.  This
+        * is pointless for a direct-correlated subplan, since we'd have to
+        * recompute its results each time anyway.  For uncorrelated/undirect
+        * correlated subplans, we add MATERIAL if the subplan's top plan node
+        * is anything more complicated than a plain sequential scan, and we
+        * do it even for seqscan if the qual appears selective enough to
+        * eliminate many tuples.
         *
         * XXX It's pretty ugly to be inserting a MATERIAL node at this
         * point.  Since subquery_planner has already run SS_finalize_plan
@@ -310,7 +330,7 @@ make_subplan(SubLink *slink, List *lefthand)
         * the MATERIAL node.  Possibly this could be fixed by postponing
         * SS_finalize_plan processing until setrefs.c is run.
         */
-       if (node->parParam == NIL)
+       else if (node->parParam == NIL)
        {
            bool        use_material;
 
@@ -365,11 +385,11 @@ make_subplan(SubLink *slink, List *lefthand)
            }
        }
 
-       /* Convert the SubLink's oper list into executable form */
-       node->oper = convert_sublink_opers(slink->oper,
-                                          lefthand,
-                                          plan->targetlist,
-                                          NULL);
+       /* Convert the lefthand exprs and oper OIDs into executable exprs */
+       node->exprs = convert_sublink_opers(lefthand,
+                                           slink->operOids,
+                                           plan->targetlist,
+                                           &node->paramIds);
 
        /*
         * Make node->args from parParam.
@@ -398,29 +418,26 @@ make_subplan(SubLink *slink, List *lefthand)
 }
 
 /*
- * convert_sublink_opers: convert a SubLink's oper list from the
- * parser/rewriter format into the executor's format.
+ * convert_sublink_opers: given a lefthand-expressions list and a list of
+ * operator OIDs, build a list of actually executable expressions.  The
+ * righthand sides of the expressions are Params representing the results
+ * of the sub-select.
  *
- * The oper list is initially a list of OpExpr nodes with NIL args.  We
- * convert it to a list of actually executable expressions, in which the
- * specified operators are applied to corresponding elements of the
- * lefthand list and Params representing the results of the subplan.
- *
- * If setParams is not NULL, the paramids of the Params created are added
- * to the *setParams list.
+ * The paramids of the Params created are returned in the *paramIds list.
  */
 static List *
-convert_sublink_opers(List *operlist, List *lefthand,
-                     List *targetlist, List **setParams)
+convert_sublink_opers(List *lefthand, List *operOids,
+                     List *targetlist, List **paramIds)
 {
-   List       *newoper = NIL;
-   List       *leftlist = lefthand;
+   List       *result = NIL;
    List       *lst;
 
-   foreach(lst, operlist)
+   *paramIds = NIL;
+
+   foreach(lst, operOids)
    {
-       OpExpr     *oper = (OpExpr *) lfirst(lst);
-       Node       *leftop = lfirst(leftlist);
+       Oid         opid = (Oid) lfirsti(lst);
+       Node       *leftop = lfirst(lefthand);
        TargetEntry *te = lfirst(targetlist);
        Param      *prm;
        Operator    tup;
@@ -428,21 +445,21 @@ convert_sublink_opers(List *operlist, List *lefthand,
        Node       *left,
                   *right;
 
+       Assert(!te->resdom->resjunk);
+
        /* Make the Param node representing the subplan's result */
        prm = generate_new_param(te->resdom->restype,
                                 te->resdom->restypmod);
 
-       /* Record its ID if needed */
-       if (setParams)
-           *setParams = lappendi(*setParams, prm->paramid);
+       /* Record its ID */
+       *paramIds = lappendi(*paramIds, prm->paramid);
 
-       /* Look up the operator to check its declared input types */
-       Assert(IsA(oper, OpExpr));
+       /* Look up the operator to get its declared input types */
        tup = SearchSysCache(OPEROID,
-                            ObjectIdGetDatum(oper->opno),
+                            ObjectIdGetDatum(opid),
                             0, 0, 0);
        if (!HeapTupleIsValid(tup))
-           elog(ERROR, "cache lookup failed for operator %u", oper->opno);
+           elog(ERROR, "cache lookup failed for operator %u", opid);
        opform = (Form_pg_operator) GETSTRUCT(tup);
 
        /*
@@ -453,20 +470,86 @@ convert_sublink_opers(List *operlist, List *lefthand,
         */
        left = make_operand(leftop, exprType(leftop), opform->oprleft);
        right = make_operand((Node *) prm, prm->paramtype, opform->oprright);
-       newoper = lappend(newoper,
-                         make_opclause(oper->opno,
-                                       oper->opresulttype,
-                                       oper->opretset,
-                                       (Expr *) left,
-                                       (Expr *) right));
+       result = lappend(result,
+                        make_opclause(opid,
+                                      opform->oprresult,
+                                      false, /* set-result not allowed */
+                                      (Expr *) left,
+                                      (Expr *) right));
 
        ReleaseSysCache(tup);
 
-       leftlist = lnext(leftlist);
+       lefthand = lnext(lefthand);
        targetlist = lnext(targetlist);
    }
 
-   return newoper;
+   return result;
+}
+
+/*
+ * subplan_is_hashable: decide whether we can implement a subplan by hashing
+ *
+ * Caution: the SubPlan node is not completely filled in yet.  We can rely
+ * on its plan and parParam fields, however.
+ */
+static bool
+subplan_is_hashable(SubLink *slink, SubPlan *node)
+{
+   double      subquery_size;
+   List       *opids;
+
+   /*
+    * The sublink type must be "= ANY" --- that is, an IN operator.
+    * (We require the operator name to be unqualified, which may be
+    * overly paranoid, or may not be.)  XXX since we also check that the
+    * operators are hashable, the test on operator name may be redundant?
+    */
+   if (slink->subLinkType != ANY_SUBLINK)
+       return false;
+   if (length(slink->operName) != 1 ||
+       strcmp(strVal(lfirst(slink->operName)), "=") != 0)
+       return false;
+   /*
+    * The subplan must not have any direct correlation vars --- else we'd
+    * have to recompute its output each time, so that the hashtable wouldn't
+    * gain anything.
+    */
+   if (node->parParam != NIL)
+       return false;
+   /*
+    * The estimated size of the subquery result must fit in SortMem.
+    * (XXX what about hashtable overhead?)
+    */
+   subquery_size = node->plan->plan_rows *
+       (MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleData)));
+   if (subquery_size > SortMem * 1024L)
+       return false;
+   /*
+    * The combining operators must be hashable and strict.  (Without
+    * strictness, behavior in the presence of nulls is too unpredictable.
+    * We actually must assume even more than plain strictness, see
+    * nodeSubplan.c for details.)
+    */
+   foreach(opids, slink->operOids)
+   {
+       Oid         opid = (Oid) lfirsti(opids);
+       HeapTuple   tup;
+       Form_pg_operator optup;
+
+       tup = SearchSysCache(OPEROID,
+                            ObjectIdGetDatum(opid),
+                            0, 0, 0);
+       if (!HeapTupleIsValid(tup))
+           elog(ERROR, "cache lookup failed for operator %u", opid);
+       optup = (Form_pg_operator) GETSTRUCT(tup);
+       if (!optup->oprcanhash || !func_strict(optup->oprcode))
+       {
+           ReleaseSysCache(tup);
+           return false;
+       }
+       ReleaseSysCache(tup);
+   }
+   return true;
 }
 
 /*
index 4c87a95c3b1ebd3d750b8d669a9b7535d7307a18..e38fc46821d5eab93fdf18f5fe8eede2e77b1d75 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.120 2002/12/15 16:17:50 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.121 2003/01/10 21:08:13 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -721,7 +721,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
  * mistakenly think that something like "WHERE random() < 0.5" can be treated
  * as a constant qualification.
  *
- * XXX we do not examine sublinks/subplans to see if they contain uses of
+ * XXX we do not examine sub-selects to see if they contain uses of
  * mutable functions.  It's not real clear if that is correct or not...
  */
 bool
@@ -759,6 +759,18 @@ contain_mutable_functions_walker(Node *node, void *context)
            return true;
        /* else fall through to check args */
    }
+   if (IsA(node, SubLink))
+   {
+       SubLink    *sublink = (SubLink *) node;
+       List       *opid;
+
+       foreach(opid, sublink->operOids)
+       {
+           if (op_volatile((Oid) lfirsti(opid)) != PROVOLATILE_IMMUTABLE)
+               return true;
+       }
+       /* else fall through to check args */
+   }
    return expression_tree_walker(node, contain_mutable_functions_walker,
                                  context);
 }
@@ -776,7 +788,7 @@ contain_mutable_functions_walker(Node *node, void *context)
  * volatile function) is found. This test prevents invalid conversions
  * of volatile expressions into indexscan quals.
  *
- * XXX we do not examine sublinks/subplans to see if they contain uses of
+ * XXX we do not examine sub-selects to see if they contain uses of
  * volatile functions. It's not real clear if that is correct or not...
  */
 bool
@@ -814,6 +826,18 @@ contain_volatile_functions_walker(Node *node, void *context)
            return true;
        /* else fall through to check args */
    }
+   if (IsA(node, SubLink))
+   {
+       SubLink    *sublink = (SubLink *) node;
+       List       *opid;
+
+       foreach(opid, sublink->operOids)
+       {
+           if (op_volatile((Oid) lfirsti(opid)) == PROVOLATILE_VOLATILE)
+               return true;
+       }
+       /* else fall through to check args */
+   }
    return expression_tree_walker(node, contain_volatile_functions_walker,
                                  context);
 }
@@ -830,7 +854,7 @@ contain_volatile_functions_walker(Node *node, void *context)
  * Returns true if any nonstrict construct is found --- ie, anything that
  * could produce non-NULL output with a NULL input.
  *
- * XXX we do not examine sublinks/subplans to see if they contain uses of
+ * XXX we do not examine sub-selects to see if they contain uses of
  * nonstrict functions.    It's not real clear if that is correct or not...
  * for the current usage it does not matter, since inline_function()
  * rejects cases with sublinks.
@@ -887,6 +911,18 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        return true;
    if (IsA(node, BooleanTest))
        return true;
+   if (IsA(node, SubLink))
+   {
+       SubLink    *sublink = (SubLink *) node;
+       List       *opid;
+
+       foreach(opid, sublink->operOids)
+       {
+           if (!op_strict((Oid) lfirsti(opid)))
+               return true;
+       }
+       /* else fall through to check args */
+   }
    return expression_tree_walker(node, contain_nonstrict_functions_walker,
                                  context);
 }
@@ -2130,8 +2166,8 @@ substitute_actual_parameters_mutator(Node *node,
  * walker on all the expression subtrees of the given Query node.
  *
  * expression_tree_walker will handle SubPlan nodes by recursing normally
- * into the "oper" and "args" lists (which are expressions belonging to the
- * outer plan).  It will not touch the completed subplan, however.  Since
+ * into the "exprs" and "args" lists (which are expressions belonging to
+ * the outer plan).  It will not touch the completed subplan, however.  Since
  * there is no link to the original Query, it is not possible to recurse into
  * subselects of an already-planned expression tree.  This is OK for current
  * uses, but may need to be revisited in future.
@@ -2224,11 +2260,6 @@ expression_tree_walker(Node *node,
            {
                SubLink    *sublink = (SubLink *) node;
 
-               /*
-                * We only recurse into the lefthand list (the incomplete
-                * OpExpr nodes in the oper list are deemed uninteresting,
-                * perhaps even confusing).
-                */
                if (expression_tree_walker((Node *) sublink->lefthand,
                                           walker, context))
                    return true;
@@ -2243,8 +2274,8 @@ expression_tree_walker(Node *node,
            {
                SubPlan *subplan = (SubPlan *) node;
 
-               /* recurse into the oper list, but not into the Plan */
-               if (expression_tree_walker((Node *) subplan->oper,
+               /* recurse into the exprs list, but not into the Plan */
+               if (expression_tree_walker((Node *) subplan->exprs,
                                           walker, context))
                    return true;
                /* also examine args list */
@@ -2451,7 +2482,7 @@ query_tree_walker(Query *query,
  * and qualifier clauses during the planning stage.
  *
  * expression_tree_mutator will handle a SubPlan node by recursing into
- * the "oper" and "args" lists (which belong to the outer plan), but it
+ * the "exprs" and "args" lists (which belong to the outer plan), but it
  * will simply copy the link to the inner plan, since that's typically what
  * expression tree mutators want.  A mutator that wants to modify the subplan
  * can force appropriate behavior by recognizing SubPlan expression nodes
@@ -2567,8 +2598,7 @@ expression_tree_mutator(Node *node,
        case T_SubLink:
            {
                /*
-                * We transform the lefthand side, but not the oper list nor
-                * the subquery.
+                * We transform the lefthand side, but not the subquery.
                 */
                SubLink    *sublink = (SubLink *) node;
                SubLink    *newnode;
@@ -2584,10 +2614,10 @@ expression_tree_mutator(Node *node,
                SubPlan    *newnode;
 
                FLATCOPY(newnode, subplan, SubPlan);
+               /* transform exprs list */
+               MUTATE(newnode->exprs, subplan->exprs, List *);
                /* transform args list (params to be passed to subplan) */
                MUTATE(newnode->args, subplan->args, List *);
-               /* transform oper list as well */
-               MUTATE(newnode->oper, subplan->oper, List *);
                /* but not the sub-Plan itself, which is referenced as-is */
                return (Node *) newnode;
            }
index e4868af11cd5e07acde32757bf5e35d03a90d4a9..92ff0cd5b4c003b540af9d0c16e0c9d00489be67 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.42 2002/12/14 00:17:59 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.43 2003/01/10 21:08:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,13 +107,13 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
    {
        /*
         * Already-planned subquery.  Examine the args list (parameters to
-        * be passed to subquery), as well as the "oper" list which is
+        * be passed to subquery), as well as the exprs list which is
         * executed by the outer query.  But short-circuit recursion into
         * the subquery itself, which would be a waste of effort.
         */
        SubPlan *subplan = (SubPlan *) node;
 
-       if (pull_varnos_walker((Node *) subplan->oper,
+       if (pull_varnos_walker((Node *) subplan->exprs,
                               context))
            return true;
        if (pull_varnos_walker((Node *) subplan->args,
@@ -190,13 +190,13 @@ contain_var_reference_walker(Node *node,
    {
        /*
         * Already-planned subquery.  Examine the args list (parameters to
-        * be passed to subquery), as well as the "oper" list which is
+        * be passed to subquery), as well as the exprs list which is
         * executed by the outer query.  But short-circuit recursion into
         * the subquery itself, which would be a waste of effort.
         */
        SubPlan *subplan = (SubPlan *) node;
 
-       if (contain_var_reference_walker((Node *) subplan->oper,
+       if (contain_var_reference_walker((Node *) subplan->exprs,
                                         context))
            return true;
        if (contain_var_reference_walker((Node *) subplan->args,
index bfb1e427fb43d0957479fa1815c255bce089bcd0..fd33601cc08c98655ee9ebf352e1ee6726171d33 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.393 2003/01/10 11:02:51 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.394 2003/01/10 21:08:13 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -5430,10 +5430,9 @@ opt_interval:
 r_expr:  row IN_P select_with_parens
                {
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = $1;
-                   n->oper = (List *) makeSimpleA_Expr(OP, "=", NULL, NULL);
                    n->subLinkType = ANY_SUBLINK;
-                   /* operIsEquals and useOr will be set later */
+                   n->lefthand = $1;
+                   n->operName = makeList1(makeString("="));
                    n->subselect = $3;
                    $$ = (Node *)n;
                }
@@ -5441,10 +5440,9 @@ r_expr:  row IN_P select_with_parens
                {
                    /* Make an IN node */
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = $1;
-                   n->oper = (List *) makeSimpleA_Expr(OP, "=", NULL, NULL);
                    n->subLinkType = ANY_SUBLINK;
-                   /* operIsEquals and useOr will be set later */
+                   n->lefthand = $1;
+                   n->operName = makeList1(makeString("="));
                    n->subselect = $4;
                    /* Stick a NOT on top */
                    $$ = (Node *) makeA_Expr(NOT, NIL, NULL, (Node *) n);
@@ -5453,10 +5451,9 @@ r_expr:  row IN_P select_with_parens
            %prec Op
                {
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = $1;
-                   n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
                    n->subLinkType = $3;
-                   /* operIsEquals and useOr will be set later */
+                   n->lefthand = $1;
+                   n->operName = $2;
                    n->subselect = $4;
                    $$ = (Node *)n;
                }
@@ -5464,10 +5461,9 @@ r_expr:  row IN_P select_with_parens
            %prec Op
                {
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = $1;
-                   n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
                    n->subLinkType = MULTIEXPR_SUBLINK;
-                   /* operIsEquals and useOr will be set later */
+                   n->lefthand = $1;
+                   n->operName = $2;
                    n->subselect = $3;
                    $$ = (Node *)n;
                }
@@ -5848,10 +5844,9 @@ a_expr:      c_expr                                  { $$ = $1; }
                    if (IsA($3, SubLink))
                    {
                            SubLink *n = (SubLink *)$3;
-                           n->lefthand = makeList1($1);
-                           n->oper = (List *) makeSimpleA_Expr(OP, "=", NULL, NULL);
                            n->subLinkType = ANY_SUBLINK;
-                           /* operIsEquals and useOr will be set later */
+                           n->lefthand = makeList1($1);
+                           n->operName = makeList1(makeString("="));
                            $$ = (Node *)n;
                    }
                    else
@@ -5877,10 +5872,9 @@ a_expr:      c_expr                                  { $$ = $1; }
                    {
                        /* Make an IN node */
                        SubLink *n = (SubLink *)$4;
-                       n->lefthand = makeList1($1);
-                       n->oper = (List *) makeSimpleA_Expr(OP, "=", NULL, NULL);
                        n->subLinkType = ANY_SUBLINK;
-                       /* operIsEquals and useOr will be set later */
+                       n->lefthand = makeList1($1);
+                       n->operName = makeList1(makeString("="));
                        /* Stick a NOT on top */
                        $$ = (Node *) makeA_Expr(NOT, NIL, NULL, (Node *) n);
                    }
@@ -5903,10 +5897,9 @@ a_expr:      c_expr                                  { $$ = $1; }
            | a_expr qual_all_Op sub_type select_with_parens %prec Op
                {
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = makeList1($1);
-                   n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
                    n->subLinkType = $3;
-                   /* operIsEquals and useOr will be set later */
+                   n->lefthand = makeList1($1);
+                   n->operName = $2;
                    n->subselect = $4;
                    $$ = (Node *)n;
                }
@@ -6447,18 +6440,18 @@ c_expr:     columnref                               { $$ = (Node *) $1; }
            | select_with_parens            %prec UMINUS
                {
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = NIL;
-                   n->oper = NIL;
                    n->subLinkType = EXPR_SUBLINK;
+                   n->lefthand = NIL;
+                   n->operName = NIL;
                    n->subselect = $1;
                    $$ = (Node *)n;
                }
            | EXISTS select_with_parens
                {
                    SubLink *n = makeNode(SubLink);
-                   n->lefthand = NIL;
-                   n->oper = NIL;
                    n->subLinkType = EXISTS_SUBLINK;
+                   n->lefthand = NIL;
+                   n->operName = NIL;
                    n->subselect = $2;
                    $$ = (Node *)n;
                }
@@ -6610,6 +6603,7 @@ in_expr:  select_with_parens
                {
                    SubLink *n = makeNode(SubLink);
                    n->subselect = $1;
+                   /* other fields will be filled later */
                    $$ = (Node *)n;
                }
            | '(' expr_list ')'                     { $$ = (Node *)$2; }
index 3701f41dca0b1c192d6ef54cf3a54926ce3e16db..adf45bbeef5acf6c27287b449b43db6bfc5390b7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.139 2003/01/09 20:50:52 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.140 2003/01/10 21:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -366,8 +366,8 @@ transformExpr(ParseState *pstate, Node *expr)
                     * These fields should be NIL already, but make sure.
                     */
                    sublink->lefthand = NIL;
-                   sublink->oper = NIL;
-                   sublink->operIsEquals = FALSE;
+                   sublink->operName = NIL;
+                   sublink->operOids = NIL;
                    sublink->useOr = FALSE;
                }
                else if (sublink->subLinkType == EXPR_SUBLINK)
@@ -392,8 +392,8 @@ transformExpr(ParseState *pstate, Node *expr)
                     * fields should be NIL already, but make sure.
                     */
                    sublink->lefthand = NIL;
-                   sublink->oper = NIL;
-                   sublink->operIsEquals = FALSE;
+                   sublink->operName = NIL;
+                   sublink->operOids = NIL;
                    sublink->useOr = FALSE;
                }
                else
@@ -403,20 +403,14 @@ transformExpr(ParseState *pstate, Node *expr)
                    List       *right_list = qtree->targetList;
                    int         row_length = length(left_list);
                    bool        needNot = false;
-                   List       *op;
-                   char       *opname;
+                   List       *op = sublink->operName;
+                   char       *opname = strVal(llast(op));
                    List       *elist;
 
                    /* transform lefthand expressions */
                    foreach(elist, left_list)
                        lfirst(elist) = transformExpr(pstate, lfirst(elist));
 
-                   /* get the combining-operator name */
-                   Assert(IsA(sublink->oper, A_Expr));
-                   op = ((A_Expr *) sublink->oper)->name;
-                   opname = strVal(llast(op));
-                   sublink->oper = NIL;
-
                    /*
                     * If the expression is "<> ALL" (with unqualified opname)
                     * then convert it to "NOT IN".  This is a hack to improve
@@ -428,15 +422,10 @@ transformExpr(ParseState *pstate, Node *expr)
                        sublink->subLinkType = ANY_SUBLINK;
                        opname = pstrdup("=");
                        op = makeList1(makeString(opname));
+                       sublink->operName = op;
                        needNot = true;
                    }
 
-                   /* Set operIsEquals if op is unqualified "=" */
-                   if (length(op) == 1 && strcmp(opname, "=") == 0)
-                       sublink->operIsEquals = TRUE;
-                   else
-                       sublink->operIsEquals = FALSE;
-
                    /* Set useOr if op is "<>" (possibly qualified) */
                    if (strcmp(opname, "<>") == 0)
                        sublink->useOr = TRUE;
@@ -451,19 +440,21 @@ transformExpr(ParseState *pstate, Node *expr)
                             opname);
 
                    /*
-                    * Scan subquery's targetlist to find values that will
+                    * To build the list of combining operator OIDs, we must
+                    * scan subquery's targetlist to find values that will
                     * be matched against lefthand values.  We need to
                     * ignore resjunk targets, so doing the outer
                     * iteration over right_list is easier than doing it
                     * over left_list.
                     */
+                   sublink->operOids = NIL;
+
                    while (right_list != NIL)
                    {
                        TargetEntry *tent = (TargetEntry *) lfirst(right_list);
                        Node       *lexpr;
                        Operator    optup;
                        Form_pg_operator opform;
-                       OpExpr     *newop;
 
                        right_list = lnext(right_list);
                        if (tent->resdom->resjunk)
@@ -496,14 +487,8 @@ transformExpr(ParseState *pstate, Node *expr)
                                 " to be used with quantified predicate subquery",
                                 opname);
 
-                       newop = makeNode(OpExpr);
-                       newop->opno = oprid(optup);
-                       newop->opfuncid = InvalidOid;
-                       newop->opresulttype = opform->oprresult;
-                       newop->opretset = false;
-                       newop->args = NIL; /* for now */
-
-                       sublink->oper = lappend(sublink->oper, newop);
+                       sublink->operOids = lappendi(sublink->operOids,
+                                                    oprid(optup));
 
                        ReleaseSysCache(optup);
                    }
index a8679463670859bee1e1f8a2a6efed82c6a294e8..d7474b4d7f7d64b0575b499236e40a809b237dbd 100644 (file)
@@ -3,7 +3,7 @@
  *             back to source text
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.131 2003/01/09 20:50:52 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.132 2003/01/10 21:08:15 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -167,6 +167,7 @@ static bool tleIsArrayAssign(TargetEntry *tle);
 static char *generate_relation_name(Oid relid);
 static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
+static void print_operator_name(StringInfo buf, List *opname);
 static char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
@@ -2111,7 +2112,10 @@ get_rule_expr(Node *node, deparse_context *context,
                 * rule deparsing, only while EXPLAINing a query
                 * plan. For now, just punt.
                 */
-               appendStringInfo(buf, "(subplan)");
+               if (((SubPlan *) node)->useHashTable)
+                   appendStringInfo(buf, "(hashed subplan)");
+               else
+                   appendStringInfo(buf, "(subplan)");
            }
            break;
 
@@ -2619,7 +2623,6 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
    Query      *query = (Query *) (sublink->subselect);
    List       *l;
    char       *sep;
-   OpExpr     *oper;
    bool        need_paren;
 
    appendStringInfoChar(buf, '(');
@@ -2647,11 +2650,10 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
    need_paren = true;
 
    /*
-    * XXX we assume here that we can get away without qualifying the
-    * operator name.  Since the name may imply multiple physical
-    * operators it's rather difficult to do otherwise --- in fact, if the
-    * operators are in different namespaces any attempt to qualify would
-    * surely fail.
+    * XXX we regurgitate the originally given operator name, with or without
+    * schema qualification.  This is not necessarily 100% right but it's
+    * the best we can do, since the operators actually used might not all
+    * be in the same schema.
     */
    switch (sublink->subLinkType)
    {
@@ -2660,26 +2662,27 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
            break;
 
        case ANY_SUBLINK:
-           if (sublink->operIsEquals)
+           if (length(sublink->operName) == 1 &&
+               strcmp(strVal(lfirst(sublink->operName)), "=") == 0)
            {
-               /* Represent it as IN */
+               /* Represent = ANY as IN */
                appendStringInfo(buf, "IN ");
            }
            else
            {
-               oper = (OpExpr *) lfirst(sublink->oper);
-               appendStringInfo(buf, "%s ANY ", get_opname(oper->opno));
+               print_operator_name(buf, sublink->operName);
+               appendStringInfo(buf, " ANY ");
            }
            break;
 
        case ALL_SUBLINK:
-           oper = (OpExpr *) lfirst(sublink->oper);
-           appendStringInfo(buf, "%s ALL ", get_opname(oper->opno));
+           print_operator_name(buf, sublink->operName);
+           appendStringInfo(buf, " ALL ");
            break;
 
        case MULTIEXPR_SUBLINK:
-           oper = (OpExpr *) lfirst(sublink->oper);
-           appendStringInfo(buf, "%s ", get_opname(oper->opno));
+           print_operator_name(buf, sublink->operName);
+           appendStringInfoChar(buf, ' ');
            break;
 
        case EXPR_SUBLINK:
@@ -3274,6 +3277,29 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
    return buf.data;
 }
 
+/*
+ * Print out a possibly-qualified operator name
+ */
+static void
+print_operator_name(StringInfo buf, List *opname)
+{
+   int     nnames = length(opname);
+
+   if (nnames == 1)
+       appendStringInfo(buf, "%s", strVal(lfirst(opname)));
+   else
+   {
+       appendStringInfo(buf, "OPERATOR(");
+       while (nnames-- > 1)
+       {
+           appendStringInfo(buf, "%s.",
+                            quote_identifier(strVal(lfirst(opname))));
+           opname = lnext(opname);
+       }
+       appendStringInfo(buf, "%s)", strVal(lfirst(opname)));
+   }
+}
+
 /*
  * get_relid_attribute_name
  *     Get an attribute name by its relations Oid and its attnum
index 9c3e5906adeb9d69788123327e43cc9c71f2b2bd..9e62bd4793c5d4867f29383771ff2c78525a103c 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.171 2003/01/09 20:50:53 tgl Exp $
+ * $Id: catversion.h,v 1.172 2003/01/10 21:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200301091
+#define CATALOG_VERSION_NO 200301101
 
 #endif
index a593957022cca4b35b80e52cdd9d8cfa6c138375..1ce0635c632c7540c28a6d07b6150e11ab647d7c 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: execnodes.h,v 1.88 2002/12/18 00:14:47 tgl Exp $
+ * $Id: execnodes.h,v 1.89 2003/01/10 21:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -445,15 +445,21 @@ typedef struct BoolExprState
  *     SubPlanState node
  * ----------------
  */
+/* this struct is private in nodeSubplan.c: */
+typedef struct SubPlanHashTableData *SubPlanHashTable;
+
 typedef struct SubPlanState
 {
    ExprState   xprstate;
    EState     *sub_estate;     /* subselect plan has its own EState */
    struct PlanState *planstate; /* subselect plan's state tree */
+   List       *exprs;          /* states of combining expression(s) */
+   List       *args;           /* states of argument expression(s) */
    bool        needShutdown;   /* TRUE = need to shutdown subplan */
    HeapTuple   curTuple;       /* copy of most recent tuple from subplan */
-   List       *oper;           /* states for executable combining exprs */
-   List       *args;           /* states of argument expression(s) */
+   /* these are used when hashing the subselect's output: */
+   SubPlanHashTable hashtable; /* hash table for no-nulls subselect rows */
+   SubPlanHashTable hashnulls; /* hash table for rows with null(s) */
 } SubPlanState;
 
 /* ----------------
index d9ea05994dd326a46703cf02676cfd0cafdef1cb..b187c98fdc735fdcce51bd7ca149136198a1fd87 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.76 2003/01/09 20:50:53 tgl Exp $
+ * $Id: primnodes.h,v 1.77 2003/01/10 21:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -381,25 +381,18 @@ typedef struct BoolExpr
  * it must be replaced in the expression tree by a SubPlan node during
  * planning.
  *
- * NOTE: in the raw output of gram.y, lefthand contains a list of (raw)
- * expressions, and oper contains a single A_Expr (not a list!) containing
- * the string name of the operator, but no arguments.  Also, subselect is
- * a raw parsetree.  During parse analysis, the parser transforms the
+ * NOTE: in the raw output of gram.y, lefthand contains a list of raw
+ * expressions; useOr and operOids are not filled in yet.  Also, subselect
+ * is a raw parsetree.  During parse analysis, the parser transforms the
  * lefthand expression list using normal expression transformation rules.
- * It replaces oper with a list of OpExpr nodes, one per lefthand expression.
- * These nodes represent the parser's resolution of exactly which operator
- * to apply to each pair of lefthand and targetlist expressions.  However,
- * we have not constructed complete Expr trees for these operations yet:
- * the args fields of the OpExpr nodes are NIL.  And subselect is transformed
- * to a Query.  This is the representation seen in saved rules and in the
- * rewriter.
- *
- * In EXISTS and EXPR SubLinks, both lefthand and oper are unused and are
- * always NIL. useOr is not significant either for these sublink types.
- *
- * The operIsEquals field is TRUE when the combining operator was written as
- * "=" --- if the subLinkType is ANY_SUBLINK, this means the operation is
- * equivalent to "IN".  This case allows special optimizations to be used.
+ * It fills operOids with the OIDs representing the specific operator(s)
+ * to apply to each pair of lefthand and targetlist expressions.
+ * And subselect is transformed to a Query.  This is the representation
+ * seen in saved rules and in the rewriter.
+ *
+ * In EXISTS and EXPR SubLinks, lefthand, operName, and operOids are unused
+ * and are always NIL.  useOr is not significant either for these sublink
+ * types.
  * ----------------
  */
 typedef enum SubLinkType
@@ -412,13 +405,12 @@ typedef struct SubLink
 {
    Expr        xpr;
    SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
-   bool        operIsEquals;   /* TRUE if combining operator is "=" */
    bool        useOr;          /* TRUE to combine column results with
                                 * "OR" not "AND" */
    List       *lefthand;       /* list of outer-query expressions on the
                                 * left */
-   List       *oper;           /* list of arg-less OpExpr nodes for
-                                * combining operators */
+   List       *operName;       /* originally specified operator name */
+   List       *operOids;       /* OIDs of actual combining operators */
    Node       *subselect;      /* subselect as Query* or parsetree */
 } SubLink;
 
@@ -427,15 +419,16 @@ typedef struct SubLink
  *
  * The planner replaces SubLink nodes in expression trees with SubPlan
  * nodes after it has finished planning the subquery.  SubPlan contains
- * a sub-plantree and rtable instead of a sub-Query.  Its "oper" field
- * corresponds to the original SubLink's oper list, but has been expanded
- * into valid executable expressions representing the application of the
- * combining operator(s) to the lefthand expressions and values from the
- * inner targetlist.  The original lefthand expressions now appear as
- * left-hand arguments of the OpExpr nodes, while the inner targetlist items
- * are represented by PARAM_EXEC Param nodes.  (Note: if the sub-select
- * becomes an InitPlan rather than a SubPlan, the rebuilt oper list is
- * part of the outer plan tree and so is not stored in the oper field.)
+ * a sub-plantree and rtable instead of a sub-Query.
+ *
+ * In an ordinary subplan, "exprs" points to a list of executable expressions
+ * (OpExpr trees) for the combining operators; their left-hand arguments are
+ * the original lefthand expressions, and their right-hand arguments are
+ * PARAM_EXEC Param nodes representing the outputs of the sub-select.
+ * (NOTE: runtime coercion functions may be inserted as well.)  But if the
+ * sub-select becomes an initplan rather than a subplan, these executable
+ * expressions are part of the outer plan's expression tree (and the SubPlan
+ * node itself is not).  In this case "exprs" is NIL to avoid duplication.
  *
  * The planner also derives lists of the values that need to be passed into
  * and out of the subplan.  Input values are represented as a list "args" of
@@ -444,7 +437,7 @@ typedef struct SubLink
  * The values are assigned to the global PARAM_EXEC params indexed by parParam
  * (the parParam and args lists must have the same length).  setParam is a
  * list of the PARAM_EXEC params that are computed by the sub-select, if it
- * is an initPlan.
+ * is an initplan.
  */
 typedef struct SubPlan
 {
@@ -453,8 +446,9 @@ typedef struct SubPlan
    SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
    bool        useOr;          /* TRUE to combine column results with
                                 * "OR" not "AND" */
-   List       *oper;           /* list of executable expressions for
-                                * combining operators (with arguments) */
+   /* The combining operators, transformed to executable expressions: */
+   List       *exprs;          /* list of OpExpr expression trees */
+   List       *paramIds;       /* IDs of Params embedded in the above */
    /* The subselect, transformed to a Plan: */
    struct Plan *plan;          /* subselect plan itself */
    int         plan_id;        /* dummy thing because of we haven't equal
@@ -462,10 +456,16 @@ typedef struct SubPlan
                                 * could put *plan itself somewhere else
                                 * (TopPlan node ?)... */
    List       *rtable;         /* range table for subselect */
+   /* Information about execution strategy: */
+   bool        useHashTable;   /* TRUE to store subselect output in a hash
+                                * table (implies we are doing "IN") */
+   bool        unknownEqFalse; /* TRUE if it's okay to return FALSE when
+                                * the spec result is UNKNOWN; this allows
+                                * much simpler handling of null values */
    /* Information for passing params into and out of the subselect: */
    /* setParam and parParam are lists of integers (param IDs) */
-   List       *setParam;       /* non-correlated EXPR & EXISTS subqueries
-                                * have to set some Params for paren Plan */
+   List       *setParam;       /* initplan subqueries have to set these
+                                * Params for parent plan */
    List       *parParam;       /* indices of input Params from parent plan */
    List       *args;           /* exprs to pass as parParam values */
 } SubPlan;