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