* subselect.c
* Planning routines for subselects and parameters.
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.43 2000/10/05 19:11:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.58 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/subselect.h"
+#include "parser/parsetree.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
-#include "utils/lsyscache.h"
+#include "utils/syscache.h"
Index PlannerQueryLevel; /* level of current query */
* and update the list elements when we enter or exit a subplan
* recursion level. But we must pay attention not to confuse this
* meaning with the normal meaning of varlevelsup.
+ *
+ * We also need to create Param slots that don't correspond to any outer Var.
+ * For these, we set varno = 0 and varlevelsup = 0, so that they can't
+ * accidentally match an outer Var.
*--------------------
*/
+static void convert_sublink_opers(SubLink *slink, List *targetlist,
+ List **setParams);
+
+
/*
* Create a new entry in the PlannerParamVar list, and return its index.
*
- * var contains the data to be copied, except for varlevelsup which
- * is set from the absolute level value given by varlevel.
+ * var contains the data to use, except for varlevelsup which
+ * is set from the absolute level value given by varlevel. NOTE that
+ * the passed var is scribbled on and placed directly into the list!
+ * Generally, caller should have just created or copied it.
*/
static int
new_param(Var *var, Index varlevel)
{
- Var *paramVar = (Var *) copyObject(var);
+ var->varlevelsup = varlevel;
- paramVar->varlevelsup = varlevel;
-
- PlannerParamVar = lappend(PlannerParamVar, paramVar);
+ PlannerParamVar = lappend(PlannerParamVar, var);
return length(PlannerParamVar) - 1;
}
/*
* If there's already a PlannerParamVar entry for this same Var, just
- * use it. NOTE: in sufficiently complex querytrees, it is
- * possible for the same varno/varlevel to refer to different RTEs in
- * different parts of the parsetree, so that different fields might
- * end up sharing the same Param number. As long as we check the
- * vartype as well, I believe that this sort of aliasing will cause no
- * trouble. The correct field should get stored into the Param slot at
+ * use it. NOTE: in sufficiently complex querytrees, it is possible
+ * for the same varno/varlevel to refer to different RTEs in different
+ * parts of the parsetree, so that different fields might end up
+ * sharing the same Param number. As long as we check the vartype as
+ * well, I believe that this sort of aliasing will cause no trouble.
+ * The correct field should get stored into the Param slot at
* execution in each part of the tree.
*/
i = 0;
if (!ppv)
{
/* Nope, so make a new one */
- i = new_param(var, varlevel);
+ i = new_param((Var *) copyObject(var), varlevel);
}
retval = makeNode(Param);
}
/*
+ * Generate a new Param node that will not conflict with any other.
+ */
+static Param *
+generate_new_param(Oid paramtype, int32 paramtypmod)
+{
+ Var *var = makeVar(0, 0, paramtype, paramtypmod, 0);
+ Param *retval = makeNode(Param);
+
+ retval->paramkind = PARAM_EXEC;
+ retval->paramid = (AttrNumber) new_param(var, 0);
+ retval->paramtype = paramtype;
+
+ return retval;
+}
+
+/*
* Convert a bare SubLink (as created by the parser) into a SubPlan.
*/
static Node *
{
SubPlan *node = makeNode(SubPlan);
Query *subquery = (Query *) (slink->subselect);
+ Oid result_type = exprType((Node *) slink);
double tuple_fraction;
Plan *plan;
List *lst;
elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");
/*
- * Copy the source Query node. This is a quick and dirty kluge to resolve
- * the fact that the parser can generate trees with multiple links to the
- * same sub-Query node, but the planner wants to scribble on the Query.
- * Try to clean this up when we do querytree redesign...
+ * Copy the source Query node. This is a quick and dirty kluge to
+ * resolve the fact that the parser can generate trees with multiple
+ * links to the same sub-Query node, but the planner wants to scribble
+ * on the Query. Try to clean this up when we do querytree redesign...
*/
subquery = (Query *) copyObject(subquery);
*/
node->plan = plan = subquery_planner(subquery, tuple_fraction);
- node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
+ node->plan_id = PlannerPlanId++; /* Assign unique ID to this
+ * SubPlan */
node->rtable = subquery->rtable;
node->sublink = slink;
slink->subselect = NULL; /* cool ?! see error check above! */
/*
- * Make parParam list of params that current query level will pass
- * to this child plan.
+ * Make parParam list of params that current query level will pass to
+ * this child plan.
*/
foreach(lst, plan->extParam)
{
*/
if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
{
- Var *var = makeVar(0, 0, BOOLOID, -1, 0);
- Param *prm = makeNode(Param);
+ Param *prm;
- prm->paramkind = PARAM_EXEC;
- prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
- prm->paramtype = var->vartype;
- pfree(var); /* var is only needed for new_param */
+ prm = generate_new_param(BOOLOID, -1);
node->setParam = lappendi(node->setParam, prm->paramid);
PlannerInitPlan = lappend(PlannerInitPlan, node);
result = (Node *) prm;
else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
{
TargetEntry *te = lfirst(plan->targetlist);
+ Param *prm;
- /* need a var node just to pass to new_param()... */
- Var *var = makeVar(0, 0, te->resdom->restype,
- te->resdom->restypmod, 0);
- Param *prm = makeNode(Param);
-
- prm->paramkind = PARAM_EXEC;
- prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
- prm->paramtype = var->vartype;
- pfree(var); /* var is only needed for new_param */
+ prm = generate_new_param(te->resdom->restype, te->resdom->restypmod);
node->setParam = lappendi(node->setParam, prm->paramid);
PlannerInitPlan = lappend(PlannerInitPlan, node);
result = (Node *) prm;
}
else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
{
- List *newoper = NIL;
- int i = 0;
-
- /*
- * Convert oper list of Opers into a list of Exprs, using lefthand
- * arguments and Params representing inside results.
- */
- foreach(lst, slink->oper)
- {
- Oper *oper = (Oper *) lfirst(lst);
- Node *lefthand = nth(i, slink->lefthand);
- TargetEntry *te = nth(i, plan->targetlist);
-
- /* need a var node just to pass to new_param()... */
- Var *var = makeVar(0, 0, te->resdom->restype,
- te->resdom->restypmod, 0);
- Param *prm = makeNode(Param);
- Operator tup;
- Form_pg_operator opform;
- Node *left,
- *right;
-
- prm->paramkind = PARAM_EXEC;
- prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
- prm->paramtype = var->vartype;
- pfree(var); /* var is only needed for new_param */
-
- Assert(IsA(oper, Oper));
- tup = get_operator_tuple(oper->opno);
- Assert(HeapTupleIsValid(tup));
- opform = (Form_pg_operator) GETSTRUCT(tup);
-
- /*
- * Note: we use make_operand in case runtime type conversion
- * function calls must be inserted for this operator!
- */
- left = make_operand("", lefthand,
- exprType(lefthand), opform->oprleft);
- right = make_operand("", (Node *) prm,
- prm->paramtype, opform->oprright);
- newoper = lappend(newoper,
- make_opclause(oper,
- (Var *) left,
- (Var *) right));
- node->setParam = lappendi(node->setParam, prm->paramid);
- i++;
- }
- slink->oper = newoper;
- slink->lefthand = NIL;
+ convert_sublink_opers(slink, plan->targetlist, &node->setParam);
PlannerInitPlan = lappend(PlannerInitPlan, node);
- if (i > 1)
- result = (Node *) ((slink->useor) ? make_orclause(newoper) :
- make_andclause(newoper));
+ if (length(slink->oper) > 1)
+ result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
+ make_andclause(slink->oper));
else
- result = (Node *) lfirst(newoper);
+ result = (Node *) lfirst(slink->oper);
}
else
{
Expr *expr = makeNode(Expr);
List *args = NIL;
- List *newoper = NIL;
- int i = 0;
/*
* We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
* 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
+ * on the subplan tree, we have to kluge up parameter lists for
+ * the MATERIAL node. Possibly this could be fixed by postponing
+ * SS_finalize_plan processing until setrefs.c is run.
*/
if (node->parParam == NIL)
{
}
break;
case T_Material:
+ case T_FunctionScan:
case T_Sort:
/*
* Don't add another Material node if there's one
- * already, nor if the top node is a Sort, since Sort
- * materializes its output anyway. (I doubt either
- * case can happen in practice for a subplan, but...)
+ * already, nor if the top node is any other type that
+ * materializes its output anyway.
*/
use_material = false;
break;
}
if (use_material)
{
- plan = (Plan *) make_material(plan->targetlist, plan);
- node->plan = plan;
+ Plan *matplan;
+ Path matpath; /* dummy for result of cost_material */
+
+ matplan = (Plan *) make_material(plan->targetlist, plan);
+ /* need to calculate costs */
+ cost_material(&matpath,
+ plan->total_cost,
+ plan->plan_rows,
+ plan->plan_width);
+ matplan->startup_cost = matpath.startup_cost;
+ matplan->total_cost = matpath.total_cost;
+ /* parameter kluge --- see comments above */
+ matplan->extParam = listCopy(plan->extParam);
+ matplan->locParam = listCopy(plan->locParam);
+ node->plan = plan = matplan;
}
}
+ /* Fix the SubLink's oper list */
+ convert_sublink_opers(slink, plan->targetlist, NULL);
+
/*
* Make expression of SUBPLAN type
*/
- expr->typeOid = BOOLOID;/* bogus, but we don't really care */
+ expr->typeOid = result_type;
expr->opType = SUBPLAN_EXPR;
expr->oper = (Node *) node;
}
expr->args = args;
+ result = (Node *) expr;
+ }
+
+ return result;
+}
+
+/*
+ * convert_sublink_opers: convert a SubLink's oper list from the
+ * parser/rewriter format into the executor's format.
+ *
+ * The oper list is initially just a list of Oper nodes. We replace it
+ * with 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. lefthand is then
+ * set to NIL.
+ *
+ * If setParams is not NULL, the paramids of the Params created are added
+ * to the *setParams list.
+ */
+static void
+convert_sublink_opers(SubLink *slink, List *targetlist,
+ List **setParams)
+{
+ List *newoper = NIL;
+ List *leftlist = slink->lefthand;
+ List *lst;
+
+ foreach(lst, slink->oper)
+ {
+ Oper *oper = (Oper *) lfirst(lst);
+ Node *lefthand = lfirst(leftlist);
+ TargetEntry *te = lfirst(targetlist);
+ Param *prm;
+ Operator tup;
+ Form_pg_operator opform;
+ Node *left,
+ *right;
+
+ /* 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);
+
+ /* Look up the operator to check its declared input types */
+ Assert(IsA(oper, Oper));
+ tup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(oper->opno),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for operator %u", oper->opno);
+ opform = (Form_pg_operator) GETSTRUCT(tup);
+
/*
- * Convert oper list of Opers into a list of Exprs, using lefthand
- * arguments and Consts representing inside results.
+ * Make the expression node.
+ *
+ * Note: we use make_operand in case runtime type conversion
+ * function calls must be inserted for this operator!
*/
- foreach(lst, slink->oper)
- {
- Oper *oper = (Oper *) lfirst(lst);
- Node *lefthand = nth(i, slink->lefthand);
- TargetEntry *te = nth(i, plan->targetlist);
- Const *con;
- Operator tup;
- Form_pg_operator opform;
- Node *left,
- *right;
-
- /*
- * XXX really ought to fill in constlen and constbyval
- * correctly, but right now ExecEvalExpr won't look at them...
- */
- con = makeConst(te->resdom->restype, 0, 0, true, 0, 0, 0);
+ left = make_operand(lefthand, exprType(lefthand), opform->oprleft);
+ right = make_operand((Node *) prm, prm->paramtype, opform->oprright);
+ newoper = lappend(newoper,
+ make_opclause(oper,
+ (Var *) left,
+ (Var *) right));
- Assert(IsA(oper, Oper));
- tup = get_operator_tuple(oper->opno);
- Assert(HeapTupleIsValid(tup));
- opform = (Form_pg_operator) GETSTRUCT(tup);
+ ReleaseSysCache(tup);
- /*
- * Note: we use make_operand in case runtime type conversion
- * function calls must be inserted for this operator!
- */
- left = make_operand("", lefthand,
- exprType(lefthand), opform->oprleft);
- right = make_operand("", (Node *) con,
- con->consttype, opform->oprright);
- newoper = lappend(newoper,
- make_opclause(oper,
- (Var *) left,
- (Var *) right));
- i++;
- }
- slink->oper = newoper;
- slink->lefthand = NIL;
- result = (Node *) expr;
+ leftlist = lnext(leftlist);
+ targetlist = lnext(targetlist);
}
- return result;
+ slink->oper = newoper;
+ slink->lefthand = NIL;
}
/*
}
List *
-SS_finalize_plan(Plan *plan)
+SS_finalize_plan(Plan *plan, List *rtable)
{
List *extParam = NIL;
List *locParam = NIL;
&results);
break;
- case T_Append:
- foreach(lst, ((Append *) plan)->appendplans)
- results.paramids = set_unioni(results.paramids,
- SS_finalize_plan((Plan *) lfirst(lst)));
- break;
-
- case T_SubqueryScan:
- results.paramids = set_unioni(results.paramids,
- SS_finalize_plan(((SubqueryScan *) plan)->subplan));
- break;
-
case T_IndexScan:
finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
&results);
*/
break;
+ case T_TidScan:
+ finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+ &results);
+ break;
+
+ case T_SubqueryScan:
+
+ /*
+ * In a SubqueryScan, SS_finalize_plan has already been run on
+ * the subplan by the inner invocation of subquery_planner, so
+ * there's no need to do it again. Instead, just pull out the
+ * subplan's extParams list, which represents the params it
+ * needs from my level and higher levels.
+ */
+ results.paramids = set_unioni(results.paramids,
+ ((SubqueryScan *) plan)->subplan->extParam);
+ break;
+
+ case T_FunctionScan:
+ {
+ RangeTblEntry *rte;
+
+ rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
+ rtable);
+ Assert(rte->rtekind == RTE_FUNCTION);
+ finalize_primnode(rte->funcexpr, &results);
+ }
+ break;
+
+ case T_Append:
+ foreach(lst, ((Append *) plan)->appendplans)
+ results.paramids = set_unioni(results.paramids,
+ SS_finalize_plan((Plan *) lfirst(lst),
+ rtable));
+ break;
+
case T_NestLoop:
finalize_primnode((Node *) ((Join *) plan)->joinqual,
&results);
break;
case T_Hash:
- finalize_primnode(((Hash *) plan)->hashkey,
- &results);
- break;
-
- case T_TidScan:
- finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+ finalize_primnode((Node *) ((Hash *) plan)->hashkeys,
&results);
break;
case T_Sort:
case T_Unique:
case T_SetOp:
+ case T_Limit:
case T_Group:
break;
/* Process left and right subplans, if any */
results.paramids = set_unioni(results.paramids,
- SS_finalize_plan(plan->lefttree));
+ SS_finalize_plan(plan->lefttree,
+ rtable));
results.paramids = set_unioni(results.paramids,
- SS_finalize_plan(plan->righttree));
+ SS_finalize_plan(plan->righttree,
+ rtable));
/* Now we have all the paramids and subplans */