Age Owner Branch data TLA 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 void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
109 : : const Datum *values, const bool *nulls, const bool *replaces);
110 : : static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
111 : :
112 : : /*
113 : : * Insert or Update Attribute Statistics
114 : : *
115 : : * See pg_statistic.h for an explanation of how each statistic kind is
116 : : * stored. Custom statistics kinds are not supported.
117 : : *
118 : : * Depending on the statistics kind, we need to derive information from the
119 : : * attribute for which we're storing the stats. For instance, the MCVs are
120 : : * stored as an anyarray, and the representation of the array needs to store
121 : : * the correct element type, which must be derived from the attribute.
122 : : *
123 : : * Major errors, such as the table not existing, the attribute not existing,
124 : : * or a permissions failure are always reported at ERROR. Other errors, such
125 : : * as a conversion failure on one statistic kind, are reported as a WARNING
126 : : * and other statistic kinds may still be updated.
127 : : */
128 : : static bool
490 jdavis@postgresql.or 129 :CBC 885 : attribute_statistics_update(FunctionCallInfo fcinfo)
130 : : {
131 : : char *nspname;
132 : : char *relname;
133 : : Oid reloid;
134 : : char *attname;
135 : : AttrNumber attnum;
136 : : bool inherited;
258 nathan@postgresql.or 137 : 885 : Oid locked_table = InvalidOid;
138 : :
139 : : Relation starel;
140 : : HeapTuple statup;
141 : :
616 jdavis@postgresql.or 142 : 885 : Oid atttypid = InvalidOid;
143 : : int32 atttypmod;
144 : : char atttyptype;
145 : 885 : Oid atttypcoll = InvalidOid;
146 : 885 : Oid eq_opr = InvalidOid;
147 : 885 : Oid lt_opr = InvalidOid;
148 : :
149 : 885 : Oid elemtypid = InvalidOid;
150 : 885 : Oid elem_eq_opr = InvalidOid;
151 : :
152 : : FmgrInfo array_in_fn;
153 : :
154 [ + + ]: 1301 : bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
155 [ + + ]: 416 : !PG_ARGISNULL(MOST_COMMON_VALS_ARG);
156 : 885 : bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
157 : 885 : bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
158 [ + + ]: 913 : bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
159 [ + + ]: 28 : !PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
160 : 885 : bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
161 : 885 : bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
162 [ + + ]: 909 : bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
163 [ + + ]: 24 : !PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
164 : :
165 : 885 : Datum values[Natts_pg_statistic] = {0};
166 : 885 : bool nulls[Natts_pg_statistic] = {0};
167 : 885 : bool replaces[Natts_pg_statistic] = {0};
168 : :
169 : 885 : bool result = true;
170 : :
462 171 : 885 : stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
172 : 881 : stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
173 : :
174 : 873 : nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
175 : 873 : relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
176 : :
587 fujii@postgresql.org 177 [ - + ]: 873 : if (RecoveryInProgress())
587 fujii@postgresql.org 178 [ # # ]:UBC 0 : ereport(ERROR,
179 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
180 : : errmsg("recovery is in progress"),
181 : : errhint("Statistics cannot be modified during recovery.")));
182 : :
183 : : /* lock before looking up attribute */
258 nathan@postgresql.or 184 :CBC 873 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
185 : : ShareUpdateExclusiveLock, 0,
186 : : RangeVarCallbackForStats, &locked_table);
187 : :
188 : : /* user can specify either attname or attnum, but not both */
489 tgl@sss.pgh.pa.us 189 [ + + ]: 865 : if (!PG_ARGISNULL(ATTNAME_ARG))
190 : : {
191 [ + + ]: 838 : if (!PG_ARGISNULL(ATTNUM_ARG))
192 [ + - ]: 4 : ereport(ERROR,
193 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
194 : : errmsg("cannot specify both \"%s\" and \"%s\"", "attname", "attnum")));
462 jdavis@postgresql.or 195 : 834 : attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
489 tgl@sss.pgh.pa.us 196 : 834 : attnum = get_attnum(reloid, attname);
197 : : /* note that this test covers attisdropped cases too: */
198 [ + + ]: 834 : if (attnum == InvalidAttrNumber)
199 [ + - ]: 4 : ereport(ERROR,
200 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
201 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
202 : : attname, relname)));
203 : : }
204 [ + + ]: 27 : else if (!PG_ARGISNULL(ATTNUM_ARG))
205 : : {
206 : 19 : attnum = PG_GETARG_INT16(ATTNUM_ARG);
207 : 19 : attname = get_attname(reloid, attnum, true);
208 : : /* annoyingly, get_attname doesn't check attisdropped */
209 [ + - ]: 19 : if (attname == NULL ||
210 [ - + ]: 19 : !SearchSysCacheExistsAttName(reloid, attname))
489 tgl@sss.pgh.pa.us 211 [ # # ]:UBC 0 : ereport(ERROR,
212 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
213 : : errmsg("column %d of relation \"%s\" does not exist",
214 : : attnum, relname)));
215 : : }
216 : : else
217 : : {
489 tgl@sss.pgh.pa.us 218 [ + - ]:CBC 8 : ereport(ERROR,
219 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
220 : : errmsg("must specify either \"%s\" or \"%s\"", "attname", "attnum")));
221 : : attname = NULL; /* keep compiler quiet */
222 : : attnum = 0;
223 : : }
224 : :
585 jdavis@postgresql.or 225 [ + + ]: 849 : if (attnum < 0)
226 [ + - ]: 4 : ereport(ERROR,
227 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
228 : : errmsg("cannot modify statistics on system column \"%s\"",
229 : : attname)));
230 : :
616 231 : 845 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
232 : 841 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
233 : :
234 : : /*
235 : : * Check argument sanity. If some arguments are unusable, emit a WARNING
236 : : * and set the corresponding argument to NULL in fcinfo.
237 : : */
238 : :
490 239 [ - + ]: 841 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG))
240 : : {
616 jdavis@postgresql.or 241 :UBC 0 : do_mcv = false;
242 : 0 : result = false;
243 : : }
244 : :
490 jdavis@postgresql.or 245 [ - + ]:CBC 841 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG))
246 : : {
616 jdavis@postgresql.or 247 :UBC 0 : do_mcelem = false;
248 : 0 : result = false;
249 : : }
490 jdavis@postgresql.or 250 [ + + ]:CBC 841 : if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG))
251 : : {
616 252 : 4 : do_dechist = false;
253 : 4 : result = false;
254 : : }
255 : :
256 [ + + ]: 841 : if (!stats_check_arg_pair(fcinfo, attarginfo,
257 : : MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG))
258 : : {
259 : 12 : do_mcv = false;
260 : 12 : result = false;
261 : : }
262 : :
263 [ + + ]: 841 : if (!stats_check_arg_pair(fcinfo, attarginfo,
264 : : MOST_COMMON_ELEMS_ARG,
265 : : MOST_COMMON_ELEM_FREQS_ARG))
266 : : {
267 : 8 : do_mcelem = false;
268 : 8 : result = false;
269 : : }
270 : :
271 [ + + ]: 841 : if (!stats_check_arg_pair(fcinfo, attarginfo,
272 : : RANGE_LENGTH_HISTOGRAM_ARG,
273 : : RANGE_EMPTY_FRAC_ARG))
274 : : {
275 : 8 : do_range_length_histogram = false;
276 : 8 : result = false;
277 : : }
278 : :
279 : : /* derive information from attribute */
187 michael@paquier.xyz 280 :GNC 841 : statatt_get_type(reloid, attnum,
281 : : &atttypid, &atttypmod,
282 : : &atttyptype, &atttypcoll,
283 : : &eq_opr, <_opr);
284 : :
285 : : /* if needed, derive element type */
616 jdavis@postgresql.or 286 [ + + + + ]:CBC 841 : if (do_mcelem || do_dechist)
287 : : {
187 michael@paquier.xyz 288 [ + + ]:GNC 32 : if (!statatt_get_elem_type(atttypid, atttyptype,
289 : : &elemtypid, &elem_eq_opr))
290 : : {
490 jdavis@postgresql.or 291 [ + - ]:CBC 12 : ereport(WARNING,
292 : : (errmsg("could not determine element type of column \"%s\"", attname),
293 : : errdetail("Cannot set %s or %s.",
294 : : "STATISTIC_KIND_MCELEM", "STATISTIC_KIND_DECHIST")));
616 295 : 12 : elemtypid = InvalidOid;
296 : 12 : elem_eq_opr = InvalidOid;
297 : :
298 : 12 : do_mcelem = false;
299 : 12 : do_dechist = false;
300 : 12 : result = false;
301 : : }
302 : : }
303 : :
304 : : /* histogram and correlation require less-than operator */
305 [ + + + + : 841 : if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
- + ]
306 : : {
490 jdavis@postgresql.or 307 [ # # ]:UBC 0 : ereport(WARNING,
308 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
309 : : errmsg("could not determine less-than operator for column \"%s\"", attname),
310 : : errdetail("Cannot set %s or %s.",
311 : : "STATISTIC_KIND_HISTOGRAM", "STATISTIC_KIND_CORRELATION")));
312 : :
616 313 : 0 : do_histogram = false;
314 : 0 : do_correlation = false;
315 : 0 : result = false;
316 : : }
317 : :
318 : : /* only range types can have range stats */
616 jdavis@postgresql.or 319 [ + + + + ]:CBC 841 : if ((do_range_length_histogram || do_bounds_histogram) &&
320 [ + + + - ]: 28 : !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
321 : : {
490 322 [ + - ]: 8 : ereport(WARNING,
323 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
324 : : errmsg("column \"%s\" is not a range type", attname),
325 : : errdetail("Cannot set %s or %s.",
326 : : "STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM", "STATISTIC_KIND_BOUNDS_HISTOGRAM")));
327 : :
616 328 : 8 : do_bounds_histogram = false;
329 : 8 : do_range_length_histogram = false;
330 : 8 : result = false;
331 : : }
332 : :
333 : 841 : fmgr_info(F_ARRAY_IN, &array_in_fn);
334 : :
335 : 841 : starel = table_open(StatisticRelationId, RowExclusiveLock);
336 : :
326 peter@eisentraut.org 337 :GNC 841 : statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
338 : :
339 : : /* initialize from existing tuple if exists */
616 jdavis@postgresql.or 340 [ + + ]:CBC 841 : if (HeapTupleIsValid(statup))
341 : 96 : heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
342 : : else
187 michael@paquier.xyz 343 :GNC 745 : statatt_init_empty_tuple(reloid, attnum, inherited, values, nulls,
344 : : replaces);
345 : :
346 : : /* if specified, set to argument values */
616 jdavis@postgresql.or 347 [ + + ]:CBC 841 : if (!PG_ARGISNULL(NULL_FRAC_ARG))
348 : : {
349 : 817 : values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
350 : 817 : replaces[Anum_pg_statistic_stanullfrac - 1] = true;
351 : : }
352 [ + + ]: 841 : if (!PG_ARGISNULL(AVG_WIDTH_ARG))
353 : : {
354 : 729 : values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
355 : 729 : replaces[Anum_pg_statistic_stawidth - 1] = true;
356 : : }
357 [ + + ]: 841 : if (!PG_ARGISNULL(N_DISTINCT_ARG))
358 : : {
359 : 729 : values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
360 : 729 : replaces[Anum_pg_statistic_stadistinct - 1] = true;
361 : : }
362 : :
363 : : /* STATISTIC_KIND_MCV */
364 [ + + ]: 841 : if (do_mcv)
365 : : {
366 : : bool converted;
367 : 412 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_FREQS_ARG);
187 michael@paquier.xyz 368 :GNC 412 : Datum stavalues = statatt_build_stavalues("most_common_vals",
369 : : &array_in_fn,
370 : : PG_GETARG_DATUM(MOST_COMMON_VALS_ARG),
371 : : atttypid, atttypmod,
372 : : &converted);
373 : :
616 jdavis@postgresql.or 374 [ + + ]:CBC 412 : if (converted)
375 : : {
50 michael@paquier.xyz 376 : 404 : ArrayType *vals_arr = DatumGetArrayTypeP(stavalues);
377 : 404 : ArrayType *nums_arr = DatumGetArrayTypeP(stanumbers);
378 : 404 : int nvals = ARR_DIMS(vals_arr)[0];
379 : 404 : int nnums = ARR_DIMS(nums_arr)[0];
380 : :
381 [ + + ]: 404 : if (nvals != nnums)
382 : : {
383 [ + - ]: 8 : ereport(WARNING,
384 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
385 : : errmsg("could not parse \"%s\": incorrect number of elements (same as \"%s\" required)",
386 : : "most_common_vals",
387 : : "most_common_freqs")));
388 : 8 : result = false;
389 : : }
390 : : else
391 : : {
50 michael@paquier.xyz 392 :GNC 396 : statatt_set_slot(values, nulls, replaces,
393 : : STATISTIC_KIND_MCV,
394 : : eq_opr, atttypcoll,
395 : : stanumbers, false, stavalues, false);
396 : : }
397 : : }
398 : : else
616 jdavis@postgresql.or 399 :CBC 8 : result = false;
400 : : }
401 : :
402 : : /* STATISTIC_KIND_HISTOGRAM */
403 [ + + ]: 841 : if (do_histogram)
404 : : {
405 : : Datum stavalues;
406 : 394 : bool converted = false;
407 : :
187 michael@paquier.xyz 408 :GNC 394 : stavalues = statatt_build_stavalues("histogram_bounds",
409 : : &array_in_fn,
410 : : PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
411 : : atttypid, atttypmod,
412 : : &converted);
413 : :
616 jdavis@postgresql.or 414 [ + + ]:CBC 394 : if (converted)
415 : : {
187 michael@paquier.xyz 416 :GNC 390 : statatt_set_slot(values, nulls, replaces,
417 : : STATISTIC_KIND_HISTOGRAM,
418 : : lt_opr, atttypcoll,
419 : : 0, true, stavalues, false);
420 : : }
421 : : else
616 jdavis@postgresql.or 422 :CBC 4 : result = false;
423 : : }
424 : :
425 : : /* STATISTIC_KIND_CORRELATION */
426 [ + + ]: 841 : if (do_correlation)
427 : : {
428 : 679 : Datum elems[] = {PG_GETARG_DATUM(CORRELATION_ARG)};
429 : 679 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
430 : 679 : Datum stanumbers = PointerGetDatum(arry);
431 : :
187 michael@paquier.xyz 432 :GNC 679 : statatt_set_slot(values, nulls, replaces,
433 : : STATISTIC_KIND_CORRELATION,
434 : : lt_opr, atttypcoll,
435 : : stanumbers, false, 0, true);
436 : : }
437 : :
438 : : /* STATISTIC_KIND_MCELEM */
616 jdavis@postgresql.or 439 [ + + ]:CBC 841 : if (do_mcelem)
440 : : {
441 : 16 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_ELEM_FREQS_ARG);
442 : 16 : bool converted = false;
443 : : Datum stavalues;
444 : :
187 michael@paquier.xyz 445 :GNC 16 : stavalues = statatt_build_stavalues("most_common_elems",
446 : : &array_in_fn,
447 : : PG_GETARG_DATUM(MOST_COMMON_ELEMS_ARG),
448 : : elemtypid, atttypmod,
449 : : &converted);
450 : :
616 jdavis@postgresql.or 451 [ + - ]:CBC 16 : if (converted)
452 : : {
187 michael@paquier.xyz 453 :GNC 16 : statatt_set_slot(values, nulls, replaces,
454 : : STATISTIC_KIND_MCELEM,
455 : : elem_eq_opr, atttypcoll,
456 : : stanumbers, false, stavalues, false);
457 : : }
458 : : else
616 jdavis@postgresql.or 459 :UBC 0 : result = false;
460 : : }
461 : :
462 : : /* STATISTIC_KIND_DECHIST */
616 jdavis@postgresql.or 463 [ + + ]:CBC 841 : if (do_dechist)
464 : : {
465 : 15 : Datum stanumbers = PG_GETARG_DATUM(ELEM_COUNT_HISTOGRAM_ARG);
466 : :
187 michael@paquier.xyz 467 :GNC 15 : statatt_set_slot(values, nulls, replaces,
468 : : STATISTIC_KIND_DECHIST,
469 : : elem_eq_opr, atttypcoll,
470 : : stanumbers, false, 0, true);
471 : : }
472 : :
473 : : /*
474 : : * STATISTIC_KIND_BOUNDS_HISTOGRAM
475 : : *
476 : : * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
477 : : * though it is numerically greater, and all other stakinds appear in
478 : : * numerical order. We duplicate this quirk for consistency.
479 : : */
616 jdavis@postgresql.or 480 [ + + ]:CBC 841 : if (do_bounds_histogram)
481 : : {
482 : 16 : bool converted = false;
483 : : Datum stavalues;
484 : :
187 michael@paquier.xyz 485 :GNC 16 : stavalues = statatt_build_stavalues("range_bounds_histogram",
486 : : &array_in_fn,
487 : : PG_GETARG_DATUM(RANGE_BOUNDS_HISTOGRAM_ARG),
488 : : atttypid, atttypmod,
489 : : &converted);
490 : :
616 jdavis@postgresql.or 491 [ + - ]:CBC 16 : if (converted)
492 : : {
187 michael@paquier.xyz 493 :GNC 16 : statatt_set_slot(values, nulls, replaces,
494 : : STATISTIC_KIND_BOUNDS_HISTOGRAM,
495 : : InvalidOid, InvalidOid,
496 : : 0, true, stavalues, false);
497 : : }
498 : : else
616 jdavis@postgresql.or 499 :UBC 0 : result = false;
500 : : }
501 : :
502 : : /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
616 jdavis@postgresql.or 503 [ + + ]:CBC 841 : if (do_range_length_histogram)
504 : : {
505 : : /* The anyarray is always a float8[] for this stakind */
506 : 16 : Datum elems[] = {PG_GETARG_DATUM(RANGE_EMPTY_FRAC_ARG)};
507 : 16 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
508 : 16 : Datum stanumbers = PointerGetDatum(arry);
509 : :
510 : 16 : bool converted = false;
511 : : Datum stavalues;
512 : :
187 michael@paquier.xyz 513 :GNC 16 : stavalues = statatt_build_stavalues("range_length_histogram",
514 : : &array_in_fn,
515 : : PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
516 : : FLOAT8OID, 0, &converted);
517 : :
616 jdavis@postgresql.or 518 [ + - ]:CBC 16 : if (converted)
519 : : {
187 michael@paquier.xyz 520 :GNC 16 : statatt_set_slot(values, nulls, replaces,
521 : : STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
522 : : Float8LessOperator, InvalidOid,
523 : : stanumbers, false, stavalues, false);
524 : : }
525 : : else
616 jdavis@postgresql.or 526 :UBC 0 : result = false;
527 : : }
528 : :
616 jdavis@postgresql.or 529 :CBC 841 : upsert_pg_statistic(starel, statup, values, nulls, replaces);
530 : :
531 [ + + ]: 841 : if (HeapTupleIsValid(statup))
532 : 96 : ReleaseSysCache(statup);
533 : 841 : table_close(starel, RowExclusiveLock);
534 : :
535 : 841 : return result;
536 : : }
537 : :
538 : : /*
539 : : * Upsert the pg_statistic record.
540 : : */
541 : : static void
542 : 841 : upsert_pg_statistic(Relation starel, HeapTuple oldtup,
543 : : const Datum *values, const bool *nulls, const bool *replaces)
544 : : {
545 : : HeapTuple newtup;
546 : :
547 [ + + ]: 841 : if (HeapTupleIsValid(oldtup))
548 : : {
549 : 96 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(starel),
550 : : values, nulls, replaces);
551 : 96 : CatalogTupleUpdate(starel, &newtup->t_self, newtup);
552 : : }
553 : : else
554 : : {
555 : 745 : newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
556 : 745 : CatalogTupleInsert(starel, newtup);
557 : : }
558 : :
559 : 841 : heap_freetuple(newtup);
560 : :
609 561 : 841 : CommandCounterIncrement();
616 562 : 841 : }
563 : :
564 : : /*
565 : : * Delete pg_statistic record.
566 : : */
567 : : static bool
568 : 15 : delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
569 : : {
570 : 15 : Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
571 : : HeapTuple oldtup;
609 572 : 15 : bool result = false;
573 : :
574 : : /* Is there already a pg_statistic tuple for this attribute? */
616 575 : 15 : oldtup = SearchSysCache3(STATRELATTINH,
576 : : ObjectIdGetDatum(reloid),
577 : : Int16GetDatum(attnum),
578 : : BoolGetDatum(stainherit));
579 : :
580 [ + + ]: 15 : if (HeapTupleIsValid(oldtup))
581 : : {
582 : 13 : CatalogTupleDelete(sd, &oldtup->t_self);
583 : 13 : ReleaseSysCache(oldtup);
609 584 : 13 : result = true;
585 : : }
586 : :
616 587 : 15 : table_close(sd, RowExclusiveLock);
588 : :
609 589 : 15 : CommandCounterIncrement();
590 : :
591 : 15 : return result;
592 : : }
593 : :
594 : : /*
595 : : * Delete statistics for the given attribute.
596 : : */
597 : : Datum
616 598 : 15 : pg_clear_attribute_stats(PG_FUNCTION_ARGS)
599 : : {
600 : : char *nspname;
601 : : char *relname;
602 : : Oid reloid;
603 : : char *attname;
604 : : AttrNumber attnum;
605 : : bool inherited;
258 nathan@postgresql.or 606 : 15 : Oid locked_table = InvalidOid;
607 : :
462 jdavis@postgresql.or 608 : 15 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
609 : 15 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
610 : 15 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
611 : 15 : stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
612 : :
613 : 15 : nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
614 : 15 : relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
615 : :
587 fujii@postgresql.org 616 [ - + ]: 15 : if (RecoveryInProgress())
587 fujii@postgresql.org 617 [ # # ]:UBC 0 : ereport(ERROR,
618 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
619 : : errmsg("recovery is in progress"),
620 : : errhint("Statistics cannot be modified during recovery.")));
621 : :
258 nathan@postgresql.or 622 :CBC 15 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
623 : : ShareUpdateExclusiveLock, 0,
624 : : RangeVarCallbackForStats, &locked_table);
625 : :
462 jdavis@postgresql.or 626 : 15 : attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
627 : 15 : attnum = get_attnum(reloid, attname);
628 : :
585 629 [ - + ]: 15 : if (attnum < 0)
585 jdavis@postgresql.or 630 [ # # ]:UBC 0 : ereport(ERROR,
631 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
632 : : errmsg("cannot clear statistics on system column \"%s\"",
633 : : attname)));
634 : :
615 jdavis@postgresql.or 635 [ - + ]:CBC 15 : if (attnum == InvalidAttrNumber)
615 jdavis@postgresql.or 636 [ # # ]:UBC 0 : ereport(ERROR,
637 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
638 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
639 : : attname, get_rel_name(reloid))));
640 : :
489 tgl@sss.pgh.pa.us 641 :CBC 15 : inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
642 : :
616 jdavis@postgresql.or 643 : 15 : delete_pg_statistic(reloid, attnum, inherited);
644 : 15 : PG_RETURN_VOID();
645 : : }
646 : :
647 : : /*
648 : : * Import statistics for a given relation attribute.
649 : : *
650 : : * Inserts or replaces a row in pg_statistic for the given relation and
651 : : * attribute name or number. It takes input parameters that correspond to
652 : : * columns in the view pg_stats.
653 : : *
654 : : * Parameters are given in a pseudo named-attribute style: they must be
655 : : * pairs of parameter names (as text) and values (of appropriate types).
656 : : * We do that, rather than using regular named-parameter notation, so
657 : : * that we can add or change parameters without fear of breaking
658 : : * carelessly-written calls.
659 : : *
660 : : * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
661 : : * columns in pg_statistic. The remaining parameters all belong to a specific
662 : : * stakind. Some stakinds require multiple parameters, which must be specified
663 : : * together (or neither specified).
664 : : *
665 : : * Parameters are only superficially validated. Omitting a parameter or
666 : : * passing NULL leaves the statistic unchanged.
667 : : *
668 : : * Parameters corresponding to ANYARRAY columns are instead passed in as text
669 : : * values, which is a valid input string for an array of the type or element
670 : : * type of the attribute. Any error generated by the array_in() function will
671 : : * in turn fail the function.
672 : : */
673 : : Datum
614 674 : 885 : pg_restore_attribute_stats(PG_FUNCTION_ARGS)
675 : : {
676 : 885 : LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
677 : 885 : bool result = true;
678 : :
679 : 885 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
680 : : InvalidOid, NULL, NULL);
681 : :
682 [ + + ]: 885 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
683 : : attarginfo))
684 : 8 : result = false;
685 : :
490 686 [ + + ]: 885 : if (!attribute_statistics_update(positional_fcinfo))
614 687 : 72 : result = false;
688 : :
689 : 841 : PG_RETURN_BOOL(result);
690 : : }
691 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
692 : : /* (content generated from coverage data) */
693 : : /* ... */
694 : : /* ... */
695 : : /* ... */
696 : : /* ... */
697 : : /* ... */
698 : : /* ... */
699 : : /* ... */
700 : : /* ... */
701 : : /* ... */
702 : : /* ... */
703 : : /* ... */
704 : : /* ... */
705 : : /* ... */
706 : : /* ... */
707 : : /* ... */
708 : : /* ... */
709 : : /* ... */
710 : : /* ... */
711 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
712 : : /* (content generated from coverage data) */
713 : : /* ... */
714 : : /* ... */
715 : : /* ... */
716 : : /* ... */
717 : : /* BEGIN: function "text_to_stavalues" */
718 : : /* ... */
719 : : /* ... */
720 : : /* ... */
721 : : /* ... */
722 : : /* ... */
723 : : /* ... */
724 : : /* ... */
725 : : /* ... */
726 : : /* ... */
727 : : /* ... */
728 : : /* ... */
729 : : /* ... */
730 : : /* ... */
731 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
732 : : /* (content generated from coverage data) */
733 : : /* ... */
734 : : /* ... */
735 : : /* ... */
736 : : /* ... */
737 : : /* ... */
738 : : /* ... */
739 : : /* ... */
740 : : /* ... */
741 : : /* ... */
742 : : /* ... */
743 : : /* ... */
744 : : /* ... */
745 : : /* ... */
746 : : /* ... */
747 : : /* ... */
748 : : /* ... */
749 : : /* ... */
750 : : /* ... */
751 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
752 : : /* (content generated from coverage data) */
753 : : /* ... */
754 : : /* ... */
755 : : /* ... */
756 : : /* ... */
757 : : /* ... */
758 : : /* ... */
759 : : /* ... */
760 : : /* ... */
761 : : /* ... */
762 : : /* ... */
763 : : /* ... */
764 : : /* ... */
765 : : /* ... */
766 : : /* ... */
767 : : /* ... */
768 : : /* ... */
769 : : /* ... */
770 : : /* ... */
771 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
772 : : /* (content generated from coverage data) */
773 : : /* ... */
774 : : /* ... */
775 : : /* ... */
776 : : /* ... */
777 : : /* ... */
778 : : /* ... */
779 : : /* BEGIN: function "set_stats_slot" */
780 : : /* ... */
781 : : /* ... */
782 : : /* ... */
783 : : /* ... */
784 : : /* ... */
785 : : /* ... */
786 : : /* ... */
787 : : /* ... */
788 : : /* ... */
789 : : /* ... */
790 : : /* ... */
791 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
792 : : /* (content generated from coverage data) */
793 : : /* ... */
794 : : /* ... */
795 : : /* ... */
796 : : /* ... */
797 : : /* ... */
798 : : /* ... */
799 : : /* ... */
800 : : /* ... */
801 : : /* ... */
802 : : /* ... */
803 : : /* ... */
804 : : /* ... */
805 : : /* ... */
806 : : /* ... */
807 : : /* ... */
808 : : /* ... */
809 : : /* ... */
810 : : /* ... */
811 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
812 : : /* (content generated from coverage data) */
813 : : /* ... */
814 : : /* ... */
815 : : /* ... */
816 : : /* ... */
817 : : /* ... */
818 : : /* ... */
819 : : /* ... */
820 : : /* ... */
821 : : /* ... */
822 : : /* ... */
823 : : /* ... */
824 : : /* ... */
825 : : /* ... */
826 : : /* ... */
827 : : /* ... */
828 : : /* ... */
829 : : /* ... */
830 : : /* ... */
831 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
832 : : /* (content generated from coverage data) */
833 : : /* ... */
834 : : /* ... */
835 : : /* ... */
836 : : /* ... */
837 : : /* ... */
838 : : /* ... */
839 : : /* ... */
840 : : /* ... */
841 : : /* ... */
842 : : /* ... */
843 : : /* ... */
844 : : /* ... */
845 : : /* ... */
846 : : /* ... */
847 : : /* ... */
848 : : /* ... */
849 : : /* ... */
850 : : /* ... */
851 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
852 : : /* (content generated from coverage data) */
853 : : /* ... */
854 : : /* ... */
855 : : /* ... */
856 : : /* ... */
857 : : /* ... */
858 : : /* ... */
859 : : /* ... */
860 : : /* ... */
861 : : /* ... */
862 : : /* ... */
863 : : /* ... */
864 : : /* ... */
865 : : /* ... */
866 : : /* ... */
867 : : /* ... */
868 : : /* ... */
869 : : /* ... */
870 : : /* ... */
871 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
872 : : /* (content generated from coverage data) */
873 : : /* ... */
874 : : /* ... */
875 : : /* ... */
876 : : /* ... */
877 : : /* ... */
878 : : /* ... */
879 : : /* ... */
880 : : /* ... */
881 : : /* ... */
882 : : /* ... */
883 : : /* ... */
884 : : /* ... */
885 : : /* ... */
886 : : /* ... */
887 : : /* ... */
888 : : /* ... */
889 : : /* ... */
890 : : /* ... */
891 : : /* /home/coverage/diff-cov-workdir/pg-current/src/backend/statistics/attribute_stats.c not long enough */
892 : : /* (content generated from coverage data) */
893 : : /* ... */
894 : : /* ... */
895 : : /* ... */
896 : : /* ... */
897 : : /* ... */
898 : : /* ... */
899 : : /* ... */
900 : : /* ... */
901 : : /* ... */
902 : : /* ... */
903 : : /* BEGIN: function "init_empty_stats_tuple" */
|