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