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