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
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 */
}
attnum = ((Var *) varinfo->var)->varattno;
+
+ if (!AttrNumberIsForUserDefinedAttr(attnum))
+ continue;
+
if (!bms_is_member(attnum, matched))
newlist = lappend(newlist, varinfo);
}
{d,f} | {"3, 4": 301, "3, 6": 301, "4, 6": 301, "3, 4, 6": 301}
(1 row)
+-- minor improvement, make sure the ctid does not break the matching
+EXPLAIN (COSTS off)
+SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b;
+ QUERY PLAN
+-----------------------------------
+ GroupAggregate
+ Group Key: ctid, a, b
+ -> Sort
+ Sort Key: ctid, a, b
+ -> Seq Scan on ndistinct
+(5 rows)
+
-- Hash Aggregate, thanks to estimates improved by the statistic
EXPLAIN (COSTS off)
SELECT COUNT(*) FROM ndistinct GROUP BY a, b;
SELECT stxkind, stxndistinct
FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
+-- minor improvement, make sure the ctid does not break the matching
+EXPLAIN (COSTS off)
+SELECT COUNT(*) FROM ndistinct GROUP BY ctid, a, b;
+
-- Hash Aggregate, thanks to estimates improved by the statistic
EXPLAIN (COSTS off)
SELECT COUNT(*) FROM ndistinct GROUP BY a, b;