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