Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : * attribute_stats.c
3 : : *
4 : : * PostgreSQL relation attribute statistics manipulation.
5 : : *
6 : : * Code supporting the direct import of relation attribute statistics, similar
7 : : * to what is done by the ANALYZE command.
8 : : *
9 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 : : * Portions Copyright (c) 1994, Regents of the University of California
11 : : *
12 : : * IDENTIFICATION
13 : : * src/backend/statistics/attribute_stats.c
14 : : *
15 : : *-------------------------------------------------------------------------
16 : : */
17 : :
18 : : #include "postgres.h"
19 : :
20 : : #include "access/heapam.h"
21 : : #include "access/relation.h"
22 : : #include "catalog/indexing.h"
23 : : #include "catalog/namespace.h"
24 : : #include "catalog/pg_operator.h"
25 : : #include "nodes/makefuncs.h"
26 : : #include "statistics/statistics.h"
27 : : #include "statistics/stat_utils.h"
28 : : #include "utils/array.h"
29 : : #include "utils/builtins.h"
30 : : #include "utils/fmgroids.h"
31 : : #include "utils/lsyscache.h"
32 : : #include "utils/syscache.h"
33 : :
34 : : /*
35 : : * Positional argument numbers, names, and types for
36 : : * attribute_statistics_update() and pg_restore_attribute_stats().
37 : : */
38 : :
39 : : enum attribute_stats_argnum
40 : : {
41 : : ATTRELSCHEMA_ARG = 0,
42 : : ATTRELNAME_ARG,
43 : : ATTNAME_ARG,
44 : : ATTNUM_ARG,
45 : : INHERITED_ARG,
46 : : NULL_FRAC_ARG,
47 : : AVG_WIDTH_ARG,
48 : : N_DISTINCT_ARG,
49 : : MOST_COMMON_VALS_ARG,
50 : : MOST_COMMON_FREQS_ARG,
51 : : HISTOGRAM_BOUNDS_ARG,
52 : : CORRELATION_ARG,
53 : : MOST_COMMON_ELEMS_ARG,
54 : : MOST_COMMON_ELEM_FREQS_ARG,
55 : : ELEM_COUNT_HISTOGRAM_ARG,
56 : : RANGE_LENGTH_HISTOGRAM_ARG,
57 : : RANGE_EMPTY_FRAC_ARG,
58 : : RANGE_BOUNDS_HISTOGRAM_ARG,
59 : : NUM_ATTRIBUTE_STATS_ARGS
60 : : };
61 : :
62 : : static struct StatsArgInfo attarginfo[] =
63 : : {
64 : : [ATTRELSCHEMA_ARG] = {"schemaname", TEXTOID},
65 : : [ATTRELNAME_ARG] = {"relname", TEXTOID},
66 : : [ATTNAME_ARG] = {"attname", TEXTOID},
67 : : [ATTNUM_ARG] = {"attnum", INT2OID},
68 : : [INHERITED_ARG] = {"inherited", BOOLOID},
69 : : [NULL_FRAC_ARG] = {"null_frac", FLOAT4OID},
70 : : [AVG_WIDTH_ARG] = {"avg_width", INT4OID},
71 : : [N_DISTINCT_ARG] = {"n_distinct", FLOAT4OID},
72 : : [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTOID},
73 : : [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT4ARRAYOID},
74 : : [HISTOGRAM_BOUNDS_ARG] = {"histogram_bounds", TEXTOID},
75 : : [CORRELATION_ARG] = {"correlation", FLOAT4OID},
76 : : [MOST_COMMON_ELEMS_ARG] = {"most_common_elems", TEXTOID},
77 : : [MOST_COMMON_ELEM_FREQS_ARG] = {"most_common_elem_freqs", FLOAT4ARRAYOID},
78 : : [ELEM_COUNT_HISTOGRAM_ARG] = {"elem_count_histogram", FLOAT4ARRAYOID},
79 : : [RANGE_LENGTH_HISTOGRAM_ARG] = {"range_length_histogram", TEXTOID},
80 : : [RANGE_EMPTY_FRAC_ARG] = {"range_empty_frac", FLOAT4OID},
81 : : [RANGE_BOUNDS_HISTOGRAM_ARG] = {"range_bounds_histogram", TEXTOID},
82 : : [NUM_ATTRIBUTE_STATS_ARGS] = {0}
83 : : };
84 : :
85 : : /*
86 : : * Positional argument numbers, names, and types for
87 : : * pg_clear_attribute_stats().
88 : : */
89 : :
90 : : enum clear_attribute_stats_argnum
91 : : {
92 : : C_ATTRELSCHEMA_ARG = 0,
93 : : C_ATTRELNAME_ARG,
94 : : C_ATTNAME_ARG,
95 : : C_INHERITED_ARG,
96 : : C_NUM_ATTRIBUTE_STATS_ARGS
97 : : };
98 : :
99 : : static struct StatsArgInfo cleararginfo[] =
100 : : {
101 : : [C_ATTRELSCHEMA_ARG] = {"schemaname", TEXTOID},
102 : : [C_ATTRELNAME_ARG] = {"relname", TEXTOID},
103 : : [C_ATTNAME_ARG] = {"attname", TEXTOID},
104 : : [C_INHERITED_ARG] = {"inherited", BOOLOID},
105 : : [C_NUM_ATTRIBUTE_STATS_ARGS] = {0}
106 : : };
107 : :
108 : : static bool attribute_statistics_update(FunctionCallInfo fcinfo);
109 : : static bool attribute_statistics_update_internal(Oid reloid,
110 : : const char *attname,
111 : : AttrNumber attnum,
112 : : bool inherited,
113 : : FunctionCallInfo fcinfo);
114 : : static void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
115 : : const Datum *values, const bool *nulls, const bool *replaces);
116 : : static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
117 : :
118 : : /*
119 : : * Insert or Update Attribute Statistics
120 : : *
121 : : * See pg_statistic.h for an explanation of how each statistic kind is
122 : : * stored. Custom statistics kinds are not supported.
123 : : *
124 : : * Depending on the statistics kind, we need to derive information from the
125 : : * attribute for which we're storing the stats. For instance, the MCVs are
126 : : * stored as an anyarray, and the representation of the array needs to store
127 : : * the correct element type, which must be derived from the attribute.
128 : : *
129 : : * Major errors, such as the table not existing, the attribute not existing,
130 : : * or a permissions failure are always reported at ERROR. Other errors, such
131 : : * as a conversion failure on one statistic kind, are reported as a WARNING
132 : : * and other statistic kinds may still be updated.
133 : : */
134 : : static bool
135 : 959 : attribute_statistics_update(FunctionCallInfo fcinfo)
136 : : {
137 : : char *nspname;
138 : : char *relname;
139 : : Oid reloid;
140 : : char *attname;
141 : : AttrNumber attnum;
142 : : bool inherited;
143 : 959 : Oid locked_table = InvalidOid;
144 : :
145 : 959 : stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
146 : 955 : stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
147 : :
148 : 947 : nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
149 : 947 : relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
150 : :
151 [ - + ]: 947 : if (RecoveryInProgress())
152 [ # # ]: 0 : ereport(ERROR,
153 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
154 : : errmsg("recovery is in progress"),
155 : : errhint("Statistics cannot be modified during recovery.")));
156 : :
157 : : /* lock before looking up attribute */
158 : 947 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
159 : : ShareUpdateExclusiveLock, 0,
160 : : RangeVarCallbackForStats, &locked_table);
161 : :
162 : : /* user can specify either attname or attnum, but not both */
163 [ + + ]: 939 : if (!PG_ARGISNULL(ATTNAME_ARG))
164 : : {
165 [ + + ]: 921 : if (!PG_ARGISNULL(ATTNUM_ARG))
166 [ + - ]: 4 : ereport(ERROR,
167 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
168 : : errmsg("cannot specify both \"%s\" and \"%s\"", "attname", "attnum")));
169 : 917 : attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
170 : 917 : attnum = get_attnum(reloid, attname);
171 : : /* note that this test covers attisdropped cases too: */
172 [ + + ]: 917 : if (attnum == InvalidAttrNumber)
173 [ + - ]: 4 : ereport(ERROR,
174 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
175 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
176 : : attname, relname)));
177 : : }
178 [ + + ]: 18 : else if (!PG_ARGISNULL(ATTNUM_ARG))
179 : : {
180 : 10 : attnum = PG_GETARG_INT16(ATTNUM_ARG);
181 : 10 : attname = get_attname(reloid, attnum, true);
182 : : /* annoyingly, get_attname doesn't check attisdropped */
183 [ + - ]: 10 : if (attname == NULL ||
184 [ - + ]: 10 : !SearchSysCacheExistsAttName(reloid, attname))
185 [ # # ]: 0 : ereport(ERROR,
186 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
187 : : errmsg("column %d of relation \"%s\" does not exist",
188 : : attnum, relname)));
189 : : }
190 : : else
191 : : {
192 [ + - ]: 8 : ereport(ERROR,
193 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
194 : : errmsg("must specify either \"%s\" or \"%s\"", "attname", "attnum")));
195 : : attname = NULL; /* keep compiler quiet */
196 : : attnum = 0;
197 : : }
198 : :
199 [ + + ]: 923 : if (attnum < 0)
200 [ + - ]: 4 : ereport(ERROR,
201 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
202 : : errmsg("cannot modify statistics on system column \"%s\"",
203 : : attname)));
204 : :
205 : 919 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
206 : 915 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
207 : :
208 : 915 : return attribute_statistics_update_internal(reloid, attname, attnum,
209 : : inherited, fcinfo);
210 : : }
211 : :
212 : : /*
213 : : * Workhorse function for attribute_statistics_update.
214 : : */
215 : : static bool
216 : 928 : attribute_statistics_update_internal(Oid reloid,
217 : : const char *attname, AttrNumber attnum,
218 : : bool inherited, FunctionCallInfo fcinfo)
219 : : {
220 : : Relation starel;
221 : : Relation targetrel;
222 : : HeapTuple statup;
223 : :
224 : 928 : Oid atttypid = InvalidOid;
225 : : int32 atttypmod;
226 : : TypeCacheEntry *basetypcache;
227 : 928 : Oid atttypcoll = InvalidOid;
228 : 928 : Oid eq_opr = InvalidOid;
229 : 928 : Oid lt_opr = InvalidOid;
230 : :
231 : 928 : Oid elemtypid = InvalidOid;
232 : 928 : Oid elem_eq_opr = InvalidOid;
233 : :
234 : 928 : Oid bounds_typid = InvalidOid;
235 : :
236 : : FmgrInfo array_in_fn;
237 : :
238 [ + + ]: 1373 : bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
239 [ + + ]: 445 : !PG_ARGISNULL(MOST_COMMON_VALS_ARG);
240 : 928 : bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
241 : 928 : bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
242 [ + + ]: 964 : bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
243 [ + + ]: 36 : !PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
244 : 928 : bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
245 : 928 : bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
246 [ + + ]: 976 : bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
247 [ + + ]: 48 : !PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
248 : :
249 : 928 : Datum values[Natts_pg_statistic] = {0};
250 : 928 : bool nulls[Natts_pg_statistic] = {0};
251 : 928 : bool replaces[Natts_pg_statistic] = {0};
252 : :
253 : 928 : bool result = true;
254 : :
255 : : /*
256 : : * Check argument sanity. If some arguments are unusable, emit a WARNING
257 : : * and skip the corresponding statistics kind, reporting back a failure.
258 : : */
259 : :
260 [ - + ]: 928 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG))
261 : : {
262 : 0 : do_mcv = false;
263 : 0 : result = false;
264 : : }
265 : :
266 [ - + ]: 928 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG))
267 : : {
268 : 0 : do_mcelem = false;
269 : 0 : result = false;
270 : : }
271 [ + + ]: 928 : if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG))
272 : : {
273 : 4 : do_dechist = false;
274 : 4 : result = false;
275 : : }
276 : :
277 [ + + ]: 928 : if (!stats_check_arg_pair(fcinfo, attarginfo,
278 : : MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG))
279 : : {
280 : 12 : do_mcv = false;
281 : 12 : result = false;
282 : : }
283 : :
284 [ + + ]: 928 : if (!stats_check_arg_pair(fcinfo, attarginfo,
285 : : MOST_COMMON_ELEMS_ARG,
286 : : MOST_COMMON_ELEM_FREQS_ARG))
287 : : {
288 : 8 : do_mcelem = false;
289 : 8 : result = false;
290 : : }
291 : :
292 [ + + ]: 928 : if (!stats_check_arg_pair(fcinfo, attarginfo,
293 : : RANGE_LENGTH_HISTOGRAM_ARG,
294 : : RANGE_EMPTY_FRAC_ARG))
295 : : {
296 : 8 : do_range_length_histogram = false;
297 : 8 : result = false;
298 : : }
299 : :
300 : : /* derive information from attribute */
301 : 928 : statatt_get_type(reloid, attnum,
302 : : &atttypid, &atttypmod,
303 : : &basetypcache, &atttypcoll,
304 : : &eq_opr, <_opr);
305 : :
306 : : /* if needed, derive element type */
307 [ + + + + ]: 928 : if (do_mcelem || do_dechist)
308 : : {
309 [ + + ]: 40 : if (!statatt_get_elem_type(basetypcache, &elemtypid, &elem_eq_opr))
310 : : {
311 [ + - ]: 12 : ereport(WARNING,
312 : : (errmsg("could not determine element type of column \"%s\"", attname),
313 : : errdetail("Cannot set %s or %s.",
314 : : "STATISTIC_KIND_MCELEM", "STATISTIC_KIND_DECHIST")));
315 : 12 : elemtypid = InvalidOid;
316 : 12 : elem_eq_opr = InvalidOid;
317 : :
318 : 12 : do_mcelem = false;
319 : 12 : do_dechist = false;
320 : 12 : result = false;
321 : : }
322 : : }
323 : :
324 : : /* histogram and correlation require less-than operator */
325 [ + + + + : 928 : if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
- + ]
326 : : {
327 [ # # ]: 0 : ereport(WARNING,
328 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
329 : : errmsg("could not determine less-than operator for column \"%s\"", attname),
330 : : errdetail("Cannot set %s or %s.",
331 : : "STATISTIC_KIND_HISTOGRAM", "STATISTIC_KIND_CORRELATION")));
332 : :
333 : 0 : do_histogram = false;
334 : 0 : do_correlation = false;
335 : 0 : result = false;
336 : : }
337 : :
338 : : /* only range types can have range stats */
339 [ + + + + ]: 928 : if ((do_range_length_histogram || do_bounds_histogram) &&
340 [ + + ]: 60 : !statatt_get_range_type(basetypcache, &bounds_typid))
341 : : {
342 [ + - ]: 12 : ereport(WARNING,
343 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
344 : : errmsg("column \"%s\" is not a range type", attname),
345 : : errdetail("Cannot set %s or %s.",
346 : : "STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM", "STATISTIC_KIND_BOUNDS_HISTOGRAM")));
347 : :
348 : 12 : do_bounds_histogram = false;
349 : 12 : do_range_length_histogram = false;
350 : 12 : result = false;
351 : : }
352 : :
353 : : /*
354 : : * Parsing the input arrays below can run user-defined code (such as a
355 : : * domain's CHECK constraint) that alters the column's type within our own
356 : : * transaction, which our lock does not prevent. Hold the relation open
357 : : * so that such a reentrant ALTER TABLE is rejected by
358 : : * CheckTableNotInUse(). NoLock is fine here as we already hold
359 : : * ShareUpdateExclusiveLock on it.
360 : : */
361 : 928 : targetrel = relation_open(reloid, NoLock);
362 : :
363 : 928 : fmgr_info(F_ARRAY_IN, &array_in_fn);
364 : :
365 : 928 : starel = table_open(StatisticRelationId, RowExclusiveLock);
366 : :
367 : 928 : statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
368 : :
369 : : /* initialize from existing tuple if exists */
370 [ + + ]: 928 : if (HeapTupleIsValid(statup))
371 : 112 : heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
372 : : else
373 : 816 : statatt_init_empty_tuple(reloid, attnum, inherited, values, nulls,
374 : : replaces);
375 : :
376 : : /* if specified, set to argument values */
377 [ + + ]: 928 : if (!PG_ARGISNULL(NULL_FRAC_ARG))
378 : : {
379 : 872 : values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
380 : 872 : replaces[Anum_pg_statistic_stanullfrac - 1] = true;
381 : : }
382 [ + + ]: 928 : if (!PG_ARGISNULL(AVG_WIDTH_ARG))
383 : : {
384 : 768 : values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
385 : 768 : replaces[Anum_pg_statistic_stawidth - 1] = true;
386 : : }
387 [ + + ]: 928 : if (!PG_ARGISNULL(N_DISTINCT_ARG))
388 : : {
389 : 768 : values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
390 : 768 : replaces[Anum_pg_statistic_stadistinct - 1] = true;
391 : : }
392 : :
393 : : /* STATISTIC_KIND_MCV */
394 [ + + ]: 928 : if (do_mcv)
395 : : {
396 : : bool converted;
397 : 441 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_FREQS_ARG);
398 : 441 : Datum stavalues = statatt_build_stavalues("most_common_vals",
399 : : &array_in_fn,
400 : : PG_GETARG_DATUM(MOST_COMMON_VALS_ARG),
401 : : atttypid, atttypmod,
402 : : &converted);
403 : :
404 [ + + ]: 437 : if (converted)
405 : : {
406 : 425 : ArrayType *vals_arr = DatumGetArrayTypeP(stavalues);
407 : 425 : ArrayType *nums_arr = DatumGetArrayTypeP(stanumbers);
408 : 425 : int nvals = ARR_DIMS(vals_arr)[0];
409 : 425 : int nnums = ARR_DIMS(nums_arr)[0];
410 : :
411 [ + + ]: 425 : if (nvals != nnums)
412 : : {
413 [ + - ]: 8 : ereport(WARNING,
414 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
415 : : errmsg("could not parse \"%s\": incorrect number of elements (same as \"%s\" required)",
416 : : "most_common_vals",
417 : : "most_common_freqs")));
418 : 8 : result = false;
419 : : }
420 : : else
421 : : {
422 : 417 : statatt_set_slot(values, nulls, replaces,
423 : : STATISTIC_KIND_MCV,
424 : : eq_opr, atttypcoll,
425 : : stanumbers, false, stavalues, false);
426 : : }
427 : : }
428 : : else
429 : 12 : result = false;
430 : : }
431 : :
432 : : /* STATISTIC_KIND_HISTOGRAM */
433 [ + + ]: 924 : if (do_histogram)
434 : : {
435 : : Datum stavalues;
436 : 404 : bool converted = false;
437 : :
438 : 404 : stavalues = statatt_build_stavalues("histogram_bounds",
439 : : &array_in_fn,
440 : : PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
441 : : atttypid, atttypmod,
442 : : &converted);
443 : :
444 [ + + ]: 404 : if (converted)
445 : : {
446 : 400 : statatt_set_slot(values, nulls, replaces,
447 : : STATISTIC_KIND_HISTOGRAM,
448 : : lt_opr, atttypcoll,
449 : : 0, true, stavalues, false);
450 : : }
451 : : else
452 : 4 : result = false;
453 : : }
454 : :
455 : : /* STATISTIC_KIND_CORRELATION */
456 [ + + ]: 924 : if (do_correlation)
457 : : {
458 : 705 : Datum elems[] = {PG_GETARG_DATUM(CORRELATION_ARG)};
459 : 705 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
460 : 705 : Datum stanumbers = PointerGetDatum(arry);
461 : :
462 : 705 : statatt_set_slot(values, nulls, replaces,
463 : : STATISTIC_KIND_CORRELATION,
464 : : lt_opr, atttypcoll,
465 : : stanumbers, false, 0, true);
466 : : }
467 : :
468 : : /* STATISTIC_KIND_MCELEM */
469 [ + + ]: 924 : if (do_mcelem)
470 : : {
471 : 24 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_ELEM_FREQS_ARG);
472 : 24 : bool converted = false;
473 : : Datum stavalues;
474 : :
475 : 24 : stavalues = statatt_build_stavalues("most_common_elems",
476 : : &array_in_fn,
477 : : PG_GETARG_DATUM(MOST_COMMON_ELEMS_ARG),
478 : : elemtypid, atttypmod,
479 : : &converted);
480 : :
481 [ + - ]: 24 : if (converted)
482 : : {
483 : 24 : statatt_set_slot(values, nulls, replaces,
484 : : STATISTIC_KIND_MCELEM,
485 : : elem_eq_opr, atttypcoll,
486 : : stanumbers, false, stavalues, false);
487 : : }
488 : : else
489 : 0 : result = false;
490 : : }
491 : :
492 : : /* STATISTIC_KIND_DECHIST */
493 [ + + ]: 924 : if (do_dechist)
494 : : {
495 : 19 : Datum stanumbers = PG_GETARG_DATUM(ELEM_COUNT_HISTOGRAM_ARG);
496 : :
497 : 19 : statatt_set_slot(values, nulls, replaces,
498 : : STATISTIC_KIND_DECHIST,
499 : : elem_eq_opr, atttypcoll,
500 : : stanumbers, false, 0, true);
501 : : }
502 : :
503 : : /*
504 : : * STATISTIC_KIND_BOUNDS_HISTOGRAM
505 : : *
506 : : * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
507 : : * though it is numerically greater, and all other stakinds appear in
508 : : * numerical order. We duplicate this quirk for consistency.
509 : : */
510 [ + + ]: 924 : if (do_bounds_histogram)
511 : : {
512 : 44 : bool converted = false;
513 : : Datum stavalues;
514 : :
515 : : Assert(OidIsValid(bounds_typid));
516 : :
517 : 44 : stavalues = statatt_build_stavalues("range_bounds_histogram",
518 : : &array_in_fn,
519 : : PG_GETARG_DATUM(RANGE_BOUNDS_HISTOGRAM_ARG),
520 : : bounds_typid, atttypmod,
521 : : &converted);
522 : :
523 [ + + + + ]: 84 : if (converted &&
524 : 40 : statatt_check_bounds_histogram(stavalues))
525 : : {
526 : 32 : statatt_set_slot(values, nulls, replaces,
527 : : STATISTIC_KIND_BOUNDS_HISTOGRAM,
528 : : InvalidOid, InvalidOid,
529 : : 0, true, stavalues, false);
530 : : }
531 : : else
532 : 12 : result = false;
533 : : }
534 : :
535 : : /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
536 [ + + ]: 924 : if (do_range_length_histogram)
537 : : {
538 : : /* The anyarray is always a float8[] for this stakind */
539 : 36 : Datum elems[] = {PG_GETARG_DATUM(RANGE_EMPTY_FRAC_ARG)};
540 : 36 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
541 : 36 : Datum stanumbers = PointerGetDatum(arry);
542 : :
543 : 36 : bool converted = false;
544 : : Datum stavalues;
545 : :
546 : 36 : stavalues = statatt_build_stavalues("range_length_histogram",
547 : : &array_in_fn,
548 : : PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
549 : : FLOAT8OID, 0, &converted);
550 : :
551 [ + - ]: 36 : if (converted)
552 : : {
553 : 36 : statatt_set_slot(values, nulls, replaces,
554 : : STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
555 : : Float8LessOperator, InvalidOid,
556 : : stanumbers, false, stavalues, false);
557 : : }
558 : : else
559 : 0 : result = false;
560 : : }
561 : :
562 : : /*
563 : : * The user-defined code run while parsing the arrays above can also drop
564 : : * the column itself through a dependency (e.g. DROP DOMAIN ... CASCADE),
565 : : * which holding the relation open does not prevent. The column must not
566 : : * go away in the middle of restoring its statistics, so error out, which
567 : : * also rolls back the drop.
568 : : */
569 [ + + ]: 924 : if (!SearchSysCacheExistsAttName(reloid, attname))
570 [ + - ]: 4 : ereport(ERROR,
571 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
572 : : errmsg("could not restore statistics for column \"%s\": it was dropped during the import",
573 : : attname)));
574 : :
575 : 920 : upsert_pg_statistic(starel, statup, values, nulls, replaces);
576 : :
577 [ + + ]: 920 : if (HeapTupleIsValid(statup))
578 : 112 : ReleaseSysCache(statup);
579 : 920 : table_close(starel, RowExclusiveLock);
580 : 920 : relation_close(targetrel, NoLock);
581 : :
582 : 920 : return result;
583 : : }
584 : :
585 : : /*
586 : : * Upsert the pg_statistic record.
587 : : */
588 : : static void
589 : 920 : upsert_pg_statistic(Relation starel, HeapTuple oldtup,
590 : : const Datum *values, const bool *nulls, const bool *replaces)
591 : : {
592 : : HeapTuple newtup;
593 : :
594 [ + + ]: 920 : if (HeapTupleIsValid(oldtup))
595 : : {
596 : 112 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(starel),
597 : : values, nulls, replaces);
598 : 112 : CatalogTupleUpdate(starel, &newtup->t_self, newtup);
599 : : }
600 : : else
601 : : {
602 : 808 : newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
603 : 808 : CatalogTupleInsert(starel, newtup);
604 : : }
605 : :
606 : 920 : heap_freetuple(newtup);
607 : :
608 : 920 : CommandCounterIncrement();
609 : 920 : }
610 : :
611 : : /*
612 : : * Delete pg_statistic record.
613 : : */
614 : : static bool
615 : 25 : delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
616 : : {
617 : 25 : Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
618 : : HeapTuple oldtup;
619 : 25 : bool result = false;
620 : :
621 : : /* Is there already a pg_statistic tuple for this attribute? */
622 : 25 : oldtup = SearchSysCache3(STATRELATTINH,
623 : : ObjectIdGetDatum(reloid),
624 : : Int16GetDatum(attnum),
625 : : BoolGetDatum(stainherit));
626 : :
627 [ + + ]: 25 : if (HeapTupleIsValid(oldtup))
628 : : {
629 : 23 : CatalogTupleDelete(sd, &oldtup->t_self);
630 : 23 : ReleaseSysCache(oldtup);
631 : 23 : result = true;
632 : : }
633 : :
634 : 25 : table_close(sd, RowExclusiveLock);
635 : :
636 : 25 : CommandCounterIncrement();
637 : :
638 : 25 : return result;
639 : : }
640 : :
641 : : /*
642 : : * Delete statistics for the given attribute.
643 : : */
644 : : Datum
645 : 52 : pg_clear_attribute_stats(PG_FUNCTION_ARGS)
646 : : {
647 : : char *nspname;
648 : : char *relname;
649 : : Oid reloid;
650 : : char *attname;
651 : : AttrNumber attnum;
652 : : bool inherited;
653 : 52 : Oid locked_table = InvalidOid;
654 : :
655 : 52 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
656 : 48 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
657 : 44 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
658 : 40 : stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
659 : :
660 : 36 : nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
661 : 36 : relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
662 : :
663 [ - + ]: 36 : if (RecoveryInProgress())
664 [ # # ]: 0 : ereport(ERROR,
665 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
666 : : errmsg("recovery is in progress"),
667 : : errhint("Statistics cannot be modified during recovery.")));
668 : :
669 : 36 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
670 : : ShareUpdateExclusiveLock, 0,
671 : : RangeVarCallbackForStats, &locked_table);
672 : :
673 : 20 : attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
674 : 20 : attnum = get_attnum(reloid, attname);
675 : :
676 [ + + ]: 20 : if (attnum < 0)
677 [ + - ]: 4 : ereport(ERROR,
678 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
679 : : errmsg("cannot clear statistics on system column \"%s\"",
680 : : attname)));
681 : :
682 [ + + ]: 16 : if (attnum == InvalidAttrNumber)
683 [ + - ]: 4 : ereport(ERROR,
684 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
685 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
686 : : attname, get_rel_name(reloid))));
687 : :
688 : 12 : inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
689 : :
690 : 12 : delete_pg_statistic(reloid, attnum, inherited);
691 : 12 : PG_RETURN_VOID();
692 : : }
693 : :
694 : : /*
695 : : * Import statistics for a given relation attribute.
696 : : *
697 : : * Inserts or replaces a row in pg_statistic for the given relation and
698 : : * attribute name or number. It takes input parameters that correspond to
699 : : * columns in the view pg_stats.
700 : : *
701 : : * Parameters are given in a pseudo named-attribute style: they must be
702 : : * pairs of parameter names (as text) and values (of appropriate types).
703 : : * We do that, rather than using regular named-parameter notation, so
704 : : * that we can add or change parameters without fear of breaking
705 : : * carelessly-written calls.
706 : : *
707 : : * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
708 : : * columns in pg_statistic. The remaining parameters all belong to a specific
709 : : * stakind. Some stakinds require multiple parameters, which must be specified
710 : : * together (or neither specified).
711 : : *
712 : : * Parameters are only superficially validated. Omitting a parameter or
713 : : * passing NULL leaves the statistic unchanged.
714 : : *
715 : : * Parameters corresponding to ANYARRAY columns are instead passed in as text
716 : : * values, which is a valid input string for an array of the type or element
717 : : * type of the attribute. Any error generated by the array_in() function will
718 : : * in turn fail the function.
719 : : */
720 : : Datum
721 : 959 : pg_restore_attribute_stats(PG_FUNCTION_ARGS)
722 : : {
723 : 959 : LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
724 : 959 : bool result = true;
725 : :
726 : 959 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
727 : : InvalidOid, NULL, NULL);
728 : :
729 [ + + ]: 959 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
730 : : attarginfo))
731 : 8 : result = false;
732 : :
733 [ + + ]: 959 : if (!attribute_statistics_update(positional_fcinfo))
734 : 92 : result = false;
735 : :
736 : 907 : PG_RETURN_BOOL(result);
737 : : }
738 : :
739 : : /*
740 : : * Import attribute statistics from NullableDatum inputs for all statistical
741 : : * values.
742 : : *
743 : : * For now, the 'version' argument is ignored. In the future it can be used
744 : : * to interpret older statistics properly.
745 : : */
746 : : bool
747 : 13 : import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited,
748 : : const NullableDatum *version,
749 : : const NullableDatum *null_frac,
750 : : const NullableDatum *avg_width,
751 : : const NullableDatum *n_distinct,
752 : : const NullableDatum *most_common_vals,
753 : : const NullableDatum *most_common_freqs,
754 : : const NullableDatum *histogram_bounds,
755 : : const NullableDatum *correlation,
756 : : const NullableDatum *most_common_elems,
757 : : const NullableDatum *most_common_elem_freqs,
758 : : const NullableDatum *elem_count_histogram,
759 : : const NullableDatum *range_length_histogram,
760 : : const NullableDatum *range_empty_frac,
761 : : const NullableDatum *range_bounds_histogram)
762 : : {
763 : 13 : LOCAL_FCINFO(newfcinfo, NUM_ATTRIBUTE_STATS_ARGS);
764 : 13 : Oid reloid = RelationGetRelid(rel);
765 : 13 : char *relname = RelationGetRelationName(rel);
766 : 13 : char *attname = get_attname(reloid, attnum, true);
767 : :
768 : : Assert(null_frac);
769 : : Assert(avg_width);
770 : : Assert(n_distinct);
771 : : Assert(most_common_vals);
772 : : Assert(most_common_freqs);
773 : : Assert(histogram_bounds);
774 : : Assert(correlation);
775 : : Assert(most_common_elems);
776 : : Assert(most_common_elem_freqs);
777 : : Assert(elem_count_histogram);
778 : : Assert(range_length_histogram);
779 : : Assert(range_empty_frac);
780 : : Assert(range_bounds_histogram);
781 : :
782 : : /* annoyingly, get_attname doesn't check attisdropped */
783 [ + - ]: 13 : if (attname == NULL ||
784 [ - + ]: 13 : !SearchSysCacheExistsAttName(reloid, attname))
785 [ # # ]: 0 : ereport(ERROR,
786 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
787 : : errmsg("column %d of relation \"%s\" does not exist",
788 : : attnum, relname)));
789 : :
790 : 13 : InitFunctionCallInfoData(*newfcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
791 : : InvalidOid, NULL, NULL);
792 : :
793 : 13 : newfcinfo->args[ATTRELSCHEMA_ARG].value =
794 : 13 : CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
795 : 13 : newfcinfo->args[ATTRELSCHEMA_ARG].isnull = false;
796 : 13 : newfcinfo->args[ATTRELNAME_ARG].value = CStringGetTextDatum(relname);
797 : 13 : newfcinfo->args[ATTRELNAME_ARG].isnull = false;
798 : 13 : newfcinfo->args[ATTNAME_ARG].value = CStringGetTextDatum(attname);
799 : 13 : newfcinfo->args[ATTNAME_ARG].isnull = false;
800 : 13 : newfcinfo->args[ATTNUM_ARG].value = Int16GetDatum(attnum);
801 : 13 : newfcinfo->args[ATTNUM_ARG].isnull = false;
802 : 13 : newfcinfo->args[INHERITED_ARG].value = BoolGetDatum(inherited);
803 : 13 : newfcinfo->args[INHERITED_ARG].isnull = false;
804 : :
805 : 13 : newfcinfo->args[NULL_FRAC_ARG] = *null_frac;
806 : 13 : newfcinfo->args[AVG_WIDTH_ARG] = *avg_width;
807 : 13 : newfcinfo->args[N_DISTINCT_ARG] = *n_distinct;
808 : 13 : newfcinfo->args[MOST_COMMON_VALS_ARG] = *most_common_vals;
809 : 13 : newfcinfo->args[MOST_COMMON_FREQS_ARG] = *most_common_freqs;
810 : 13 : newfcinfo->args[HISTOGRAM_BOUNDS_ARG] = *histogram_bounds;
811 : 13 : newfcinfo->args[CORRELATION_ARG] = *correlation;
812 : 13 : newfcinfo->args[MOST_COMMON_ELEMS_ARG] = *most_common_elems;
813 : 13 : newfcinfo->args[MOST_COMMON_ELEM_FREQS_ARG] = *most_common_elem_freqs;
814 : 13 : newfcinfo->args[ELEM_COUNT_HISTOGRAM_ARG] = *elem_count_histogram;
815 : 13 : newfcinfo->args[RANGE_LENGTH_HISTOGRAM_ARG] = *range_length_histogram;
816 : 13 : newfcinfo->args[RANGE_EMPTY_FRAC_ARG] = *range_empty_frac;
817 : 13 : newfcinfo->args[RANGE_BOUNDS_HISTOGRAM_ARG] = *range_bounds_histogram;
818 : :
819 : 13 : return attribute_statistics_update_internal(reloid, attname, attnum,
820 : : inherited, newfcinfo);
821 : : }
822 : :
823 : : /*
824 : : * Delete attribute statistics.
825 : : */
826 : : bool
827 : 13 : delete_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited)
828 : : {
829 : 13 : return delete_pg_statistic(RelationGetRelid(rel), attnum, inherited);
830 : : }
|