PostgreSQL Source Code git master
Loading...
Searching...
No Matches
stat_utils.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_class.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_database.h"
#include "catalog/pg_statistic.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "statistics/stat_utils.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for stat_utils.c:

Go to the source code of this file.

Macros

#define DEFAULT_STATATT_NULL_FRAC   Float4GetDatum(0.0) /* stanullfrac */
 
#define DEFAULT_STATATT_AVG_WIDTH
 
#define DEFAULT_STATATT_N_DISTINCT
 

Functions

static Nodestatatt_get_index_expr (Relation rel, int attnum)
 
void stats_check_required_arg (FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum)
 
bool stats_check_arg_array (FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum)
 
bool stats_check_arg_pair (FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum1, int argnum2)
 
void RangeVarCallbackForStats (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static int get_arg_by_name (const char *argname, struct StatsArgInfo *arginfo)
 
static bool stats_check_arg_type (const char *argname, Oid argtype, Oid expectedtype)
 
bool stats_fill_fcinfo_from_arg_pairs (FunctionCallInfo pairs_fcinfo, FunctionCallInfo positional_fcinfo, struct StatsArgInfo *arginfo)
 
void statatt_get_type (Oid reloid, AttrNumber attnum, Oid *atttypid, int32 *atttypmod, char *atttyptype, Oid *atttypcoll, Oid *eq_opr, Oid *lt_opr)
 
bool statatt_get_elem_type (Oid atttypid, char atttyptype, Oid *elemtypid, Oid *elem_eq_opr)
 
Datum statatt_build_stavalues (const char *staname, FmgrInfo *array_in, Datum d, Oid typid, int32 typmod, bool *ok)
 
void statatt_set_slot (Datum *values, bool *nulls, bool *replaces, int16 stakind, Oid staop, Oid stacoll, Datum stanumbers, bool stanumbers_isnull, Datum stavalues, bool stavalues_isnull)
 
void statatt_init_empty_tuple (Oid reloid, int16 attnum, bool inherited, Datum *values, bool *nulls, bool *replaces)
 
bool statatt_check_bounds_histogram (Datum arrayval)
 

Macro Definition Documentation

◆ DEFAULT_STATATT_AVG_WIDTH

#define DEFAULT_STATATT_AVG_WIDTH
Value:
Int32GetDatum(0) /* stawidth, same as
* unknown */
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212

Definition at line 43 of file stat_utils.c.

57{
61 errmsg("argument \"%s\" must not be null",
62 arginfo[argnum].argname)));
63}
64
65/*
66 * Check that argument is either NULL or a one dimensional array with no
67 * NULLs.
68 *
69 * If a problem is found, emit a WARNING, and return false. Otherwise return
70 * true.
71 */
72bool
74 struct StatsArgInfo *arginfo,
75 int argnum)
76{
77 ArrayType *arr;
78
80 return true;
81
83
84 if (ARR_NDIM(arr) != 1)
85 {
88 errmsg("argument \"%s\" must not be a multidimensional array",
89 arginfo[argnum].argname)));
90 return false;
91 }
92
93 if (array_contains_nulls(arr))
94 {
97 errmsg("argument \"%s\" array must not contain null values",
98 arginfo[argnum].argname)));
99 return false;
100 }
101
102 return true;
103}
104
105/*
106 * Enforce parameter pairs that must be specified together (or not at all) for
107 * a particular stakind, such as most_common_vals and most_common_freqs for
108 * STATISTIC_KIND_MCV.
109 *
110 * If a problem is found, emit a WARNING, and return false. Otherwise return
111 * true.
112 */
113bool
115 struct StatsArgInfo *arginfo,
116 int argnum1, int argnum2)
117{
119 return true;
120
122 {
125
128 errmsg("argument \"%s\" must be specified when argument \"%s\" is specified",
129 arginfo[nullarg].argname,
130 arginfo[otherarg].argname)));
131
132 return false;
133 }
134
135 return true;
136}
137
138/*
139 * A role has privileges to set statistics on the relation if any of the
140 * following are true:
141 * - the role owns the current database and the relation is not shared
142 * - the role has the MAINTAIN privilege on the relation
143 */
144void
145RangeVarCallbackForStats(const RangeVar *relation,
146 Oid relId, Oid oldRelId, void *arg)
147{
148 Oid *locked_oid = (Oid *) arg;
149 Oid table_oid = relId;
150 HeapTuple tuple;
152 char relkind;
153
154 /*
155 * If we previously locked some other index's heap, and the name we're
156 * looking up no longer refers to that relation, release the now-useless
157 * lock.
158 */
159 if (relId != oldRelId && OidIsValid(*locked_oid))
160 {
163 }
164
165 /* If the relation does not exist, there's nothing more to do. */
166 if (!OidIsValid(relId))
167 return;
168
169 /* If the relation does exist, check whether it's an index. */
170 relkind = get_rel_relkind(relId);
171 if (relkind == RELKIND_INDEX ||
172 relkind == RELKIND_PARTITIONED_INDEX)
173 table_oid = IndexGetRelation(relId, false);
174
175 /*
176 * If retrying yields the same OID, there are a couple of extremely
177 * unlikely scenarios we need to handle.
178 */
179 if (relId == oldRelId)
180 {
181 /*
182 * If a previous lookup found an index, but the current lookup did
183 * not, the index was dropped and the OID was reused for something
184 * else between lookups. In theory, we could simply drop our lock on
185 * the index's parent table and proceed, but in the interest of
186 * avoiding complexity, we just error.
187 */
188 if (table_oid == relId && OidIsValid(*locked_oid))
191 errmsg("index \"%s\" was concurrently dropped",
192 relation->relname)));
193
194 /*
195 * If the current lookup found an index but a previous lookup either
196 * did not find an index or found one with a different parent
197 * relation, the relation was dropped and the OID was reused for an
198 * index between lookups. RangeVarGetRelidExtended() will have
199 * already locked the index at this point, so we can't just lock the
200 * newly discovered parent table OID without risking deadlock. As
201 * above, we just error in this case.
202 */
203 if (table_oid != relId && table_oid != *locked_oid)
206 errmsg("index \"%s\" was concurrently created",
207 relation->relname)));
208 }
209
211 if (!HeapTupleIsValid(tuple))
212 elog(ERROR, "cache lookup failed for OID %u", table_oid);
213 form = (Form_pg_class) GETSTRUCT(tuple);
214
215 /* the relkinds that can be used with ANALYZE */
216 switch (form->relkind)
217 {
218 case RELKIND_RELATION:
219 case RELKIND_MATVIEW:
222 break;
223 default:
226 errmsg("cannot modify statistics for relation \"%s\"",
227 NameStr(form->relname)),
229 }
230
231 if (form->relisshared)
234 errmsg("cannot modify statistics for shared relation")));
235
236 /* Check permissions */
238 {
240 GetUserId(),
242
243 if (aclresult != ACLCHECK_OK)
245 get_relkind_objtype(form->relkind),
246 NameStr(form->relname));
247 }
248
249 ReleaseSysCache(tuple);
250
251 /* Lock heap before index to avoid deadlock. */
252 if (relId != oldRelId && table_oid != relId)
253 {
256 }
257}
258
259
260/*
261 * Find the argument number for the given argument name, returning -1 if not
262 * found.
263 */
264static int
265get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo)
266{
267 int argnum;
268
269 for (argnum = 0; arginfo[argnum].argname != NULL; argnum++)
270 if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0)
271 return argnum;
272
274 (errmsg("unrecognized argument name: \"%s\"", argname)));
275
276 return -1;
277}
278
279/*
280 * Ensure that a given argument matched the expected type.
281 */
282static bool
283stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype)
284{
285 if (argtype != expectedtype)
286 {
288 (errmsg("argument \"%s\" has type %s, expected type %s",
289 argname, format_type_be(argtype),
291 return false;
292 }
293
294 return true;
295}
296
297/*
298 * Check if attribute of an index is an expression, then retrieve the
299 * expression if is it the case.
300 *
301 * If the attnum specified is known to be an expression, then we must
302 * walk the list attributes up to the specified attnum to get the right
303 * expression.
304 */
305static Node *
307{
310
311 /* relation is not an index */
312 if (rel->rd_rel->relkind != RELKIND_INDEX &&
313 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
314 return NULL;
315
317
318 /* index has no expressions to give */
319 if (index_exprs == NIL)
320 return NULL;
321
322 /*
323 * The index's attnum points directly to a relation attnum, hence it is
324 * not an expression attribute.
325 */
326 if (rel->rd_index->indkey.values[attnum - 1] != 0)
327 return NULL;
328
330
331 for (int i = 0; i < attnum - 1; i++)
332 if (rel->rd_index->indkey.values[i] == 0)
334
335 if (indexpr_item == NULL) /* shouldn't happen */
336 elog(ERROR, "too few entries in indexprs list");
337
338 return (Node *) lfirst(indexpr_item);
339}
340
341/*
342 * Translate variadic argument pairs from 'pairs_fcinfo' into a
343 * 'positional_fcinfo' appropriate for calling relation_statistics_update() or
344 * attribute_statistics_update() with positional arguments.
345 *
346 * Caller should have already initialized positional_fcinfo with a size
347 * appropriate for calling the intended positional function, and arginfo
348 * should also match the intended positional function.
349 */
350bool
353 struct StatsArgInfo *arginfo)
354{
355 Datum *args;
356 bool *argnulls;
357 Oid *types;
358 int nargs;
359 bool result = true;
360
361 /* clear positional args */
362 for (int i = 0; arginfo[i].argname != NULL; i++)
363 {
364 positional_fcinfo->args[i].value = (Datum) 0;
365 positional_fcinfo->args[i].isnull = true;
366 }
367
368 nargs = extract_variadic_args(pairs_fcinfo, 0, true,
369 &args, &types, &argnulls);
370
371 if (nargs % 2 != 0)
373 errmsg("variadic arguments must be name/value pairs"),
374 errhint("Provide an even number of variadic arguments that can be divided into pairs."));
375
376 /*
377 * For each argument name/value pair, find corresponding positional
378 * argument for the argument name, and assign the argument value to
379 * positional_fcinfo.
380 */
381 for (int i = 0; i < nargs; i += 2)
382 {
383 int argnum;
384 char *argname;
385
386 if (argnulls[i])
388 (errmsg("name at variadic position %d is null", i + 1)));
389
390 if (types[i] != TEXTOID)
392 (errmsg("name at variadic position %d has type %s, expected type %s",
393 i + 1, format_type_be(types[i]),
395
396 if (argnulls[i + 1])
397 continue;
398
399 argname = TextDatumGetCString(args[i]);
400
401 /*
402 * The 'version' argument is a special case, not handled by arginfo
403 * because it's not a valid positional argument.
404 *
405 * For now, 'version' is accepted but ignored. In the future it can be
406 * used to interpret older statistics properly.
407 */
408 if (pg_strcasecmp(argname, "version") == 0)
409 continue;
410
411 argnum = get_arg_by_name(argname, arginfo);
412
413 if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1],
414 arginfo[argnum].argtype))
415 {
416 result = false;
417 continue;
418 }
419
420 positional_fcinfo->args[argnum].value = args[i + 1];
421 positional_fcinfo->args[argnum].isnull = false;
422 }
423
424 return result;
425}
426
427/*
428 * Derive type information from a relation attribute.
429 *
430 * This is needed for setting most slot statistics for all data types.
431 *
432 * This duplicates the logic in examine_attribute() but it will not skip the
433 * attribute if the attstattarget is 0.
434 *
435 * This information, retrieved from pg_attribute and pg_type with some
436 * specific handling for index expressions, is a prerequisite to calling
437 * any of the other statatt_*() functions.
438 */
439void
441 Oid *atttypid, int32 *atttypmod,
442 char *atttyptype, Oid *atttypcoll,
443 Oid *eq_opr, Oid *lt_opr)
444{
448 Node *expr;
449 TypeCacheEntry *typcache;
450
453
454 /* Attribute not found */
458 errmsg("column %d of relation \"%s\" does not exist",
460
462
463 if (attr->attisdropped)
466 errmsg("column %d of relation \"%s\" does not exist",
468
469 expr = statatt_get_index_expr(rel, attr->attnum);
470
471 /*
472 * When analyzing an expression index, believe the expression tree's type
473 * not the column datatype --- the latter might be the opckeytype storage
474 * type of the opclass, which is not interesting for our purposes. This
475 * mimics the behavior of examine_attribute().
476 */
477 if (expr == NULL)
478 {
479 *atttypid = attr->atttypid;
480 *atttypmod = attr->atttypmod;
481 *atttypcoll = attr->attcollation;
482 }
483 else
484 {
485 *atttypid = exprType(expr);
486 *atttypmod = exprTypmod(expr);
487
488 if (OidIsValid(attr->attcollation))
489 *atttypcoll = attr->attcollation;
490 else
491 *atttypcoll = exprCollation(expr);
492 }
494
495 /*
496 * If it's a multirange, step down to the range type, as is done by
497 * multirange_typanalyze().
498 */
499 if (type_is_multirange(*atttypid))
500 *atttypid = get_multirange_range(*atttypid);
501
502 /* finds the right operators even if atttypid is a domain */
503 typcache = lookup_type_cache(*atttypid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
504 *atttyptype = typcache->typtype;
505 *eq_opr = typcache->eq_opr;
506 *lt_opr = typcache->lt_opr;
507
508 /*
509 * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
510 * compute_tsvector_stats().
511 */
512 if (*atttypid == TSVECTOROID)
514
516}
517
518/*
519 * Derive element type information from the attribute type. This information
520 * is needed when the given type is one that contains elements of other types.
521 *
522 * The atttypid and atttyptype should be derived from a previous call to
523 * statatt_get_type().
524 */
525bool
528{
530
531 if (atttypid == TSVECTOROID)
532 {
533 /*
534 * Special case: element type for tsvector is text. See
535 * compute_tsvector_stats().
536 */
538 }
539 else
540 {
541 /* find underlying element type through any domain */
542 *elemtypid = get_base_element_type(atttypid);
543 }
544
545 if (!OidIsValid(*elemtypid))
546 return false;
547
548 /* finds the right operator even if elemtypid is a domain */
550 if (!OidIsValid(elemtypcache->eq_opr))
551 return false;
552
553 *elem_eq_opr = elemtypcache->eq_opr;
554
555 return true;
556}
557
558/*
559 * Build an array with element type typid from a text datum, used as
560 * value of an attribute in a tuple to-be-inserted into pg_statistic.
561 *
562 * The typid and typmod should be derived from a previous call to
563 * statatt_get_type().
564 *
565 * If an error is encountered, capture it and throw a WARNING, with "ok" set
566 * to false. If the resulting array contains NULLs, raise a WARNING and
567 * set "ok" to false. When the operation succeeds, set "ok" to true.
568 */
569Datum
571 int32 typmod, bool *ok)
572{
573 char *s;
576
577 escontext.details_wanted = true;
578
579 s = TextDatumGetCString(d);
580
581 if (!InputFunctionCallSafe(array_in, s, typid, typmod,
582 (Node *) &escontext, &result))
583 {
584 pfree(s);
585 escontext.error_data->elevel = WARNING;
586 ThrowErrorData(escontext.error_data);
587 *ok = false;
588 return (Datum) 0;
589 }
590
591 pfree(s);
592
594 {
597 errmsg("\"%s\" must be a one-dimensional array", staname)));
598 *ok = false;
599 return (Datum) 0;
600 }
601
603 {
606 errmsg("\"%s\" array must not contain null values", staname)));
607 *ok = false;
608 return (Datum) 0;
609 }
610
611 *ok = true;
612
613 return result;
614}
615
616/*
617 * Find and update the slot of a stakind, or use the first empty slot.
618 *
619 * Core statistics types expect the stakind value to be one of the
620 * STATISTIC_KIND_* constants defined in pg_statistic.h, but types defined
621 * by extensions are not restricted to those values.
622 *
623 * In the case of core statistics, the required staop is determined by the
624 * stakind given and will either be a hardcoded oid, or the eq/lt operator
625 * derived from statatt_get_type(). Likewise, types defined by extensions
626 * have no such restriction.
627 *
628 * The stacoll value should be either the atttypcoll derived from
629 * statatt_get_type(), or a hardcoded value required by that particular
630 * stakind.
631 *
632 * The value/null pairs for stanumbers and stavalues should be calculated
633 * based on the stakind, using statatt_build_stavalues() or constructed arrays.
634 */
635void
636statatt_set_slot(Datum *values, bool *nulls, bool *replaces,
637 int16 stakind, Oid staop, Oid stacoll,
638 Datum stanumbers, bool stanumbers_isnull,
639 Datum stavalues, bool stavalues_isnull)
640{
641 int slotidx;
642 int first_empty = -1;
646
647 /* find existing slot with given stakind */
649 {
651
652 if (first_empty < 0 &&
655 if (DatumGetInt16(values[stakind_attnum]) == stakind)
656 break;
657 }
658
661
664 (errmsg("maximum number of statistics slots exceeded: %d",
665 slotidx + 1)));
666
670
671 if (DatumGetInt16(values[stakind_attnum]) != stakind)
672 {
674 replaces[stakind_attnum] = true;
675 }
676 if (DatumGetObjectId(values[staop_attnum]) != staop)
677 {
679 replaces[staop_attnum] = true;
680 }
681 if (DatumGetObjectId(values[stacoll_attnum]) != stacoll)
682 {
684 replaces[stacoll_attnum] = true;
685 }
687 {
688 values[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = stanumbers;
689 nulls[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = false;
691 }
692 if (!stavalues_isnull)
693 {
695 nulls[Anum_pg_statistic_stavalues1 - 1 + slotidx] = false;
697 }
698}
699
700/*
701 * Initialize values and nulls for a new pg_statistic tuple.
702 *
703 * The caller is responsible for allocating the arrays where the results are
704 * stored, which should be of size Natts_pg_statistic.
705 *
706 * When using this routine for a tuple inserted into pg_statistic, reloid,
707 * attnum and inherited flags should all be set.
708 *
709 * When using this routine for a tuple that is an element of a stxdexpr
710 * array inserted into pg_statistic_ext_data, reloid, attnum and inherited
711 * should be respectively set to InvalidOid, InvalidAttrNumber and false.
712 */
713void
715 Datum *values, bool *nulls, bool *replaces)
716{
717 memset(nulls, true, sizeof(bool) * Natts_pg_statistic);
718 memset(replaces, true, sizeof(bool) * Natts_pg_statistic);
719
720 /* This must initialize non-NULL attributes */
722 nulls[Anum_pg_statistic_starelid - 1] = false;
724 nulls[Anum_pg_statistic_staattnum - 1] = false;
726 nulls[Anum_pg_statistic_stainherit - 1] = false;
727
729 nulls[Anum_pg_statistic_stanullfrac - 1] = false;
731 nulls[Anum_pg_statistic_stawidth - 1] = false;
733 nulls[Anum_pg_statistic_stadistinct - 1] = false;
734
735 /* initialize stakind, staop, and stacoll slots */
736 for (int slotnum = 0; slotnum < STATISTIC_NUM_SLOTS; slotnum++)
737 {
739 nulls[Anum_pg_statistic_stakind1 + slotnum - 1] = false;
741 nulls[Anum_pg_statistic_staop1 + slotnum - 1] = false;
743 nulls[Anum_pg_statistic_stacoll1 + slotnum - 1] = false;
744 }
745}
746
747/*
748 * Check that an imported bounds histogram (STATISTIC_KIND_BOUNDS_HISTOGRAM)
749 * is shaped the same way ANALYZE builds it in compute_range_stats().
750 *
751 * For both range-typed and multirange-typed columns the histogram is an array
752 * of ranges, so we take the range type from the array's element type.
753 */
754bool
756{
759 TypeCacheEntry *typcache;
760 int16 elmlen;
761 bool elmbyval;
762 char elmalign;
763 Datum *elems;
764 bool *nulls;
765 int nelems;
768
770
771 /*
772 * The element type should always be a range type here. This is
773 * defensive. If it isn't, the bounds histogram is never consulted by the
774 * range estimator, and there is nothing to verify.
775 */
776 if (typcache->rngelemtype == NULL)
777 return true;
778
779 get_typlenbyvalalign(rngtypid, &elmlen, &elmbyval, &elmalign);
780 deconstruct_array(arr, rngtypid, elmlen, elmbyval, elmalign,
781 &elems, &nulls, &nelems);
782
783 for (int i = 0; i < nelems; i++)
784 {
786 upper;
787 bool empty;
788
789 /*
790 * NULL elements are already rejected by statatt_build_stavalues() and
791 * array_in_safe().
792 */
793 range_deserialize(typcache, DatumGetRangeTypeP(elems[i]),
794 &lower, &upper, &empty);
795
796 if (empty)
797 {
800 errmsg("\"%s\" must not contain empty ranges",
801 "range_bounds_histogram")));
802 return false;
803 }
804
805 if (i > 0 &&
806 (range_cmp_bounds(typcache, &lower, &prev_lower) < 0 ||
807 range_cmp_bounds(typcache, &upper, &prev_upper) < 0))
808 {
811 errmsg("\"%s\" must have its lower and upper bounds sorted in ascending order",
812 "range_bounds_histogram")));
813 return false;
814 }
815
818 }
819
820 return true;
821}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4156
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4105
#define ARR_NDIM(a)
Definition array.h:290
#define DatumGetArrayTypeP(X)
Definition array.h:261
#define ARR_ELEMTYPE(a)
Definition array.h:292
bool array_contains_nulls(const ArrayType *array)
Datum array_in(PG_FUNCTION_ARGS)
Definition arrayfuncs.c:181
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
int16 AttrNumber
Definition attnum.h:21
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define TextDatumGetCString(d)
Definition builtins.h:99
#define NameStr(name)
Definition c.h:894
int16_t int16
Definition c.h:678
int32_t int32
Definition c.h:679
#define OidIsValid(objectId)
Definition c.h:917
uint32 result
struct typedefs * types
Definition ecpg.c:30
Datum arg
Definition elog.c:1323
void ThrowErrorData(ErrorData *edata)
Definition elog.c:2091
int errcode(int sqlerrcode)
Definition elog.c:875
int errhint(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:37
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, Node *escontext, Datum *result)
Definition fmgr.c:1586
#define PG_ARGISNULL(n)
Definition fmgr.h:209
#define PG_GETARG_DATUM(n)
Definition fmgr.h:268
char * format_type_be(Oid type_oid)
int extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start, bool convert_unknown, Datum **args, Oid **types, bool **nulls)
Definition funcapi.c:2011
Oid MyDatabaseId
Definition globals.c:96
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3616
int i
Definition isn.c:77
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:229
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
#define NoLock
Definition lockdefs.h:34
#define AccessShareLock
Definition lockdefs.h:36
#define ShareUpdateExclusiveLock
Definition lockdefs.h:39
Oid get_multirange_range(Oid multirangeOid)
Definition lsyscache.c:3844
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition lsyscache.c:2585
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2317
Oid get_base_element_type(Oid typid)
Definition lsyscache.c:3148
bool type_is_multirange(Oid typid)
Definition lsyscache.c:3014
void pfree(void *pointer)
Definition mcxt.c:1619
Oid GetUserId(void)
Definition miscinit.c:470
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
int32 exprTypmod(const Node *expr)
Definition nodeFuncs.c:304
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
static char * errmsg
ObjectType get_relkind_objtype(char relkind)
Datum lower(PG_FUNCTION_ARGS)
Datum upper(PG_FUNCTION_ARGS)
#define ACL_MAINTAIN
Definition parsenodes.h:90
int16 attnum
FormData_pg_attribute * Form_pg_attribute
int errdetail_relkind_not_supported(char relkind)
Definition pg_class.c:24
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
#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:375
#define STATISTIC_NUM_SLOTS
int pg_strcasecmp(const char *s1, const char *s2)
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:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static int16 DatumGetInt16(Datum X)
Definition postgres.h:162
#define InvalidOid
unsigned int Oid
static int fb(int x)
int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1, const RangeBound *b2)
void range_deserialize(TypeCacheEntry *typcache, const RangeType *range, RangeBound *lower, RangeBound *upper, bool *empty)
static RangeType * DatumGetRangeTypeP(Datum X)
Definition rangetypes.h:73
#define RelationGetRelationName(relation)
Definition rel.h:550
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5109
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
#define DEFAULT_STATATT_NULL_FRAC
Definition stat_utils.c:42
bool statatt_get_elem_type(Oid atttypid, char atttyptype, Oid *elemtypid, Oid *elem_eq_opr)
Definition stat_utils.c:524
Datum statatt_build_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid, int32 typmod, bool *ok)
Definition stat_utils.c:568
bool stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo, FunctionCallInfo positional_fcinfo, struct StatsArgInfo *arginfo)
Definition stat_utils.c:349
static int get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo)
Definition stat_utils.c:263
void RangeVarCallbackForStats(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition stat_utils.c:143
void statatt_init_empty_tuple(Oid reloid, int16 attnum, bool inherited, Datum *values, bool *nulls, bool *replaces)
Definition stat_utils.c:712
#define DEFAULT_STATATT_AVG_WIDTH
Definition stat_utils.c:43
static bool stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype)
Definition stat_utils.c:281
bool stats_check_arg_array(FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum)
Definition stat_utils.c:71
void statatt_get_type(Oid reloid, AttrNumber attnum, Oid *atttypid, int32 *atttypmod, char *atttyptype, Oid *atttypcoll, Oid *eq_opr, Oid *lt_opr)
Definition stat_utils.c:438
bool statatt_check_bounds_histogram(Datum arrayval)
Definition stat_utils.c:753
static Node * statatt_get_index_expr(Relation rel, int attnum)
Definition stat_utils.c:304
#define DEFAULT_STATATT_N_DISTINCT
Definition stat_utils.c:44
void statatt_set_slot(Datum *values, bool *nulls, bool *replaces, int16 stakind, Oid staop, Oid stacoll, Datum stanumbers, bool stanumbers_isnull, Datum stavalues, bool stavalues_isnull)
Definition stat_utils.c:634
bool stats_check_arg_pair(FunctionCallInfo fcinfo, struct StatsArgInfo *arginfo, int argnum1, int argnum2)
Definition stat_utils.c:112
Definition pg_list.h:54
Definition nodes.h:133
char * relname
Definition primnodes.h:84
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
struct TypeCacheEntry * rngelemtype
Definition typcache.h:99
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:231
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:389
#define TYPECACHE_EQ_OPR
Definition typcache.h:138
#define TYPECACHE_RANGE_INFO
Definition typcache.h:149
#define TYPECACHE_LT_OPR
Definition typcache.h:139

