1 /*-------------------------------------------------------------------------
4 * PostgreSQL statistics manipulation utilities.
6 * Code supporting the direct manipulation of statistics.
8 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/statistics/stat_utils.c
14 *-------------------------------------------------------------------------
19 #include "access/relation.h"
20 #include "catalog/pg_database.h"
22 #include "miscadmin.h"
23 #include "statistics/stat_utils.h"
24 #include "utils/acl.h"
25 #include "utils/array.h"
26 #include "utils/builtins.h"
27 #include "utils/rel.h"
30 * Ensure that a given argument is not null.
33 stats_check_required_arg(FunctionCallInfo fcinfo
,
34 struct StatsArgInfo
*arginfo
,
37 if (PG_ARGISNULL(argnum
))
39 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
40 errmsg("\"%s\" cannot be NULL",
41 arginfo
[argnum
].argname
)));
45 * Check that argument is either NULL or a one dimensional array with no
48 * If a problem is found, emit at elevel, and return false. Otherwise return
52 stats_check_arg_array(FunctionCallInfo fcinfo
,
53 struct StatsArgInfo
*arginfo
,
54 int argnum
, int elevel
)
58 if (PG_ARGISNULL(argnum
))
61 arr
= DatumGetArrayTypeP(PG_GETARG_DATUM(argnum
));
63 if (ARR_NDIM(arr
) != 1)
66 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
67 errmsg("\"%s\" cannot be a multidimensional array",
68 arginfo
[argnum
].argname
)));
72 if (array_contains_nulls(arr
))
75 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
76 errmsg("\"%s\" array cannot contain NULL values",
77 arginfo
[argnum
].argname
)));
85 * Enforce parameter pairs that must be specified together (or not at all) for
86 * a particular stakind, such as most_common_vals and most_common_freqs for
89 * If a problem is found, emit at elevel, and return false. Otherwise return
93 stats_check_arg_pair(FunctionCallInfo fcinfo
,
94 struct StatsArgInfo
*arginfo
,
95 int argnum1
, int argnum2
, int elevel
)
97 if (PG_ARGISNULL(argnum1
) && PG_ARGISNULL(argnum2
))
100 if (PG_ARGISNULL(argnum1
) || PG_ARGISNULL(argnum2
))
102 int nullarg
= PG_ARGISNULL(argnum1
) ? argnum1
: argnum2
;
103 int otherarg
= PG_ARGISNULL(argnum1
) ? argnum2
: argnum1
;
106 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
107 errmsg("\"%s\" must be specified when \"%s\" is specified",
108 arginfo
[nullarg
].argname
,
109 arginfo
[otherarg
].argname
)));
118 * Lock relation in ShareUpdateExclusive mode, check privileges, and close the
119 * relation (but retain the lock).
121 * A role has privileges to set statistics on the relation if any of the
122 * following are true:
123 * - the role owns the current database and the relation is not shared
124 * - the role has the MAINTAIN privilege on the relation
127 stats_lock_check_privileges(Oid reloid
)
129 Relation rel
= relation_open(reloid
, ShareUpdateExclusiveLock
);
130 const char relkind
= rel
->rd_rel
->relkind
;
132 /* All of the types that can be used with ANALYZE, plus indexes */
135 case RELKIND_RELATION
:
137 case RELKIND_MATVIEW
:
138 case RELKIND_FOREIGN_TABLE
:
139 case RELKIND_PARTITIONED_TABLE
:
140 case RELKIND_PARTITIONED_INDEX
:
144 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
145 errmsg("cannot modify statistics for relation \"%s\"",
146 RelationGetRelationName(rel
)),
147 errdetail_relkind_not_supported(rel
->rd_rel
->relkind
)));
150 if (rel
->rd_rel
->relisshared
)
152 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
153 errmsg("cannot modify statistics for shared relation")));
155 if (!object_ownercheck(DatabaseRelationId
, MyDatabaseId
, GetUserId()))
157 AclResult aclresult
= pg_class_aclcheck(RelationGetRelid(rel
),
161 if (aclresult
!= ACLCHECK_OK
)
162 aclcheck_error(aclresult
,
163 get_relkind_objtype(rel
->rd_rel
->relkind
),
164 NameStr(rel
->rd_rel
->relname
));
167 relation_close(rel
, NoLock
);
171 * Find the argument number for the given argument name, returning -1 if not
175 get_arg_by_name(const char *argname
, struct StatsArgInfo
*arginfo
, int elevel
)
179 for (argnum
= 0; arginfo
[argnum
].argname
!= NULL
; argnum
++)
180 if (pg_strcasecmp(argname
, arginfo
[argnum
].argname
) == 0)
184 (errmsg("unrecognized argument name: \"%s\"", argname
)));
190 * Ensure that a given argument matched the expected type.
193 stats_check_arg_type(const char *argname
, Oid argtype
, Oid expectedtype
, int elevel
)
195 if (argtype
!= expectedtype
)
198 (errmsg("argument \"%s\" has type \"%s\", expected type \"%s\"",
199 argname
, format_type_be(argtype
),
200 format_type_be(expectedtype
))));
208 * Translate variadic argument pairs from 'pairs_fcinfo' into a
209 * 'positional_fcinfo' appropriate for calling relation_statistics_update() or
210 * attribute_statistics_update() with positional arguments.
212 * Caller should have already initialized positional_fcinfo with a size
213 * appropriate for calling the intended positional function, and arginfo
214 * should also match the intended positional function.
217 stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo
,
218 FunctionCallInfo positional_fcinfo
,
219 struct StatsArgInfo
*arginfo
,
228 /* clear positional args */
229 for (int i
= 0; arginfo
[i
].argname
!= NULL
; i
++)
231 positional_fcinfo
->args
[i
].value
= (Datum
) 0;
232 positional_fcinfo
->args
[i
].isnull
= true;
235 nargs
= extract_variadic_args(pairs_fcinfo
, 0, true,
236 &args
, &types
, &argnulls
);
240 errmsg("variadic arguments must be name/value pairs"),
241 errhint("Provide an even number of variadic arguments that can be divided into pairs."));
244 * For each argument name/value pair, find corresponding positional
245 * argument for the argument name, and assign the argument value to
246 * postitional_fcinfo.
248 for (int i
= 0; i
< nargs
; i
+= 2)
255 (errmsg("name at variadic position %d is NULL", i
+ 1)));
257 if (types
[i
] != TEXTOID
)
259 (errmsg("name at variadic position %d has type \"%s\", expected type \"%s\"",
260 i
+ 1, format_type_be(types
[i
]),
261 format_type_be(TEXTOID
))));
266 argname
= TextDatumGetCString(args
[i
]);
269 * The 'version' argument is a special case, not handled by arginfo
270 * because it's not a valid positional argument.
272 * For now, 'version' is accepted but ignored. In the future it can be
273 * used to interpret older statistics properly.
275 if (pg_strcasecmp(argname
, "version") == 0)
278 argnum
= get_arg_by_name(argname
, arginfo
, elevel
);
280 if (argnum
< 0 || !stats_check_arg_type(argname
, types
[i
+ 1],
281 arginfo
[argnum
].argtype
,
288 positional_fcinfo
->args
[argnum
].value
= args
[i
+ 1];
289 positional_fcinfo
->args
[argnum
].isnull
= false;