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