◆ DEFAULT_STATATT_N_DISTINCT

#define DEFAULT_STATATT_N_DISTINCT
Value:
Float4GetDatum(0.0) /* stadistinct, same as
* unknown */
static Datum Float4GetDatum(float4 X)
Definition postgres.h:481

Definition at line 44 of file stat_utils.c.

◆ DEFAULT_STATATT_NULL_FRAC

#define DEFAULT_STATATT_NULL_FRAC   Float4GetDatum(0.0) /* stanullfrac */

Definition at line 42 of file stat_utils.c.

Function Documentation

◆ get_arg_by_name()

static int get_arg_by_name ( const char argname,
struct StatsArgInfo arginfo 
)
static

Definition at line 263 of file stat_utils.c.

266{
267 int argnum;
268
269 for (argnum = 0; arginfo[argnum].argname != NULL; argnum++)
270 if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0)
271 return argnum;
272
274 (errmsg("unrecognized argument name: \"%s\"", argname)));
275

References ereport, errmsg, fb(), pg_strcasecmp(), and WARNING.

Referenced by stats_fill_fcinfo_from_arg_pairs().

◆ RangeVarCallbackForStats()

void RangeVarCallbackForStats ( const RangeVar relation,
Oid  relId,
Oid  oldRelId,
void arg 
)

