mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Per recent discussion on -hackers, we should sometimes reorder the
columns of the grouping clause to avoid redundant sorts. The optimizer is not currently capable of doing this, so this patch implements a simple hack in the analysis phase (transformGroupClause): if any subset of the GROUP BY clause matches a prefix of the ORDER BY list, that prefix is moved to the front of the GROUP BY clause. This shouldn't change the semantics of the query, and allows a redundant sort to be avoided for queries like "GROUP BY a, b ORDER BY b".
This commit is contained in:
parent
f9520ac110
commit
99114a2473
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.146 2006/03/05 15:58:33 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.147 2006/03/05 21:34:34 neilc Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1296,6 +1296,16 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|||||||
return target_result;
|
return target_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GroupClause *
|
||||||
|
make_group_clause(TargetEntry *tle, List *targetlist, Oid sortop)
|
||||||
|
{
|
||||||
|
GroupClause *result;
|
||||||
|
|
||||||
|
result = makeNode(GroupClause);
|
||||||
|
result->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
|
||||||
|
result->sortop = sortop;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformGroupClause -
|
* transformGroupClause -
|
||||||
@ -1303,71 +1313,105 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|||||||
*
|
*
|
||||||
* GROUP BY items will be added to the targetlist (as resjunk columns)
|
* GROUP BY items will be added to the targetlist (as resjunk columns)
|
||||||
* if not already present, so the targetlist must be passed by reference.
|
* if not already present, so the targetlist must be passed by reference.
|
||||||
|
*
|
||||||
|
* The order of the elements of the grouping clause does not affect
|
||||||
|
* the semantics of the query. However, the optimizer is not currently
|
||||||
|
* smart enough to reorder the grouping clause, so we try to do some
|
||||||
|
* primitive reordering here.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
transformGroupClause(ParseState *pstate, List *grouplist,
|
transformGroupClause(ParseState *pstate, List *grouplist,
|
||||||
List **targetlist, List *sortClause)
|
List **targetlist, List *sortClause)
|
||||||
{
|
{
|
||||||
List *glist = NIL;
|
List *result = NIL;
|
||||||
ListCell *gl;
|
List *tle_list = NIL;
|
||||||
ListCell *sortItem;
|
ListCell *l;
|
||||||
|
|
||||||
sortItem = list_head(sortClause);
|
/* Preprocess the grouping clause, lookup TLEs */
|
||||||
|
foreach (l, grouplist)
|
||||||
foreach(gl, grouplist)
|
|
||||||
{
|
{
|
||||||
TargetEntry *tle;
|
TargetEntry *tle;
|
||||||
Oid restype;
|
Oid restype;
|
||||||
Oid ordering_op;
|
|
||||||
GroupClause *grpcl;
|
|
||||||
|
|
||||||
tle = findTargetlistEntry(pstate, lfirst(gl),
|
tle = findTargetlistEntry(pstate, lfirst(l),
|
||||||
targetlist, GROUP_CLAUSE);
|
targetlist, GROUP_CLAUSE);
|
||||||
|
|
||||||
/* avoid making duplicate grouplist entries */
|
|
||||||
if (targetIsInSortList(tle, glist))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* if tlist item is an UNKNOWN literal, change it to TEXT */
|
/* if tlist item is an UNKNOWN literal, change it to TEXT */
|
||||||
restype = exprType((Node *) tle->expr);
|
restype = exprType((Node *) tle->expr);
|
||||||
|
|
||||||
if (restype == UNKNOWNOID)
|
if (restype == UNKNOWNOID)
|
||||||
{
|
|
||||||
tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,
|
tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,
|
||||||
restype, TEXTOID, -1,
|
restype, TEXTOID, -1,
|
||||||
COERCION_IMPLICIT,
|
COERCION_IMPLICIT,
|
||||||
COERCE_IMPLICIT_CAST);
|
COERCE_IMPLICIT_CAST);
|
||||||
restype = TEXTOID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
tle_list = lappend(tle_list, tle);
|
||||||
* If the GROUP BY clause matches the ORDER BY clause, we want to
|
|
||||||
* adopt the ordering operators from the latter rather than using the
|
|
||||||
* default ops. This allows "GROUP BY foo ORDER BY foo DESC" to be
|
|
||||||
* done with only one sort step. Note we are assuming that any
|
|
||||||
* user-supplied ordering operator will bring equal values together,
|
|
||||||
* which is all that GROUP BY needs.
|
|
||||||
*/
|
|
||||||
if (sortItem &&
|
|
||||||
((SortClause *) lfirst(sortItem))->tleSortGroupRef ==
|
|
||||||
tle->ressortgroupref)
|
|
||||||
{
|
|
||||||
ordering_op = ((SortClause *) lfirst(sortItem))->sortop;
|
|
||||||
sortItem = lnext(sortItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ordering_op = ordering_oper_opid(restype);
|
|
||||||
sortItem = NULL; /* disregard ORDER BY once match fails */
|
|
||||||
}
|
|
||||||
|
|
||||||
grpcl = makeNode(GroupClause);
|
|
||||||
grpcl->tleSortGroupRef = assignSortGroupRef(tle, *targetlist);
|
|
||||||
grpcl->sortop = ordering_op;
|
|
||||||
glist = lappend(glist, grpcl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return glist;
|
/*
|
||||||
|
* Now iterate through the ORDER BY clause. If we find a grouping
|
||||||
|
* element that matches the ORDER BY element, append the grouping
|
||||||
|
* element to the result set immediately. Otherwise, stop
|
||||||
|
* iterating. The effect of this is to look for a prefix of the
|
||||||
|
* ORDER BY list in the grouping clauses, and to move that prefix
|
||||||
|
* to the front of the GROUP BY.
|
||||||
|
*/
|
||||||
|
foreach (l, sortClause)
|
||||||
|
{
|
||||||
|
SortClause *sc = (SortClause *) lfirst(l);
|
||||||
|
ListCell *prev = NULL;
|
||||||
|
ListCell *tl;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
foreach (tl, tle_list)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||||
|
|
||||||
|
if (sc->tleSortGroupRef == tle->ressortgroupref)
|
||||||
|
{
|
||||||
|
GroupClause *gc;
|
||||||
|
|
||||||
|
tle_list = list_delete_cell(tle_list, tl, prev);
|
||||||
|
|
||||||
|
/* Use the sort clause's sorting operator */
|
||||||
|
gc = make_group_clause(tle, *targetlist, sc->sortop);
|
||||||
|
result = lappend(result, gc);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = tl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* As soon as we've failed to match an ORDER BY element, stop */
|
||||||
|
if (!found)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now add any remaining elements of the GROUP BY list in the
|
||||||
|
* order we received them.
|
||||||
|
*
|
||||||
|
* XXX: are there any additional criteria to consider when
|
||||||
|
* ordering grouping clauses?
|
||||||
|
*/
|
||||||
|
foreach(l, tle_list)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
|
GroupClause *gc;
|
||||||
|
Oid sort_op;
|
||||||
|
|
||||||
|
/* avoid making duplicate grouplist entries */
|
||||||
|
if (targetIsInSortList(tle, result))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sort_op = ordering_oper_opid(exprType((Node *) tle->expr));
|
||||||
|
gc = make_group_clause(tle, *targetlist, sort_op);
|
||||||
|
result = lappend(result, gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_free(tle_list);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user