8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
22
22
#include "optimizer/pathnode.h"
23
23
#include "optimizer/paths.h"
24
24
#include "optimizer/planmain.h"
25
+ #include "optimizer/predtest.h"
25
26
#include "optimizer/subselect.h"
26
27
#include "parser/parse_clause.h"
27
28
#include "parser/parse_expr.h"
@@ -35,6 +36,7 @@ typedef struct
35
36
Oid aggfnoid ; /* pg_proc Oid of the aggregate */
36
37
Oid aggsortop ; /* Oid of its sort operator */
37
38
Expr * target ; /* expression we are aggregating on */
39
+ Expr * notnulltest ; /* expression for "target IS NOT NULL" */
38
40
IndexPath * path ; /* access path for index scan */
39
41
Cost pathcost ; /* estimated cost to fetch first row */
40
42
bool nulls_first ; /* null ordering direction matching index */
@@ -285,8 +287,23 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
285
287
IndexPath * best_path = NULL ;
286
288
Cost best_cost = 0 ;
287
289
bool best_nulls_first = false;
290
+ NullTest * ntest ;
291
+ List * allquals ;
288
292
ListCell * l ;
289
293
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
+
290
307
foreach (l , rel -> indexlist )
291
308
{
292
309
IndexOptInfo * index = (IndexOptInfo * ) lfirst (l );
@@ -302,8 +319,13 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
302
319
if (index -> relam != BTREE_AM_OID )
303
320
continue ;
304
321
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 ))
307
329
continue ;
308
330
309
331
/*
@@ -441,7 +463,6 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
441
463
Plan * iplan ;
442
464
TargetEntry * tle ;
443
465
SortClause * sortcl ;
444
- NullTest * ntest ;
445
466
446
467
/*
447
468
* Generate a suitably modified query. Much of the work here is probably
@@ -487,7 +508,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
487
508
* basic indexscan, but we have to convert it to a Plan and attach a LIMIT
488
509
* node above it.
489
510
*
490
- * Also we must add a "WHERE foo IS NOT NULL" restriction to the
511
+ * Also we must add a "WHERE target IS NOT NULL" restriction to the
491
512
* indexscan, to be sure we don't return a NULL, which'd be contrary to
492
513
* the standard behavior of MIN/MAX. XXX ideally this should be done
493
514
* earlier, so that the selectivity of the restriction could be included
@@ -497,6 +518,9 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
497
518
* The NOT NULL qual has to go on the actual indexscan; create_plan might
498
519
* have stuck a gating Result atop that, if there were any pseudoconstant
499
520
* 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.
500
524
*/
501
525
plan = create_plan (& subroot , (Path * ) info -> path );
502
526
@@ -508,11 +532,9 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
508
532
iplan = plan ;
509
533
Assert (IsA (iplan , IndexScan ));
510
534
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 );
516
538
517
539
plan = (Plan * ) make_limit (plan ,
518
540
subparse -> limitOffset ,
0 commit comments