Definition at line 143 of file stat_utils.c.

147{
148 Oid *locked_oid = (Oid *) arg;
149 Oid table_oid = relId;
150 HeapTuple tuple;
152 char relkind;
153
154 /*
155 * If we previously locked some other index's heap, and the name we're
156 * looking up no longer refers to that relation, release the now-useless
157 * lock.
158 */
159 if (relId != oldRelId && OidIsValid(*locked_oid))
160 {
163 }
164
165 /* If the relation does not exist, there's nothing more to do. */
166 if (!OidIsValid(relId))
167 return;
168
169 /* If the relation does exist, check whether it's an index. */
170 relkind = get_rel_relkind(relId);
171 if (relkind == RELKIND_INDEX ||
172 relkind == RELKIND_PARTITIONED_INDEX)
173 table_oid = IndexGetRelation(relId, false);
174
175 /*
176 * If retrying yields the same OID, there are a couple of extremely
177 * unlikely scenarios we need to handle.
178 */
179 if (relId == oldRelId)
180 {
181 /*
182 * If a previous lookup found an index, but the current lookup did
183 * not, the index was dropped and the OID was reused for something
184 * else between lookups. In theory, we could simply drop our lock on
185 * the index's parent table and proceed, but in the interest of
186 * avoiding complexity, we just error.
187 */
188 if (table_oid == relId && OidIsValid(*locked_oid))
191 errmsg("index \"%s\" was concurrently dropped",
192 relation->relname)));
193
194 /*
195 * If the current lookup found an index but a previous lookup either
196 * did not find an index or found one with a different parent
197 * relation, the relation was dropped and the OID was reused for an
198 * index between lookups. RangeVarGetRelidExtended() will have
199 * already locked the index at this point, so we can't just lock the
200 * newly discovered parent table OID without risking deadlock. As
201 * above, we just error in this case.
202 */
203 if (table_oid != relId && table_oid != *locked_oid)
206 errmsg("index \"%s\" was concurrently created",
207 relation->relname)));
208 }
209
211 if (!HeapTupleIsValid(tuple))
212 elog(ERROR, "cache lookup failed for OID %u", table_oid);
213 form = (Form_pg_class) GETSTRUCT(tuple);
214
215 /* the relkinds that can be used with ANALYZE */
216 switch (form->relkind)
217 {
218 case RELKIND_RELATION:
219 case RELKIND_MATVIEW:
222 break;
223 default:
226 errmsg("cannot modify statistics for relation \"%s\"",
227 NameStr(form->relname)),
229 }
230
231 if (form->relisshared)
234 errmsg("cannot modify statistics for shared relation")));
235
236 /* Check permissions */
238 {
240 GetUserId(),
242
243 if (aclresult != ACLCHECK_OK)
245 get_relkind_objtype(form->relkind),
246 NameStr(form->relname));
247 }
248
249 ReleaseSysCache(tuple);
250
251 /* Lock heap before index to avoid deadlock. */
252 if (relId != oldRelId && table_oid != relId)
253 {

References ACL_MAINTAIN, aclcheck_error(), ACLCHECK_OK, arg, elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IndexGetRelation(), InvalidOid, LockRelationOid(), MyDatabaseId, NameStr, object_ownercheck(), ObjectIdGetDatum(), OidIsValid, pg_class_aclcheck(), ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), ShareUpdateExclusiveLock, and UnlockRelationOid().

