88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.32 2007/04/27 22:05:47 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.33 2007/10/13 00:58:03 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
2222#include "optimizer/pathnode.h"
2323#include "optimizer/paths.h"
2424#include "optimizer/planmain.h"
25+ #include "optimizer/predtest.h"
2526#include "optimizer/subselect.h"
2627#include "parser/parse_clause.h"
2728#include "parser/parse_expr.h"
@@ -35,6 +36,7 @@ typedef struct
3536Oid aggfnoid ;/* pg_proc Oid of the aggregate */
3637Oid aggsortop ;/* Oid of its sort operator */
3738Expr * target ;/* expression we are aggregating on */
39+ Expr * notnulltest ;/* expression for "target IS NOT NULL" */
3840IndexPath * path ;/* access path for index scan */
3941Cost pathcost ;/* estimated cost to fetch first row */
4042bool nulls_first ;/* null ordering direction matching index */
@@ -285,8 +287,23 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
285287IndexPath * best_path = NULL ;
286288Cost best_cost = 0 ;
287289bool best_nulls_first = false;
290+ NullTest * ntest ;
291+ List * allquals ;
288292ListCell * l ;
289293
294+ /* Build "target IS NOT NULL" expression for use below */
295+ ntest = makeNode (NullTest );
296+ ntest -> nulltesttype = IS_NOT_NULL ;
297+ ntest -> arg = copyObject (info -> target );
298+ info -> notnulltest = (Expr * )ntest ;
299+
300+ /*
301+ * Build list of existing restriction clauses plus the notnull test.
302+ * We cheat a bit by not bothering with a RestrictInfo node for the
303+ * notnull test --- predicate_implied_by() won't care.
304+ */
305+ allquals = list_concat (list_make1 (ntest ),rel -> baserestrictinfo );
306+
290307foreach (l ,rel -> indexlist )
291308{
292309IndexOptInfo * index = (IndexOptInfo * )lfirst (l );
@@ -302,8 +319,13 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
302319if (index -> relam != BTREE_AM_OID )
303320continue ;
304321
305- /* Ignore partial indexes that do not match the query */
306- if (index -> indpred != NIL && !index -> predOK )
322+ /*
323+ * Ignore partial indexes that do not match the query --- unless
324+ * their predicates can be proven from the baserestrict list plus
325+ * the IS NOT NULL test. In that case we can use them.
326+ */
327+ if (index -> indpred != NIL && !index -> predOK &&
328+ !predicate_implied_by (index -> indpred ,allquals ))
307329continue ;
308330
309331/*
@@ -441,7 +463,6 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
441463Plan * iplan ;
442464TargetEntry * tle ;
443465SortClause * sortcl ;
444- NullTest * ntest ;
445466
446467/*
447468 * Generate a suitably modified query.Much of the work here is probably
@@ -487,7 +508,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
487508 * basic indexscan, but we have to convert it to a Plan and attach a LIMIT
488509 * node above it.
489510 *
490- * Also we must add a "WHEREfoo IS NOT NULL" restriction to the
511+ * Also we must add a "WHEREtarget IS NOT NULL" restriction to the
491512 * indexscan, to be sure we don't return a NULL, which'd be contrary to
492513 * the standard behavior of MIN/MAX. XXX ideally this should be done
493514 * earlier, so that the selectivity of the restriction could be included
@@ -497,6 +518,9 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
497518 * The NOT NULL qual has to go on the actual indexscan; create_plan might
498519 * have stuck a gating Result atop that, if there were any pseudoconstant
499520 * quals.
521+ *
522+ * We can skip adding the NOT NULL qual if it's redundant with either
523+ * an already-given WHERE condition, or a clause of the index predicate.
500524 */
501525plan = create_plan (& subroot , (Path * )info -> path );
502526
@@ -508,11 +532,9 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
508532iplan = plan ;
509533Assert (IsA (iplan ,IndexScan ));
510534
511- ntest = makeNode (NullTest );
512- ntest -> nulltesttype = IS_NOT_NULL ;
513- ntest -> arg = copyObject (info -> target );
514-
515- iplan -> qual = lcons (ntest ,iplan -> qual );
535+ if (!list_member (iplan -> qual ,info -> notnulltest )&&
536+ !list_member (info -> path -> indexinfo -> indpred ,info -> notnulltest ))
537+ iplan -> qual = lcons (info -> notnulltest ,iplan -> qual );
516538
517539plan = (Plan * )make_limit (plan ,
518540subparse -> limitOffset ,