Fix IOS planning when only some index columns can return an attribute.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 1 Mar 2018 20:35:03 +0000 (15:35 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 1 Mar 2018 20:35:03 +0000 (15:35 -0500)
Since 9.5, it's possible that some but not all columns of an index
support returning the indexed value for index-only scans.  If the
same indexed column appears in index columns that behave both ways,
check_index_only() supposed that it'd be OK to do an index-only scan
testing that column; but that fails if we have to recheck the indexed
condition on one of the columns that doesn't support this.

In principle we could make this work by remapping the recheck expressions
to pull the value from a column that does support returning the indexed
value.  But such cases are so weird and rare that, at least for now,
it doesn't seem worth the trouble.  Instead, just teach check_index_only
that a value is returnable only if all the index columns containing it
are returnable, rather than any of them.

Per report from David Pereiro Lagares.  Back-patch to 9.5 where the
possibility of this situation appeared.

Kyotaro Horiguchi

Discussion: https://postgr.es/m/1516210494.1798.16.camel@nlpgo.com

contrib/btree_gist/expected/inet.out
contrib/btree_gist/sql/inet.sql
src/backend/optimizer/path/indxpath.c

index 721a78e701ba526be65d6bc8de9adf3988e29016..905f55d740beed5db045d1f48dd09663906a28d4 100644 (file)
@@ -64,3 +64,42 @@ SELECT count(*) FROM inettmp WHERE a >  '89.225.196.191'::inet;
    386
 (1 row)
 
+VACUUM inettmp;
+-- gist_inet_ops lacks a fetch function, so this should not be index-only scan
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on inettmp
+         Recheck Cond: (a = '89.225.196.191'::inet)
+         ->  Bitmap Index Scan on inetidx
+               Index Cond: (a = '89.225.196.191'::inet)
+(5 rows)
+
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+ count 
+-------
+     1
+(1 row)
+
+DROP INDEX inetidx;
+CREATE INDEX ON inettmp USING gist (a gist_inet_ops, a inet_ops);
+-- likewise here (checks for core planner bug)
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on inettmp
+         Recheck Cond: (a = '89.225.196.191'::inet)
+         ->  Bitmap Index Scan on inettmp_a_a1_idx
+               Index Cond: (a = '89.225.196.191'::inet)
+(5 rows)
+
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+ count 
+-------
+     1
+(1 row)
+
index 328846c0a38e50a23b93798af88133316ffdba2d..08952f2c4491950ab101d22c44d6a120667ac638 100644 (file)
@@ -29,3 +29,21 @@ SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
 SELECT count(*) FROM inettmp WHERE a >= '89.225.196.191'::inet;
 
 SELECT count(*) FROM inettmp WHERE a >  '89.225.196.191'::inet;
+
+VACUUM inettmp;
+
+-- gist_inet_ops lacks a fetch function, so this should not be index-only scan
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+
+DROP INDEX inetidx;
+
+CREATE INDEX ON inettmp USING gist (a gist_inet_ops, a inet_ops);
+
+-- likewise here (checks for core planner bug)
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
+
+SELECT count(*) FROM inettmp WHERE a  = '89.225.196.191'::inet;
index 2952bfb7c226507ffa7b9dcf15c77831983ca549..47a01b3ad0a12b62e911435f2cb7e7f3f8497cbd 100644 (file)
@@ -1791,6 +1791,7 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
        bool            result;
        Bitmapset  *attrs_used = NULL;
        Bitmapset  *index_canreturn_attrs = NULL;
+       Bitmapset  *index_cannotreturn_attrs = NULL;
        ListCell   *lc;
        int                     i;
 
@@ -1830,7 +1831,11 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 
        /*
         * Construct a bitmapset of columns that the index can return back in an
-        * index-only scan.
+        * index-only scan.  If there are multiple index columns containing the
+        * same attribute, all of them must be capable of returning the value,
+        * since we might recheck operators on any of them.  (Potentially we could
+        * be smarter about that, but it's such a weird situation that it doesn't
+        * seem worth spending a lot of sweat on.)
         */
        for (i = 0; i < index->ncolumns; i++)
        {
@@ -1847,13 +1852,21 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
                        index_canreturn_attrs =
                                bms_add_member(index_canreturn_attrs,
                                                           attno - FirstLowInvalidHeapAttributeNumber);
+               else
+                       index_cannotreturn_attrs =
+                               bms_add_member(index_cannotreturn_attrs,
+                                                          attno - FirstLowInvalidHeapAttributeNumber);
        }
 
+       index_canreturn_attrs = bms_del_members(index_canreturn_attrs,
+                                                                                       index_cannotreturn_attrs);
+
        /* Do we have all the necessary attributes? */
        result = bms_is_subset(attrs_used, index_canreturn_attrs);
 
        bms_free(attrs_used);
        bms_free(index_canreturn_attrs);
+       bms_free(index_cannotreturn_attrs);
 
        return result;
 }