Referenced by attribute_statistics_update(), extended_statistics_update(), pg_clear_attribute_stats(), pg_clear_extended_stats(), and relation_statistics_update().

◆ statatt_build_stavalues()

Datum statatt_build_stavalues ( const char staname,
FmgrInfo array_in,
Datum  d,
Oid  typid,
int32  typmod,
bool ok 
)

Definition at line 568 of file stat_utils.c.

572{
573 char *s;
576
577 escontext.details_wanted = true;
578
579 s = TextDatumGetCString(d);
580
581 if (!InputFunctionCallSafe(array_in, s, typid, typmod,
582 (Node *) &escontext, &result))
583 {
584 pfree(s);
585 escontext.error_data->elevel = WARNING;
586 ThrowErrorData(escontext.error_data);
587 *ok = false;
588 return (Datum) 0;
589 }
590
591 pfree(s);
592
594 {
597 errmsg("\"%s\" must be a one-dimensional array", staname)));
598 *ok = false;
599 return (Datum) 0;
600 }
601
603 {
606 errmsg("\"%s\" array must not contain null values", staname)));
607 *ok = false;
608 return (Datum) 0;
609 }
610
611 *ok = true;
612

References ARR_NDIM, array_contains_nulls(), array_in(), DatumGetArrayTypeP, ErrorSaveContext::details_wanted, ErrorData::elevel, ereport, errcode(), errmsg, ErrorSaveContext::error_data, fb(), InputFunctionCallSafe(), pfree(), result, TextDatumGetCString, ThrowErrorData(), and WARNING.

