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