v29-0001-Create-statistics-manipulation-utility-functions.patch
text/x-patch
Filename: v29-0001-Create-statistics-manipulation-utility-functions.patch
Type: text/x-patch
Part: 1
Message:
Re: Statistics Import and Export
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v29-0001
Subject: Create statistics manipulation utility functions.
| File | + | − |
|---|---|---|
| src/backend/statistics/Makefile | 2 | 1 |
| src/backend/statistics/meson.build | 1 | 0 |
| src/backend/statistics/stats_utils.c | 156 | 0 |
| src/include/statistics/stats_utils.h | 36 | 0 |
From 4bd77f59b1953ea419230b861b3632dc534bf0e2 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Mon, 16 Sep 2024 03:11:47 -0400
Subject: [PATCH v29 1/7] Create statistics manipulation utility functions.
Creates the following functions, all of which are in support of the
manipulation (import, deletion, etc) of statistics of all kinds
(relation, attribute, extended).
- stats_check_required_arg()
Ensure that a given argument exist.
- stats_check_arg_type():
Check that a given argument is of the expected type.
- stats_check_arg_array():
Check that an argument is a one-dimensional array with no NULL values.
- stats_check_arg_pair():
Check that a given arg pair is either both present and or both missing,
and in the event of a mismatch disable the stat that is present.
- stats_lock_check_privileges():
Ensure that the caller has the permissions to modify statistics for the
given relation.
---
src/include/statistics/stats_utils.h | 36 +++++++
src/backend/statistics/Makefile | 3 +-
src/backend/statistics/meson.build | 1 +
src/backend/statistics/stats_utils.c | 156 +++++++++++++++++++++++++++
4 files changed, 195 insertions(+), 1 deletion(-)
create mode 100644 src/include/statistics/stats_utils.h
create mode 100644 src/backend/statistics/stats_utils.c
diff --git a/src/include/statistics/stats_utils.h b/src/include/statistics/stats_utils.h
new file mode 100644
index 0000000000..a6926ce79a
--- /dev/null
+++ b/src/include/statistics/stats_utils.h
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * statistics.h
+ * Extended statistics and selectivity estimation functions.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/statistics/statistics.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STATS_UTILS_H
+#define STATS_UTILS_H
+
+#include "fmgr.h"
+#include "postgres_ext.h"
+
+typedef struct StatsArgInfo
+{
+ const char *argname;
+ Oid argtype;
+} StatsArgInfo;
+
+extern void stats_check_required_arg(FunctionCallInfo fcinfo,
+ StatsArgInfo *arginfo, int argnum);
+extern bool stats_check_arg_type(const char *argname, Oid argtype,
+ Oid expectedtype, int elevel);
+extern void stats_check_arg_array(FunctionCallInfo fcinfo, StatsArgInfo *arginfo,
+ int argnum, int elevel);
+extern void stats_check_arg_pair(FunctionCallInfo fcinfo, StatsArgInfo *arginfo,
+ int argnum1, int argnum2, int elevel);
+
+extern void stats_lock_check_privileges(Oid reloid);
+
+#endif /* STATS_UTILS_H */
diff --git a/src/backend/statistics/Makefile b/src/backend/statistics/Makefile
index 89cf8c2797..3ffc8f38e6 100644
--- a/src/backend/statistics/Makefile
+++ b/src/backend/statistics/Makefile
@@ -16,6 +16,7 @@ OBJS = \
dependencies.o \
extended_stats.o \
mcv.o \
- mvdistinct.o
+ mvdistinct.o \
+ stats_utils.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/statistics/meson.build b/src/backend/statistics/meson.build
index 73b29a3d50..74c5dc6afa 100644
--- a/src/backend/statistics/meson.build
+++ b/src/backend/statistics/meson.build
@@ -5,4 +5,5 @@ backend_sources += files(
'extended_stats.c',
'mcv.c',
'mvdistinct.c',
+ 'stats_utils.c'
)
diff --git a/src/backend/statistics/stats_utils.c b/src/backend/statistics/stats_utils.c
new file mode 100644
index 0000000000..f51144d4ac
--- /dev/null
+++ b/src/backend/statistics/stats_utils.c
@@ -0,0 +1,156 @@
+/*-------------------------------------------------------------------------
+ * stats_utils.c
+ *
+ * PostgreSQL statistics manipulation utilities.
+ *
+ * Code supporting the direct manipulation of statistics.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/statistics/stats_privs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/relation.h"
+#include "catalog/pg_database.h"
+#include "miscadmin.h"
+#include "statistics/stats_utils.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/acl.h"
+#include "utils/rel.h"
+
+/*
+ * Ensure that a given argument matched the expected type.
+ */
+bool
+stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype, int elevel)
+{
+ if (argtype != expectedtype)
+ {
+ ereport(elevel,
+ (errmsg("argument \"%s\" has type \"%s\", expected type \"%s\"",
+ argname, format_type_be(argtype),
+ format_type_be(expectedtype))));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Ensure that a given argument is not null
+ */
+void
+stats_check_required_arg(FunctionCallInfo fcinfo, StatsArgInfo *arginfo,
+ int argnum)
+{
+ if (PG_ARGISNULL(argnum))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" cannot be NULL",
+ arginfo[argnum].argname)));
+}
+
+/*
+ * Check that array argument is one dimensional with no NULLs.
+ *
+ * If not, emit at elevel, and set argument to NULL in fcinfo.
+ */
+void
+stats_check_arg_array(FunctionCallInfo fcinfo, StatsArgInfo *arginfo,
+ int argnum, int elevel)
+{
+ ArrayType *arr;
+
+ if (PG_ARGISNULL(argnum))
+ return;
+
+ arr = DatumGetArrayTypeP(PG_GETARG_DATUM(argnum));
+
+ if (ARR_NDIM(arr) != 1)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" cannot be a multidimensional array",
+ arginfo[argnum].argname)));
+ fcinfo->args[argnum].isnull = true;
+ }
+
+ if (array_contains_nulls(arr))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" array cannot contain NULL values",
+ arginfo[argnum].argname)));
+ fcinfo->args[argnum].isnull = true;
+ }
+}
+
+/*
+ * Enforce parameter pairs that must be specified together for a particular
+ * stakind, such as most_common_vals and most_common_freqs for
+ * STATISTIC_KIND_MCV. If one is NULL and the other is not, emit at elevel,
+ * and ignore the stakind by setting both to NULL in fcinfo.
+ */
+void
+stats_check_arg_pair(FunctionCallInfo fcinfo, StatsArgInfo *arginfo,
+ int argnum1, int argnum2, int elevel)
+{
+ if (PG_ARGISNULL(argnum1) && !PG_ARGISNULL(argnum2))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" must be specified when \"%s\" is specified",
+ arginfo[argnum1].argname,
+ arginfo[argnum2].argname)));
+ fcinfo->args[argnum2].isnull = true;
+ }
+ if (!PG_ARGISNULL(argnum1) && PG_ARGISNULL(argnum2))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("\"%s\" must be specified when \"%s\" is specified",
+ arginfo[argnum2].argname,
+ arginfo[argnum1].argname)));
+ fcinfo->args[argnum1].isnull = true;
+ }
+}
+
+/*
+ * Lock relation in ShareUpdateExclusive mode, check privileges, and close the
+ * relation (but retain the lock).
+ *
+ * A role has privileges to set statistics on the relation if any of the
+ * following are true:
+ * - the role owns the current database and the relation is not shared
+ * - the role has the MAINTAIN privilege on the relation
+ */
+void
+stats_lock_check_privileges(Oid reloid)
+{
+ Relation rel = relation_open(reloid, ShareUpdateExclusiveLock);
+
+ if (rel->rd_rel->relisshared)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot modify statistics for shared relation")));
+
+ if (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()))
+ {
+ AclResult aclresult = pg_class_aclcheck(RelationGetRelid(rel),
+ GetUserId(),
+ ACL_MAINTAIN);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult,
+ get_relkind_objtype(rel->rd_rel->relkind),
+ NameStr(rel->rd_rel->relname));
+ }
+
+ relation_close(rel, NoLock);
+}
--
2.46.0