Referenced by attribute_statistics_update_internal().

◆ statatt_check_bounds_histogram()

bool statatt_check_bounds_histogram ( Datum  arrayval)

Definition at line 753 of file stat_utils.c.

756{
759 TypeCacheEntry *typcache;
760 int16 elmlen;
761 bool elmbyval;
762 char elmalign;
763 Datum *elems;
764 bool *nulls;
765 int nelems;
768
770
771 /*
772 * The element type should always be a range type here. This is
773 * defensive. If it isn't, the bounds histogram is never consulted by the
774 * range estimator, and there is nothing to verify.
775 */
776 if (typcache->rngelemtype == NULL)
777 return true;
778
779 get_typlenbyvalalign(rngtypid, &elmlen, &elmbyval, &elmalign);
780 deconstruct_array(arr, rngtypid, elmlen, elmbyval, elmalign,
781 &elems, &nulls, &nelems);
782
783 for (int i = 0; i < nelems; i++)
784 {
786 upper;
787 bool empty;
788
789 /*
790 * NULL elements are already rejected by statatt_build_stavalues() and
791 * array_in_safe().
792 */
793 range_deserialize(typcache, DatumGetRangeTypeP(elems[i]),
794 &lower, &upper, &empty);
795
796 if (empty)
797 {
800 errmsg("\"%s\" must not contain empty ranges",
801 "range_bounds_histogram")));
802 return false;
803 }
804
805 if (i > 0 &&
806 (range_cmp_bounds(typcache, &lower, &prev_lower) < 0 ||
807 range_cmp_bounds(typcache, &upper, &prev_upper) < 0))
808 {
811 errmsg("\"%s\" must have its lower and upper bounds sorted in ascending order",
812 "range_bounds_histogram")));
813 return false;
814 }
815
818 }
819

