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-2025, 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/pg_collation.h"
23 : #include "catalog/pg_operator.h"
24 : #include "nodes/nodeFuncs.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 : #define DEFAULT_NULL_FRAC Float4GetDatum(0.0)
34 : #define DEFAULT_AVG_WIDTH Int32GetDatum(0) /* unknown */
35 : #define DEFAULT_N_DISTINCT Float4GetDatum(0.0) /* unknown */
36 :
37 : enum attribute_stats_argnum
38 : {
39 : ATTRELATION_ARG = 0,
40 : ATTNAME_ARG,
41 : INHERITED_ARG,
42 : NULL_FRAC_ARG,
43 : AVG_WIDTH_ARG,
44 : N_DISTINCT_ARG,
45 : MOST_COMMON_VALS_ARG,
46 : MOST_COMMON_FREQS_ARG,
47 : HISTOGRAM_BOUNDS_ARG,
48 : CORRELATION_ARG,
49 : MOST_COMMON_ELEMS_ARG,
50 : MOST_COMMON_ELEM_FREQS_ARG,
51 : ELEM_COUNT_HISTOGRAM_ARG,
52 : RANGE_LENGTH_HISTOGRAM_ARG,
53 : RANGE_EMPTY_FRAC_ARG,
54 : RANGE_BOUNDS_HISTOGRAM_ARG,
55 : NUM_ATTRIBUTE_STATS_ARGS
56 : };
57 :
58 : static struct StatsArgInfo attarginfo[] =
59 : {
60 : [ATTRELATION_ARG] = {"relation", REGCLASSOID},
61 : [ATTNAME_ARG] = {"attname", NAMEOID},
62 : [INHERITED_ARG] = {"inherited", BOOLOID},
63 : [NULL_FRAC_ARG] = {"null_frac", FLOAT4OID},
64 : [AVG_WIDTH_ARG] = {"avg_width", INT4OID},
65 : [N_DISTINCT_ARG] = {"n_distinct", FLOAT4OID},
66 : [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTOID},
67 : [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT4ARRAYOID},
68 : [HISTOGRAM_BOUNDS_ARG] = {"histogram_bounds", TEXTOID},
69 : [CORRELATION_ARG] = {"correlation", FLOAT4OID},
70 : [MOST_COMMON_ELEMS_ARG] = {"most_common_elems", TEXTOID},
71 : [MOST_COMMON_ELEM_FREQS_ARG] = {"most_common_elem_freqs", FLOAT4ARRAYOID},
72 : [ELEM_COUNT_HISTOGRAM_ARG] = {"elem_count_histogram", FLOAT4ARRAYOID},
73 : [RANGE_LENGTH_HISTOGRAM_ARG] = {"range_length_histogram", TEXTOID},
74 : [RANGE_EMPTY_FRAC_ARG] = {"range_empty_frac", FLOAT4OID},
75 : [RANGE_BOUNDS_HISTOGRAM_ARG] = {"range_bounds_histogram", TEXTOID},
76 : [NUM_ATTRIBUTE_STATS_ARGS] = {0}
77 : };
78 :
79 : static bool attribute_statistics_update(FunctionCallInfo fcinfo, int elevel);
80 : static Node *get_attr_expr(Relation rel, int attnum);
81 : static void get_attr_stat_type(Oid reloid, AttrNumber attnum, int elevel,
82 : Oid *atttypid, int32 *atttypmod,
83 : char *atttyptype, Oid *atttypcoll,
84 : Oid *eq_opr, Oid *lt_opr);
85 : static bool get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
86 : Oid *elemtypid, Oid *elem_eq_opr);
87 : static Datum text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d,
88 : Oid typid, int32 typmod, int elevel, bool *ok);
89 : static void set_stats_slot(Datum *values, bool *nulls, bool *replaces,
90 : int16 stakind, Oid staop, Oid stacoll,
91 : Datum stanumbers, bool stanumbers_isnull,
92 : Datum stavalues, bool stavalues_isnull);
93 : static void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
94 : Datum *values, bool *nulls, bool *replaces);
95 : static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
96 : static void init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
97 : Datum *values, bool *nulls, bool *replaces);
98 :
99 : /*
100 : * Insert or Update Attribute Statistics
101 : *
102 : * See pg_statistic.h for an explanation of how each statistic kind is
103 : * stored. Custom statistics kinds are not supported.
104 : *
105 : * Depending on the statistics kind, we need to derive information from the
106 : * attribute for which we're storing the stats. For instance, the MCVs are
107 : * stored as an anyarray, and the representation of the array needs to store
108 : * the correct element type, which must be derived from the attribute.
109 : *
110 : * Major errors, such as the table not existing, the attribute not existing,
111 : * or a permissions failure are always reported at ERROR. Other errors, such
112 : * as a conversion failure on one statistic kind, are reported at 'elevel',
113 : * and other statistic kinds may still be updated.
114 : */
115 : static bool
116 384 : attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
117 : {
118 : Oid reloid;
119 : Name attname;
120 : bool inherited;
121 : AttrNumber attnum;
122 :
123 : Relation starel;
124 : HeapTuple statup;
125 :
126 384 : Oid atttypid = InvalidOid;
127 : int32 atttypmod;
128 : char atttyptype;
129 384 : Oid atttypcoll = InvalidOid;
130 384 : Oid eq_opr = InvalidOid;
131 384 : Oid lt_opr = InvalidOid;
132 :
133 384 : Oid elemtypid = InvalidOid;
134 384 : Oid elem_eq_opr = InvalidOid;
135 :
136 : FmgrInfo array_in_fn;
137 :
138 450 : bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
139 66 : !PG_ARGISNULL(MOST_COMMON_VALS_ARG);
140 384 : bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
141 384 : bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
142 426 : bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
143 42 : !PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
144 384 : bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
145 384 : bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
146 444 : bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
147 60 : !PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
148 :
149 384 : Datum values[Natts_pg_statistic] = {0};
150 384 : bool nulls[Natts_pg_statistic] = {0};
151 384 : bool replaces[Natts_pg_statistic] = {0};
152 :
153 384 : bool result = true;
154 :
155 384 : stats_check_required_arg(fcinfo, attarginfo, ATTRELATION_ARG);
156 372 : reloid = PG_GETARG_OID(ATTRELATION_ARG);
157 :
158 372 : if (RecoveryInProgress())
159 0 : ereport(ERROR,
160 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
161 : errmsg("recovery is in progress"),
162 : errhint("Statistics cannot be modified during recovery.")));
163 :
164 : /* lock before looking up attribute */
165 372 : stats_lock_check_privileges(reloid);
166 :
167 360 : stats_check_required_arg(fcinfo, attarginfo, ATTNAME_ARG);
168 348 : attname = PG_GETARG_NAME(ATTNAME_ARG);
169 348 : attnum = get_attnum(reloid, NameStr(*attname));
170 :
171 348 : if (attnum < 0)
172 6 : ereport(ERROR,
173 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
174 : errmsg("cannot modify statistics on system column \"%s\"",
175 : NameStr(*attname))));
176 :
177 342 : if (attnum == InvalidAttrNumber)
178 12 : ereport(ERROR,
179 : (errcode(ERRCODE_UNDEFINED_COLUMN),
180 : errmsg("column \"%s\" of relation \"%s\" does not exist",
181 : NameStr(*attname), get_rel_name(reloid))));
182 :
183 330 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
184 318 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
185 :
186 : /*
187 : * Check argument sanity. If some arguments are unusable, emit at elevel
188 : * and set the corresponding argument to NULL in fcinfo.
189 : */
190 :
191 318 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG,
192 : elevel))
193 : {
194 0 : do_mcv = false;
195 0 : result = false;
196 : }
197 :
198 318 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG,
199 : elevel))
200 : {
201 0 : do_mcelem = false;
202 0 : result = false;
203 : }
204 318 : if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG,
205 : elevel))
206 : {
207 6 : do_dechist = false;
208 6 : result = false;
209 : }
210 :
211 312 : if (!stats_check_arg_pair(fcinfo, attarginfo,
212 : MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG,
213 : elevel))
214 : {
215 18 : do_mcv = false;
216 18 : result = false;
217 : }
218 :
219 300 : if (!stats_check_arg_pair(fcinfo, attarginfo,
220 : MOST_COMMON_ELEMS_ARG,
221 : MOST_COMMON_ELEM_FREQS_ARG, elevel))
222 : {
223 0 : do_mcelem = false;
224 0 : result = false;
225 : }
226 :
227 288 : if (!stats_check_arg_pair(fcinfo, attarginfo,
228 : RANGE_LENGTH_HISTOGRAM_ARG,
229 : RANGE_EMPTY_FRAC_ARG, elevel))
230 : {
231 12 : do_range_length_histogram = false;
232 12 : result = false;
233 : }
234 :
235 : /* derive information from attribute */
236 276 : get_attr_stat_type(reloid, attnum, elevel,
237 : &atttypid, &atttypmod,
238 : &atttyptype, &atttypcoll,
239 : &eq_opr, <_opr);
240 :
241 : /* if needed, derive element type */
242 276 : if (do_mcelem || do_dechist)
243 : {
244 54 : if (!get_elem_stat_type(atttypid, atttyptype, elevel,
245 : &elemtypid, &elem_eq_opr))
246 : {
247 24 : ereport(elevel,
248 : (errmsg("unable to determine element type of attribute \"%s\"", NameStr(*attname)),
249 : errdetail("Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.")));
250 6 : elemtypid = InvalidOid;
251 6 : elem_eq_opr = InvalidOid;
252 :
253 6 : do_mcelem = false;
254 6 : do_dechist = false;
255 6 : result = false;
256 : }
257 : }
258 :
259 : /* histogram and correlation require less-than operator */
260 258 : if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
261 : {
262 0 : ereport(elevel,
263 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
264 : errmsg("could not determine less-than operator for attribute \"%s\"", NameStr(*attname)),
265 : errdetail("Cannot set STATISTIC_KIND_HISTOGRAM or STATISTIC_KIND_CORRELATION.")));
266 :
267 0 : do_histogram = false;
268 0 : do_correlation = false;
269 0 : result = false;
270 : }
271 :
272 : /* only range types can have range stats */
273 258 : if ((do_range_length_histogram || do_bounds_histogram) &&
274 66 : !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
275 : {
276 24 : ereport(elevel,
277 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
278 : errmsg("attribute \"%s\" is not a range type", NameStr(*attname)),
279 : errdetail("Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM.")));
280 :
281 12 : do_bounds_histogram = false;
282 12 : do_range_length_histogram = false;
283 12 : result = false;
284 : }
285 :
286 246 : fmgr_info(F_ARRAY_IN, &array_in_fn);
287 :
288 246 : starel = table_open(StatisticRelationId, RowExclusiveLock);
289 :
290 246 : statup = SearchSysCache3(STATRELATTINH, reloid, attnum, inherited);
291 :
292 : /* initialize from existing tuple if exists */
293 246 : if (HeapTupleIsValid(statup))
294 138 : heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
295 : else
296 108 : init_empty_stats_tuple(reloid, attnum, inherited, values, nulls,
297 : replaces);
298 :
299 : /* if specified, set to argument values */
300 246 : if (!PG_ARGISNULL(NULL_FRAC_ARG))
301 : {
302 246 : values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
303 246 : replaces[Anum_pg_statistic_stanullfrac - 1] = true;
304 : }
305 246 : if (!PG_ARGISNULL(AVG_WIDTH_ARG))
306 : {
307 240 : values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
308 240 : replaces[Anum_pg_statistic_stawidth - 1] = true;
309 : }
310 246 : if (!PG_ARGISNULL(N_DISTINCT_ARG))
311 : {
312 240 : values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
313 240 : replaces[Anum_pg_statistic_stadistinct - 1] = true;
314 : }
315 :
316 : /* STATISTIC_KIND_MCV */
317 246 : if (do_mcv)
318 : {
319 : bool converted;
320 48 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_FREQS_ARG);
321 48 : Datum stavalues = text_to_stavalues("most_common_vals",
322 : &array_in_fn,
323 : PG_GETARG_DATUM(MOST_COMMON_VALS_ARG),
324 : atttypid, atttypmod,
325 : elevel, &converted);
326 :
327 36 : if (converted)
328 : {
329 30 : set_stats_slot(values, nulls, replaces,
330 : STATISTIC_KIND_MCV,
331 : eq_opr, atttypcoll,
332 : stanumbers, false, stavalues, false);
333 : }
334 : else
335 6 : result = false;
336 : }
337 :
338 : /* STATISTIC_KIND_HISTOGRAM */
339 234 : if (do_histogram)
340 : {
341 : Datum stavalues;
342 78 : bool converted = false;
343 :
344 78 : stavalues = text_to_stavalues("histogram_bounds",
345 : &array_in_fn,
346 : PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
347 : atttypid, atttypmod, elevel,
348 : &converted);
349 :
350 72 : if (converted)
351 : {
352 66 : set_stats_slot(values, nulls, replaces,
353 : STATISTIC_KIND_HISTOGRAM,
354 : lt_opr, atttypcoll,
355 : 0, true, stavalues, false);
356 : }
357 : else
358 6 : result = false;
359 : }
360 :
361 : /* STATISTIC_KIND_CORRELATION */
362 228 : if (do_correlation)
363 : {
364 72 : Datum elems[] = {PG_GETARG_DATUM(CORRELATION_ARG)};
365 72 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
366 72 : Datum stanumbers = PointerGetDatum(arry);
367 :
368 72 : set_stats_slot(values, nulls, replaces,
369 : STATISTIC_KIND_CORRELATION,
370 : lt_opr, atttypcoll,
371 : stanumbers, false, 0, true);
372 : }
373 :
374 : /* STATISTIC_KIND_MCELEM */
375 228 : if (do_mcelem)
376 : {
377 18 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_ELEM_FREQS_ARG);
378 18 : bool converted = false;
379 : Datum stavalues;
380 :
381 18 : stavalues = text_to_stavalues("most_common_elems",
382 : &array_in_fn,
383 : PG_GETARG_DATUM(MOST_COMMON_ELEMS_ARG),
384 : elemtypid, atttypmod,
385 : elevel, &converted);
386 :
387 18 : if (converted)
388 : {
389 18 : set_stats_slot(values, nulls, replaces,
390 : STATISTIC_KIND_MCELEM,
391 : elem_eq_opr, atttypcoll,
392 : stanumbers, false, stavalues, false);
393 : }
394 : else
395 0 : result = false;
396 : }
397 :
398 : /* STATISTIC_KIND_DECHIST */
399 228 : if (do_dechist)
400 : {
401 24 : Datum stanumbers = PG_GETARG_DATUM(ELEM_COUNT_HISTOGRAM_ARG);
402 :
403 24 : set_stats_slot(values, nulls, replaces,
404 : STATISTIC_KIND_DECHIST,
405 : elem_eq_opr, atttypcoll,
406 : stanumbers, false, 0, true);
407 : }
408 :
409 : /*
410 : * STATISTIC_KIND_BOUNDS_HISTOGRAM
411 : *
412 : * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
413 : * though it is numerically greater, and all other stakinds appear in
414 : * numerical order. We duplicate this quirk for consistency.
415 : */
416 228 : if (do_bounds_histogram)
417 : {
418 30 : bool converted = false;
419 : Datum stavalues;
420 :
421 30 : stavalues = text_to_stavalues("range_bounds_histogram",
422 : &array_in_fn,
423 : PG_GETARG_DATUM(RANGE_BOUNDS_HISTOGRAM_ARG),
424 : atttypid, atttypmod,
425 : elevel, &converted);
426 :
427 30 : if (converted)
428 : {
429 30 : set_stats_slot(values, nulls, replaces,
430 : STATISTIC_KIND_BOUNDS_HISTOGRAM,
431 : InvalidOid, InvalidOid,
432 : 0, true, stavalues, false);
433 : }
434 : else
435 0 : result = false;
436 : }
437 :
438 : /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
439 228 : if (do_range_length_histogram)
440 : {
441 : /* The anyarray is always a float8[] for this stakind */
442 30 : Datum elems[] = {PG_GETARG_DATUM(RANGE_EMPTY_FRAC_ARG)};
443 30 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
444 30 : Datum stanumbers = PointerGetDatum(arry);
445 :
446 30 : bool converted = false;
447 : Datum stavalues;
448 :
449 30 : stavalues = text_to_stavalues("range_length_histogram",
450 : &array_in_fn,
451 : PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
452 : FLOAT8OID, 0, elevel, &converted);
453 :
454 30 : if (converted)
455 : {
456 30 : set_stats_slot(values, nulls, replaces,
457 : STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
458 : Float8LessOperator, InvalidOid,
459 : stanumbers, false, stavalues, false);
460 : }
461 : else
462 0 : result = false;
463 : }
464 :
465 228 : upsert_pg_statistic(starel, statup, values, nulls, replaces);
466 :
467 228 : if (HeapTupleIsValid(statup))
468 120 : ReleaseSysCache(statup);
469 228 : table_close(starel, RowExclusiveLock);
470 :
471 228 : return result;
472 : }
473 :
474 : /*
475 : * If this relation is an index and that index has expressions in it, and
476 : * the attnum specified is known to be an expression, then we must walk
477 : * the list attributes up to the specified attnum to get the right
478 : * expression.
479 : */
480 : static Node *
481 276 : get_attr_expr(Relation rel, int attnum)
482 : {
483 276 : if ((rel->rd_rel->relkind == RELKIND_INDEX
484 264 : || (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX))
485 12 : && (rel->rd_indexprs != NIL)
486 0 : && (rel->rd_index->indkey.values[attnum - 1] == 0))
487 : {
488 0 : ListCell *indexpr_item = list_head(rel->rd_indexprs);
489 :
490 0 : for (int i = 0; i < attnum - 1; i++)
491 0 : if (rel->rd_index->indkey.values[i] == 0)
492 0 : indexpr_item = lnext(rel->rd_indexprs, indexpr_item);
493 :
494 0 : if (indexpr_item == NULL) /* shouldn't happen */
495 0 : elog(ERROR, "too few entries in indexprs list");
496 :
497 0 : return (Node *) lfirst(indexpr_item);
498 : }
499 276 : return NULL;
500 : }
501 :
502 : /*
503 : * Derive type information from the attribute.
504 : */
505 : static void
506 276 : get_attr_stat_type(Oid reloid, AttrNumber attnum, int elevel,
507 : Oid *atttypid, int32 *atttypmod,
508 : char *atttyptype, Oid *atttypcoll,
509 : Oid *eq_opr, Oid *lt_opr)
510 : {
511 276 : Relation rel = relation_open(reloid, AccessShareLock);
512 : Form_pg_attribute attr;
513 : HeapTuple atup;
514 : Node *expr;
515 : TypeCacheEntry *typcache;
516 :
517 276 : atup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(reloid),
518 : Int16GetDatum(attnum));
519 :
520 : /* Attribute not found */
521 276 : if (!HeapTupleIsValid(atup))
522 0 : ereport(ERROR,
523 : (errcode(ERRCODE_UNDEFINED_COLUMN),
524 : errmsg("attribute %d of relation \"%s\" does not exist",
525 : attnum, RelationGetRelationName(rel))));
526 :
527 276 : attr = (Form_pg_attribute) GETSTRUCT(atup);
528 :
529 276 : if (attr->attisdropped)
530 0 : ereport(ERROR,
531 : (errcode(ERRCODE_UNDEFINED_COLUMN),
532 : errmsg("attribute %d of relation \"%s\" does not exist",
533 : attnum, RelationGetRelationName(rel))));
534 :
535 276 : expr = get_attr_expr(rel, attr->attnum);
536 :
537 : /*
538 : * When analyzing an expression index, believe the expression tree's type
539 : * not the column datatype --- the latter might be the opckeytype storage
540 : * type of the opclass, which is not interesting for our purposes. This
541 : * mimics the behavior of examine_attribute().
542 : */
543 276 : if (expr == NULL)
544 : {
545 276 : *atttypid = attr->atttypid;
546 276 : *atttypmod = attr->atttypmod;
547 276 : *atttypcoll = attr->attcollation;
548 : }
549 : else
550 : {
551 0 : *atttypid = exprType(expr);
552 0 : *atttypmod = exprTypmod(expr);
553 :
554 0 : if (OidIsValid(attr->attcollation))
555 0 : *atttypcoll = attr->attcollation;
556 : else
557 0 : *atttypcoll = exprCollation(expr);
558 : }
559 276 : ReleaseSysCache(atup);
560 :
561 : /*
562 : * If it's a multirange, step down to the range type, as is done by
563 : * multirange_typanalyze().
564 : */
565 276 : if (type_is_multirange(*atttypid))
566 0 : *atttypid = get_multirange_range(*atttypid);
567 :
568 : /* finds the right operators even if atttypid is a domain */
569 276 : typcache = lookup_type_cache(*atttypid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
570 276 : *atttyptype = typcache->typtype;
571 276 : *eq_opr = typcache->eq_opr;
572 276 : *lt_opr = typcache->lt_opr;
573 :
574 : /*
575 : * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
576 : * compute_tsvector_stats().
577 : */
578 276 : if (*atttypid == TSVECTOROID)
579 0 : *atttypcoll = DEFAULT_COLLATION_OID;
580 :
581 276 : relation_close(rel, NoLock);
582 276 : }
583 :
584 : /*
585 : * Derive element type information from the attribute type.
586 : */
587 : static bool
588 54 : get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
589 : Oid *elemtypid, Oid *elem_eq_opr)
590 : {
591 : TypeCacheEntry *elemtypcache;
592 :
593 54 : if (atttypid == TSVECTOROID)
594 : {
595 : /*
596 : * Special case: element type for tsvector is text. See
597 : * compute_tsvector_stats().
598 : */
599 0 : *elemtypid = TEXTOID;
600 : }
601 : else
602 : {
603 : /* find underlying element type through any domain */
604 54 : *elemtypid = get_base_element_type(atttypid);
605 : }
606 :
607 54 : if (!OidIsValid(*elemtypid))
608 24 : return false;
609 :
610 : /* finds the right operator even if elemtypid is a domain */
611 30 : elemtypcache = lookup_type_cache(*elemtypid, TYPECACHE_EQ_OPR);
612 30 : if (!OidIsValid(elemtypcache->eq_opr))
613 0 : return false;
614 :
615 30 : *elem_eq_opr = elemtypcache->eq_opr;
616 :
617 30 : return true;
618 : }
619 :
620 : /*
621 : * Cast a text datum into an array with element type elemtypid.
622 : *
623 : * If an error is encountered, capture it and re-throw at elevel, and set ok
624 : * to false. If the resulting array contains NULLs, raise an error at elevel
625 : * and set ok to false. Otherwise, set ok to true.
626 : */
627 : static Datum
628 204 : text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
629 : int32 typmod, int elevel, bool *ok)
630 : {
631 204 : LOCAL_FCINFO(fcinfo, 8);
632 : char *s;
633 : Datum result;
634 204 : ErrorSaveContext escontext = {T_ErrorSaveContext};
635 :
636 204 : escontext.details_wanted = true;
637 :
638 204 : s = TextDatumGetCString(d);
639 :
640 204 : InitFunctionCallInfoData(*fcinfo, array_in, 3, InvalidOid,
641 : (Node *) &escontext, NULL);
642 :
643 204 : fcinfo->args[0].value = CStringGetDatum(s);
644 204 : fcinfo->args[0].isnull = false;
645 204 : fcinfo->args[1].value = ObjectIdGetDatum(typid);
646 204 : fcinfo->args[1].isnull = false;
647 204 : fcinfo->args[2].value = Int32GetDatum(typmod);
648 204 : fcinfo->args[2].isnull = false;
649 :
650 204 : result = FunctionCallInvoke(fcinfo);
651 :
652 204 : pfree(s);
653 :
654 204 : if (escontext.error_occurred)
655 : {
656 18 : if (elevel != ERROR)
657 6 : escontext.error_data->elevel = elevel;
658 18 : ThrowErrorData(escontext.error_data);
659 6 : *ok = false;
660 6 : return (Datum) 0;
661 : }
662 :
663 186 : if (array_contains_nulls(DatumGetArrayTypeP(result)))
664 : {
665 12 : ereport(elevel,
666 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
667 : errmsg("\"%s\" array cannot contain NULL values", staname)));
668 6 : *ok = false;
669 6 : return (Datum) 0;
670 : }
671 :
672 174 : *ok = true;
673 :
674 174 : return result;
675 : }
676 :
677 : /*
678 : * Find and update the slot with the given stakind, or use the first empty
679 : * slot.
680 : */
681 : static void
682 270 : set_stats_slot(Datum *values, bool *nulls, bool *replaces,
683 : int16 stakind, Oid staop, Oid stacoll,
684 : Datum stanumbers, bool stanumbers_isnull,
685 : Datum stavalues, bool stavalues_isnull)
686 : {
687 : int slotidx;
688 270 : int first_empty = -1;
689 : AttrNumber stakind_attnum;
690 : AttrNumber staop_attnum;
691 : AttrNumber stacoll_attnum;
692 :
693 : /* find existing slot with given stakind */
694 1566 : for (slotidx = 0; slotidx < STATISTIC_NUM_SLOTS; slotidx++)
695 : {
696 1308 : stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
697 :
698 1812 : if (first_empty < 0 &&
699 504 : DatumGetInt16(values[stakind_attnum]) == 0)
700 258 : first_empty = slotidx;
701 1308 : if (DatumGetInt16(values[stakind_attnum]) == stakind)
702 12 : break;
703 : }
704 :
705 270 : if (slotidx >= STATISTIC_NUM_SLOTS && first_empty >= 0)
706 258 : slotidx = first_empty;
707 :
708 270 : if (slotidx >= STATISTIC_NUM_SLOTS)
709 0 : ereport(ERROR,
710 : (errmsg("maximum number of statistics slots exceeded: %d",
711 : slotidx + 1)));
712 :
713 270 : stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
714 270 : staop_attnum = Anum_pg_statistic_staop1 - 1 + slotidx;
715 270 : stacoll_attnum = Anum_pg_statistic_stacoll1 - 1 + slotidx;
716 :
717 270 : if (DatumGetInt16(values[stakind_attnum]) != stakind)
718 : {
719 258 : values[stakind_attnum] = Int16GetDatum(stakind);
720 258 : replaces[stakind_attnum] = true;
721 : }
722 270 : if (DatumGetObjectId(values[staop_attnum]) != staop)
723 : {
724 234 : values[staop_attnum] = ObjectIdGetDatum(staop);
725 234 : replaces[staop_attnum] = true;
726 : }
727 270 : if (DatumGetObjectId(values[stacoll_attnum]) != stacoll)
728 : {
729 90 : values[stacoll_attnum] = ObjectIdGetDatum(stacoll);
730 90 : replaces[stacoll_attnum] = true;
731 : }
732 270 : if (!stanumbers_isnull)
733 : {
734 174 : values[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = stanumbers;
735 174 : nulls[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = false;
736 174 : replaces[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = true;
737 : }
738 270 : if (!stavalues_isnull)
739 : {
740 174 : values[Anum_pg_statistic_stavalues1 - 1 + slotidx] = stavalues;
741 174 : nulls[Anum_pg_statistic_stavalues1 - 1 + slotidx] = false;
742 174 : replaces[Anum_pg_statistic_stavalues1 - 1 + slotidx] = true;
743 : }
744 270 : }
745 :
746 : /*
747 : * Upsert the pg_statistic record.
748 : */
749 : static void
750 228 : upsert_pg_statistic(Relation starel, HeapTuple oldtup,
751 : Datum *values, bool *nulls, bool *replaces)
752 : {
753 : HeapTuple newtup;
754 :
755 228 : if (HeapTupleIsValid(oldtup))
756 : {
757 120 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(starel),
758 : values, nulls, replaces);
759 120 : CatalogTupleUpdate(starel, &newtup->t_self, newtup);
760 : }
761 : else
762 : {
763 108 : newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
764 108 : CatalogTupleInsert(starel, newtup);
765 : }
766 :
767 228 : heap_freetuple(newtup);
768 :
769 228 : CommandCounterIncrement();
770 228 : }
771 :
772 : /*
773 : * Delete pg_statistic record.
774 : */
775 : static bool
776 54 : delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
777 : {
778 54 : Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
779 : HeapTuple oldtup;
780 54 : bool result = false;
781 :
782 : /* Is there already a pg_statistic tuple for this attribute? */
783 54 : oldtup = SearchSysCache3(STATRELATTINH,
784 : ObjectIdGetDatum(reloid),
785 : Int16GetDatum(attnum),
786 : BoolGetDatum(stainherit));
787 :
788 54 : if (HeapTupleIsValid(oldtup))
789 : {
790 54 : CatalogTupleDelete(sd, &oldtup->t_self);
791 54 : ReleaseSysCache(oldtup);
792 54 : result = true;
793 : }
794 :
795 54 : table_close(sd, RowExclusiveLock);
796 :
797 54 : CommandCounterIncrement();
798 :
799 54 : return result;
800 : }
801 :
802 : /*
803 : * Initialize values and nulls for a new stats tuple.
804 : */
805 : static void
806 108 : init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
807 : Datum *values, bool *nulls, bool *replaces)
808 : {
809 108 : memset(nulls, true, sizeof(bool) * Natts_pg_statistic);
810 108 : memset(replaces, true, sizeof(bool) * Natts_pg_statistic);
811 :
812 : /* must initialize non-NULL attributes */
813 :
814 108 : values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(reloid);
815 108 : nulls[Anum_pg_statistic_starelid - 1] = false;
816 108 : values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(attnum);
817 108 : nulls[Anum_pg_statistic_staattnum - 1] = false;
818 108 : values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inherited);
819 108 : nulls[Anum_pg_statistic_stainherit - 1] = false;
820 :
821 108 : values[Anum_pg_statistic_stanullfrac - 1] = DEFAULT_NULL_FRAC;
822 108 : nulls[Anum_pg_statistic_stanullfrac - 1] = false;
823 108 : values[Anum_pg_statistic_stawidth - 1] = DEFAULT_AVG_WIDTH;
824 108 : nulls[Anum_pg_statistic_stawidth - 1] = false;
825 108 : values[Anum_pg_statistic_stadistinct - 1] = DEFAULT_N_DISTINCT;
826 108 : nulls[Anum_pg_statistic_stadistinct - 1] = false;
827 :
828 : /* initialize stakind, staop, and stacoll slots */
829 648 : for (int slotnum = 0; slotnum < STATISTIC_NUM_SLOTS; slotnum++)
830 : {
831 540 : values[Anum_pg_statistic_stakind1 + slotnum - 1] = (Datum) 0;
832 540 : nulls[Anum_pg_statistic_stakind1 + slotnum - 1] = false;
833 540 : values[Anum_pg_statistic_staop1 + slotnum - 1] = InvalidOid;
834 540 : nulls[Anum_pg_statistic_staop1 + slotnum - 1] = false;
835 540 : values[Anum_pg_statistic_stacoll1 + slotnum - 1] = InvalidOid;
836 540 : nulls[Anum_pg_statistic_stacoll1 + slotnum - 1] = false;
837 : }
838 108 : }
839 :
840 : /*
841 : * Import statistics for a given relation attribute.
842 : *
843 : * Inserts or replaces a row in pg_statistic for the given relation and
844 : * attribute name. It takes input parameters that correspond to columns in the
845 : * view pg_stats.
846 : *
847 : * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
848 : * columns in pg_statistic. The remaining parameters all belong to a specific
849 : * stakind. Some stakinds require multiple parameters, which must be specified
850 : * together (or neither specified).
851 : *
852 : * Parameters are only superficially validated. Omitting a parameter or
853 : * passing NULL leaves the statistic unchanged.
854 : *
855 : * Parameters corresponding to ANYARRAY columns are instead passed in as text
856 : * values, which is a valid input string for an array of the type or element
857 : * type of the attribute. Any error generated by the array_in() function will
858 : * in turn fail the function.
859 : */
860 : Datum
861 210 : pg_set_attribute_stats(PG_FUNCTION_ARGS)
862 : {
863 210 : attribute_statistics_update(fcinfo, ERROR);
864 84 : PG_RETURN_VOID();
865 : }
866 :
867 : /*
868 : * Delete statistics for the given attribute.
869 : */
870 : Datum
871 72 : pg_clear_attribute_stats(PG_FUNCTION_ARGS)
872 : {
873 : Oid reloid;
874 : Name attname;
875 : AttrNumber attnum;
876 : bool inherited;
877 :
878 72 : stats_check_required_arg(fcinfo, attarginfo, ATTRELATION_ARG);
879 72 : reloid = PG_GETARG_OID(ATTRELATION_ARG);
880 :
881 72 : if (RecoveryInProgress())
882 0 : ereport(ERROR,
883 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
884 : errmsg("recovery is in progress"),
885 : errhint("Statistics cannot be modified during recovery.")));
886 :
887 72 : stats_lock_check_privileges(reloid);
888 :
889 66 : stats_check_required_arg(fcinfo, attarginfo, ATTNAME_ARG);
890 66 : attname = PG_GETARG_NAME(ATTNAME_ARG);
891 66 : attnum = get_attnum(reloid, NameStr(*attname));
892 :
893 66 : if (attnum < 0)
894 6 : ereport(ERROR,
895 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
896 : errmsg("cannot clear statistics on system column \"%s\"",
897 : NameStr(*attname))));
898 :
899 60 : if (attnum == InvalidAttrNumber)
900 6 : ereport(ERROR,
901 : (errcode(ERRCODE_UNDEFINED_COLUMN),
902 : errmsg("column \"%s\" of relation \"%s\" does not exist",
903 : NameStr(*attname), get_rel_name(reloid))));
904 :
905 54 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
906 54 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
907 :
908 54 : delete_pg_statistic(reloid, attnum, inherited);
909 54 : PG_RETURN_VOID();
910 : }
911 :
912 : Datum
913 174 : pg_restore_attribute_stats(PG_FUNCTION_ARGS)
914 : {
915 174 : LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
916 174 : bool result = true;
917 :
918 174 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
919 : InvalidOid, NULL, NULL);
920 :
921 174 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
922 : attarginfo, WARNING))
923 12 : result = false;
924 :
925 174 : if (!attribute_statistics_update(positional_fcinfo, WARNING))
926 66 : result = false;
927 :
928 144 : PG_RETURN_BOOL(result);
929 : }
|