Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bf2a691

Browse files
committed
Fix extended statistics with partial analyzes
Either because of a previous ALTER TABLE .. SET STATISTICS 0 or because of being invoked with a partial column list, ANALYZE could fail to acquire sufficient data to build extended statistics. Previously, this would draw an ERROR and fail to collect any statistics at all (extended and regular). Change things so that we raise a WARNING instead, and remove a hint that was wrong in half the cases. Reported by: David Rowley Discussion: https://postgr.es/m/CAKJS1f9Kk0NF6Fg7TA=JUXsjpS9kX6NVu27pb5QDCpOYAvb-Og@mail.gmail.com
1 parent 419a23b commit bf2a691

File tree

3 files changed

+60
-31
lines changed

3 files changed

+60
-31
lines changed

src/backend/statistics/extended_stats.c

+54-29
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
#include "catalog/pg_collation.h"
2424
#include "catalog/pg_statistic_ext.h"
2525
#include "nodes/relation.h"
26+
#include "postmaster/autovacuum.h"
2627
#include "statistics/extended_stats_internal.h"
2728
#include "statistics/statistics.h"
2829
#include "utils/builtins.h"
2930
#include "utils/fmgroids.h"
3031
#include "utils/lsyscache.h"
32+
#include "utils/memutils.h"
3133
#include "utils/rel.h"
3234
#include "utils/syscache.h"
3335

@@ -38,14 +40,16 @@
3840
typedef struct StatExtEntry
3941
{
4042
Oid statOid; /* OID of pg_statistic_ext entry */
43+
char *schema; /* statistics schema */
44+
char *name; /* statistics name */
4145
Bitmapset *columns; /* attribute numbers covered by the statistics */
4246
List *types; /* 'char' list of enabled statistic kinds */
4347
} StatExtEntry;
4448

4549

4650
static List *fetch_statentries_for_relation(Relation pg_statext, Oid relid);
4751
static VacAttrStats **lookup_var_attr_stats(Relation rel, Bitmapset *attrs,
48-
int natts, VacAttrStats **vacattrstats);
52+
int nvacatts, VacAttrStats **vacatts);
4953
static void statext_store(Relation pg_stext, Oid relid,
5054
MVNDistinct *ndistinct, MVDependencies *dependencies,
5155
VacAttrStats **stats);
@@ -66,6 +70,12 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
6670
Relation pg_stext;
6771
ListCell *lc;
6872
List *stats;
73+
MemoryContext cxt;
74+
MemoryContext oldcxt;
75+
76+
cxt = AllocSetContextCreate(CurrentMemoryContext, "stats ext",
77+
ALLOCSET_DEFAULT_SIZES);
78+
oldcxt = MemoryContextSwitchTo(cxt);
6979

7080
pg_stext = heap_open(StatisticExtRelationId, RowExclusiveLock);
7181
stats = fetch_statentries_for_relation(pg_stext, RelationGetRelid(onerel));
@@ -78,9 +88,23 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
7888
VacAttrStats **stats;
7989
ListCell *lc2;
8090

81-
/* filter only the interesting vacattrstats records */
91+
/*
92+
* Check if we can build these stats based on the column analyzed.
93+
* If not, report this fact (except in autovacuum) and move on.
94+
*/
8295
stats = lookup_var_attr_stats(onerel, stat->columns,
8396
natts, vacattrstats);
97+
if (!stats && !IsAutoVacuumWorkerProcess())
98+
{
99+
ereport(WARNING,
100+
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
101+
errmsg("extended statistics \"%s.%s\" could not be collected for relation %s.%s",
102+
stat->schema, stat->name,
103+
get_namespace_name(onerel->rd_rel->relnamespace),
104+
RelationGetRelationName(onerel)),
105+
errtable(onerel)));
106+
continue;
107+
}
84108

85109
/* check allowed number of dimensions */
86110
Assert(bms_num_members(stat->columns) >= 2 &&
@@ -104,6 +128,9 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
104128
}
105129

106130
heap_close(pg_stext, RowExclusiveLock);
131+
132+
MemoryContextSwitchTo(oldcxt);
133+
MemoryContextDelete(cxt);
107134
}
108135

