aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/subselect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/subselect.c')
-rw-r--r--src/backend/optimizer/plan/subselect.c338
1 files changed, 178 insertions, 160 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 5775b0521fb..5efbb10f037 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
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.102 2005/11/26 22:14:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.103 2005/12/28 01:29:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,14 +19,12 @@
#include "nodes/makefuncs.h"
#include "nodes/params.h"
#include "optimizer/clauses.h"
-#include "optimizer/cost.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
-#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
@@ -74,6 +72,12 @@ typedef struct PlannerParamItem
} PlannerParamItem;
+typedef struct convert_testexpr_context
+{
+ int rtindex; /* RT index for Vars, or 0 for Params */
+ List *righthandIds; /* accumulated list of Vars or Param IDs */
+} convert_testexpr_context;
+
typedef struct finalize_primnode_context
{
Bitmapset *paramids; /* Set of PARAM_EXEC paramids found */
@@ -81,10 +85,13 @@ typedef struct finalize_primnode_context
} finalize_primnode_context;
-static List *convert_sublink_opers(List *lefthand, List *operOids,
- List *targetlist, int rtindex,
- List **righthandIds);
+static Node *convert_testexpr(Node *testexpr,
+ int rtindex,
+ List **righthandIds);
+static Node *convert_testexpr_mutator(Node *node,
+ convert_testexpr_context *context);
static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
+static bool hash_ok_operator(OpExpr *expr);
static Node *replace_correlation_vars_mutator(Node *node, void *context);
static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
static Bitmapset *finalize_plan(Plan *plan, List *rtable,
@@ -228,20 +235,20 @@ generate_new_param(Oid paramtype, int32 paramtypmod)
}
/*
- * Convert a bare SubLink (as created by the parser) into a SubPlan.
+ * Convert a SubLink (as created by the parser) into a SubPlan.
*
- * We are given the raw SubLink and the already-processed lefthand argument
- * list (use this instead of the SubLink's own field). We are also told if
+ * We are given the original SubLink and the already-processed testexpr
+ * (use this instead of the SubLink's own field). We are also told if
* this expression appears at top level of a WHERE/HAVING qual.
*
* The result is whatever we need to substitute in place of the SubLink
* node in the executable expression. This will be either the SubPlan
* node (if we have to do the subplan as a subplan), or a Param node
- * representing the result of an InitPlan, or possibly an AND or OR tree
- * containing InitPlan Param nodes.
+ * representing the result of an InitPlan, or a row comparison expression
+ * tree containing InitPlan Param nodes.
*/
static Node *
-make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
+make_subplan(SubLink *slink, Node *testexpr, bool isTopQual)
{
SubPlan *node = makeNode(SubPlan);
Query *subquery = (Query *) (slink->subselect);
@@ -264,7 +271,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
* first tuple will be retrieved. For ALL and ANY subplans, we will be
* able to stop evaluating if the test condition fails, so very often not
* all the tuples will be retrieved; for lack of a better idea, specify
- * 50% retrieval. For EXPR and MULTIEXPR subplans, use default behavior
+ * 50% retrieval. For EXPR and ROWCOMPARE subplans, use default behavior
* (we're only expecting one row out, anyway).
*
* NOTE: if you change these numbers, also change cost_qual_eval_walker()
@@ -300,8 +307,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
* Initialize other fields of the SubPlan node.
*/
node->subLinkType = slink->subLinkType;
- node->useOr = slink->useOr;
- node->exprs = NIL;
+ node->testexpr = NULL;
node->paramIds = NIL;
node->useHashTable = false;
/* At top level of a qual, can treat UNKNOWN the same as FALSE */
@@ -326,11 +332,11 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
/*
* Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or
- * MULTIEXPR types can be used as initPlans. For EXISTS, EXPR, or ARRAY,
+ * ROWCOMPARE types can be used as initPlans. For EXISTS, EXPR, or ARRAY,
* we just produce a Param referring to the result of evaluating the
- * initPlan. For MULTIEXPR, we must build an AND or OR-clause of the
- * individual comparison operators, using the appropriate lefthand side
- * expressions and Params for the initPlan's target items.
+ * initPlan. For ROWCOMPARE, we must modify the testexpr tree to contain
+ * PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted by the
+ * parser.
*/
if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
{
@@ -369,34 +375,30 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
PlannerInitPlan = lappend(PlannerInitPlan, node);
result = (Node *) prm;
}
- else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
+ else if (node->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK)
{
- List *exprs;
-
- /* Convert the lefthand exprs and oper OIDs into executable exprs */
- exprs = convert_sublink_opers(lefthand,
- slink->operOids,
- plan->targetlist,
- 0,
- &node->paramIds);
+ /* Adjust the Params */
+ result = convert_testexpr(testexpr,
+ 0,
+ &node->paramIds);
node->setParam = list_copy(node->paramIds);
PlannerInitPlan = lappend(PlannerInitPlan, node);
/*
- * The executable expressions are returned to become part of the outer
- * plan's expression tree; they are not kept in the initplan node.
+ * The executable expression is returned to become part of the outer
+ * plan's expression tree; it is not kept in the initplan node.
*/
- if (list_length(exprs) > 1)
- result = (Node *) (node->useOr ? make_orclause(exprs) :
- make_andclause(exprs));
- else
- result = (Node *) linitial(exprs);
}
else
{
List *args;
ListCell *l;
+ /* Adjust the Params */
+ node->testexpr = convert_testexpr(testexpr,
+ 0,
+ &node->paramIds);
+
/*
* We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to
* initPlans, even when they are uncorrelated or undirect correlated,
@@ -434,13 +436,6 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
node->plan = plan = materialize_finished_plan(plan);
}
- /* Convert the lefthand exprs and oper OIDs into executable exprs */
- node->exprs = convert_sublink_opers(lefthand,
- slink->operOids,
- plan->targetlist,
- 0,
- &node->paramIds);
-
/*
* Make node->args from parParam.
*/
@@ -465,10 +460,9 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
}
/*
- * 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 or Vars representing the
- * results of the sub-select.
+ * convert_testexpr: convert the testexpr given by the parser into
+ * actually executable form. This entails replacing PARAM_SUBLINK Params
+ * with Params or Vars representing the results of the sub-select:
*
* If rtindex is 0, we build Params to represent the sub-select outputs.
* The paramids of the Params created are returned in the *righthandIds list.
@@ -476,88 +470,82 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
* If rtindex is not 0, we build Vars using that rtindex as varno. Copies
* of the Var nodes are returned in *righthandIds (this is a bit of a type
* cheat, but we can get away with it).
+ *
+ * The given testexpr has already been recursively processed by
+ * process_sublinks_mutator. Hence it can no longer contain any
+ * PARAM_SUBLINK Params for lower SubLink nodes; we can safely assume that
+ * any we find are for our own level of SubLink.
*/
-static List *
-convert_sublink_opers(List *lefthand, List *operOids,
- List *targetlist, int rtindex,
- List **righthandIds)
+static Node *
+convert_testexpr(Node *testexpr,
+ int rtindex,
+ List **righthandIds)
{
- List *result = NIL;
- ListCell *l,
- *lefthand_item,
- *tlist_item;
+ Node *result;
+ convert_testexpr_context context;
- *righthandIds = NIL;
- lefthand_item = list_head(lefthand);
- tlist_item = list_head(targetlist);
+ context.rtindex = rtindex;
+ context.righthandIds = NIL;
+ result = convert_testexpr_mutator(testexpr, &context);
+ *righthandIds = context.righthandIds;
+ return result;
+}
- foreach(l, operOids)
+static Node *
+convert_testexpr_mutator(Node *node,
+ convert_testexpr_context *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Param))
{
- Oid opid = lfirst_oid(l);
- Node *leftop = (Node *) lfirst(lefthand_item);
- TargetEntry *te = (TargetEntry *) lfirst(tlist_item);
- Node *rightop;
- Operator tup;
+ Param *param = (Param *) node;
- Assert(!te->resjunk);
-
- if (rtindex)
+ if (param->paramkind == PARAM_SUBLINK)
{
- /* Make the Var node representing the subplan's result */
- rightop = (Node *) makeVar(rtindex,
- te->resno,
- exprType((Node *) te->expr),
- exprTypmod((Node *) te->expr),
- 0);
-
/*
- * Copy it for caller. NB: we need a copy to avoid having
- * doubly-linked substructure in the modified parse tree.
+ * We expect to encounter the Params in column-number sequence.
+ * We could handle non-sequential order if necessary, but for now
+ * there's no need. (This is also a useful cross-check that we
+ * aren't finding any unexpected Params.)
*/
- *righthandIds = lappend(*righthandIds, copyObject(rightop));
- }
- else
- {
- /* Make the Param node representing the subplan's result */
- Param *prm;
-
- prm = generate_new_param(exprType((Node *) te->expr),
- exprTypmod((Node *) te->expr));
- /* Record its ID */
- *righthandIds = lappend_int(*righthandIds, prm->paramid);
- rightop = (Node *) prm;
- }
-
- /* Look up the operator to pass to make_op_expr */
- tup = SearchSysCache(OPEROID,
- ObjectIdGetDatum(opid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for operator %u", opid);
+ if (param->paramid != list_length(context->righthandIds) + 1)
+ elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);
- /*
- * Make the expression node.
- *
- * Note: we use make_op_expr in case runtime type conversion function
- * calls must be inserted for this operator! (But we are not
- * expecting to have to resolve unknown Params, so it's okay to pass a
- * null pstate.)
- */
- result = lappend(result,
- make_op_expr(NULL,
- tup,
- leftop,
- rightop,
- exprType(leftop),
- exprType((Node *) te->expr)));
-
- ReleaseSysCache(tup);
-
- lefthand_item = lnext(lefthand_item);
- tlist_item = lnext(tlist_item);
+ if (context->rtindex)
+ {
+ /* Make the Var node representing the subplan's result */
+ Var *newvar;
+
+ newvar = makeVar(context->rtindex,
+ param->paramid,
+ param->paramtype,
+ -1,
+ 0);
+ /*
+ * Copy it for caller. NB: we need a copy to avoid having
+ * doubly-linked substructure in the modified parse tree.
+ */
+ context->righthandIds = lappend(context->righthandIds,
+ copyObject(newvar));
+ return (Node *) newvar;
+ }
+ else
+ {
+ /* Make the Param node representing the subplan's result */
+ Param *newparam;
+
+ newparam = generate_new_param(param->paramtype, -1);
+ /* Record its ID */
+ context->righthandIds = lappend_int(context->righthandIds,
+ newparam->paramid);
+ return (Node *) newparam;
+ }
+ }
}
-
- return result;
+ return expression_tree_mutator(node,
+ convert_testexpr_mutator,
+ (void *) context);
}
/*
@@ -573,15 +561,19 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
ListCell *l;
/*
- * 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?
+ * The sublink type must be "= ANY" --- that is, an IN operator. We
+ * expect that the test expression will be either a single OpExpr, or an
+ * AND-clause containing OpExprs. (If it's anything else then the parser
+ * must have determined that the operators have non-equality-like
+ * semantics. In the OpExpr case we can't be sure what the operator's
+ * semantics are like, but the test below for hashability will reject
+ * anything that's not equality.)
*/
if (slink->subLinkType != ANY_SUBLINK)
return false;
- if (list_length(slink->operName) != 1 ||
- strcmp(strVal(linitial(slink->operName)), "=") != 0)
+ if (slink->testexpr == NULL ||
+ (!IsA(slink->testexpr, OpExpr) &&
+ !and_clause(slink->testexpr)))
return false;
/*
@@ -614,26 +606,47 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
* could be relaxed by using two different sets of operators with the hash
* table, but there is no obvious usefulness to that at present.)
*/
- foreach(l, slink->operOids)
+ if (IsA(slink->testexpr, OpExpr))
{
- Oid opid = lfirst_oid(l);
- 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 || optup->oprcom != opid ||
- !func_strict(optup->oprcode))
- {
- ReleaseSysCache(tup);
+ if (!hash_ok_operator((OpExpr *) slink->testexpr))
return false;
+ }
+ else
+ {
+ foreach(l, ((BoolExpr *) slink->testexpr)->args)
+ {
+ Node *andarg = (Node *) lfirst(l);
+
+ if (!IsA(andarg, OpExpr))
+ return false; /* probably can't happen */
+ if (!hash_ok_operator((OpExpr *) andarg))
+ return false;
}
+ }
+
+ return true;
+}
+
+static bool
+hash_ok_operator(OpExpr *expr)
+{
+ Oid opid = expr->opno;
+ 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 || optup->oprcom != opid ||
+ !func_strict(optup->oprcode))
+ {
ReleaseSysCache(tup);
+ return false;
}
+ ReleaseSysCache(tup);
return true;
}
@@ -659,17 +672,28 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
RangeTblEntry *rte;
RangeTblRef *rtr;
InClauseInfo *ininfo;
- List *exprs;
/*
- * 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.)
+ * The sublink type must be "= ANY" --- that is, an IN operator. We
+ * expect that the test expression will be either a single OpExpr, or an
+ * AND-clause containing OpExprs. (If it's anything else then the parser
+ * must have determined that the operators have non-equality-like
+ * semantics. In the OpExpr case we can't be sure what the operator's
+ * semantics are like, and must check for ourselves.)
*/
if (sublink->subLinkType != ANY_SUBLINK)
return NULL;
- if (list_length(sublink->operName) != 1 ||
- strcmp(strVal(linitial(sublink->operName)), "=") != 0)
+ if (sublink->testexpr && IsA(sublink->testexpr, OpExpr))
+ {
+ List *opclasses;
+ List *opstrats;
+
+ get_op_btree_interpretation(((OpExpr *) sublink->testexpr)->opno,
+ &opclasses, &opstrats);
+ if (!list_member_int(opstrats, ROWCOMPARE_EQ))
+ return NULL;
+ }
+ else if (!and_clause(sublink->testexpr))
return NULL;
/*
@@ -683,16 +707,14 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
* The left-hand expressions must contain some Vars of the current query,
* else it's not gonna be a join.
*/
- left_varnos = pull_varnos((Node *) sublink->lefthand);
+ left_varnos = pull_varnos(sublink->testexpr);
if (bms_is_empty(left_varnos))
return NULL;
/*
- * The left-hand expressions mustn't be volatile. (Perhaps we should test
- * the combining operators, too? We'd only need to point the function
- * directly at the sublink ...)
+ * The combining operators and left-hand expressions mustn't be volatile.
*/
- if (contain_volatile_functions((Node *) sublink->lefthand))
+ if (contain_volatile_functions(sublink->testexpr))
return NULL;
/*
@@ -722,16 +744,13 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
root->in_info_list = lappend(root->in_info_list, ininfo);
/*
- * Build the result qual expressions. As a side effect,
+ * Build the result qual expression. As a side effect,
* ininfo->sub_targetlist is filled with a list of Vars representing the
* subselect outputs.
*/
- exprs = convert_sublink_opers(sublink->lefthand,
- sublink->operOids,
- subselect->targetList,
- rtindex,
- &ininfo->sub_targetlist);
- return (Node *) make_ands_explicit(exprs);
+ return convert_testexpr(sublink->testexpr,
+ rtindex,
+ &ininfo->sub_targetlist);
}
/*
@@ -802,19 +821,18 @@ process_sublinks_mutator(Node *node, bool *isTopQual)
if (IsA(node, SubLink))
{
SubLink *sublink = (SubLink *) node;
- List *lefthand;
+ Node *testexpr;
/*
* First, recursively process the lefthand-side expressions, if any.
*/
locTopQual = false;
- lefthand = (List *)
- process_sublinks_mutator((Node *) sublink->lefthand, &locTopQual);
+ testexpr = process_sublinks_mutator(sublink->testexpr, &locTopQual);
/*
* Now build the SubPlan node and make the expr to return.
*/
- return make_subplan(sublink, lefthand, *isTopQual);
+ return make_subplan(sublink, testexpr, *isTopQual);
}
/*