From: Tom Lane Date: Fri, 10 Jan 2003 21:08:15 +0000 (+0000) Subject: Further tweaking of parsetree & plantree representation of SubLinks. X-Git-Tag: REL9_0_0~15960 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=e69785debfcca308a5999946bbf4cfefd0ab5e3c;p=pg-rex%2Fsyncrep.git 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. --- diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index bcc92bfab0..688d4fb114 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 971773b121..49986de274 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 c9a02814bf..40eca6749e 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 4b326b7522..e260bc6595 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 affa7f48a8..0ef9b3fa22 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 204c00ad67..e7d8fa71ed 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 99afc0438c..457e04eb9f 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 5081f9c340..c76d67b93e 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 f8086d9ab6..460d5c3883 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 4c87a95c3b..e38fc46821 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 e4868af11c..92ff0cd5b4 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 bfb1e427fb..fd33601cc0 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 3701f41dca..adf45bbeef 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 a867946367..d7474b4d7f 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 9c3e5906ad..9e62bd4793 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 a593957022..1ce0635c63 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 d9ea05994d..b187c98fdc 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;