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