aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-01-10 21:08:15 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-01-10 21:08:15 +0000
commite69785debfcca308a5999946bbf4cfefd0ab5e3c (patch)
tree067093f426b8034288590a71f538650b3c53d485 /src
parent36ea26793a14d016059de2f1c83a05cf87a8bb92 (diff)
downloadpostgresql-e69785debfcca308a5999946bbf4cfefd0ab5e3c.tar.gz
postgresql-e69785debfcca308a5999946bbf4cfefd0ab5e3c.zip
Further tweaking of parsetree & plantree representation of SubLinks.
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/dependency.c14
-rw-r--r--src/backend/executor/execQual.c8
-rw-r--r--src/backend/executor/nodeSubplan.c43
-rw-r--r--src/backend/nodes/copyfuncs.c11
-rw-r--r--src/backend/nodes/equalfuncs.c11
-rw-r--r--src/backend/nodes/outfuncs.c11
-rw-r--r--src/backend/nodes/readfuncs.c6
-rw-r--r--src/backend/optimizer/plan/setrefs.c39
-rw-r--r--src/backend/optimizer/plan/subselect.c203
-rw-r--r--src/backend/optimizer/util/clauses.c66
-rw-r--r--src/backend/optimizer/util/var.c10
-rw-r--r--src/backend/parser/gram.y46
-rw-r--r--src/backend/parser/parse_expr.c43
-rw-r--r--src/backend/utils/adt/ruleutils.c58
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/nodes/execnodes.h12
-rw-r--r--src/include/nodes/primnodes.h72
17 files changed, 409 insertions, 248 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index bcc92bfab0f..688d4fb114b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -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 */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 971773b1212..49986de2748 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -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;
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index c9a02814bff..40eca6749ec 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -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
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4b326b75224..e260bc65950 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index affa7f48a88..0ef9b3fa220 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 204c00ad674..e7d8fa71ed7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 99afc0438c3..457e04eb9f6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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();
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 5081f9c3401..c76d67b93e5 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -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:
@@ -269,6 +284,28 @@ fix_expr_references_walker(Node *node, void *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,
* by setting the varnos to OUTER or INNER and setting attno values to the
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index f8086d9ab6e..460d5c38835 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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;
}
/*
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 4c87a95c3b1..e38fc46821d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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;
}
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index e4868af11cd..92ff0cd5b4c 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -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,
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index bfb1e427fb4..fd33601cc08 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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; }
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 3701f41dca0..adf45bbeef5 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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);
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index a8679463670..d7474b4d7f7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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:
@@ -3275,6 +3278,29 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
}
/*
+ * 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
*
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 9c3e5906ade..9e62bd4793c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a593957022c..1ce0635c632 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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;
/* ----------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index d9ea05994dd..b187c98fdc7 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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;