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] = {"relation", TEXTOID},
101 : : [C_ATTRELNAME_ARG] = {"relation", 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 : 793 : 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 : 793 : Oid locked_table = InvalidOid;
143 : :
144 : 793 : stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
145 : 789 : stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
146 : :
147 : 781 : nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
148 : 781 : relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
149 : :
150 [ - + ]: 781 : 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 : 781 : 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 [ + + ]: 773 : if (!PG_ARGISNULL(ATTNAME_ARG))
163 : : {
164 [ + + ]: 757 : 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 : 753 : attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
169 : 753 : attnum = get_attnum(reloid, attname);
170 : : /* note that this test covers attisdropped cases too: */
171 [ + + ]: 753 : 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 [ + + ]: 16 : else if (!PG_ARGISNULL(ATTNUM_ARG))
178 : : {
179 : 8 : attnum = PG_GETARG_INT16(ATTNUM_ARG);
180 : 8 : attname = get_attname(reloid, attnum, true);
181 : : /* annoyingly, get_attname doesn't check attisdropped */
182 [ + - ]: 8 : if (attname == NULL ||
183 [ - + ]: 8 : !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 [ + + ]: 757 : 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 : 753 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
205 : 749 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
206 : :
207 : 749 : 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 : 760 : 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 : 760 : Oid atttypid = InvalidOid;
223 : : int32 atttypmod;
224 : : char atttyptype;
225 : 760 : Oid atttypcoll = InvalidOid;
226 : 760 : Oid eq_opr = InvalidOid;
227 : 760 : Oid lt_opr = InvalidOid;
228 : :
229 : 760 : Oid elemtypid = InvalidOid;
230 : 760 : Oid elem_eq_opr = InvalidOid;
231 : :
232 : : FmgrInfo array_in_fn;
233 : :
234 [ + + ]: 1113 : bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
235 [ + + ]: 353 : !PG_ARGISNULL(MOST_COMMON_VALS_ARG);
236 : 760 : bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
237 : 760 : bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
238 [ + + ]: 788 : bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
239 [ + + ]: 28 : !PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
240 : 760 : bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
241 : 760 : bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
242 [ + + ]: 783 : bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
243 [ + + ]: 23 : !PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
244 : :
245 : 760 : Datum values[Natts_pg_statistic] = {0};
246 : 760 : bool nulls[Natts_pg_statistic] = {0};
247 : 760 : bool replaces[Natts_pg_statistic] = {0};
248 : :
249 : 760 : bool result = true;
250 : :
251 : : /*
252 : : * Check argument sanity. If some arguments are unusable, emit a WARNING
253 : : * and set the corresponding argument to NULL in fcinfo.
254 : : */
255 : :
256 [ - + ]: 760 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG))
257 : : {
258 : 0 : do_mcv = false;
259 : 0 : result = false;
260 : : }
261 : :
262 [ - + ]: 760 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG))
263 : : {
264 : 0 : do_mcelem = false;
265 : 0 : result = false;
266 : : }
267 [ + + ]: 760 : if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG))
268 : : {
269 : 4 : do_dechist = false;
270 : 4 : result = false;
271 : : }
272 : :
273 [ + + ]: 760 : if (!stats_check_arg_pair(fcinfo, attarginfo,
274 : : MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG))
275 : : {
276 : 12 : do_mcv = false;
277 : 12 : result = false;
278 : : }
279 : :
280 [ + + ]: 760 : if (!stats_check_arg_pair(fcinfo, attarginfo,
281 : : MOST_COMMON_ELEMS_ARG,
282 : : MOST_COMMON_ELEM_FREQS_ARG))
283 : : {
284 : 8 : do_mcelem = false;
285 : 8 : result = false;
286 : : }
287 : :
288 [ + + ]: 760 : if (!stats_check_arg_pair(fcinfo, attarginfo,
289 : : RANGE_LENGTH_HISTOGRAM_ARG,
290 : : RANGE_EMPTY_FRAC_ARG))
291 : : {
292 : 8 : do_range_length_histogram = false;
293 : 8 : result = false;
294 : : }
295 : :
296 : : /* derive information from attribute */
297 : 760 : statatt_get_type(reloid, attnum,
298 : : &atttypid, &atttypmod,
299 : : &atttyptype, &atttypcoll,
300 : : &eq_opr, <_opr);
301 : :
302 : : /* if needed, derive element type */
303 [ + + + + ]: 760 : if (do_mcelem || do_dechist)
304 : : {
305 [ + + ]: 32 : if (!statatt_get_elem_type(atttypid, atttyptype,
306 : : &elemtypid, &elem_eq_opr))
307 : : {
308 [ + - ]: 12 : ereport(WARNING,
309 : : (errmsg("could not determine element type of column \"%s\"", attname),
310 : : errdetail("Cannot set %s or %s.",
311 : : "STATISTIC_KIND_MCELEM", "STATISTIC_KIND_DECHIST")));
312 : 12 : elemtypid = InvalidOid;
313 : 12 : elem_eq_opr = InvalidOid;
314 : :
315 : 12 : do_mcelem = false;
316 : 12 : do_dechist = false;
317 : 12 : result = false;
318 : : }
319 : : }
320 : :
321 : : /* histogram and correlation require less-than operator */
322 [ + + + + : 760 : if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
- + ]
323 : : {
324 [ # # ]: 0 : ereport(WARNING,
325 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
326 : : errmsg("could not determine less-than operator for column \"%s\"", attname),
327 : : errdetail("Cannot set %s or %s.",
328 : : "STATISTIC_KIND_HISTOGRAM", "STATISTIC_KIND_CORRELATION")));
329 : :
330 : 0 : do_histogram = false;
331 : 0 : do_correlation = false;
332 : 0 : result = false;
333 : : }
334 : :
335 : : /* only range types can have range stats */
336 [ + + + + ]: 760 : if ((do_range_length_histogram || do_bounds_histogram) &&
337 [ + + + - ]: 35 : !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
338 : : {
339 [ + - ]: 8 : ereport(WARNING,
340 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
341 : : errmsg("column \"%s\" is not a range type", attname),
342 : : errdetail("Cannot set %s or %s.",
343 : : "STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM", "STATISTIC_KIND_BOUNDS_HISTOGRAM")));
344 : :
345 : 8 : do_bounds_histogram = false;
346 : 8 : do_range_length_histogram = false;
347 : 8 : result = false;
348 : : }
349 : :
350 : 760 : fmgr_info(F_ARRAY_IN, &array_in_fn);
351 : :
352 : 760 : starel = table_open(StatisticRelationId, RowExclusiveLock);
353 : :
354 : 760 : statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
355 : :
356 : : /* initialize from existing tuple if exists */
357 [ + + ]: 760 : if (HeapTupleIsValid(statup))
358 : 104 : heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
359 : : else
360 : 656 : statatt_init_empty_tuple(reloid, attnum, inherited, values, nulls,
361 : : replaces);
362 : :
363 : : /* if specified, set to argument values */
364 [ + + ]: 760 : if (!PG_ARGISNULL(NULL_FRAC_ARG))
365 : : {
366 : 728 : values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
367 : 728 : replaces[Anum_pg_statistic_stanullfrac - 1] = true;
368 : : }
369 [ + + ]: 760 : if (!PG_ARGISNULL(AVG_WIDTH_ARG))
370 : : {
371 : 640 : values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
372 : 640 : replaces[Anum_pg_statistic_stawidth - 1] = true;
373 : : }
374 [ + + ]: 760 : if (!PG_ARGISNULL(N_DISTINCT_ARG))
375 : : {
376 : 640 : values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
377 : 640 : replaces[Anum_pg_statistic_stadistinct - 1] = true;
378 : : }
379 : :
380 : : /* STATISTIC_KIND_MCV */
381 [ + + ]: 760 : if (do_mcv)
382 : : {
383 : : bool converted;
384 : 349 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_FREQS_ARG);
385 : 349 : Datum stavalues = statatt_build_stavalues("most_common_vals",
386 : : &array_in_fn,
387 : : PG_GETARG_DATUM(MOST_COMMON_VALS_ARG),
388 : : atttypid, atttypmod,
389 : : &converted);
390 : :
391 [ + + ]: 349 : if (converted)
392 : : {
393 : 341 : ArrayType *vals_arr = DatumGetArrayTypeP(stavalues);
394 : 341 : ArrayType *nums_arr = DatumGetArrayTypeP(stanumbers);
395 : 341 : int nvals = ARR_DIMS(vals_arr)[0];
396 : 341 : int nnums = ARR_DIMS(nums_arr)[0];
397 : :
398 [ + + ]: 341 : if (nvals != nnums)
399 : : {
400 [ + - ]: 8 : ereport(WARNING,
401 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
402 : : errmsg("could not parse \"%s\": incorrect number of elements (same as \"%s\" required)",
403 : : "most_common_vals",
404 : : "most_common_freqs")));
405 : 8 : result = false;
406 : : }
407 : : else
408 : : {
409 : 333 : statatt_set_slot(values, nulls, replaces,
410 : : STATISTIC_KIND_MCV,
411 : : eq_opr, atttypcoll,
412 : : stanumbers, false, stavalues, false);
413 : : }
414 : : }
415 : : else
416 : 8 : result = false;
417 : : }
418 : :
419 : : /* STATISTIC_KIND_HISTOGRAM */
420 [ + + ]: 760 : if (do_histogram)
421 : : {
422 : : Datum stavalues;
423 : 323 : bool converted = false;
424 : :
425 : 323 : stavalues = statatt_build_stavalues("histogram_bounds",
426 : : &array_in_fn,
427 : : PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
428 : : atttypid, atttypmod,
429 : : &converted);
430 : :
431 [ + + ]: 323 : if (converted)
432 : : {
433 : 319 : statatt_set_slot(values, nulls, replaces,
434 : : STATISTIC_KIND_HISTOGRAM,
435 : : lt_opr, atttypcoll,
436 : : 0, true, stavalues, false);
437 : : }
438 : : else
439 : 4 : result = false;
440 : : }
441 : :
442 : : /* STATISTIC_KIND_CORRELATION */
443 [ + + ]: 760 : if (do_correlation)
444 : : {
445 : 599 : Datum elems[] = {PG_GETARG_DATUM(CORRELATION_ARG)};
446 : 599 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
447 : 599 : Datum stanumbers = PointerGetDatum(arry);
448 : :
449 : 599 : statatt_set_slot(values, nulls, replaces,
450 : : STATISTIC_KIND_CORRELATION,
451 : : lt_opr, atttypcoll,
452 : : stanumbers, false, 0, true);
453 : : }
454 : :
455 : : /* STATISTIC_KIND_MCELEM */
456 [ + + ]: 760 : if (do_mcelem)
457 : : {
458 : 16 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_ELEM_FREQS_ARG);
459 : 16 : bool converted = false;
460 : : Datum stavalues;
461 : :
462 : 16 : stavalues = statatt_build_stavalues("most_common_elems",
463 : : &array_in_fn,
464 : : PG_GETARG_DATUM(MOST_COMMON_ELEMS_ARG),
465 : : elemtypid, atttypmod,
466 : : &converted);
467 : :
468 [ + - ]: 16 : if (converted)
469 : : {
470 : 16 : statatt_set_slot(values, nulls, replaces,
471 : : STATISTIC_KIND_MCELEM,
472 : : elem_eq_opr, atttypcoll,
473 : : stanumbers, false, stavalues, false);
474 : : }
475 : : else
476 : 0 : result = false;
477 : : }
478 : :
479 : : /* STATISTIC_KIND_DECHIST */
480 [ + + ]: 760 : if (do_dechist)
481 : : {
482 : 15 : Datum stanumbers = PG_GETARG_DATUM(ELEM_COUNT_HISTOGRAM_ARG);
483 : :
484 : 15 : statatt_set_slot(values, nulls, replaces,
485 : : STATISTIC_KIND_DECHIST,
486 : : elem_eq_opr, atttypcoll,
487 : : stanumbers, false, 0, true);
488 : : }
489 : :
490 : : /*
491 : : * STATISTIC_KIND_BOUNDS_HISTOGRAM
492 : : *
493 : : * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
494 : : * though it is numerically greater, and all other stakinds appear in
495 : : * numerical order. We duplicate this quirk for consistency.
496 : : */
497 [ + + ]: 760 : if (do_bounds_histogram)
498 : : {
499 : 23 : bool converted = false;
500 : : Datum stavalues;
501 : :
502 : 23 : stavalues = statatt_build_stavalues("range_bounds_histogram",
503 : : &array_in_fn,
504 : : PG_GETARG_DATUM(RANGE_BOUNDS_HISTOGRAM_ARG),
505 : : atttypid, atttypmod,
506 : : &converted);
507 : :
508 [ + - + + ]: 46 : if (converted &&
509 : 23 : statatt_check_bounds_histogram(stavalues))
510 : : {
511 : 15 : statatt_set_slot(values, nulls, replaces,
512 : : STATISTIC_KIND_BOUNDS_HISTOGRAM,
513 : : InvalidOid, InvalidOid,
514 : : 0, true, stavalues, false);
515 : : }
516 : : else
517 : 8 : result = false;
518 : : }
519 : :
520 : : /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
521 [ + + ]: 760 : if (do_range_length_histogram)
522 : : {
523 : : /* The anyarray is always a float8[] for this stakind */
524 : 15 : Datum elems[] = {PG_GETARG_DATUM(RANGE_EMPTY_FRAC_ARG)};
525 : 15 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
526 : 15 : Datum stanumbers = PointerGetDatum(arry);
527 : :
528 : 15 : bool converted = false;
529 : : Datum stavalues;
530 : :
531 : 15 : stavalues = statatt_build_stavalues("range_length_histogram",
532 : : &array_in_fn,
533 : : PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
534 : : FLOAT8OID, 0, &converted);
535 : :
536 [ + - ]: 15 : if (converted)
537 : : {
538 : 15 : statatt_set_slot(values, nulls, replaces,
539 : : STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
540 : : Float8LessOperator, InvalidOid,
541 : : stanumbers, false, stavalues, false);
542 : : }
543 : : else
544 : 0 : result = false;
545 : : }
546 : :
547 : 760 : upsert_pg_statistic(starel, statup, values, nulls, replaces);
548 : :
549 [ + + ]: 760 : if (HeapTupleIsValid(statup))
550 : 104 : ReleaseSysCache(statup);
551 : 760 : table_close(starel, RowExclusiveLock);
552 : :
553 : 760 : return result;
554 : : }
555 : :
556 : : /*
557 : : * Upsert the pg_statistic record.
558 : : */
559 : : static void
560 : 760 : upsert_pg_statistic(Relation starel, HeapTuple oldtup,
561 : : const Datum *values, const bool *nulls, const bool *replaces)
562 : : {
563 : : HeapTuple newtup;
564 : :
565 [ + + ]: 760 : if (HeapTupleIsValid(oldtup))
566 : : {
567 : 104 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(starel),
568 : : values, nulls, replaces);
569 : 104 : CatalogTupleUpdate(starel, &newtup->t_self, newtup);
570 : : }
571 : : else
572 : : {
573 : 656 : newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
574 : 656 : CatalogTupleInsert(starel, newtup);
575 : : }
576 : :
577 : 760 : heap_freetuple(newtup);
578 : :
579 : 760 : CommandCounterIncrement();
580 : 760 : }
581 : :
582 : : /*
583 : : * Delete pg_statistic record.
584 : : */
585 : : static bool
586 : 15 : delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
587 : : {
588 : 15 : Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
589 : : HeapTuple oldtup;
590 : 15 : bool result = false;
591 : :
592 : : /* Is there already a pg_statistic tuple for this attribute? */
593 : 15 : oldtup = SearchSysCache3(STATRELATTINH,
594 : : ObjectIdGetDatum(reloid),
595 : : Int16GetDatum(attnum),
596 : : BoolGetDatum(stainherit));
597 : :
598 [ + + ]: 15 : if (HeapTupleIsValid(oldtup))
599 : : {
600 : 13 : CatalogTupleDelete(sd, &oldtup->t_self);
601 : 13 : ReleaseSysCache(oldtup);
602 : 13 : result = true;
603 : : }
604 : :
605 : 15 : table_close(sd, RowExclusiveLock);
606 : :
607 : 15 : CommandCounterIncrement();
608 : :
609 : 15 : return result;
610 : : }
611 : :
612 : : /*
613 : : * Delete statistics for the given attribute.
614 : : */
615 : : Datum
616 : 4 : pg_clear_attribute_stats(PG_FUNCTION_ARGS)
617 : : {
618 : : char *nspname;
619 : : char *relname;
620 : : Oid reloid;
621 : : char *attname;
622 : : AttrNumber attnum;
623 : : bool inherited;
624 : 4 : Oid locked_table = InvalidOid;
625 : :
626 : 4 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
627 : 4 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
628 : 4 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
629 : 4 : stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
630 : :
631 : 4 : nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
632 : 4 : relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
633 : :
634 [ - + ]: 4 : if (RecoveryInProgress())
635 [ # # ]: 0 : ereport(ERROR,
636 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
637 : : errmsg("recovery is in progress"),
638 : : errhint("Statistics cannot be modified during recovery.")));
639 : :
640 : 4 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
641 : : ShareUpdateExclusiveLock, 0,
642 : : RangeVarCallbackForStats, &locked_table);
643 : :
644 : 4 : attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
645 : 4 : attnum = get_attnum(reloid, attname);
646 : :
647 [ - + ]: 4 : if (attnum < 0)
648 [ # # ]: 0 : ereport(ERROR,
649 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
650 : : errmsg("cannot clear statistics on system column \"%s\"",
651 : : attname)));
652 : :
653 [ - + ]: 4 : if (attnum == InvalidAttrNumber)
654 [ # # ]: 0 : ereport(ERROR,
655 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
656 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
657 : : attname, get_rel_name(reloid))));
658 : :
659 : 4 : inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
660 : :
661 : 4 : delete_pg_statistic(reloid, attnum, inherited);
662 : 4 : PG_RETURN_VOID();
663 : : }
664 : :
665 : : /*
666 : : * Import statistics for a given relation attribute.
667 : : *
668 : : * Inserts or replaces a row in pg_statistic for the given relation and
669 : : * attribute name or number. It takes input parameters that correspond to
670 : : * columns in the view pg_stats.
671 : : *
672 : : * Parameters are given in a pseudo named-attribute style: they must be
673 : : * pairs of parameter names (as text) and values (of appropriate types).
674 : : * We do that, rather than using regular named-parameter notation, so
675 : : * that we can add or change parameters without fear of breaking
676 : : * carelessly-written calls.
677 : : *
678 : : * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
679 : : * columns in pg_statistic. The remaining parameters all belong to a specific
680 : : * stakind. Some stakinds require multiple parameters, which must be specified
681 : : * together (or neither specified).
682 : : *
683 : : * Parameters are only superficially validated. Omitting a parameter or
684 : : * passing NULL leaves the statistic unchanged.
685 : : *
686 : : * Parameters corresponding to ANYARRAY columns are instead passed in as text
687 : : * values, which is a valid input string for an array of the type or element
688 : : * type of the attribute. Any error generated by the array_in() function will
689 : : * in turn fail the function.
690 : : */
691 : : Datum
692 : 793 : pg_restore_attribute_stats(PG_FUNCTION_ARGS)
693 : : {
694 : 793 : LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
695 : 793 : bool result = true;
696 : :
697 : 793 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
698 : : InvalidOid, NULL, NULL);
699 : :
700 [ + + ]: 793 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
701 : : attarginfo))
702 : 8 : result = false;
703 : :
704 [ + + ]: 793 : if (!attribute_statistics_update(positional_fcinfo))
705 : 80 : result = false;
706 : :
707 : 749 : PG_RETURN_BOOL(result);
708 : : }
709 : :
710 : : /*
711 : : * Import attribute statistics from NullableDatum inputs for all statistical
712 : : * values.
713 : : *
714 : : * For now, the 'version' argument is ignored. In the future it can be used
715 : : * to interpret older statistics properly.
716 : : */
717 : : bool
718 : 11 : import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited,
719 : : const NullableDatum *version,
720 : : const NullableDatum *null_frac,
721 : : const NullableDatum *avg_width,
722 : : const NullableDatum *n_distinct,
723 : : const NullableDatum *most_common_vals,
724 : : const NullableDatum *most_common_freqs,
725 : : const NullableDatum *histogram_bounds,
726 : : const NullableDatum *correlation,
727 : : const NullableDatum *most_common_elems,
728 : : const NullableDatum *most_common_elem_freqs,
729 : : const NullableDatum *elem_count_histogram,
730 : : const NullableDatum *range_length_histogram,
731 : : const NullableDatum *range_empty_frac,
732 : : const NullableDatum *range_bounds_histogram)
733 : : {
734 : 11 : LOCAL_FCINFO(newfcinfo, NUM_ATTRIBUTE_STATS_ARGS);
735 : 11 : Oid reloid = RelationGetRelid(rel);
736 : 11 : char *relname = RelationGetRelationName(rel);
737 : 11 : char *attname = get_attname(reloid, attnum, true);
738 : :
739 : : Assert(null_frac);
740 : : Assert(avg_width);
741 : : Assert(n_distinct);
742 : : Assert(most_common_vals);
743 : : Assert(most_common_freqs);
744 : : Assert(histogram_bounds);
745 : : Assert(correlation);
746 : : Assert(most_common_elems);
747 : : Assert(most_common_elem_freqs);
748 : : Assert(elem_count_histogram);
749 : : Assert(range_length_histogram);
750 : : Assert(range_empty_frac);
751 : : Assert(range_bounds_histogram);
752 : :
753 : : /* annoyingly, get_attname doesn't check attisdropped */
754 [ + - ]: 11 : if (attname == NULL ||
755 [ - + ]: 11 : !SearchSysCacheExistsAttName(reloid, attname))
756 [ # # ]: 0 : ereport(ERROR,
757 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
758 : : errmsg("column %d of relation \"%s\" does not exist",
759 : : attnum, relname)));
760 : :
761 : 11 : InitFunctionCallInfoData(*newfcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
762 : : InvalidOid, NULL, NULL);
763 : :
764 : 11 : newfcinfo->args[ATTRELSCHEMA_ARG].value =
765 : 11 : CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
766 : 11 : newfcinfo->args[ATTRELSCHEMA_ARG].isnull = false;
767 : 11 : newfcinfo->args[ATTRELNAME_ARG].value = CStringGetTextDatum(relname);
768 : 11 : newfcinfo->args[ATTRELNAME_ARG].isnull = false;
769 : 11 : newfcinfo->args[ATTNAME_ARG].value = CStringGetTextDatum(attname);
770 : 11 : newfcinfo->args[ATTNAME_ARG].isnull = false;
771 : 11 : newfcinfo->args[ATTNUM_ARG].value = Int16GetDatum(attnum);
772 : 11 : newfcinfo->args[ATTNUM_ARG].isnull = false;
773 : 11 : newfcinfo->args[INHERITED_ARG].value = BoolGetDatum(inherited);
774 : 11 : newfcinfo->args[INHERITED_ARG].isnull = false;
775 : :
776 : 11 : newfcinfo->args[NULL_FRAC_ARG] = *null_frac;
777 : 11 : newfcinfo->args[AVG_WIDTH_ARG] = *avg_width;
778 : 11 : newfcinfo->args[N_DISTINCT_ARG] = *n_distinct;
779 : 11 : newfcinfo->args[MOST_COMMON_VALS_ARG] = *most_common_vals;
780 : 11 : newfcinfo->args[MOST_COMMON_FREQS_ARG] = *most_common_freqs;
781 : 11 : newfcinfo->args[HISTOGRAM_BOUNDS_ARG] = *histogram_bounds;
782 : 11 : newfcinfo->args[CORRELATION_ARG] = *correlation;
783 : 11 : newfcinfo->args[MOST_COMMON_ELEMS_ARG] = *most_common_elems;
784 : 11 : newfcinfo->args[MOST_COMMON_ELEM_FREQS_ARG] = *most_common_elem_freqs;
785 : 11 : newfcinfo->args[ELEM_COUNT_HISTOGRAM_ARG] = *elem_count_histogram;
786 : 11 : newfcinfo->args[RANGE_LENGTH_HISTOGRAM_ARG] = *range_length_histogram;
787 : 11 : newfcinfo->args[RANGE_EMPTY_FRAC_ARG] = *range_empty_frac;
788 : 11 : newfcinfo->args[RANGE_BOUNDS_HISTOGRAM_ARG] = *range_bounds_histogram;
789 : :
790 : 11 : return attribute_statistics_update_internal(reloid, attname, attnum,
791 : : inherited, newfcinfo);
792 : : }
793 : :
794 : : /*
795 : : * Delete attribute statistics.
796 : : */
797 : : bool
798 : 11 : delete_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited)
799 : : {
800 : 11 : return delete_pg_statistic(RelationGetRelid(rel), attnum, inherited);
801 : : }
|