109136
/*
@@ -168,6 +195,8 @@ fetch_statentries_for_relation(Relation pg_statext, Oid relid)
168195
entry = palloc0(sizeof(StatExtEntry));
169196
entry->statOid = HeapTupleGetOid(htup);
170197
staForm = (Form_pg_statistic_ext) GETSTRUCT(htup);
198+
entry->schema = get_namespace_name(staForm->stanamespace);
199+
entry->name = pstrdup(NameStr(staForm->staname));
171200
for (i = 0; i < staForm->stakeys.dim1; i++)
172201
{
173202
entry->columns = bms_add_member(entry->columns,
@@ -200,18 +229,19 @@ fetch_statentries_for_relation(Relation pg_statext, Oid relid)
200229
}
201230

202231
/*
203-
* Using 'vacattrstats' of size 'natts' as input data, return a newly built
204-
* VacAttrStats array which includes only the items corresponding to attributes
205-
* indicated by 'attrs'.
232+
* Using 'vacatts' of size 'nvacatts' as input data, return a newly built
233+
* VacAttrStats array which includes only the items corresponding to
234+
* attributes indicated by 'stakeys'. If we don't have all of the per column
235+
* stats available to compute the extended stats, then we return NULL to indicate
236+
* to the caller that the stats should not be built.
206237
*/
207238
static VacAttrStats **
208-
lookup_var_attr_stats(Relation rel, Bitmapset *attrs, int natts,
209-
VacAttrStats **vacattrstats)
239+
lookup_var_attr_stats(Relation rel, Bitmapset *attrs,
240+
int nvacatts, VacAttrStats **vacatts)
210241
{
211242
int i = 0;
212243
int x = -1;
213244
VacAttrStats **stats;
214-
Bitmapset *matched = NULL;
215245

216246
stats = (VacAttrStats **)
217247
palloc(bms_num_members(attrs) * sizeof(VacAttrStats *));
@@ -222,39 +252,34 @@ lookup_var_attr_stats(Relation rel, Bitmapset *attrs, int natts,
222252
int j;
223253

224254
stats[i] = NULL;
225-
for (j = 0; j < natts; j++)
255+
for (j = 0; j < nvacatts; j++)
226256
{
227-
if (x == vacattrstats[j]->tupattnum)
257+
if (x == vacatts[j]->tupattnum)
228258
{
229-
stats[i] = vacattrstats[j];
259+
stats[i] = vacatts[j];
230260
break;
231261
}
232262
}
233263

234264
if (!stats[i])
235-
ereport(ERROR,
236-
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
237-
errmsg("extended statistics could not be collected for column \"%s\" of relation %s.%s",
238-
NameStr(RelationGetDescr(rel)->attrs[x - 1]->attname),
239-
get_namespace_name(rel->rd_rel->relnamespace),
240-
RelationGetRelationName(rel)),
241-
errhint("Consider ALTER TABLE \"%s\".\"%s\" ALTER \"%s\" SET STATISTICS -1",
242-
get_namespace_name(rel->rd_rel->relnamespace),
243-
RelationGetRelationName(rel),
244-
NameStr(RelationGetDescr(rel)->attrs[x - 1]->attname))));
265+
{
266+
/*
267+
* Looks like stats were not gathered for one of the columns
268+
* required. We'll be unable to build the extended stats without
269+
* this column.
270+
*/
271+
pfree(stats);
272+
return NULL;
273+
}
245274

246-
/*
247-
* Check that we found a non-dropped column and that the attnum
248-
* matches.
249-
*/
275+
/*
276+
* Sanity check that the column is not dropped - stats should have
277+
* been removed in this case.
278+
*/
250279
Assert(!stats[i]->attr->attisdropped);
251-
matched = bms_add_member(matched, stats[i]->tupattnum);
252280

253281
i++;
254282
}
255-
if (bms_subset_compare(matched, attrs) != BMS_EQUAL)
256-
elog(ERROR, "could not find all attributes in attribute stats array");
257-
bms_free(matched);
258283

259284
return stats;
260285
}

src/test/regress/expected/stats_ext.out

+4-2
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ ALTER TABLE ab1 ALTER a SET STATISTICS 0;
4040
INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a;
4141
CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1;
4242
ANALYZE ab1;
43-
ERROR: extended statistics could not be collected for column "a" of relation public.ab1
44-
HINT: Consider ALTER TABLE "public"."ab1" ALTER "a" SET STATISTICS -1
43+
WARNING: extended statistics "public.ab1_a_b_stats" could not be collected for relation public.ab1
4544
ALTER TABLE ab1 ALTER a SET STATISTICS -1;
45+
-- partial analyze doesn't build stats either
46+
ANALYZE ab1 (a);
47+
WARNING: extended statistics "public.ab1_a_b_stats" could not be collected for relation public.ab1
4648
ANALYZE ab1;
4749
DROP TABLE ab1;
4850
-- n-distinct tests

src/test/regress/sql/stats_ext.sql

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a;
3535
CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1;
3636
ANALYZE ab1;
3737
ALTER TABLE ab1 ALTER a SET STATISTICS -1;
38+
-- partial analyze doesn't build stats either
39+
ANALYZE ab1 (a);
3840
ANALYZE ab1;
3941
DROP TABLE ab1;
4042

0 commit comments

Comments
 (0)