References ARR_ELEMTYPE, DatumGetArrayTypeP, DatumGetRangeTypeP(), deconstruct_array(), ereport, errcode(), errmsg, fb(), get_typlenbyvalalign(), i, lookup_type_cache(), lower(), range_cmp_bounds(), range_deserialize(), TypeCacheEntry::rngelemtype, TYPECACHE_RANGE_INFO, upper(), and WARNING.

Referenced by attribute_statistics_update_internal(), and import_pg_statistic().

◆ statatt_get_elem_type()

bool statatt_get_elem_type ( Oid  atttypid,
char  atttyptype,
Oid elemtypid,
Oid elem_eq_opr 
)

Definition at line 524 of file stat_utils.c.

528{
530
531 if (atttypid == TSVECTOROID)
532 {
533 /*
534 * Special case: element type for tsvector is text. See
535 * compute_tsvector_stats().
536 */
538 }
539 else
540 {
541 /* find underlying element type through any domain */
542 *elemtypid = get_base_element_type(atttypid);
543 }
544
545 if (!OidIsValid(*elemtypid))
546 return false;
547
548 /* finds the right operator even if elemtypid is a domain */
550 if (!OidIsValid(elemtypcache->eq_opr))
551 return false;
552
553 *elem_eq_opr = elemtypcache->eq_opr;
554

References fb(), get_base_element_type(), lookup_type_cache(), OidIsValid, and TYPECACHE_EQ_OPR.

Referenced by attribute_statistics_update_internal(), and import_pg_statistic().

◆ statatt_get_index_expr()

static Node * statatt_get_index_expr ( Relation  rel,
int  attnum 
)
static

Definition at line 304 of file stat_utils.c.

307{
310
311 /* relation is not an index */
312 if (rel->rd_rel->relkind != RELKIND_INDEX &&
313 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
314 return NULL;
315
317
318 /* index has no expressions to give */
319 if (index_exprs == NIL)
320 return NULL;
321
322 /*
323 * The index's attnum points directly to a relation attnum, hence it is
324 * not an expression attribute.
325 */
326 if (rel->rd_index->indkey.values[attnum - 1] != 0)
327 return NULL;
328
330
331 for (int i = 0; i < attnum - 1; i++)
332 if (rel->rd_index->indkey.values[i] == 0)
334
335 if (indexpr_item == NULL) /* shouldn't happen */
336 elog(ERROR, "too few entries in indexprs list");
337

References attnum, elog, ERROR, fb(), i, lfirst, list_head(), lnext(), NIL, RelationData::rd_index, RelationData::rd_indexprs, RelationData::rd_rel, and RelationGetIndexExpressions().

Referenced by statatt_get_type().

◆ statatt_get_type()

void statatt_get_type ( Oid  reloid,
AttrNumber  attnum,
Oid atttypid,
int32 atttypmod,
char atttyptype,
Oid atttypcoll,
Oid eq_opr,
Oid lt_opr 
)

Definition at line 438 of file stat_utils.c.

444{
448 Node *expr;
449 TypeCacheEntry *typcache;
450
453
454 /* Attribute not found */
458 errmsg("column %d of relation \"%s\" does not exist",
460
462
463 if (attr->attisdropped)
466 errmsg("column %d of relation \"%s\" does not exist",
468
469 expr = statatt_get_index_expr(rel, attr->attnum);
470
471 /*
472 * When analyzing an expression index, believe the expression tree's type
473 * not the column datatype --- the latter might be the opckeytype storage
474 * type of the opclass, which is not interesting for our purposes. This
475 * mimics the behavior of examine_attribute().
476 */
477 if (expr == NULL)
478 {
479 *atttypid = attr->atttypid;
480 *atttypmod = attr->atttypmod;
481 *atttypcoll = attr->attcollation;
482 }
483 else
484 {
485 *atttypid = exprType(expr);
486 *atttypmod = exprTypmod(expr);
487
488 if (OidIsValid(attr->attcollation))
489 *atttypcoll = attr->attcollation;
490 else
491 *atttypcoll = exprCollation(expr);
492 }
494
495 /*
496 * If it's a multirange, step down to the range type, as is done by
497 * multirange_typanalyze().
498 */
499 if (type_is_multirange(*atttypid))
500 *atttypid = get_multirange_range(*atttypid);
501
502 /* finds the right operators even if atttypid is a domain */
503 typcache = lookup_type_cache(*atttypid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
504 *atttyptype = typcache->typtype;
505 *eq_opr = typcache->eq_opr;
506 *lt_opr = typcache->lt_opr;
507
508 /*
509 * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
510 * compute_tsvector_stats().
511 */
512 if (*atttypid == TSVECTOROID)
514

References AccessShareLock, attnum, TypeCacheEntry::eq_opr, ereport, errcode(), errmsg, ERROR, exprCollation(), exprType(), exprTypmod(), fb(), get_multirange_range(), GETSTRUCT(), HeapTupleIsValid, Int16GetDatum(), lookup_type_cache(), TypeCacheEntry::lt_opr, NoLock, ObjectIdGetDatum(), OidIsValid, relation_close(), relation_open(), RelationGetRelationName, ReleaseSysCache(), SearchSysCache2(), statatt_get_index_expr(), type_is_multirange(), TYPECACHE_EQ_OPR, TYPECACHE_LT_OPR, and TypeCacheEntry::typtype.

Referenced by attribute_statistics_update_internal().

◆ statatt_init_empty_tuple()

void statatt_init_empty_tuple ( Oid  reloid,
int16  attnum,
bool  inherited,
Datum values,
bool nulls,
bool replaces 
)

Definition at line 712 of file stat_utils.c.

716{
717 memset(nulls, true, sizeof(bool) * Natts_pg_statistic);
718 memset(replaces, true, sizeof(bool) * Natts_pg_statistic);
719
720 /* This must initialize non-NULL attributes */
722 nulls[Anum_pg_statistic_starelid - 1] = false;
724 nulls[Anum_pg_statistic_staattnum - 1] = false;
726 nulls[Anum_pg_statistic_stainherit - 1] = false;
727
729 nulls[Anum_pg_statistic_stanullfrac - 1] = false;
731 nulls[Anum_pg_statistic_stawidth - 1] = false;
733 nulls[Anum_pg_statistic_stadistinct - 1] = false;
734
735 /* initialize stakind, staop, and stacoll slots */
736 for (int slotnum = 0; slotnum < STATISTIC_NUM_SLOTS; slotnum++)
737 {
739 nulls[Anum_pg_statistic_stakind1 + slotnum - 1] = false;
741 nulls[Anum_pg_statistic_staop1 + slotnum - 1] = false;
743 nulls[Anum_pg_statistic_stacoll1 + slotnum - 1] = false;

References attnum, BoolGetDatum(), DEFAULT_STATATT_AVG_WIDTH, DEFAULT_STATATT_N_DISTINCT, DEFAULT_STATATT_NULL_FRAC, fb(), Int16GetDatum(), InvalidOid, ObjectIdGetDatum(), STATISTIC_NUM_SLOTS, and values.

Referenced by attribute_statistics_update_internal(), and import_pg_statistic().

◆ statatt_set_slot()

void statatt_set_slot ( Datum values,
bool nulls,
bool replaces,
int16  stakind,
Oid  staop,
Oid  stacoll,
Datum  stanumbers,
bool  stanumbers_isnull,
Datum  stavalues,
bool  stavalues_isnull 
)

Definition at line 634 of file stat_utils.c.

640{
641 int slotidx;
642 int first_empty = -1;
646
647 /* find existing slot with given stakind */
649 {
651
652 if (first_empty < 0 &&
655 if (DatumGetInt16(values[stakind_attnum]) == stakind)
656 break;
657 }
658
661
664 (errmsg("maximum number of statistics slots exceeded: %d",
665 slotidx + 1)));
666
670
671 if (DatumGetInt16(values[stakind_attnum]) != stakind)
672 {
674 replaces[stakind_attnum] = true;
675 }
676 if (DatumGetObjectId(values[staop_attnum]) != staop)
677 {
679 replaces[staop_attnum] = true;
680 }
681 if (DatumGetObjectId(values[stacoll_attnum]) != stacoll)
682 {
684 replaces[stacoll_attnum] = true;
685 }
687 {
688 values[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = stanumbers;
689 nulls[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = false;
691 }
692 if (!stavalues_isnull)
693 {
695 nulls[Anum_pg_statistic_stavalues1 - 1 + slotidx] = false;

References DatumGetInt16(), DatumGetObjectId(), ereport, errmsg, ERROR, fb(), Int16GetDatum(), ObjectIdGetDatum(), STATISTIC_NUM_SLOTS, and values.

Referenced by attribute_statistics_update_internal(), and import_pg_statistic().

◆ stats_check_arg_array()

bool stats_check_arg_array ( FunctionCallInfo  fcinfo,
struct StatsArgInfo arginfo,
int  argnum 
)

Definition at line 71 of file stat_utils.c.

76{
77 ArrayType *arr;
78
80 return true;
81
83
84 if (ARR_NDIM(arr) != 1)
85 {
88 errmsg("argument \"%s\" must not be a multidimensional array",
89 arginfo[argnum].argname)));
90 return false;
91 }
92
93 if (array_contains_nulls(arr))
94 {
97 errmsg("argument \"%s\" array must not contain null values",
98 arginfo[argnum].argname)));
99 return false;
100 }
101

References ARR_NDIM, array_contains_nulls(), DatumGetArrayTypeP, ereport, errcode(), errmsg, fb(), PG_ARGISNULL, PG_GETARG_DATUM, and WARNING.

Referenced by attribute_statistics_update_internal().

◆ stats_check_arg_pair()

bool stats_check_arg_pair ( FunctionCallInfo  fcinfo,
struct StatsArgInfo arginfo,
int  argnum1,
int  argnum2 
)

Definition at line 112 of file stat_utils.c.

117{
119 return true;
120
122 {
125
128 errmsg("argument \"%s\" must be specified when argument \"%s\" is specified",
129 arginfo[nullarg].argname,
130 arginfo[otherarg].argname)));
131
132 return false;
133 }
134

References ereport, errcode(), errmsg, fb(), PG_ARGISNULL, and WARNING.

Referenced by attribute_statistics_update_internal().

◆ stats_check_arg_type()

static bool stats_check_arg_type ( const char argname,
Oid  argtype,
Oid  expectedtype 
)
static

Definition at line 281 of file stat_utils.c.

284{
285 if (argtype != expectedtype)
286 {
288 (errmsg("argument \"%s\" has type %s, expected type %s",
289 argname, format_type_be(argtype),
291 return false;
292 }
293

References ereport, errmsg, fb(), format_type_be(), and WARNING.

Referenced by stats_fill_fcinfo_from_arg_pairs().

◆ stats_check_required_arg()

void stats_check_required_arg ( FunctionCallInfo  fcinfo,
struct StatsArgInfo arginfo,
int  argnum 
)

◆ stats_fill_fcinfo_from_arg_pairs()

bool stats_fill_fcinfo_from_arg_pairs ( FunctionCallInfo  pairs_fcinfo,
FunctionCallInfo  positional_fcinfo,
struct StatsArgInfo arginfo 
)

Definition at line 349 of file stat_utils.c.

354{
355 Datum *args;
356 bool *argnulls;
357 Oid *types;
358 int nargs;
359 bool result = true;
360
361 /* clear positional args */
362 for (int i = 0; arginfo[i].argname != NULL; i++)
363 {
364 positional_fcinfo->args[i].value = (Datum) 0;
365 positional_fcinfo->args[i].isnull = true;
366 }
367
368 nargs = extract_variadic_args(pairs_fcinfo, 0, true,
369 &args, &types, &argnulls);
370
371 if (nargs % 2 != 0)
373 errmsg("variadic arguments must be name/value pairs"),
374 errhint("Provide an even number of variadic arguments that can be divided into pairs."));
375
376 /*
377 * For each argument name/value pair, find corresponding positional
378 * argument for the argument name, and assign the argument value to
379 * positional_fcinfo.
380 */
381 for (int i = 0; i < nargs; i += 2)
382 {
383 int argnum;
384 char *argname;
385
386 if (argnulls[i])
388 (errmsg("name at variadic position %d is null", i + 1)));
389
390 if (types[i] != TEXTOID)
392 (errmsg("name at variadic position %d has type %s, expected type %s",
393 i + 1, format_type_be(types[i]),
395
396 if (argnulls[i + 1])
397 continue;
398
399 argname = TextDatumGetCString(args[i]);
400
401 /*
402 * The 'version' argument is a special case, not handled by arginfo
403 * because it's not a valid positional argument.
404 *
405 * For now, 'version' is accepted but ignored. In the future it can be
406 * used to interpret older statistics properly.
407 */
408 if (pg_strcasecmp(argname, "version") == 0)
409 continue;
410
411 argnum = get_arg_by_name(argname, arginfo);
412
413 if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1],
414 arginfo[argnum].argtype))
415 {
416 result = false;
417 continue;
418 }
419
420 positional_fcinfo->args[argnum].value = args[i + 1];
421 positional_fcinfo->args[argnum].isnull = false;
422 }
423

References ereport, errhint(), errmsg, ERROR, extract_variadic_args(), fb(), format_type_be(), get_arg_by_name(), i, pg_strcasecmp(), result, stats_check_arg_type(), TextDatumGetCString, and types.

Referenced by pg_restore_attribute_stats(), pg_restore_extended_stats(), and pg_restore_relation_stats().