Introduce routines to validate and free MVNDistinct and MVDependencies
authorMichael Paquier <michael@paquier.xyz>
Thu, 15 Jan 2026 00:36:05 +0000 (09:36 +0900)
committerMichael Paquier <michael@paquier.xyz>
Thu, 15 Jan 2026 00:36:05 +0000 (09:36 +0900)
These routines are useful to perform some basic validation checks on
each object structure, working currently on attribute numbers for
non-expression and expression attnums.  These checks could be extended
in the future.

Note that this code is not used yet in the tree, and that these
functions will become handy for an upcoming patch for the import of
extended statistics data.  However, they are worth their own independent
change as they are actually useful by themselves, with at least the
extension code argument in mind (or perhaps I am just feeling more
pedantic today).

Extracted from a larger patch by the same author, with many adjustments
and fixes by me.

Author: Corey Huinker <corey.huinker@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CADkLM=dpz3KFnqP-dgJ-zvRvtjsa8UZv8wDAQdqho=qN3kX0Zg@mail.gmail.com

src/backend/statistics/dependencies.c
src/backend/statistics/mvdistinct.c
src/include/statistics/extended_stats_internal.h

index f634f20dd66372711c80b1d701c365437c713498..e3a2f5817e0c7bbc4fe795eee95682a1a554075c 100644 (file)
@@ -579,6 +579,82 @@ statext_dependencies_deserialize(bytea *data)
    return dependencies;
 }
 
+/*
+ * Free allocations of a MVDependencies.
+ */
+void
+statext_dependencies_free(MVDependencies *dependencies)
+{
+   for (int i = 0; i < dependencies->ndeps; i++)
+       pfree(dependencies->deps[i]);
+   pfree(dependencies);
+}
+
+/*
+ * Validate a set of MVDependencies against the extended statistics object
+ * definition.
+ *
+ * Every MVDependencies must be checked to ensure that the attnums in the
+ * attributes list correspond to attnums/expressions defined by the
+ * extended statistics object.
+ *
+ * Positive attnums are attributes which must be found in the stxkeys, while
+ * negative attnums correspond to an expression number, no attribute number
+ * can be below (0 - numexprs).
+ */
+bool
+statext_dependencies_validate(const MVDependencies *dependencies,
+                             const int2vector *stxkeys,
+                             int numexprs, int elevel)
+{
+   int         attnum_expr_lowbound = 0 - numexprs;
+
+   /* Scan through each dependency entry */
+   for (int i = 0; i < dependencies->ndeps; i++)
+   {
+       const MVDependency *dep = dependencies->deps[i];
+
+       /*
+        * Cross-check each attribute in a dependency entry with the extended
+        * stats object definition.
+        */
+       for (int j = 0; j < dep->nattributes; j++)
+       {
+           AttrNumber  attnum = dep->attributes[j];
+           bool        ok = false;
+
+           if (attnum > 0)
+           {
+               /* attribute number in stxkeys */
+               for (int k = 0; k < stxkeys->dim1; k++)
+               {
+                   if (attnum == stxkeys->values[k])
+                   {
+                       ok = true;
+                       break;
+                   }
+               }
+           }
+           else if ((attnum < 0) && (attnum >= attnum_expr_lowbound))
+           {
+               /* attribute number for an expression */
+               ok = true;
+           }
+
+           if (!ok)
+           {
+               ereport(elevel,
+                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                        errmsg("could not validate \"%s\" object: invalid attribute number %d found",
+                               "pg_dependencies", attnum)));
+               return false;
+           }
+       }
+   }
+
+   return true;
+}
+
 /*
  * dependency_is_fully_matched
  *     checks that a functional dependency is fully matched given clauses on
index c87e2ecbfa06756e840a3832bf0cedb64fe2078a..4f8f578a22f2bbe5105411c7c280c95057051326 100644 (file)
@@ -325,6 +325,82 @@ statext_ndistinct_deserialize(bytea *data)
    return ndistinct;
 }
 
+/*
+ * Free allocations of a MVNDistinct.
+ */
+void
+statext_ndistinct_free(MVNDistinct *ndistinct)
+{
+   for (int i = 0; i < ndistinct->nitems; i++)
+       pfree(ndistinct->items[i].attributes);
+   pfree(ndistinct);
+}
+
+/*
+ * Validate a set of MVNDistincts against the extended statistics object
+ * definition.
+ *
+ * Every MVNDistinctItem must be checked to ensure that the attnums in the
+ * attributes list correspond to attnums/expressions defined by the extended
+ * statistics object.
+ *
+ * Positive attnums are attributes which must be found in the stxkeys,
+ * while negative attnums correspond to an expression number, no attribute
+ * number can be below (0 - numexprs).
+ */
+bool
+statext_ndistinct_validate(const MVNDistinct *ndistinct,
+                          const int2vector *stxkeys,
+                          int numexprs, int elevel)
+{
+   int         attnum_expr_lowbound = 0 - numexprs;
+
+   /* Scan through each MVNDistinct entry */
+   for (int i = 0; i < ndistinct->nitems; i++)
+   {
+       MVNDistinctItem item = ndistinct->items[i];
+
+       /*
+        * Cross-check each attribute in a MVNDistinct entry with the extended
+        * stats object definition.
+        */
+       for (int j = 0; j < item.nattributes; j++)
+       {
+           AttrNumber  attnum = item.attributes[j];
+           bool        ok = false;
+
+           if (attnum > 0)
+           {
+               /* attribute number in stxkeys */
+               for (int k = 0; k < stxkeys->dim1; k++)
+               {
+                   if (attnum == stxkeys->values[k])
+                   {
+                       ok = true;
+                       break;
+                   }
+               }
+           }
+           else if ((attnum < 0) && (attnum >= attnum_expr_lowbound))
+           {
+               /* attribute number for an expression */
+               ok = true;
+           }
+
+           if (!ok)
+           {
+               ereport(elevel,
+                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                        errmsg("could not validate \"%s\" object: invalid attribute number %d found",
+                               "pg_ndistinct", attnum)));
+               return false;
+           }
+       }
+   }
+
+   return true;
+}
+
 /*
  * ndistinct_for_combination
  *     Estimates number of distinct values in a combination of columns.
index 229f1a755a6750bb360c66ac91cd27b083ce813f..b5003ec242c15b1a52c3b3c1bceb7bfeda870bcb 100644 (file)
@@ -72,10 +72,18 @@ typedef struct StatsBuildData
 extern MVNDistinct *statext_ndistinct_build(double totalrows, StatsBuildData *data);
 extern bytea *statext_ndistinct_serialize(MVNDistinct *ndistinct);
 extern MVNDistinct *statext_ndistinct_deserialize(bytea *data);
+extern bool statext_ndistinct_validate(const MVNDistinct *ndistinct,
+                                      const int2vector *stxkeys,
+                                      int numexprs, int elevel);
+extern void statext_ndistinct_free(MVNDistinct *ndistinct);
 
 extern MVDependencies *statext_dependencies_build(StatsBuildData *data);
 extern bytea *statext_dependencies_serialize(MVDependencies *dependencies);
 extern MVDependencies *statext_dependencies_deserialize(bytea *data);
+extern bool statext_dependencies_validate(const MVDependencies *dependencies,
+                                         const int2vector *stxkeys,
+                                         int numexprs, int elevel);
+extern void statext_dependencies_free(MVDependencies *dependencies);
 
 extern MCVList *statext_mcv_build(StatsBuildData *data,
                                  double totalrows, int stattarget);