mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-15 10:57:02 +08:00
Fix optimizer to not try to push WHERE clauses down into a sub-SELECT that
has a DISTINCT ON clause, per bug report from Anthony Wood. While at it, improve the DISTINCT-ON-clause recognizer routine to not be fooled by out- of-order DISTINCT lists. Also, back-patch earlier fix to not push down into sub-SELECT with LIMIT.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.72 2001/03/22 03:59:34 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.72.2.1 2001/07/31 18:39:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -125,11 +125,17 @@ set_base_rel_pathlists(Query *root)
|
||||
* Non-pushed-down clauses will get evaluated as qpquals of
|
||||
* the SubqueryScan node.
|
||||
*
|
||||
* We can't push down into subqueries with LIMIT or DISTINCT ON
|
||||
* clauses, either.
|
||||
*
|
||||
* XXX Are there any cases where we want to make a policy
|
||||
* decision not to push down, because it'd result in a worse
|
||||
* plan?
|
||||
*/
|
||||
if (rte->subquery->setOperations == NULL)
|
||||
if (rte->subquery->setOperations == NULL &&
|
||||
rte->subquery->limitOffset == NULL &&
|
||||
rte->subquery->limitCount == NULL &&
|
||||
!has_distinct_on_clause(rte->subquery))
|
||||
{
|
||||
/* OK to consider pushing down individual quals */
|
||||
List *upperrestrictlist = NIL;
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84 2001/03/27 17:12:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84.2.1 2001/07/31 18:39:12 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -729,6 +729,58 @@ pull_constant_clauses(List *quals, List **constantQual)
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Tests on clauses of queries
|
||||
*
|
||||
* Possibly this code should go someplace else, since this isn't quite the
|
||||
* same meaning of "clause" as is used elsewhere in this module. But I can't
|
||||
* think of a better place for it...
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Test whether a query uses DISTINCT ON, ie, has a distinct-list that is
|
||||
* just a subset of the output columns.
|
||||
*/
|
||||
bool
|
||||
has_distinct_on_clause(Query *query)
|
||||
{
|
||||
List *targetList;
|
||||
|
||||
/* Is there a DISTINCT clause at all? */
|
||||
if (query->distinctClause == NIL)
|
||||
return false;
|
||||
/*
|
||||
* If the DISTINCT list contains all the nonjunk targetlist items,
|
||||
* then it's a simple DISTINCT, else it's DISTINCT ON. We do not
|
||||
* require the lists to be in the same order (since the parser may
|
||||
* have adjusted the DISTINCT clause ordering to agree with ORDER BY).
|
||||
*/
|
||||
foreach(targetList, query->targetList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(targetList);
|
||||
Index ressortgroupref;
|
||||
List *distinctClause;
|
||||
|
||||
if (tle->resdom->resjunk)
|
||||
continue;
|
||||
ressortgroupref = tle->resdom->ressortgroupref;
|
||||
if (ressortgroupref == 0)
|
||||
return true; /* definitely not in DISTINCT list */
|
||||
foreach(distinctClause, query->distinctClause)
|
||||
{
|
||||
SortClause *scl = (SortClause *) lfirst(distinctClause);
|
||||
|
||||
if (scl->tleSortGroupRef == ressortgroupref)
|
||||
break; /* found TLE in DISTINCT */
|
||||
}
|
||||
if (distinctClause == NIL)
|
||||
return true; /* this TLE is not in DISTINCT list */
|
||||
}
|
||||
/* It's a simple DISTINCT */
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* General clause-manipulating routines *
|
||||
@ -736,7 +788,7 @@ pull_constant_clauses(List *quals, List **constantQual)
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* clause_relids_vars
|
||||
* clause_get_relids_vars
|
||||
* Retrieves distinct relids and vars appearing within a clause.
|
||||
*
|
||||
* '*relids' is set to an integer list of all distinct "varno"s appearing
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.77 2001/04/18 17:04:24 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.77.2.1 2001/07/31 18:39:12 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -119,7 +119,6 @@ static void get_utility_query_def(Query *query, deparse_context *context);
|
||||
static void get_basic_select_query(Query *query, deparse_context *context);
|
||||
static void get_setop_query(Node *setOp, Query *query,
|
||||
deparse_context *context, bool toplevel);
|
||||
static bool simple_distinct(List *distinctClause, List *targetList);
|
||||
static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
|
||||
bool force_colno,
|
||||
deparse_context *context);
|
||||
@ -1006,9 +1005,7 @@ get_basic_select_query(Query *query, deparse_context *context)
|
||||
/* Add the DISTINCT clause if given */
|
||||
if (query->distinctClause != NIL)
|
||||
{
|
||||
if (simple_distinct(query->distinctClause, query->targetList))
|
||||
appendStringInfo(buf, " DISTINCT");
|
||||
else
|
||||
if (has_distinct_on_clause(query))
|
||||
{
|
||||
appendStringInfo(buf, " DISTINCT ON (");
|
||||
sep = "";
|
||||
@ -1023,6 +1020,8 @@ get_basic_select_query(Query *query, deparse_context *context)
|
||||
}
|
||||
appendStringInfo(buf, ")");
|
||||
}
|
||||
else
|
||||
appendStringInfo(buf, " DISTINCT");
|
||||
}
|
||||
|
||||
/* Then we tell what to select (the targetlist) */
|
||||
@ -1149,34 +1148,6 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect whether a DISTINCT list can be represented as just DISTINCT
|
||||
* or needs DISTINCT ON. It's simple if it contains exactly the nonjunk
|
||||
* targetlist items.
|
||||
*/
|
||||
static bool
|
||||
simple_distinct(List *distinctClause, List *targetList)
|
||||
{
|
||||
while (targetList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(targetList);
|
||||
|
||||
if (!tle->resdom->resjunk)
|
||||
{
|
||||
if (distinctClause == NIL)
|
||||
return false;
|
||||
if (((SortClause *) lfirst(distinctClause))->tleSortGroupRef !=
|
||||
tle->resdom->ressortgroupref)
|
||||
return false;
|
||||
distinctClause = lnext(distinctClause);
|
||||
}
|
||||
targetList = lnext(targetList);
|
||||
}
|
||||
if (distinctClause != NIL)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display a sort/group clause.
|
||||
*/
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: clauses.h,v 1.43 2001/03/22 04:00:53 momjian Exp $
|
||||
* $Id: clauses.h,v 1.43.2.1 2001/07/31 18:39:13 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,6 +59,8 @@ extern bool contain_noncachable_functions(Node *clause);
|
||||
extern bool is_pseudo_constant_clause(Node *clause);
|
||||
extern List *pull_constant_clauses(List *quals, List **constantQual);
|
||||
|
||||
extern bool has_distinct_on_clause(Query *query);
|
||||
|
||||
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
|
||||
extern int NumRelids(Node *clause);
|
||||
extern void get_relattval(Node *clause, int targetrelid,
|
||||
|
||||
Reference in New Issue
Block a user