Skip system attributes when applying mvdistinct stats
authorTomas Vondra <tomas.vondra@postgresql.org>
Sat, 16 Nov 2019 00:17:15 +0000 (01:17 +0100)
committerTomas Vondra <tomas.vondra@postgresql.org>
Sat, 16 Nov 2019 00:17:15 +0000 (01:17 +0100)
When estimating number of distinct groups, we failed to ignore system
attributes when matching the group expressions to mvdistinct stats,
causing failures like

  ERROR: negative bitmapset member not allowed

Fix that by simply skipping anything that is not a regular attribute.
Backpatch to PostgreSQL 10, where the extended stats were introduced.

Bug: #16111
Reported-by: Tuomas Leikola
Author: Tomas Vondra
Backpatch-through: 10
Discussion: https://postgr.es/m/16111-687799584c3a7e73@postgresql.org

src/backend/utils/adt/selfuncs.c
src/test/regress/expected/stats_ext.out
src/test/regress/sql/stats_ext.sql

index 35a8995f623829e968a3529022068b27cee8f921..024f325eb0206b3e09d9668843a2788a1be8766f 100644 (file)
@@ -3582,14 +3582,19 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
        foreach(lc, *varinfos)
        {
                GroupVarInfo *varinfo = (GroupVarInfo *) lfirst(lc);
+               AttrNumber      attnum;
 
                Assert(varinfo->rel == rel);
 
-               if (IsA(varinfo->var, Var))
-               {
-                       attnums = bms_add_member(attnums,
-                                                                        ((Var *) varinfo->var)->varattno);
-               }
+               if (!IsA(varinfo->var, Var))
+                       continue;
+
+               attnum = ((Var *) varinfo->var)->varattno;
+
+               if (!AttrNumberIsForUserDefinedAttr(attnum))
+                       continue;
+
+               attnums = bms_add_member(attnums, attnum);
        }
 
        /* look for the ndistinct statistics matching the most vars */
@@ -3669,6 +3674,10 @@ estimate_multivariate_ndistinct(PlannerInfo *root, RelOptInfo *rel,
                        }
 
                        attnum = ((Var *) varinfo->var)->varattno;
+
+                       if (!AttrNumberIsForUserDefinedAttr(attnum))
+                               continue;
+
                        if (!bms_is_member(attnum, matched))
                                newlist = lappend(newlist, varinfo);
                }
index b65228fa07accc98250dfba45a224d2620a242d9..dfbc41c390d3decc3a2fa11a9d2bbcc2cb852a71 100644 (file)
@@ -233,6 +233,13 @@ SELECT s.stxkind, d.stxdndistinct
  {d,f,m} | {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
 (1 row)
 
+-- minor improvement, make sure the ctid does not break the matching
+SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b');
+ estimated | actual 
+-----------+--------
+        11 |   1000
+(1 row)
+
 -- Hash Aggregate, thanks to estimates improved by the statistic
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
  estimated | actual 
index 040ee97a1e51870ae1858292732cbfed6f6148f5..6237fb25c205570f686609dccc41ce3b85b4a9eb 100644 (file)
@@ -167,6 +167,9 @@ SELECT s.stxkind, d.stxdndistinct
  WHERE s.stxrelid = 'ndistinct'::regclass
    AND d.stxoid = s.oid;
 
+-- minor improvement, make sure the ctid does not break the matching
+SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b');
+
 -- Hash Aggregate, thanks to estimates improved by the statistic
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');