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