*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.236 2008/08/02 21:32:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.237 2008/08/03 19:10:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int64 *offset_est, int64 *count_est);
static void preprocess_groupclause(PlannerInfo *root);
static Oid *extract_grouping_ops(List *groupClause);
+static bool grouping_is_sortable(List *groupClause);
+static bool grouping_is_hashable(List *groupClause);
static bool choose_hashed_grouping(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
Path *cheapest_path, Path *sorted_path,
- Oid *groupOperators, double dNumGroups,
- AggClauseCounts *agg_counts);
+ 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,
List *sub_tlist;
List *group_pathkeys;
AttrNumber *groupColIdx = NULL;
- Oid *groupOperators = NULL;
bool need_tlist_eval = true;
QualCost tlist_cost;
Path *cheapest_path;
* DISTINCT and ORDER BY requirements. This should be changed
* someday, but DISTINCT ON is a bit of a problem ...
*/
- root->group_pathkeys =
- make_pathkeys_for_sortclauses(root,
- parse->groupClause,
- tlist,
- false);
+ if (parse->groupClause && grouping_is_sortable(parse->groupClause))
+ root->group_pathkeys =
+ make_pathkeys_for_sortclauses(root,
+ parse->groupClause,
+ tlist,
+ false);
+ else
+ root->group_pathkeys = NIL;
+
if (list_length(parse->distinctClause) > list_length(parse->sortClause))
root->sort_pathkeys =
make_pathkeys_for_sortclauses(root,
/*
* Figure out whether we need a sorted result from query_planner.
*
- * If we have a GROUP BY clause, then we want a result sorted properly
- * for grouping. Otherwise, if there is an ORDER BY clause, we want
- * to sort by the ORDER BY clause. (Note: if we have both, and ORDER
- * BY is a superset of GROUP BY, it would be tempting to request sort
- * by ORDER BY --- but that might just leave us failing to exploit an
- * available sort order at all. Needs more thought...)
+ * If we have a sortable GROUP BY clause, then we want a result sorted
+ * properly for grouping. Otherwise, if there is an ORDER BY clause,
+ * we want to sort by the ORDER BY clause. (Note: if we have both, and
+ * ORDER BY is a superset of GROUP BY, it would be tempting to request
+ * sort by ORDER BY --- but that might just leave us failing to
+ * exploit an available sort order at all. Needs more thought...)
*/
if (root->group_pathkeys)
root->query_pathkeys = root->group_pathkeys;
sort_pathkeys = root->sort_pathkeys;
/*
- * If grouping, extract the grouping operators and decide whether we
- * want to use hashed grouping.
+ * If grouping, decide whether to use sorted or hashed grouping.
*/
if (parse->groupClause)
{
- groupOperators = extract_grouping_ops(parse->groupClause);
- use_hashed_grouping =
- choose_hashed_grouping(root, tuple_fraction, limit_tuples,
- cheapest_path, sorted_path,
- groupOperators, dNumGroups,
- &agg_counts);
+ bool can_hash;
+ bool can_sort;
+
+ /*
+ * Executor doesn't support hashed aggregation with DISTINCT
+ * aggregates. (Doing so would imply storing *all* the input
+ * values in the hash table, which seems like a certain loser.)
+ */
+ can_hash = (agg_counts.numDistinctAggs == 0 &&
+ grouping_is_hashable(parse->groupClause));
+ can_sort = grouping_is_sortable(parse->groupClause);
+ if (can_hash && can_sort)
+ {
+ /* we have a meaningful choice to make ... */
+ use_hashed_grouping =
+ choose_hashed_grouping(root,
+ tuple_fraction, limit_tuples,
+ cheapest_path, sorted_path,
+ dNumGroups, &agg_counts);
+ }
+ else if (can_hash)
+ use_hashed_grouping = true;
+ else if (can_sort)
+ use_hashed_grouping = false;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not implement GROUP BY"),
+ errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
/* Also convert # groups to long int --- but 'ware overflow! */
numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
AGG_HASHED,
numGroupCols,
groupColIdx,
- groupOperators,
+ extract_grouping_ops(parse->groupClause),
numGroups,
agg_counts.numAggs,
result_plan);
aggstrategy,
numGroupCols,
groupColIdx,
- groupOperators,
+ extract_grouping_ops(parse->groupClause),
numGroups,
agg_counts.numAggs,
result_plan);
(List *) parse->havingQual,
numGroupCols,
groupColIdx,
- groupOperators,
+ extract_grouping_ops(parse->groupClause),
dNumGroups,
result_plan);
/* The Group node won't change sort ordering */
* GROUP BY elements, which could match the sort ordering of other
* possible plans (eg an indexscan) and thereby reduce cost. We don't
* bother with that, though. Hashed grouping will frequently win anyway.
+ *
+ * Note: we need no comparable processing of the distinctClause because
+ * the parser already enforced that that matches ORDER BY.
*/
static void
preprocess_groupclause(PlannerInfo *root)
ListCell *sl;
ListCell *gl;
- /* If no ORDER BY, nothing useful to do here anyway */
+ /* If no ORDER BY, nothing useful to do here */
if (parse->sortClause == NIL)
return;
* were able to make a complete match. In other words, we only
* rearrange the GROUP BY list if the result is that one list is a
* prefix of the other --- otherwise there's no possibility of a
- * common sort.
+ * common sort. Also, give up if there are any non-sortable GROUP BY
+ * items, since then there's no hope anyway.
*/
foreach(gl, parse->groupClause)
{
continue; /* it matched an ORDER BY item */
if (partial_match)
return; /* give up, no common sort possible */
+ if (!OidIsValid(gc->sortop))
+ return; /* give up, GROUP BY can't be sorted */
new_groupclause = lappend(new_groupclause, gc);
}
/*
* extract_grouping_ops - make an array of the equality operator OIDs
- * for the GROUP BY clause
+ * for a SortGroupClause list
*/
static Oid *
extract_grouping_ops(List *groupClause)
return groupOperators;
}
+/*
+ * grouping_is_sortable - is it possible to implement grouping list by sorting?
+ *
+ * This is easy since the parser will have included a sortop if one exists.
+ */
+static bool
+grouping_is_sortable(List *groupClause)
+{
+ ListCell *glitem;
+
+ foreach(glitem, groupClause)
+ {
+ SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
+
+ if (!OidIsValid(groupcl->sortop))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * grouping_is_hashable - is it possible to implement grouping list by hashing?
+ *
+ * We assume hashing is OK if the equality operators are marked oprcanhash.
+ * (If there isn't actually a supporting hash function, the executor will
+ * complain at runtime; but this is a misdeclaration of the operator, not
+ * a system bug.)
+ */
+static bool
+grouping_is_hashable(List *groupClause)
+{
+ ListCell *glitem;
+
+ foreach(glitem, groupClause)
+ {
+ SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
+
+ if (!op_hashjoinable(groupcl->eqop))
+ return false;
+ }
+ return true;
+}
+
/*
* choose_hashed_grouping - should we use hashed grouping?
+ *
+ * Note: this is only applied when both alternatives are actually feasible.
*/
static bool
choose_hashed_grouping(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
Path *cheapest_path, Path *sorted_path,
- Oid *groupOperators, double dNumGroups,
- AggClauseCounts *agg_counts)
+ double dNumGroups, AggClauseCounts *agg_counts)
{
int numGroupCols = list_length(root->parse->groupClause);
double cheapest_path_rows;
List *current_pathkeys;
Path hashed_p;
Path sorted_p;
- int i;
- /*
- * Check can't-do-it conditions, including whether the grouping operators
- * 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,
- * which seems like a certain loser.)
- */
+ /* Prefer sorting when enable_hashagg is off */
if (!enable_hashagg)
return false;
- if (agg_counts->numDistinctAggs != 0)
- 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