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-2024, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994, Regents of the University of California
11  *
12  * IDENTIFICATION
13  * src/backend/statistics/attribute_stats.c
14  *
15  *-------------------------------------------------------------------------
16  */
17 
18 #include "postgres.h"
19 
20 #include "access/heapam.h"
21 #include "catalog/indexing.h"
22 #include "catalog/pg_collation.h"
23 #include "catalog/pg_operator.h"
24 #include "nodes/nodeFuncs.h"
25 #include "statistics/statistics.h"
26 #include "statistics/stat_utils.h"
27 #include "utils/array.h"
28 #include "utils/builtins.h"
29 #include "utils/fmgroids.h"
30 #include "utils/lsyscache.h"
31 #include "utils/syscache.h"
32 
33 #define DEFAULT_NULL_FRAC Float4GetDatum(0.0)
34 #define DEFAULT_AVG_WIDTH Int32GetDatum(0) /* unknown */
35 #define DEFAULT_N_DISTINCT Float4GetDatum(0.0) /* unknown */
36 
38 {
56 };
57 
58 static struct StatsArgInfo attarginfo[] =
59 {
60  [ATTRELATION_ARG] = {"relation", REGCLASSOID},
61  [ATTNAME_ARG] = {"attname", NAMEOID},
62  [INHERITED_ARG] = {"inherited", BOOLOID},
63  [NULL_FRAC_ARG] = {"null_frac", FLOAT4OID},
64  [AVG_WIDTH_ARG] = {"avg_width", INT4OID},
65  [N_DISTINCT_ARG] = {"n_distinct", FLOAT4OID},
66  [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTOID},
67  [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT4ARRAYOID},
68  [HISTOGRAM_BOUNDS_ARG] = {"histogram_bounds", TEXTOID},
69  [CORRELATION_ARG] = {"correlation", FLOAT4OID},
70  [MOST_COMMON_ELEMS_ARG] = {"most_common_elems", TEXTOID},
71  [MOST_COMMON_ELEM_FREQS_ARG] = {"most_common_elem_freqs", FLOAT4ARRAYOID},
72  [ELEM_COUNT_HISTOGRAM_ARG] = {"elem_count_histogram", FLOAT4ARRAYOID},
73  [RANGE_LENGTH_HISTOGRAM_ARG] = {"range_length_histogram", TEXTOID},
74  [RANGE_EMPTY_FRAC_ARG] = {"range_empty_frac", FLOAT4OID},
75  [RANGE_BOUNDS_HISTOGRAM_ARG] = {"range_bounds_histogram", TEXTOID},
77 };
78 
79 static bool attribute_statistics_update(FunctionCallInfo fcinfo, int elevel);
80 static Node *get_attr_expr(Relation rel, int attnum);
81 static void get_attr_stat_type(Oid reloid, AttrNumber attnum, int elevel,
82  Oid *atttypid, int32 *atttypmod,
83  char *atttyptype, Oid *atttypcoll,
84  Oid *eq_opr, Oid *lt_opr);
85 static bool get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
86  Oid *elemtypid, Oid *elem_eq_opr);
87 static Datum text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d,
88  Oid typid, int32 typmod, int elevel, bool *ok);
89 static void set_stats_slot(Datum *values, bool *nulls, bool *replaces,
90  int16 stakind, Oid staop, Oid stacoll,
91  Datum stanumbers, bool stanumbers_isnull,
92  Datum stavalues, bool stavalues_isnull);
93 static void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
94  Datum *values, bool *nulls, bool *replaces);
95 static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
96 static void init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
97  Datum *values, bool *nulls, bool *replaces);
98 
99 /*
100  * Insert or Update Attribute Statistics
101  *
102  * See pg_statistic.h for an explanation of how each statistic kind is
103  * stored. Custom statistics kinds are not supported.
104  *
105  * Depending on the statistics kind, we need to derive information from the
106  * attribute for which we're storing the stats. For instance, the MCVs are
107  * stored as an anyarray, and the representation of the array needs to store
108  * the correct element type, which must be derived from the attribute.
109  *
110  * Major errors, such as the table not existing, the attribute not existing,
111  * or a permissions failure are always reported at ERROR. Other errors, such
112  * as a conversion failure on one statistic kind, are reported at 'elevel',
113  * and other statistic kinds may still be updated.
114  */
115 static bool
117 {
118  Oid reloid;
119  Name attname;
120  bool inherited;
122 
123  Relation starel;
124  HeapTuple statup;
125 
126  Oid atttypid = InvalidOid;
127  int32 atttypmod;
128  char atttyptype;
129  Oid atttypcoll = InvalidOid;
130  Oid eq_opr = InvalidOid;
131  Oid lt_opr = InvalidOid;
132 
133  Oid elemtypid = InvalidOid;
134  Oid elem_eq_opr = InvalidOid;
135 
136  FmgrInfo array_in_fn;
137 
138  bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
140  bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
141  bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
142  bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
144  bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
145  bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
146  bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
148 
149  Datum values[Natts_pg_statistic] = {0};
150  bool nulls[Natts_pg_statistic] = {0};
151  bool replaces[Natts_pg_statistic] = {0};
152 
153  bool result = true;
154 
156  reloid = PG_GETARG_OID(ATTRELATION_ARG);
157 
158  if (RecoveryInProgress())
159  ereport(ERROR,
160  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
161  errmsg("recovery is in progress"),
162  errhint("Statistics cannot be modified during recovery.")));
163 
164  /* lock before looking up attribute */
166 
169  attnum = get_attnum(reloid, NameStr(*attname));
170 
171  if (attnum < 0)
172  ereport(ERROR,
173  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
174  errmsg("cannot modify statistics on system column \"%s\"",
175  NameStr(*attname))));
176 
177  if (attnum == InvalidAttrNumber)
178  ereport(ERROR,
179  (errcode(ERRCODE_UNDEFINED_COLUMN),
180  errmsg("column \"%s\" of relation \"%s\" does not exist",
181  NameStr(*attname), get_rel_name(reloid))));
182 
184  inherited = PG_GETARG_BOOL(INHERITED_ARG);
185 
186  /*
187  * Check argument sanity. If some arguments are unusable, emit at elevel
188  * and set the corresponding argument to NULL in fcinfo.
189  */
190 
192  elevel))
193  {
194  do_mcv = false;
195  result = false;
196  }
197 
199  elevel))
200  {
201  do_mcelem = false;
202  result = false;
203  }
205  elevel))
206  {
207  do_dechist = false;
208  result = false;
209  }
210 
211  if (!stats_check_arg_pair(fcinfo, attarginfo,
213  elevel))
214  {
215  do_mcv = false;
216  result = false;
217  }
218 
219  if (!stats_check_arg_pair(fcinfo, attarginfo,
222  {
223  do_mcelem = false;
224  result = false;
225  }
226 
227  if (!stats_check_arg_pair(fcinfo, attarginfo,
229  RANGE_EMPTY_FRAC_ARG, elevel))
230  {
231  do_range_length_histogram = false;
232  result = false;
233  }
234 
235  /* derive information from attribute */
236  get_attr_stat_type(reloid, attnum, elevel,
237  &atttypid, &atttypmod,
238  &atttyptype, &atttypcoll,
239  &eq_opr, &lt_opr);
240 
241  /* if needed, derive element type */
242  if (do_mcelem || do_dechist)
243  {
244  if (!get_elem_stat_type(atttypid, atttyptype, elevel,
245  &elemtypid, &elem_eq_opr))
246  {
247  ereport(elevel,
248  (errmsg("unable to determine element type of attribute \"%s\"", NameStr(*attname)),
249  errdetail("Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST.")));
250  elemtypid = InvalidOid;
251  elem_eq_opr = InvalidOid;
252 
253  do_mcelem = false;
254  do_dechist = false;
255  result = false;
256  }
257  }
258 
259  /* histogram and correlation require less-than operator */
260  if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
261  {
262  ereport(elevel,
263  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
264  errmsg("could not determine less-than operator for attribute \"%s\"", NameStr(*attname)),
265  errdetail("Cannot set STATISTIC_KIND_HISTOGRAM or STATISTIC_KIND_CORRELATION.")));
266 
267  do_histogram = false;
268  do_correlation = false;
269  result = false;
270  }
271 
272  /* only range types can have range stats */
273  if ((do_range_length_histogram || do_bounds_histogram) &&
274  !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
275  {
276  ereport(elevel,
277  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
278  errmsg("attribute \"%s\" is not a range type", NameStr(*attname)),
279  errdetail("Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM.")));
280 
281  do_bounds_histogram = false;
282  do_range_length_histogram = false;
283  result = false;
284  }
285 
286  fmgr_info(F_ARRAY_IN, &array_in_fn);
287 
288  starel = table_open(StatisticRelationId, RowExclusiveLock);
289 
290  statup = SearchSysCache3(STATRELATTINH, reloid, attnum, inherited);
291 
292  /* initialize from existing tuple if exists */
293  if (HeapTupleIsValid(statup))
294  heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
295  else
296  init_empty_stats_tuple(reloid, attnum, inherited, values, nulls,
297  replaces);
298 
299  /* if specified, set to argument values */
301  {
302  values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
303  replaces[Anum_pg_statistic_stanullfrac - 1] = true;
304  }
306  {
307  values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
308  replaces[Anum_pg_statistic_stawidth - 1] = true;
309  }
311  {
312  values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
313  replaces[Anum_pg_statistic_stadistinct - 1] = true;
314  }
315 
316  /* STATISTIC_KIND_MCV */
317  if (do_mcv)
318  {
319  bool converted;
321  Datum stavalues = text_to_stavalues("most_common_vals",
322  &array_in_fn,
324  atttypid, atttypmod,
325  elevel, &converted);
326 
327  if (converted)
328  {
329  set_stats_slot(values, nulls, replaces,
330  STATISTIC_KIND_MCV,
331  eq_opr, atttypcoll,
332  stanumbers, false, stavalues, false);
333  }
334  else
335  result = false;
336  }
337 
338  /* STATISTIC_KIND_HISTOGRAM */
339  if (do_histogram)
340  {
341  Datum stavalues;
342  bool converted = false;
343 
344  stavalues = text_to_stavalues("histogram_bounds",
345  &array_in_fn,
347  atttypid, atttypmod, elevel,
348  &converted);
349 
350  if (converted)
351  {
352  set_stats_slot(values, nulls, replaces,
353  STATISTIC_KIND_HISTOGRAM,
354  lt_opr, atttypcoll,
355  0, true, stavalues, false);
356  }
357  else
358  result = false;
359  }
360 
361  /* STATISTIC_KIND_CORRELATION */
362  if (do_correlation)
363  {
365  ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
366  Datum stanumbers = PointerGetDatum(arry);
367 
368  set_stats_slot(values, nulls, replaces,
369  STATISTIC_KIND_CORRELATION,
370  lt_opr, atttypcoll,
371  stanumbers, false, 0, true);
372  }
373 
374  /* STATISTIC_KIND_MCELEM */
375  if (do_mcelem)
376  {
378  bool converted = false;
379  Datum stavalues;
380 
381  stavalues = text_to_stavalues("most_common_elems",
382  &array_in_fn,
384  elemtypid, atttypmod,
385  elevel, &converted);
386 
387  if (converted)
388  {
389  set_stats_slot(values, nulls, replaces,
390  STATISTIC_KIND_MCELEM,
391  elem_eq_opr, atttypcoll,
392  stanumbers, false, stavalues, false);
393  }
394  else
395  result = false;
396  }
397 
398  /* STATISTIC_KIND_DECHIST */
399  if (do_dechist)
400  {
402 
403  set_stats_slot(values, nulls, replaces,
404  STATISTIC_KIND_DECHIST,
405  elem_eq_opr, atttypcoll,
406  stanumbers, false, 0, true);
407  }
408 
409  /*
410  * STATISTIC_KIND_BOUNDS_HISTOGRAM
411  *
412  * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
413  * though it is numerically greater, and all other stakinds appear in
414  * numerical order. We duplicate this quirk for consistency.
415  */
416  if (do_bounds_histogram)
417  {
418  bool converted = false;
419  Datum stavalues;
420 
421  stavalues = text_to_stavalues("range_bounds_histogram",
422  &array_in_fn,
424  atttypid, atttypmod,
425  elevel, &converted);
426 
427  if (converted)
428  {
429  set_stats_slot(values, nulls, replaces,
430  STATISTIC_KIND_BOUNDS_HISTOGRAM,
432  0, true, stavalues, false);
433  }
434  else
435  result = false;
436  }
437 
438  /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
439  if (do_range_length_histogram)
440  {
441  /* The anyarray is always a float8[] for this stakind */
443  ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
444  Datum stanumbers = PointerGetDatum(arry);
445 
446  bool converted = false;
447  Datum stavalues;
448 
449  stavalues = text_to_stavalues("range_length_histogram",
450  &array_in_fn,
452  FLOAT8OID, 0, elevel, &converted);
453 
454  if (converted)
455  {
456  set_stats_slot(values, nulls, replaces,
457  STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
458  Float8LessOperator, InvalidOid,
459  stanumbers, false, stavalues, false);
460  }
461  else
462  result = false;
463  }
464 
465  upsert_pg_statistic(starel, statup, values, nulls, replaces);
466 
467  if (HeapTupleIsValid(statup))
468  ReleaseSysCache(statup);
469  table_close(starel, RowExclusiveLock);
470 
471  return result;
472 }
473 
474 /*
475  * If this relation is an index and that index has expressions in it, and
476  * the attnum specified is known to be an expression, then we must walk
477  * the list attributes up to the specified attnum to get the right
478  * expression.
479  */
480 static Node *
482 {
483  if ((rel->rd_rel->relkind == RELKIND_INDEX
484  || (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX))
485  && (rel->rd_indexprs != NIL)
486  && (rel->rd_index->indkey.values[attnum - 1] == 0))
487  {
488  ListCell *indexpr_item = list_head(rel->rd_indexprs);
489 
490  for (int i = 0; i < attnum - 1; i++)
491  if (rel->rd_index->indkey.values[i] == 0)
492  indexpr_item = lnext(rel->rd_indexprs, indexpr_item);
493 
494  if (indexpr_item == NULL) /* shouldn't happen */
495  elog(ERROR, "too few entries in indexprs list");
496 
497  return (Node *) lfirst(indexpr_item);
498  }
499  return NULL;
500 }
501 
502 /*
503  * Derive type information from the attribute.
504  */
505 static void
507  Oid *atttypid, int32 *atttypmod,
508  char *atttyptype, Oid *atttypcoll,
509  Oid *eq_opr, Oid *lt_opr)
510 {
511  Relation rel = relation_open(reloid, AccessShareLock);
512  Form_pg_attribute attr;
513  HeapTuple atup;
514  Node *expr;
515  TypeCacheEntry *typcache;
516 
517  atup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(reloid),
519 
520  /* Attribute not found */
521  if (!HeapTupleIsValid(atup))
522  ereport(ERROR,
523  (errcode(ERRCODE_UNDEFINED_COLUMN),
524  errmsg("attribute %d of relation \"%s\" does not exist",
526 
527  attr = (Form_pg_attribute) GETSTRUCT(atup);
528 
529  if (attr->attisdropped)
530  ereport(ERROR,
531  (errcode(ERRCODE_UNDEFINED_COLUMN),
532  errmsg("attribute %d of relation \"%s\" does not exist",
534 
535  expr = get_attr_expr(rel, attr->attnum);
536 
537  /*
538  * When analyzing an expression index, believe the expression tree's type
539  * not the column datatype --- the latter might be the opckeytype storage
540  * type of the opclass, which is not interesting for our purposes. This
541  * mimics the behvior of examine_attribute().
542  */
543  if (expr == NULL)
544  {
545  *atttypid = attr->atttypid;
546  *atttypmod = attr->atttypmod;
547  *atttypcoll = attr->attcollation;
548  }
549  else
550  {
551  *atttypid = exprType(expr);
552  *atttypmod = exprTypmod(expr);
553 
554  if (OidIsValid(attr->attcollation))
555  *atttypcoll = attr->attcollation;
556  else
557  *atttypcoll = exprCollation(expr);
558  }
559  ReleaseSysCache(atup);
560 
561  /*
562  * If it's a multirange, step down to the range type, as is done by
563  * multirange_typanalyze().
564  */
565  if (type_is_multirange(*atttypid))
566  *atttypid = get_multirange_range(*atttypid);
567 
568  /* finds the right operators even if atttypid is a domain */
569  typcache = lookup_type_cache(*atttypid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
570  *atttyptype = typcache->typtype;
571  *eq_opr = typcache->eq_opr;
572  *lt_opr = typcache->lt_opr;
573 
574  /*
575  * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
576  * compute_tsvector_stats().
577  */
578  if (*atttypid == TSVECTOROID)
579  *atttypcoll = DEFAULT_COLLATION_OID;
580 
581  relation_close(rel, NoLock);
582 }
583 
584 /*
585  * Derive element type information from the attribute type.
586  */
587 static bool
588 get_elem_stat_type(Oid atttypid, char atttyptype, int elevel,
589  Oid *elemtypid, Oid *elem_eq_opr)
590 {
591  TypeCacheEntry *elemtypcache;
592 
593  if (atttypid == TSVECTOROID)
594  {
595  /*
596  * Special case: element type for tsvector is text. See
597  * compute_tsvector_stats().
598  */
599  *elemtypid = TEXTOID;
600  }
601  else
602  {
603  /* find underlying element type through any domain */
604  *elemtypid = get_base_element_type(atttypid);
605  }
606 
607  if (!OidIsValid(*elemtypid))
608  return false;
609 
610  /* finds the right operator even if elemtypid is a domain */
611  elemtypcache = lookup_type_cache(*elemtypid, TYPECACHE_EQ_OPR);
612  if (!OidIsValid(elemtypcache->eq_opr))
613  return false;
614 
615  *elem_eq_opr = elemtypcache->eq_opr;
616 
617  return true;
618 }
619 
620 /*
621  * Cast a text datum into an array with element type elemtypid.
622  *
623  * If an error is encountered, capture it and re-throw at elevel, and set ok
624  * to false. If the resulting array contains NULLs, raise an error at elevel
625  * and set ok to false. Otherwise, set ok to true.
626  */
627 static Datum
628 text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
629  int32 typmod, int elevel, bool *ok)
630 {
631  LOCAL_FCINFO(fcinfo, 8);
632  char *s;
633  Datum result;
634  ErrorSaveContext escontext = {T_ErrorSaveContext};
635 
636  escontext.details_wanted = true;
637 
638  s = TextDatumGetCString(d);
639 
641  (Node *) &escontext, NULL);
642 
643  fcinfo->args[0].value = CStringGetDatum(s);
644  fcinfo->args[0].isnull = false;
645  fcinfo->args[1].value = ObjectIdGetDatum(typid);
646  fcinfo->args[1].isnull = false;
647  fcinfo->args[2].value = Int32GetDatum(typmod);
648  fcinfo->args[2].isnull = false;
649 
650  result = FunctionCallInvoke(fcinfo);
651 
652  pfree(s);
653 
654  if (escontext.error_occurred)
655  {
656  if (elevel != ERROR)
657  escontext.error_data->elevel = elevel;
658  ThrowErrorData(escontext.error_data);
659  *ok = false;
660  return (Datum) 0;
661  }
662 
664  {
665  ereport(elevel,
666  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
667  errmsg("\"%s\" array cannot contain NULL values", staname)));
668  *ok = false;
669  return (Datum) 0;
670  }
671 
672  *ok = true;
673 
674  return result;
675 }
676 
677 /*
678  * Find and update the slot with the given stakind, or use the first empty
679  * slot.
680  */
681 static void
682 set_stats_slot(Datum *values, bool *nulls, bool *replaces,
683  int16 stakind, Oid staop, Oid stacoll,
684  Datum stanumbers, bool stanumbers_isnull,
685  Datum stavalues, bool stavalues_isnull)
686 {
687  int slotidx;
688  int first_empty = -1;
689  AttrNumber stakind_attnum;
690  AttrNumber staop_attnum;
691  AttrNumber stacoll_attnum;
692 
693  /* find existing slot with given stakind */
694  for (slotidx = 0; slotidx < STATISTIC_NUM_SLOTS; slotidx++)
695  {
696  stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
697 
698  if (first_empty < 0 &&
699  DatumGetInt16(values[stakind_attnum]) == 0)
700  first_empty = slotidx;
701  if (DatumGetInt16(values[stakind_attnum]) == stakind)
702  break;
703  }
704 
705  if (slotidx >= STATISTIC_NUM_SLOTS && first_empty >= 0)
706  slotidx = first_empty;
707 
708  if (slotidx >= STATISTIC_NUM_SLOTS)
709  ereport(ERROR,
710  (errmsg("maximum number of statistics slots exceeded: %d",
711  slotidx + 1)));
712 
713  stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
714  staop_attnum = Anum_pg_statistic_staop1 - 1 + slotidx;
715  stacoll_attnum = Anum_pg_statistic_stacoll1 - 1 + slotidx;
716 
717  if (DatumGetInt16(values[stakind_attnum]) != stakind)
718  {
719  values[stakind_attnum] = Int16GetDatum(stakind);
720  replaces[stakind_attnum] = true;
721  }
722  if (DatumGetObjectId(values[staop_attnum]) != staop)
723  {
724  values[staop_attnum] = ObjectIdGetDatum(staop);
725  replaces[staop_attnum] = true;
726  }
727  if (DatumGetObjectId(values[stacoll_attnum]) != stacoll)
728  {
729  values[stacoll_attnum] = ObjectIdGetDatum(stacoll);
730  replaces[stacoll_attnum] = true;
731  }
732  if (!stanumbers_isnull)
733  {
734  values[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = stanumbers;
735  nulls[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = false;
736  replaces[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = true;
737  }
738  if (!stavalues_isnull)
739  {
740  values[Anum_pg_statistic_stavalues1 - 1 + slotidx] = stavalues;
741  nulls[Anum_pg_statistic_stavalues1 - 1 + slotidx] = false;
742  replaces[Anum_pg_statistic_stavalues1 - 1 + slotidx] = true;
743  }
744 }
745 
746 /*
747  * Upsert the pg_statistic record.
748  */
749 static void
751  Datum *values, bool *nulls, bool *replaces)
752 {
753  HeapTuple newtup;
754 
755  if (HeapTupleIsValid(oldtup))
756  {
757  newtup = heap_modify_tuple(oldtup, RelationGetDescr(starel),
758  values, nulls, replaces);
759  CatalogTupleUpdate(starel, &newtup->t_self, newtup);
760  }
761  else
762  {
763  newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
764  CatalogTupleInsert(starel, newtup);
765  }
766 
767  heap_freetuple(newtup);
768 
770 }
771 
772 /*
773  * Delete pg_statistic record.
774  */
775 static bool
776 delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
777 {
778  Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
779  HeapTuple oldtup;
780  bool result = false;
781 
782  /* Is there already a pg_statistic tuple for this attribute? */
783  oldtup = SearchSysCache3(STATRELATTINH,
784  ObjectIdGetDatum(reloid),
786  BoolGetDatum(stainherit));
787 
788  if (HeapTupleIsValid(oldtup))
789  {
790  CatalogTupleDelete(sd, &oldtup->t_self);
791  ReleaseSysCache(oldtup);
792  result = true;
793  }
794 
796 
798 
799  return result;
800 }
801 
802 /*
803  * Initialize values and nulls for a new stats tuple.
804  */
805 static void
806 init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
807  Datum *values, bool *nulls, bool *replaces)
808 {
809  memset(nulls, true, sizeof(bool) * Natts_pg_statistic);
810  memset(replaces, true, sizeof(bool) * Natts_pg_statistic);
811 
812  /* must initialize non-NULL attributes */
813 
814  values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(reloid);
815  nulls[Anum_pg_statistic_starelid - 1] = false;
816  values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(attnum);
817  nulls[Anum_pg_statistic_staattnum - 1] = false;
818  values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inherited);
819  nulls[Anum_pg_statistic_stainherit - 1] = false;
820 
821  values[Anum_pg_statistic_stanullfrac - 1] = DEFAULT_NULL_FRAC;
822  nulls[Anum_pg_statistic_stanullfrac - 1] = false;
823  values[Anum_pg_statistic_stawidth - 1] = DEFAULT_AVG_WIDTH;
824  nulls[Anum_pg_statistic_stawidth - 1] = false;
825  values[Anum_pg_statistic_stadistinct - 1] = DEFAULT_N_DISTINCT;
826  nulls[Anum_pg_statistic_stadistinct - 1] = false;
827 
828  /* initialize stakind, staop, and stacoll slots */
829  for (int slotnum = 0; slotnum < STATISTIC_NUM_SLOTS; slotnum++)
830  {
831  values[Anum_pg_statistic_stakind1 + slotnum - 1] = (Datum) 0;
832  nulls[Anum_pg_statistic_stakind1 + slotnum - 1] = false;
833  values[Anum_pg_statistic_staop1 + slotnum - 1] = InvalidOid;
834  nulls[Anum_pg_statistic_staop1 + slotnum - 1] = false;
835  values[Anum_pg_statistic_stacoll1 + slotnum - 1] = InvalidOid;
836  nulls[Anum_pg_statistic_stacoll1 + slotnum - 1] = false;
837  }
838 }
839 
840 /*
841  * Import statistics for a given relation attribute.
842  *
843  * Inserts or replaces a row in pg_statistic for the given relation and
844  * attribute name. It takes input parameters that correspond to columns in the
845  * view pg_stats.
846  *
847  * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
848  * columns in pg_statistic. The remaining parameters all belong to a specific
849  * stakind. Some stakinds require multiple parameters, which must be specified
850  * together (or neither specified).
851  *
852  * Parameters are only superficially validated. Omitting a parameter or
853  * passing NULL leaves the statistic unchanged.
854  *
855  * Parameters corresponding to ANYARRAY columns are instead passed in as text
856  * values, which is a valid input string for an array of the type or element
857  * type of the attribute. Any error generated by the array_in() function will
858  * in turn fail the function.
859  */
860 Datum
862 {
864  PG_RETURN_VOID();
865 }
866 
867 /*
868  * Delete statistics for the given attribute.
869  */
870 Datum
872 {
873  Oid reloid;
874  Name attname;
876  bool inherited;
877 
879  reloid = PG_GETARG_OID(ATTRELATION_ARG);
880 
881  if (RecoveryInProgress())
882  ereport(ERROR,
883  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
884  errmsg("recovery is in progress"),
885  errhint("Statistics cannot be modified during recovery.")));
886 
888 
891  attnum = get_attnum(reloid, NameStr(*attname));
892 
893  if (attnum < 0)
894  ereport(ERROR,
895  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
896  errmsg("cannot clear statistics on system column \"%s\"",
897  NameStr(*attname))));
898 
899  if (attnum == InvalidAttrNumber)
900  ereport(ERROR,
901  (errcode(ERRCODE_UNDEFINED_COLUMN),
902  errmsg("column \"%s\" of relation \"%s\" does not exist",
903  NameStr(*attname), get_rel_name(reloid))));
904 
906  inherited = PG_GETARG_BOOL(INHERITED_ARG);
907 
908  delete_pg_statistic(reloid, attnum, inherited);
909  PG_RETURN_VOID();
910 }
911 
912 Datum
914 {
915  LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
916  bool result = true;
917 
918  InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
919  InvalidOid, NULL, NULL);
920 
921  if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
923  result = false;
924 
925  if (!attribute_statistics_update(positional_fcinfo, WARNING))
926  result = false;
927 
928  PG_RETURN_BOOL(result);
929 }
#define DatumGetArrayTypeP(X)
Definition: array.h:261
bool array_contains_nulls(ArrayType *array)
Definition: arrayfuncs.c:3767
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
Datum array_in(PG_FUNCTION_ARGS)
Definition: arrayfuncs.c:179
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
static Datum text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid, int32 typmod, int elevel, bool *ok)
static bool get_elem_stat_type(Oid atttypid, char atttyptype, int elevel, Oid *elemtypid, Oid *elem_eq_opr)
static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
static bool attribute_statistics_update(FunctionCallInfo fcinfo, int elevel)
attribute_stats_argnum
@ RANGE_LENGTH_HISTOGRAM_ARG
@ RANGE_BOUNDS_HISTOGRAM_ARG
@ AVG_WIDTH_ARG
@ INHERITED_ARG
@ ATTRELATION_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
#define DEFAULT_NULL_FRAC
static struct StatsArgInfo attarginfo[]
#define DEFAULT_N_DISTINCT
Datum pg_clear_attribute_stats(PG_FUNCTION_ARGS)
static Node * get_attr_expr(Relation rel, int attnum)
Datum pg_set_attribute_stats(PG_FUNCTION_ARGS)
#define DEFAULT_AVG_WIDTH
static void get_attr_stat_type(Oid reloid, AttrNumber attnum, int elevel, Oid *atttypid, int32 *atttypmod, char *atttyptype, Oid *atttypcoll, Oid *eq_opr, Oid *lt_opr)
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
#define NameStr(name)
Definition: c.h:700
int16_t int16
Definition: c.h:480
int32_t int32
Definition: c.h:481
#define OidIsValid(objectId)
Definition: c.h:729
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
void ThrowErrorData(ErrorData *edata)
Definition: elog.c:1895
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#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 PG_GETARG_OID(n)
Definition: fmgr.h:275
#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 PG_GETARG_NAME(n)
Definition: fmgr.h:278
#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
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1209
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1345
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
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:72
#define NoLock
Definition: lockdefs.h:34
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:858
Oid get_multirange_range(Oid multirangeOid)
Definition: lsyscache.c:3483
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
Oid get_base_element_type(Oid typid)
Definition: lsyscache.c:2832
bool type_is_multirange(Oid typid)
Definition: lsyscache.c:2698
void pfree(void *pointer)
Definition: mcxt.c:1521
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition: nodeFuncs.c:298
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:816
NameData attname
Definition: pg_attribute.h:41
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
#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:322
uintptr_t Datum
Definition: postgres.h:64
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
static int16 DatumGetInt16(Datum X)
Definition: postgres.h:162
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
bool stats_check_arg_array(FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum, int elevel)
Definition: stat_utils.c:52
void stats_check_required_arg(FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum)
Definition: stat_utils.c:33
bool stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo, FunctionCallInfo positional_fcinfo, struct StatsArgInfo *arginfo, int elevel)
Definition: stat_utils.c:217
bool stats_check_arg_pair(FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum1, int argnum2, int elevel)
Definition: stat_utils.c:93
void stats_lock_check_privileges(Oid reloid)
Definition: stat_utils.c:127
int elevel
Definition: elog.h:429
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: nodes.h:129
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
Definition: c.h:695
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:243
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:137
#define TYPECACHE_LT_OPR
Definition: typcache.h:138
void CommandCounterIncrement(void)
Definition: xact.c:1099
bool RecoveryInProgress(void)
Definition: xlog.c:6334