diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2007-01-10 18:06:05 +0000 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2007-01-10 18:06:05 +0000 |
| commit | a191a169d6d0b9558da4519e66510c4540204a51 (patch) | |
| tree | cd32b62bc013145015f4932fef1f7687737205b3 /src/backend/optimizer/plan | |
| parent | 5f6d735356c9090d87e184c9322bfe37a165a014 (diff) | |
| download | postgresql-a191a169d6d0b9558da4519e66510c4540204a51.tar.gz | |
Change the planner-to-executor API so that the planner tells the executor
which comparison operators to use for plan nodes involving tuple comparison
(Agg, Group, Unique, SetOp). Formerly the executor looked up the default
equality operator for the datatype, which was really pretty shaky, since it's
possible that the data being fed to the node is sorted according to some
nondefault operator class that could have an incompatible idea of equality.
The planner knows what it has sorted by and therefore can provide the right
equality operator to use. Also, this change moves a couple of catalog lookups
out of the executor and into the planner, which should help startup time for
pre-planned queries by some small amount. Modify the planner to remove some
other cavalier assumptions about always being able to use the default
operators. Also add "nulls first/last" info to the Plan node for a mergejoin
--- neither the executor nor the planner can cope yet, but at least the API is
in place.
Diffstat (limited to 'src/backend/optimizer/plan')
| -rw-r--r-- | src/backend/optimizer/plan/createplan.c | 114 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/planner.c | 92 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/subselect.c | 41 |
3 files changed, 176 insertions, 71 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 8f1d8e81cc..9a8204392a 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.220 2007/01/09 02:14:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.221 2007/01/10 18:06:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -113,7 +113,10 @@ static HashJoin *make_hashjoin(List *tlist, static Hash *make_hash(Plan *lefttree); static MergeJoin *make_mergejoin(List *tlist, List *joinclauses, List *otherclauses, - List *mergeclauses, List *mergefamilies, List *mergestrategies, + List *mergeclauses, + Oid *mergefamilies, + int *mergestrategies, + bool *mergenullsfirst, Plan *lefttree, Plan *righttree, JoinType jointype); static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, @@ -595,6 +598,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) Plan *plan; Plan *subplan; List *uniq_exprs; + List *in_operators; List *newtlist; int nextresno; bool newitems; @@ -626,10 +630,12 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) * To find the correct list of values to unique-ify, we look in the * information saved for IN expressions. If this code is ever used in * other scenarios, some other way of finding what to unique-ify will - * be needed. + * be needed. The IN clause's operators are needed too, since they + * determine what the meaning of "unique" is in this context. *---------- */ uniq_exprs = NIL; /* just to keep compiler quiet */ + in_operators = NIL; foreach(l, root->in_info_list) { InClauseInfo *ininfo = (InClauseInfo *) lfirst(l); @@ -637,6 +643,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) if (bms_equal(ininfo->righthand, best_path->path.parent->relids)) { uniq_exprs = ininfo->sub_targetlist; + in_operators = ininfo->in_operators; break; } } @@ -687,8 +694,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) newtlist = subplan->targetlist; numGroupCols = list_length(uniq_exprs); groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber)); - groupColPos = 0; + groupColPos = 0; foreach(l, uniq_exprs) { Node *uniqexpr = lfirst(l); @@ -703,10 +710,31 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) if (best_path->umethod == UNIQUE_PATH_HASH) { long numGroups; + Oid *groupOperators; numGroups = (long) Min(best_path->rows, (double) LONG_MAX); /* + * Get the (presumed hashable) equality operators for the Agg node + * to use. Normally these are the same as the IN clause operators, + * but if those are cross-type operators then the equality operators + * are the ones for the IN clause operators' RHS datatype. + */ + groupOperators = (Oid *) palloc(numGroupCols * sizeof(Oid)); + groupColPos = 0; + foreach(l, in_operators) + { + Oid in_oper = lfirst_oid(l); + Oid eq_oper; + + eq_oper = get_compatible_hash_operator(in_oper, false); + if (!OidIsValid(eq_oper)) /* shouldn't happen */ + elog(ERROR, "could not find compatible hash operator for operator %u", + in_oper); + groupOperators[groupColPos++] = eq_oper; + } + + /* * Since the Agg node is going to project anyway, we can give it the * minimum output tlist, without any stuff we might have added to the * subplan tlist. @@ -717,6 +745,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) AGG_HASHED, numGroupCols, groupColIdx, + groupOperators, numGroups, 0, subplan); @@ -725,18 +754,29 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) { List *sortList = NIL; - for (groupColPos = 0; groupColPos < numGroupCols; groupColPos++) + /* Create an ORDER BY list to sort the input compatibly */ + groupColPos = 0; + foreach(l, in_operators) { + Oid in_oper = lfirst_oid(l); + Oid sortop; TargetEntry *tle; + SortClause *sortcl; + sortop = get_ordering_op_for_equality_op(in_oper, false); + if (!OidIsValid(sortop)) /* shouldn't happen */ + elog(ERROR, "could not find ordering operator for equality operator %u", + in_oper); tle = get_tle_by_resno(subplan->targetlist, groupColIdx[groupColPos]); Assert(tle != NULL); - sortList = addTargetToSortList(NULL, tle, - sortList, subplan->targetlist, - SORTBY_DEFAULT, - SORTBY_NULLS_DEFAULT, - NIL, false); + sortcl = makeNode(SortClause); + sortcl->tleSortGroupRef = assignSortGroupRef(tle, + subplan->targetlist); + sortcl->sortop = sortop; + sortcl->nulls_first = false; + sortList = lappend(sortList, sortcl); + groupColPos++; } plan = (Plan *) make_sort_from_sortclauses(root, sortList, subplan); plan = (Plan *) make_unique(plan, sortList); @@ -1542,8 +1582,9 @@ create_mergejoin_plan(PlannerInfo *root, joinclauses, otherclauses, mergeclauses, - best_path->path_mergefamilies, - best_path->path_mergestrategies, + best_path->path_mergeFamilies, + best_path->path_mergeStrategies, + best_path->path_mergeNullsFirst, outer_plan, inner_plan, best_path->jpath.jointype); @@ -2335,8 +2376,9 @@ make_mergejoin(List *tlist, List *joinclauses, List *otherclauses, List *mergeclauses, - List *mergefamilies, - List *mergestrategies, + Oid *mergefamilies, + int *mergestrategies, + bool *mergenullsfirst, Plan *lefttree, Plan *righttree, JoinType jointype) @@ -2350,8 +2392,9 @@ make_mergejoin(List *tlist, plan->lefttree = lefttree; plan->righttree = righttree; node->mergeclauses = mergeclauses; - node->mergefamilies = mergefamilies; - node->mergestrategies = mergestrategies; + node->mergeFamilies = mergefamilies; + node->mergeStrategies = mergestrategies; + node->mergeNullsFirst = mergenullsfirst; node->join.jointype = jointype; node->join.joinqual = joinclauses; @@ -2613,7 +2656,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) * This might look like it could be merged with make_sort_from_sortclauses, * but presently we *must* use the grpColIdx[] array to locate sort columns, * because the child plan's tlist is not marked with ressortgroupref info - * appropriate to the grouping node. So, only the sort direction info + * appropriate to the grouping node. So, only the sort ordering info * is used from the GroupClause entries. */ Sort * @@ -2716,7 +2759,7 @@ materialize_finished_plan(Plan *subplan) Agg * make_agg(PlannerInfo *root, List *tlist, List *qual, AggStrategy aggstrategy, - int numGroupCols, AttrNumber *grpColIdx, + int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, long numGroups, int numAggs, Plan *lefttree) { @@ -2728,6 +2771,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, node->aggstrategy = aggstrategy; node->numCols = numGroupCols; node->grpColIdx = grpColIdx; + node->grpOperators = grpOperators; node->numGroups = numGroups; copy_plan_costsize(plan, lefttree); /* only care about copying size */ @@ -2784,6 +2828,7 @@ make_group(PlannerInfo *root, List *qual, int numGroupCols, AttrNumber *grpColIdx, + Oid *grpOperators, double numGroups, Plan *lefttree) { @@ -2794,6 +2839,7 @@ make_group(PlannerInfo *root, node->numCols = numGroupCols; node->grpColIdx = grpColIdx; + node->grpOperators = grpOperators; copy_plan_costsize(plan, lefttree); /* only care about copying size */ cost_group(&group_path, root, @@ -2841,7 +2887,8 @@ make_group(PlannerInfo *root, /* * distinctList is a list of SortClauses, identifying the targetlist items - * that should be considered by the Unique filter. + * that should be considered by the Unique filter. The input path must + * already be sorted accordingly. */ Unique * make_unique(Plan *lefttree, List *distinctList) @@ -2851,6 +2898,7 @@ make_unique(Plan *lefttree, List *distinctList) int numCols = list_length(distinctList); int keyno = 0; AttrNumber *uniqColIdx; + Oid *uniqOperators; ListCell *slitem; copy_plan_costsize(plan, lefttree); @@ -2874,28 +2922,37 @@ make_unique(Plan *lefttree, List *distinctList) plan->righttree = NULL; /* - * convert SortClause list into array of attr indexes, as wanted by exec + * convert SortClause list into arrays of attr indexes and equality + * operators, as wanted by executor */ Assert(numCols > 0); uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); + uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols); foreach(slitem, distinctList) { SortClause *sortcl = (SortClause *) lfirst(slitem); TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist); - uniqColIdx[keyno++] = tle->resno; + uniqColIdx[keyno] = tle->resno; + uniqOperators[keyno] = get_equality_op_for_ordering_op(sortcl->sortop); + if (!OidIsValid(uniqOperators[keyno])) /* shouldn't happen */ + elog(ERROR, "could not find equality operator for ordering operator %u", + sortcl->sortop); + keyno++; } node->numCols = numCols; node->uniqColIdx = uniqColIdx; + node->uniqOperators = uniqOperators; return node; } /* * distinctList is a list of SortClauses, identifying the targetlist items - * that should be considered by the SetOp filter. + * that should be considered by the SetOp filter. The input path must + * already be sorted accordingly. */ SetOp * make_setop(SetOpCmd cmd, Plan *lefttree, @@ -2906,6 +2963,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree, int numCols = list_length(distinctList); int keyno = 0; AttrNumber *dupColIdx; + Oid *dupOperators; ListCell *slitem; copy_plan_costsize(plan, lefttree); @@ -2930,22 +2988,30 @@ make_setop(SetOpCmd cmd, Plan *lefttree, plan->righttree = NULL; /* - * convert SortClause list into array of attr indexes, as wanted by exec + * convert SortClause list into arrays of attr indexes and equality + * operators, as wanted by executor */ Assert(numCols > 0); dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); + dupOperators = (Oid *) palloc(sizeof(Oid) * numCols); foreach(slitem, distinctList) { SortClause *sortcl = (SortClause *) lfirst(slitem); TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist); - dupColIdx[keyno++] = tle->resno; + dupColIdx[keyno] = tle->resno; + dupOperators[keyno] = get_equality_op_for_ordering_op(sortcl->sortop); + if (!OidIsValid(dupOperators[keyno])) /* shouldn't happen */ + elog(ERROR, "could not find equality operator for ordering operator %u", + sortcl->sortop); + keyno++; } node->cmd = cmd; node->numCols = numCols; node->dupColIdx = dupColIdx; + node->dupOperators = dupOperators; node->flagColIdx = flagColIdx; return node; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index a4de2dd36a..5fcfc58bf9 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.210 2007/01/05 22:19:32 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.211 2007/01/10 18:06:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parsetree.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" @@ -62,10 +63,11 @@ static bool is_dummy_plan(Plan *plan); static double preprocess_limit(PlannerInfo *root, double tuple_fraction, int64 *offset_est, int64 *count_est); +static Oid *extract_grouping_ops(List *groupClause); static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, Path *cheapest_path, Path *sorted_path, - double dNumGroups, AggClauseCounts *agg_counts); -static bool hash_safe_grouping(PlannerInfo *root); + Oid *groupOperators, double dNumGroups, + AggClauseCounts *agg_counts); static List *make_subplanTargetList(PlannerInfo *root, List *tlist, AttrNumber **groupColIdx, bool *need_tlist_eval); static void locate_grouping_columns(PlannerInfo *root, @@ -750,6 +752,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) List *sub_tlist; List *group_pathkeys; AttrNumber *groupColIdx = NULL; + Oid *groupOperators = NULL; bool need_tlist_eval = true; QualCost tlist_cost; Path *cheapest_path; @@ -829,14 +832,17 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) sort_pathkeys = root->sort_pathkeys; /* - * If grouping, decide whether we want to use hashed grouping. + * If grouping, extract the grouping operators and decide whether we + * want to use hashed grouping. */ if (parse->groupClause) { + groupOperators = extract_grouping_ops(parse->groupClause); use_hashed_grouping = choose_hashed_grouping(root, tuple_fraction, cheapest_path, sorted_path, - dNumGroups, &agg_counts); + groupOperators, dNumGroups, + &agg_counts); /* Also convert # groups to long int --- but 'ware overflow! */ numGroups = (long) Min(dNumGroups, (double) LONG_MAX); @@ -956,6 +962,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) AGG_HASHED, numGroupCols, groupColIdx, + groupOperators, numGroups, agg_counts.numAggs, result_plan); @@ -999,6 +1006,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) aggstrategy, numGroupCols, groupColIdx, + groupOperators, numGroups, agg_counts.numAggs, result_plan); @@ -1027,6 +1035,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) (List *) parse->havingQual, numGroupCols, groupColIdx, + groupOperators, dNumGroups, result_plan); /* The Group node won't change sort ordering */ @@ -1338,12 +1347,41 @@ preprocess_limit(PlannerInfo *root, double tuple_fraction, } /* + * extract_grouping_ops - make an array of the equality operator OIDs + * for the GROUP BY clause + */ +static Oid * +extract_grouping_ops(List *groupClause) +{ + int numCols = list_length(groupClause); + int colno = 0; + Oid *groupOperators; + ListCell *glitem; + + groupOperators = (Oid *) palloc(sizeof(Oid) * numCols); + + foreach(glitem, groupClause) + { + GroupClause *groupcl = (GroupClause *) lfirst(glitem); + + groupOperators[colno] = get_equality_op_for_ordering_op(groupcl->sortop); + if (!OidIsValid(groupOperators[colno])) /* shouldn't happen */ + elog(ERROR, "could not find equality operator for ordering operator %u", + groupcl->sortop); + colno++; + } + + return groupOperators; +} + +/* * choose_hashed_grouping - should we use hashed grouping? */ static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, Path *cheapest_path, Path *sorted_path, - double dNumGroups, AggClauseCounts *agg_counts) + Oid *groupOperators, double dNumGroups, + AggClauseCounts *agg_counts) { int numGroupCols = list_length(root->parse->groupClause); double cheapest_path_rows; @@ -1352,10 +1390,13 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, List *current_pathkeys; Path hashed_p; Path sorted_p; + int i; /* * Check can't-do-it conditions, including whether the grouping operators - * are hashjoinable. + * are hashjoinable. (We assume hashing is OK if they are marked + * oprcanhash. If there isn't actually a supporting hash function, + * the executor will complain at runtime.) * * Executor doesn't support hashed aggregation with DISTINCT aggregates. * (Doing so would imply storing *all* the input values in the hash table, @@ -1365,8 +1406,11 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, return false; if (agg_counts->numDistinctAggs != 0) return false; - if (!hash_safe_grouping(root)) - return false; + for (i = 0; i < numGroupCols; i++) + { + if (!op_hashjoinable(groupOperators[i])) + return false; + } /* * Don't do it if it doesn't look like the hashtable will fit into @@ -1471,36 +1515,6 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, return false; } -/* - * hash_safe_grouping - are grouping operators hashable? - * - * We assume hashed aggregation will work if the datatype's equality operator - * is marked hashjoinable. - */ -static bool -hash_safe_grouping(PlannerInfo *root) -{ - ListCell *gl; - - foreach(gl, root->parse->groupClause) - { - GroupClause *grpcl = (GroupClause *) lfirst(gl); - TargetEntry *tle = get_sortgroupclause_tle(grpcl, - root->parse->targetList); - Operator optup; - bool oprcanhash; - - optup = equality_oper(exprType((Node *) tle->expr), true); - if (!optup) - return false; - oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash; - ReleaseSysCache(optup); - if (!oprcanhash) - return false; - } - return true; -} - /*--------------- * make_subplanTargetList * Generate appropriate target list when grouping is required. diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index ed76612dc0..7339445e04 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.116 2007/01/05 22:19:32 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.117 2007/01/10 18:06:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -671,11 +671,13 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink) { Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; + List *in_operators; Relids left_varnos; int rtindex; RangeTblEntry *rte; RangeTblRef *rtr; InClauseInfo *ininfo; + Node *result; /* * The sublink type must be "= ANY" --- that is, an IN operator. We @@ -689,15 +691,31 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink) return NULL; if (sublink->testexpr && IsA(sublink->testexpr, OpExpr)) { + Oid opno = ((OpExpr *) sublink->testexpr)->opno; List *opfamilies; List *opstrats; - get_op_btree_interpretation(((OpExpr *) sublink->testexpr)->opno, - &opfamilies, &opstrats); + get_op_btree_interpretation(opno, &opfamilies, &opstrats); if (!list_member_int(opstrats, ROWCOMPARE_EQ)) return NULL; + in_operators = list_make1_oid(opno); + } + else if (and_clause(sublink->testexpr)) + { + ListCell *lc; + + /* OK, but we need to extract the per-column operator OIDs */ + in_operators = NIL; + foreach(lc, ((BoolExpr *) sublink->testexpr)->args) + { + OpExpr *op = (OpExpr *) lfirst(lc); + + if (!IsA(op, OpExpr)) /* probably shouldn't happen */ + return NULL; + in_operators = lappend_oid(in_operators, op->opno); + } } - else if (!and_clause(sublink->testexpr)) + else return NULL; /* @@ -745,16 +763,23 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink) ininfo = makeNode(InClauseInfo); ininfo->lefthand = left_varnos; ininfo->righthand = bms_make_singleton(rtindex); - root->in_info_list = lappend(root->in_info_list, ininfo); + ininfo->in_operators = in_operators; /* * Build the result qual expression. As a side effect, * ininfo->sub_targetlist is filled with a list of Vars representing the * subselect outputs. */ - return convert_testexpr(sublink->testexpr, - rtindex, - &ininfo->sub_targetlist); + result = convert_testexpr(sublink->testexpr, + rtindex, + &ininfo->sub_targetlist); + + Assert(list_length(in_operators) == list_length(ininfo->sub_targetlist)); + + /* Add the completed node to the query's list */ + root->in_info_list = lappend(root->in_info_list, ininfo); + + return result; } /* |
