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
129 794 : attribute_statistics_update(FunctionCallInfo fcinfo)
130 : {
131 : char *nspname;
132 : char *relname;
133 : Oid reloid;
134 : char *attname;
135 : AttrNumber attnum;
136 : bool inherited;
137 794 : Oid locked_table = InvalidOid;
138 :
139 : Relation starel;
140 : HeapTuple statup;
141 :
142 794 : Oid atttypid = InvalidOid;
143 : int32 atttypmod;
144 : char atttyptype;
145 794 : Oid atttypcoll = InvalidOid;
146 794 : Oid eq_opr = InvalidOid;
147 794 : Oid lt_opr = InvalidOid;
148 :
149 794 : Oid elemtypid = InvalidOid;
150 794 : Oid elem_eq_opr = InvalidOid;
151 :
152 : FmgrInfo array_in_fn;
153 :
154 1147 : bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
155 353 : !PG_ARGISNULL(MOST_COMMON_VALS_ARG);
156 794 : bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
157 794 : bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
158 822 : bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
159 28 : !PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
160 794 : bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
161 794 : bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
162 817 : bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
163 23 : !PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
164 :
165 794 : Datum values[Natts_pg_statistic] = {0};
166 794 : bool nulls[Natts_pg_statistic] = {0};
167 794 : bool replaces[Natts_pg_statistic] = {0};
168 :
169 794 : bool result = true;
170 :
171 794 : stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
172 790 : stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
173 :
174 782 : nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
175 782 : relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
176 :
177 782 : if (RecoveryInProgress())
178 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 */
184 782 : 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 */
189 774 : if (!PG_ARGISNULL(ATTNAME_ARG))
190 : {
191 749 : 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")));
195 745 : attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
196 745 : attnum = get_attnum(reloid, attname);
197 : /* note that this test covers attisdropped cases too: */
198 745 : 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 25 : else if (!PG_ARGISNULL(ATTNUM_ARG))
205 : {
206 17 : attnum = PG_GETARG_INT16(ATTNUM_ARG);
207 17 : attname = get_attname(reloid, attnum, true);
208 : /* annoyingly, get_attname doesn't check attisdropped */
209 17 : if (attname == NULL ||
210 17 : !SearchSysCacheExistsAttName(reloid, attname))
211 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 : {
218 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 :
225 758 : 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 :
231 754 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
232 750 : 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 :
239 750 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG))
240 : {
241 0 : do_mcv = false;
242 0 : result = false;
243 : }
244 :
245 750 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG))
246 : {
247 0 : do_mcelem = false;
248 0 : result = false;
249 : }
250 750 : if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG))
251 : {
252 4 : do_dechist = false;
253 4 : result = false;
254 : }
255 :
256 750 : 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 750 : 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 750 : 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 */
280 750 : statatt_get_type(reloid, attnum,
281 : &atttypid, &atttypmod,
282 : &atttyptype, &atttypcoll,
283 : &eq_opr, <_opr);
284 :
285 : /* if needed, derive element type */
286 750 : if (do_mcelem || do_dechist)
287 : {
288 32 : if (!statatt_get_elem_type(atttypid, atttyptype,
289 : &elemtypid, &elem_eq_opr))
290 : {
291 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")));
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 750 : if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
306 : {
307 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 :
313 0 : do_histogram = false;
314 0 : do_correlation = false;
315 0 : result = false;
316 : }
317 :
318 : /* only range types can have range stats */
319 750 : if ((do_range_length_histogram || do_bounds_histogram) &&
320 27 : !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
321 : {
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 :
328 8 : do_bounds_histogram = false;
329 8 : do_range_length_histogram = false;
330 8 : result = false;
331 : }
332 :
333 750 : fmgr_info(F_ARRAY_IN, &array_in_fn);
334 :
335 750 : starel = table_open(StatisticRelationId, RowExclusiveLock);
336 :
337 750 : statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
338 :
339 : /* initialize from existing tuple if exists */
340 750 : if (HeapTupleIsValid(statup))
341 96 : heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
342 : else
343 654 : statatt_init_empty_tuple(reloid, attnum, inherited, values, nulls,
344 : replaces);
345 :
346 : /* if specified, set to argument values */
347 750 : if (!PG_ARGISNULL(NULL_FRAC_ARG))
348 : {
349 726 : values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
350 726 : replaces[Anum_pg_statistic_stanullfrac - 1] = true;
351 : }
352 750 : if (!PG_ARGISNULL(AVG_WIDTH_ARG))
353 : {
354 638 : values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
355 638 : replaces[Anum_pg_statistic_stawidth - 1] = true;
356 : }
357 750 : if (!PG_ARGISNULL(N_DISTINCT_ARG))
358 : {
359 638 : values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
360 638 : replaces[Anum_pg_statistic_stadistinct - 1] = true;
361 : }
362 :
363 : /* STATISTIC_KIND_MCV */
364 750 : if (do_mcv)
365 : {
366 : bool converted;
367 349 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_FREQS_ARG);
368 349 : 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 :
374 349 : if (converted)
375 : {
376 341 : ArrayType *vals_arr = DatumGetArrayTypeP(stavalues);
377 341 : ArrayType *nums_arr = DatumGetArrayTypeP(stanumbers);
378 341 : int nvals = ARR_DIMS(vals_arr)[0];
379 341 : int nnums = ARR_DIMS(nums_arr)[0];
380 :
381 341 : 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 : {
392 333 : statatt_set_slot(values, nulls, replaces,
393 : STATISTIC_KIND_MCV,
394 : eq_opr, atttypcoll,
395 : stanumbers, false, stavalues, false);
396 : }
397 : }
398 : else
399 8 : result = false;
400 : }
401 :
402 : /* STATISTIC_KIND_HISTOGRAM */
403 750 : if (do_histogram)
404 : {
405 : Datum stavalues;
406 321 : bool converted = false;
407 :
408 321 : stavalues = statatt_build_stavalues("histogram_bounds",
409 : &array_in_fn,
410 : PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
411 : atttypid, atttypmod,
412 : &converted);
413 :
414 321 : if (converted)
415 : {
416 317 : statatt_set_slot(values, nulls, replaces,
417 : STATISTIC_KIND_HISTOGRAM,
418 : lt_opr, atttypcoll,
419 : 0, true, stavalues, false);
420 : }
421 : else
422 4 : result = false;
423 : }
424 :
425 : /* STATISTIC_KIND_CORRELATION */
426 750 : if (do_correlation)
427 : {
428 597 : Datum elems[] = {PG_GETARG_DATUM(CORRELATION_ARG)};
429 597 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
430 597 : Datum stanumbers = PointerGetDatum(arry);
431 :
432 597 : 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 */
439 750 : 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 :
445 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 :
451 16 : if (converted)
452 : {
453 16 : statatt_set_slot(values, nulls, replaces,
454 : STATISTIC_KIND_MCELEM,
455 : elem_eq_opr, atttypcoll,
456 : stanumbers, false, stavalues, false);
457 : }
458 : else
459 0 : result = false;
460 : }
461 :
462 : /* STATISTIC_KIND_DECHIST */
463 750 : if (do_dechist)
464 : {
465 15 : Datum stanumbers = PG_GETARG_DATUM(ELEM_COUNT_HISTOGRAM_ARG);
466 :
467 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 : */
480 750 : if (do_bounds_histogram)
481 : {
482 15 : bool converted = false;
483 : Datum stavalues;
484 :
485 15 : 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 :
491 15 : if (converted)
492 : {
493 15 : statatt_set_slot(values, nulls, replaces,
494 : STATISTIC_KIND_BOUNDS_HISTOGRAM,
495 : InvalidOid, InvalidOid,
496 : 0, true, stavalues, false);
497 : }
498 : else
499 0 : result = false;
500 : }
501 :
502 : /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
503 750 : if (do_range_length_histogram)
504 : {
505 : /* The anyarray is always a float8[] for this stakind */
506 15 : Datum elems[] = {PG_GETARG_DATUM(RANGE_EMPTY_FRAC_ARG)};
507 15 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
508 15 : Datum stanumbers = PointerGetDatum(arry);
509 :
510 15 : bool converted = false;
511 : Datum stavalues;
512 :
513 15 : stavalues = statatt_build_stavalues("range_length_histogram",
514 : &array_in_fn,
515 : PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
516 : FLOAT8OID, 0, &converted);
517 :
518 15 : if (converted)
519 : {
520 15 : statatt_set_slot(values, nulls, replaces,
521 : STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
522 : Float8LessOperator, InvalidOid,
523 : stanumbers, false, stavalues, false);
524 : }
525 : else
526 0 : result = false;
527 : }
528 :
529 750 : upsert_pg_statistic(starel, statup, values, nulls, replaces);
530 :
531 750 : if (HeapTupleIsValid(statup))
532 96 : ReleaseSysCache(statup);
533 750 : table_close(starel, RowExclusiveLock);
534 :
535 750 : return result;
536 : }
537 :
538 : /*
539 : * Upsert the pg_statistic record.
540 : */
541 : static void
542 750 : upsert_pg_statistic(Relation starel, HeapTuple oldtup,
543 : const Datum *values, const bool *nulls, const bool *replaces)
544 : {
545 : HeapTuple newtup;
546 :
547 750 : 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 654 : newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
556 654 : CatalogTupleInsert(starel, newtup);
557 : }
558 :
559 750 : heap_freetuple(newtup);
560 :
561 750 : CommandCounterIncrement();
562 750 : }
563 :
564 : /*
565 : * Delete pg_statistic record.
566 : */
567 : static bool
568 13 : delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
569 : {
570 13 : Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
571 : HeapTuple oldtup;
572 13 : bool result = false;
573 :
574 : /* Is there already a pg_statistic tuple for this attribute? */
575 13 : oldtup = SearchSysCache3(STATRELATTINH,
576 : ObjectIdGetDatum(reloid),
577 : Int16GetDatum(attnum),
578 : BoolGetDatum(stainherit));
579 :
580 13 : if (HeapTupleIsValid(oldtup))
581 : {
582 13 : CatalogTupleDelete(sd, &oldtup->t_self);
583 13 : ReleaseSysCache(oldtup);
584 13 : result = true;
585 : }
586 :
587 13 : table_close(sd, RowExclusiveLock);
588 :
589 13 : CommandCounterIncrement();
590 :
591 13 : return result;
592 : }
593 :
594 : /*
595 : * Delete statistics for the given attribute.
596 : */
597 : Datum
598 13 : 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;
606 13 : Oid locked_table = InvalidOid;
607 :
608 13 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
609 13 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
610 13 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
611 13 : stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
612 :
613 13 : nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
614 13 : relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
615 :
616 13 : if (RecoveryInProgress())
617 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 :
622 13 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
623 : ShareUpdateExclusiveLock, 0,
624 : RangeVarCallbackForStats, &locked_table);
625 :
626 13 : attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
627 13 : attnum = get_attnum(reloid, attname);
628 :
629 13 : if (attnum < 0)
630 0 : ereport(ERROR,
631 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
632 : errmsg("cannot clear statistics on system column \"%s\"",
633 : attname)));
634 :
635 13 : if (attnum == InvalidAttrNumber)
636 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 :
641 13 : inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
642 :
643 13 : delete_pg_statistic(reloid, attnum, inherited);
644 13 : 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
674 794 : pg_restore_attribute_stats(PG_FUNCTION_ARGS)
675 : {
676 794 : LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
677 794 : bool result = true;
678 :
679 794 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
680 : InvalidOid, NULL, NULL);
681 :
682 794 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
683 : attarginfo))
684 8 : result = false;
685 :
686 794 : if (!attribute_statistics_update(positional_fcinfo))
687 72 : result = false;
688 :
689 750 : PG_RETURN_BOOL(result);
690 : }
|