Re: Get more from indices. - Mailing list pgsql-hackers
From | Kyotaro HORIGUCHI |
---|---|
Subject | Re: Get more from indices. |
Date | |
Msg-id | 20131112.174841.74104536.horiguchi.kyotaro@lab.ntt.co.jp Whole thread Raw |
In response to | Re: Get more from indices. (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>) |
Responses |
Re: Get more from indices.
(Peter Eisentraut <peter_e@gmx.net>)
Re: Get more from indices. (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>) |
List | pgsql-hackers |
Hello, this is the revised patch. > Hmm, that sounds quite resonable in general. But the conflation > is already found in grouping_planner to some extent. Although this new patch is not split into unique-index sutff and others, it removes current_pathkeys from grouping_planner, and adds pathkeys and is_unique into struct Plan instead. This eliminates independent and longstanding current_pathkeys variable and calculus involving it from grouping_planner() so it would be made clearer and easier to read, I expect. > - is_unique and pathkeys is added to the type Path. (umm...) > > - create function like pathkeys_satisfies(check_pathkeys, > pathkeys, isuniq) or path_ordered_by(pathkeys, path) as > needed. This was different from my thought. Finally I added plan_is_ordered(plan, path) and some of make_<nodename>() functions take pathkeys and/or uniquely_ordered as parameter and some of others take them from given leftree plan. Plan nodes propagate the attributes in autonomous way so explicit current_pathkeys handling could be thrown away. > - Plan will be set ordered and pathkeys derived from source path > and its process and grouping_planner consults it to deceide > whether to do sort (to hide the currently naked code). > > - Add check for NULLuble columns :-) Now IndexOptInfo has new attribute full_ordered and it is set true if the index does not cover any nullAble columns in get_relation_info(). And expect file of isolation test is modified to fit the change in result plan. Finally, this version became to conflict with the another UNION patch so I detached from it and rebased to current master. regards, -- Kyotaro Horiguchi NTT Open Source Software Center diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 606734a..43be0a5 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -953,7 +953,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, index_pathkeys = build_index_pathkeys(root,index, ForwardScanDirection); useful_pathkeys= truncate_useless_pathkeys(root, rel, - index_pathkeys); + index_pathkeys, + index->full_ordered); orderbyclauses = NIL; orderbyclausecols= NIL; } @@ -1015,7 +1016,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, index_pathkeys = build_index_pathkeys(root,index, BackwardScanDirection); useful_pathkeys= truncate_useless_pathkeys(root, rel, - index_pathkeys); + index_pathkeys, + index->full_ordered); if (useful_pathkeys != NIL) { ipath = create_index_path(root, index, diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 6724996..954d8a8 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -363,6 +363,68 @@ get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,}/* + * path_is_uniquely_ordered + * Return true if the path is apparently uniquely ordered on the pathkeys. + */ +bool +path_is_uniquely_ordered(Path *path, List *pathkeys) +{ + if (pathkeys && path->pathkeys && + IsA(path, IndexPath) && + ((IndexPath*)path)->indexinfo->full_ordered && + (pathkeys_contained_in(path->pathkeys, pathkeys))) + { + return true; + } + + return false; +} + + +/* + * path_is_ordered + * Return true if the path is apparently ordered on the pathkeys. The given + * path might be modified so that it will be recognized later as sufficiently + * ordered. + */ +bool +path_is_ordered(Path *path, List *pathkeys) +{ + if (pathkeys_contained_in(pathkeys, path->pathkeys)) + return true; + + /* path is obviously ordered when uniquely ordered */ + if (path_is_uniquely_ordered(path, pathkeys)) + return true; + + /* + * MergeAppendPath's pathkeys can be replaced by arbitrary pathkeys when + * all subpaths are uniquely ordered on the target pathkeys. + */ + if (IsA(path, MergeAppendPath)) + { + ListCell *lc; + MergeAppendPath *mpath = (MergeAppendPath *)path; + + foreach (lc, mpath->subpaths) + { + Path *subpath = (Path *) lfirst(lc); + if (!path_is_uniquely_ordered(subpath, pathkeys)) break; + } + if (!lc) + { + /* + * This MergeAppendPath is sufficiently ordered on the target + * pathkeys. Reflect that on this path. + */ + mpath->path.pathkeys = pathkeys; + return true; + } + } + return false; +} + +/* * get_cheapest_fractional_path_for_pathkeys * Find the cheapest path (for retrieving a specified fraction of all* the tuples) that satisfies the given pathkeys and parameterization. @@ -397,7 +459,7 @@ get_cheapest_fractional_path_for_pathkeys(List *paths, compare_fractional_path_costs(matched_path,path, fraction) <= 0) continue; - if (pathkeys_contained_in(pathkeys, path->pathkeys) && + if (path_is_ordered(path, pathkeys) && bms_is_subset(PATH_REQ_OUTER(path), required_outer)) matched_path = path; } @@ -733,7 +795,7 @@ build_join_pathkeys(PlannerInfo *root, * contain pathkeys that were useful for forming this joinrelbut are * uninteresting to higher levels. */ - return truncate_useless_pathkeys(root, joinrel, outer_pathkeys); + return truncate_useless_pathkeys(root, joinrel, outer_pathkeys, false);}/**************************************************************************** @@ -1378,9 +1440,14 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey) * Unlike merge pathkeys, this is an all-or-nothingaffair: it does us * no good to order by just the first key(s) of the requested ordering. * So the result isalways either 0 or list_length(root->query_pathkeys). + + * pk_full_ordered can be true when pathkeys are strictly unique key (which + * should not contain no NULLable column). Tuples are sufficiently ordered + * when the pathkeys are the subset of the query_pathkeys for the case. */static int -pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys) +pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys, + bool pk_full_ordered){ if (root->query_pathkeys == NIL) return 0; /* no special ordering requested */ @@ -1394,23 +1461,35 @@ pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys) return list_length(root->query_pathkeys); } + /* + * Given that the pathkeys compose an unique key, they are useful for + * ordering when they are a sublist of query_pathkeys. + */ + if (pk_full_ordered && + pathkeys_contained_in(pathkeys, root->query_pathkeys)) + return list_length(pathkeys); + return 0; /* path ordering not useful */}/* * truncate_useless_pathkeys * Shorten the givenpathkey list to just the useful pathkeys. + * + * When pk_full_ordered is true, reconsider counting the uniqueness even if + * the pathkeys are once judged as useless by the general criteria. */List *truncate_useless_pathkeys(PlannerInfo *root, RelOptInfo *rel, - List *pathkeys) + List *pathkeys, + bool pk_full_ordered){ int nuseful; int nuseful2; nuseful = pathkeys_useful_for_merging(root,rel, pathkeys); - nuseful2 = pathkeys_useful_for_ordering(root, pathkeys); + nuseful2 = pathkeys_useful_for_ordering(root, pathkeys, pk_full_ordered); if (nuseful2 > nuseful) nuseful= nuseful2; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 5947e5b..7755cbb 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -98,11 +98,13 @@ static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);static IndexScan *make_indexscan(List*qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig, List *indexorderby, List *indexorderbyorig, + List *pathkeys, bool uniquely_ordered, ScanDirection indexscandir);static IndexOnlyScan *make_indexonlyscan(List*qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual,List *indexorderby, List *indextlist, + List *pathkeys, bool uniquely_ordered, ScanDirection indexscandir);static BitmapIndexScan*make_bitmap_indexscan(Index scanrelid, Oid indexid, List *indexqual, @@ -150,10 +152,10 @@ static MergeJoin *make_mergejoin(List *tlist, bool *mergenullsfirst, Plan*lefttree, Plan *righttree, JoinType jointype); -static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, - AttrNumber *sortColIdx, Oid *sortOperators, +static Sort *make_sort(PlannerInfo *root, Plan *lefttree, List *pathkeys, + int numCols, AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst, - double limit_tuples); + double limit_tuples);static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan*lefttree, List *pathkeys, Relids relids, @@ -750,6 +752,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) plan->qual = NIL; plan->lefttree= NULL; plan->righttree = NULL; + plan->pathkeys = pathkeys; + plan->is_unique = false; /* Compute sort column info, and adjust MergeAppend's tlist as needed */ (void) prepare_sort_from_pathkeys(root,plan, pathkeys, @@ -809,8 +813,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) numsortkeys* sizeof(bool)) == 0); /* Now, insert a Sort node if subplan isn't sufficiently ordered */ - if (!pathkeys_contained_in(pathkeys, subpath->pathkeys)) - subplan = (Plan *) make_sort(root, subplan, numsortkeys, + if (!path_is_ordered(subpath, pathkeys)) + subplan = (Plan *) make_sort(root, subplan, pathkeys, numsortkeys, sortColIdx,sortOperators, collations, nullsFirst, best_path->limit_tuples); @@ -1147,6 +1151,7 @@ create_indexscan_plan(PlannerInfo *root, List *fixed_indexquals; List *fixed_indexorderbys; ListCell *l; + bool uniquely_ordered = false; /* it should be a base rel... */ Assert(baserelid > 0); @@ -1254,6 +1259,9 @@ create_indexscan_plan(PlannerInfo *root, replace_nestloop_params(root, (Node *) indexorderbys); } + uniquely_ordered = + path_is_uniquely_ordered((Path*)best_path, root->query_pathkeys); + /* Finally ready to build the plan node */ if (indexonly) scan_plan = (Scan *) make_indexonlyscan(tlist, @@ -1263,6 +1271,8 @@ create_indexscan_plan(PlannerInfo *root, fixed_indexquals, fixed_indexorderbys, best_path->indexinfo->indextlist, + best_path->path.pathkeys, + uniquely_ordered, best_path->indexscandir); else scan_plan = (Scan *) make_indexscan(tlist, @@ -1273,6 +1283,8 @@ create_indexscan_plan(PlannerInfo *root, stripped_indexquals, fixed_indexorderbys, indexorderbys, + best_path->path.pathkeys, + uniquely_ordered, best_path->indexscandir); copy_path_costsize(&scan_plan->plan, &best_path->path); @@ -3245,6 +3257,8 @@ make_indexscan(List *qptlist, List *indexqualorig, List *indexorderby, List *indexorderbyorig, + List *pathkeys, + bool uniquely_ordered, ScanDirection indexscandir){ IndexScan *node = makeNode(IndexScan); @@ -3255,6 +3269,8 @@ make_indexscan(List *qptlist, plan->qual = qpqual; plan->lefttree = NULL; plan->righttree= NULL; + plan->pathkeys = pathkeys; + plan->is_unique = uniquely_ordered; node->scan.scanrelid = scanrelid; node->indexid = indexid; node->indexqual= indexqual; @@ -3274,6 +3290,8 @@ make_indexonlyscan(List *qptlist, List *indexqual, List *indexorderby, List *indextlist, + List *pathkeys, + bool uniquely_ordered, ScanDirection indexscandir){ IndexOnlyScan *node = makeNode(IndexOnlyScan); @@ -3284,6 +3302,8 @@ make_indexonlyscan(List *qptlist, plan->qual = qpqual; plan->lefttree = NULL; plan->righttree= NULL; + plan->pathkeys = pathkeys; + plan->is_unique = uniquely_ordered; node->scan.scanrelid = scanrelid; node->indexid = indexid; node->indexqual= indexqual; @@ -3753,8 +3773,8 @@ make_mergejoin(List *tlist, * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)*/static Sort * -make_sort(PlannerInfo *root, Plan *lefttree, int numCols, - AttrNumber *sortColIdx, Oid *sortOperators, +make_sort(PlannerInfo *root, Plan *lefttree, List *pathkeys, + int numCols, AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst, double limit_tuples){ @@ -3776,6 +3796,8 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, plan->qual = NIL; plan->lefttree =lefttree; plan->righttree = NULL; + plan->pathkeys = pathkeys; + plan->is_unique = false; node->numCols = numCols; node->sortColIdx = sortColIdx; node->sortOperators = sortOperators; @@ -4125,7 +4147,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, &nullsFirst); /* Now build the Sort node */ - return make_sort(root, lefttree, numsortkeys, + return make_sort(root, lefttree, pathkeys, numsortkeys, sortColIdx, sortOperators, collations, nullsFirst, limit_tuples);} @@ -4147,6 +4169,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) Oid *sortOperators; Oid *collations; bool *nullsFirst; + List *pathkeys; /* Convert list-ish representation to arrays wanted by executor */ numsortkeys = list_length(sortcls); @@ -4168,7 +4191,9 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) numsortkeys++; } - return make_sort(root, lefttree, numsortkeys, + pathkeys = make_pathkeys_for_sortclauses(root, sortcls, sub_tlist); + + return make_sort(root, lefttree, pathkeys, numsortkeys, sortColIdx, sortOperators, collations, nullsFirst, -1.0);} @@ -4199,6 +4224,7 @@ make_sort_from_groupcols(PlannerInfo *root, Oid *sortOperators; Oid *collations; bool *nullsFirst; + List *pathkeys; /* Convert list-ish representation to arrays wanted by executor */ numsortkeys = list_length(groupcls); @@ -4220,10 +4246,17 @@ make_sort_from_groupcols(PlannerInfo *root, sortOperators[numsortkeys] = grpcl->sortop; collations[numsortkeys] = exprCollation((Node *) tle->expr); nullsFirst[numsortkeys] = grpcl->nulls_first; + + /* This tlist should not be bound with any other orderig clause */ + Assert(tle->ressortgroupref == 0); + tle->ressortgroupref = grpcl->tleSortGroupRef; + numsortkeys++; } - return make_sort(root, lefttree, numsortkeys, + pathkeys = make_pathkeys_for_sortclauses(root, groupcls, sub_tlist); + + return make_sort(root, lefttree, pathkeys, numsortkeys, sortColIdx, sortOperators, collations, nullsFirst, -1.0);} @@ -4338,6 +4371,15 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, plan->targetlist = tlist; plan->lefttree= lefttree; plan->righttree = NULL; + /* + * We can safely assume that the lefttree and therefore the result is + * sorted on group pathkeys and unique if given aggstrategy is AGG_SORTED. + */ + if (node->aggstrategy == AGG_SORTED) + plan->pathkeys = root->group_pathkeys; + else + plan->pathkeys = NIL; + plan->is_unique = true; return node;} @@ -4447,6 +4489,12 @@ make_group(PlannerInfo *root, plan->targetlist = tlist; plan->lefttree = lefttree; plan->righttree= NULL; + /* + * We assume that lefttree is ordered on the same pathkeys with that this + * group node wants. + */ + plan->pathkeys = lefttree->pathkeys; + plan->is_unique = true; return node;} @@ -4486,6 +4534,8 @@ make_unique(Plan *lefttree, List *distinctList) plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; + plan->pathkeys = lefttree->pathkeys; + plan->is_unique = true; /* * convert SortGroupClause list into arrays of attr indexes and equality @@ -4717,6 +4767,10 @@ make_result(PlannerInfo *root, plan->qual = NIL; plan->lefttree = subplan; plan->righttree= NULL; + + /* this plan emits zero or one row when subplan is NULL */ + if (subplan == NULL) plan->is_unique = true; + node->resconstantqual = resconstantqual; return node; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d8aa35d..c80556e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1011,6 +1011,19 @@ inheritance_planner(PlannerInfo *root) SS_assign_special_param(root));} +static bool +plan_is_ordered(Plan *plan, List *pathkeys) +{ + if (pathkeys_contained_in(pathkeys, plan->pathkeys)) + return true; + + if (plan->pathkeys && plan->is_unique && + pathkeys_contained_in(plan->pathkeys, pathkeys)) + return true; + + return false; +} +/*-------------------- * grouping_planner * Perform planning steps related to grouping, aggregation, etc. @@ -1039,7 +1052,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) int64 count_est = 0; double limit_tuples = -1.0; Plan *result_plan; - List *current_pathkeys; double dNumGroups = 0; bool use_hashed_distinct = false; bool tested_hashed_distinct = false; @@ -1081,15 +1093,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) &set_sortclauses); /* - * Calculate pathkeys representing the sort order (if any) of the set - * operation's result. We have to do this before overwriting the sort - * key information... - */ - current_pathkeys = make_pathkeys_for_sortclauses(root, - set_sortclauses, - result_plan->targetlist); - - /* * We should not need to call preprocess_targetlist, since we must be * in a SELECT query node. Instead, use the targetlist returned by * plan_set_operations (since this tells whether it returned any @@ -1442,15 +1445,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) tlist, &agg_costs, best_path); - if (result_plan != NULL) - { - /* - * optimize_minmax_aggregates generated the full plan, with the - * right tlist, and it has no sort order. - */ - current_pathkeys = NIL; - } - else + if (result_plan == NULL) { /* * Normal case --- create a plan according to query_planner's @@ -1459,11 +1454,10 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) bool need_sort_for_grouping= false; result_plan = create_plan(root, best_path); - current_pathkeys = best_path->pathkeys; /* Detect if we'll need an explicit sort for grouping */ if (parse->groupClause && !use_hashed_grouping && - !pathkeys_contained_in(root->group_pathkeys, current_pathkeys)) + !plan_is_ordered(result_plan, root->group_pathkeys)) { need_sort_for_grouping= true; @@ -1541,37 +1535,21 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) extract_grouping_ops(parse->groupClause), numGroups, result_plan); - /* Hashed aggregation produces randomly-ordered results */ - current_pathkeys = NIL; } else if (parse->hasAggs) { /*Plain aggregate plan --- sort if needed */ AggStrategy aggstrategy; - if (parse->groupClause) - { - if (need_sort_for_grouping) - { - result_plan = (Plan *) - make_sort_from_groupcols(root, - parse->groupClause, - groupColIdx, - result_plan); - current_pathkeys = root->group_pathkeys; - } - aggstrategy = AGG_SORTED; + aggstrategy = parse->groupClause ? AGG_SORTED : AGG_PLAIN; - /* - * The AGG node will not change the sort ordering of its - * groups, so current_pathkeys describes the result too. - */ - } - else + if (aggstrategy == AGG_SORTED && need_sort_for_grouping) { - aggstrategy = AGG_PLAIN; - /* Result will be only one row anyway; no sort order */ - current_pathkeys = NIL; + result_plan = (Plan *) + make_sort_from_groupcols(root, + parse->groupClause, + groupColIdx, + result_plan); } result_plan = (Plan *) make_agg(root, @@ -1601,7 +1579,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) parse->groupClause, groupColIdx, result_plan); - current_pathkeys = root->group_pathkeys; } result_plan = (Plan *) make_group(root, @@ -1612,7 +1589,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) extract_grouping_ops(parse->groupClause), dNumGroups, result_plan); - /* The Group node won't change sort ordering */ } else if (root->hasHavingQual) { @@ -1722,13 +1698,14 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) result_plan, window_pathkeys, -1.0); - if (!pathkeys_contained_in(window_pathkeys, - current_pathkeys)) - { - /* we do indeed need to sort */ + + /* + * we do indeed need to sort if result_plan is not ordered + * on window_pathkeys + */ + if (!plan_is_ordered(result_plan, window_pathkeys)) result_plan = (Plan *) sort_plan; - current_pathkeys = window_pathkeys; - } + /* In either case, extract the per-column information */ get_column_info_for_window(root,wc, tlist, sort_plan->numCols, @@ -1792,12 +1769,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) long numDistinctRows; /* - * If there was grouping or aggregation, use the current number of - * rows as the estimated number of DISTINCT rows (ie, assume the - * result was already mostly unique). If not, use the number of + * If result_plan emits already distinct'ed rows, use the current + * number of rows as the estimated number of DISTINCT rows (ie, assume + * the result was already mostly unique). If not, use the number of * distinct-groups calculated previously. */ - if (parse->groupClause || root->hasHavingQual || parse->hasAggs) + if (result_plan->is_unique) dNumDistinctRows = result_plan->plan_rows; else dNumDistinctRows= dNumGroups; @@ -1822,7 +1799,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) result_plan->total_cost, result_plan->startup_cost, result_plan->total_cost, - current_pathkeys, + result_plan->pathkeys, dNumDistinctRows); } @@ -1840,8 +1817,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) extract_grouping_ops(parse->distinctClause), numDistinctRows, result_plan); - /* Hashed aggregation produces randomly-ordered results */ - current_pathkeys = NIL; } else { @@ -1864,28 +1839,23 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) needed_pathkeys = root->sort_pathkeys; else needed_pathkeys = root->distinct_pathkeys; - - if (!pathkeys_contained_in(needed_pathkeys, current_pathkeys)) + + if (!plan_is_ordered(result_plan, needed_pathkeys)) { - if (list_length(root->distinct_pathkeys) >= - list_length(root->sort_pathkeys)) - current_pathkeys = root->distinct_pathkeys; - else - { - current_pathkeys = root->sort_pathkeys; - /* Assert checks that parser didn't mess up... */ - Assert(pathkeys_contained_in(root->distinct_pathkeys, - current_pathkeys)); - } - + /* Assert checks that parser didn't mess up... */ + Assert(pathkeys_contained_in(root->distinct_pathkeys, + needed_pathkeys)); + Assert(pathkeys_contained_in(root->sort_pathkeys, + needed_pathkeys)); result_plan = (Plan *) make_sort_from_pathkeys(root, result_plan, - current_pathkeys, + needed_pathkeys, -1.0); } - result_plan = (Plan *) make_unique(result_plan, - parse->distinctClause); + if (!result_plan->is_unique) + result_plan = (Plan *) make_unique(result_plan, + parse->distinctClause); result_plan->plan_rows = dNumDistinctRows; /* The Unique node won't change sort ordering */ } @@ -1897,13 +1867,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ if (parse->sortClause) { - if (!pathkeys_contained_in(root->sort_pathkeys, current_pathkeys)) + if (!plan_is_ordered(result_plan, root->sort_pathkeys)) { result_plan = (Plan *) make_sort_from_pathkeys(root, result_plan, root->sort_pathkeys, limit_tuples); - current_pathkeys = root->sort_pathkeys; } } @@ -1919,11 +1888,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) root->rowMarks, SS_assign_special_param(root)); - /* - * The result can no longer be assumed sorted, since locking might - * cause the sort key columns to be replaced with new values. - */ - current_pathkeys = NIL; } /* @@ -1942,7 +1906,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * Return the actual output orderingin query_pathkeys for possible use by * an outer query level. */ - root->query_pathkeys = current_pathkeys; + root->query_pathkeys = result_plan->pathkeys; return result_plan;} diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 954666c..d81f9e3 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -231,6 +231,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, */ if (info->relam == BTREE_AM_OID) { + bool has_nullable_keycol = false; /* * If it's a btree index, we can useits opfamily OIDs * directly as the sort ordering opfamily OIDs. @@ -244,10 +245,25 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, for (i =0; i < ncolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; + int16 keyattno = index->indkey.values[i]; info->reverse_sort[i] = (opt & INDOPTION_DESC)!= 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; + + if (keyattno > 0) + { + has_nullable_keycol |= + !relation->rd_att->attrs[keyattno - 1]->attnotnull; + } + else if (keyattno != ObjectIdAttributeNumber) + has_nullable_keycol = true; } + + /* Check to see if it is a full-ordered index */ + if (IndexIsValid(index) && + index->indisunique && index->indimmediate && + !has_nullable_keycol) + info->full_ordered = true; } else if (indexRelation->rd_am->amcanorder) { diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 44ea0b7..6f0935c 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -101,7 +101,8 @@ typedef struct Plan */ double plan_rows; /* number of rows plan is expected to emit*/ int plan_width; /* average row width in bytes */ - + List *pathkeys; /* ordered on this pathkeys if any */ + bool is_unique; /* emits unique result */ /* * Common structural data for all Plan types. */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index a2853fb..e09d75a 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -515,6 +515,7 @@ typedef struct IndexOptInfo bool predOK; /* true if predicate matches query */ bool unique; /* true if a unique index */ bool immediate; /* is uniqueness enforcedimmediately? */ + bool full_ordered; /* is fully-ordered? */ bool hypothetical; /* true if index doesn't reallyexist */ bool canreturn; /* can index return IndexTuples? */ bool amcanorderbyop; /* doesAM support order by operator result? */ diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 9ef93c7..9a6c8e0 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -156,6 +156,8 @@ typedef enumextern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);extern bool pathkeys_contained_in(List*keys1, List *keys2); +extern bool path_is_ordered(Path *path, List *pathkeys); +extern bool path_is_uniquely_ordered(Path *path, List *pathkeys);extern Path *get_cheapest_path_for_pathkeys(List *paths,List *pathkeys, Relids required_outer, CostSelector cost_criterion); @@ -190,7 +192,8 @@ extern List *make_inner_pathkeys_for_merge(PlannerInfo *root, List *outer_pathkeys);externList *truncate_useless_pathkeys(PlannerInfo *root, RelOptInfo *rel, - List *pathkeys); + List *pathkeys, + bool pk_full_ordered);extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel);#endif /* PATHS_H */ diff --git a/src/test/isolation/expected/drop-index-concurrently-1.out b/src/test/isolation/expected/drop-index-concurrently-1.out index 75dff56..ab96fa0 100644 --- a/src/test/isolation/expected/drop-index-concurrently-1.out +++ b/src/test/isolation/expected/drop-index-concurrently-1.out @@ -19,10 +19,8 @@ Sortstep explains: EXPLAIN (COSTS OFF) EXECUTE getrow_seq;QUERY PLAN -Sort - Sort Key: id, data - -> Seq Scan on test_dc - Filter: ((data)::text = '34'::text) +Index Scan using test_dc_pkey on test_dc + Filter: ((data)::text = '34'::text)step select2: SELECT * FROM test_dc WHERE data=34 ORDER BY id,data;id data
pgsql-hackers by date: