v16-0002-Add-working-input-function-for-pg_dependencies.patch
text/x-patch
Filename: v16-0002-Add-working-input-function-for-pg_dependencies.patch
Type: text/x-patch
Part: 1
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 v16-0002
Subject: Add working input function for pg_dependencies.
| File | + | − |
|---|---|---|
| src/backend/utils/adt/pg_dependencies.c | 800 | 10 |
| src/test/regress/expected/pg_dependencies.out | 376 | 0 |
| src/test/regress/parallel_schedule | 1 | 1 |
| src/test/regress/sql/pg_dependencies.sql | 99 | 0 |
From 29bc27036a1966dbc29c414d1dd6a1512e0ad524 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 17 Nov 2025 15:49:36 +0900
Subject: [PATCH v16 2/5] Add working input function for pg_dependencies.
This will consume the format that was established when the output
function for pg_dependencies was recently changed.
This will be needed for importing extended statistics.
---
src/backend/utils/adt/pg_dependencies.c | 810 +++++++++++++++++-
src/test/regress/expected/pg_dependencies.out | 376 ++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/pg_dependencies.sql | 99 +++
4 files changed, 1276 insertions(+), 11 deletions(-)
create mode 100644 src/test/regress/expected/pg_dependencies.out
create mode 100644 src/test/regress/sql/pg_dependencies.sql
diff --git a/src/backend/utils/adt/pg_dependencies.c b/src/backend/utils/adt/pg_dependencies.c
index 87181aa00e9..bc8795448b2 100644
--- a/src/backend/utils/adt/pg_dependencies.c
+++ b/src/backend/utils/adt/pg_dependencies.c
@@ -14,29 +14,819 @@
#include "postgres.h"
+#include "common/int.h"
+#include "common/jsonapi.h"
#include "lib/stringinfo.h"
+#include "mb/pg_wchar.h"
+#include "nodes/miscnodes.h"
#include "statistics/extended_stats_internal.h"
#include "statistics/statistics_format.h"
+#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/fmgrprotos.h"
+typedef enum
+{
+ DEPS_EXPECT_START = 0,
+ DEPS_EXPECT_ITEM,
+ DEPS_EXPECT_KEY,
+ DEPS_EXPECT_ATTNUM_LIST,
+ DEPS_EXPECT_ATTNUM,
+ DEPS_EXPECT_DEPENDENCY,
+ DEPS_EXPECT_DEGREE,
+ DEPS_PARSE_COMPLETE
+} DepsParseSemanticState;
+
+typedef struct
+{
+ const char *str;
+ DepsParseSemanticState state;
+
+ List *dependency_list;
+ Node *escontext;
+
+ bool found_attributes; /* Item has an attributes key */
+ bool found_dependency; /* Item has an dependency key */
+ bool found_degree; /* Item has degree key */
+ List *attnum_list; /* Accumulated attributes attnums */
+ AttrNumber dependency;
+ double degree;
+} DependenciesParseState;
+
+/*
+ * Invoked at the start of each MVDependency object.
+ *
+ * The entire JSON document should be one array of MVDependency objects.
+ *
+ * If we are anywhere else in the document, it's an error.
+ */
+static JsonParseErrorType
+dependencies_object_start(void *state)
+{
+ DependenciesParseState *parse = state;
+
+ switch(parse->state)
+ {
+ case DEPS_EXPECT_ITEM:
+ /* Now we expect to see attributes/dependency/degree keys */
+ parse->state = DEPS_EXPECT_KEY;
+ return JSON_SUCCESS;
+ break;
+
+ case DEPS_EXPECT_START:
+ /* pg_dependencies must begin with a '[' */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Initial element must be an array."));
+ break;
+
+ case DEPS_EXPECT_KEY:
+ /* In an object, expecting key */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Expected an object key."));
+ break;
+
+ case DEPS_EXPECT_ATTNUM_LIST:
+ /* Just followed an "attributes": key */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Value of \"%s\" must be an array of attribute numbers.",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES));
+ break;
+
+ case DEPS_EXPECT_ATTNUM:
+ /* In an attnum list, expect only scalar integers */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Attribute lists can only contain attribute numbers."));
+ break;
+
+ case DEPS_EXPECT_DEPENDENCY:
+ /* Just followed a "dependency" key */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Value of \"%s\" must be an integer.",
+ PG_DEPENDENCIES_KEY_DEPENDENCY));
+ break;
+
+ case DEPS_EXPECT_DEGREE:
+ /* Just followed a "degree" key */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Value of \"%s\" must be an integer.",
+ PG_DEPENDENCIES_KEY_DEGREE));
+ break;
+
+ default:
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Unexpected parse state: %d", (int) parse->state));
+ break;
+ }
+
+ return JSON_SEM_ACTION_FAILED;
+}
+
+static JsonParseErrorType
+dependencies_object_end(void *state)
+{
+ DependenciesParseState *parse = state;
+
+ MVDependency *dep;
+
+ int natts = 0;
+
+ if (parse->state != DEPS_EXPECT_KEY)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Unexpected parse state: %d", (int) parse->state));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ if (!parse->found_attributes)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Item must contain \"%s\" key",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ if (!parse->found_dependency)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Item must contain \"%s\" key.",
+ PG_DEPENDENCIES_KEY_DEPENDENCY));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ if (!parse->found_degree)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Item must contain \"%s\" key.",
+ PG_DEPENDENCIES_KEY_DEGREE));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ /*
+ * We need at least one attribute number a dependencies item, anything
+ * less is malformed.
+ */
+ natts = parse->attnum_list->length;
+ if ((natts < 1) || (natts > (STATS_MAX_DIMENSIONS - 1)))
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("The \"%s\" key must contain an array of at least %d "
+ " and no than %d elements.",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES, 1, STATS_MAX_DIMENSIONS - 1));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ /*
+ * Allocate enough space for the dependency, the attnums in the list, plus
+ * the final attnum.
+ */
+ dep = palloc0(offsetof(MVDependency, attributes) + ((natts + 1) * sizeof(AttrNumber)));
+ dep->nattributes = natts + 1;
+
+ dep->attributes[natts] = parse->dependency;
+ dep->degree = parse->degree;
+
+ for (int i = 0; i < natts; i++)
+ {
+ dep->attributes[i] = (AttrNumber) list_nth_int(parse->attnum_list, i);
+ if (dep->attributes[i] == parse->dependency)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Item \"%s\" value %d found in the \"%s\" list.",
+ PG_DEPENDENCIES_KEY_DEPENDENCY, parse->dependency,
+ PG_DEPENDENCIES_KEY_ATTRIBUTES));
+ return JSON_SEM_ACTION_FAILED;
+ }
+ }
+
+ parse->dependency_list = lappend(parse->dependency_list, (void *) dep);
+
+ /* Reset dependency item state variables */
+ list_free(parse->attnum_list);
+ parse->attnum_list = NIL;
+ parse->dependency = 0;
+ parse->degree = 0.0;
+ parse->found_attributes = false;
+ parse->found_dependency = false;
+ parse->found_degree = false;
+
+ /* Now we are looking for the next MVDependency */
+ parse->state = DEPS_EXPECT_ITEM;
+ return JSON_SUCCESS;
+}
+
+/*
+ * Dependency input format does not have arrays, so any array elements
+ * encountered are an error.
+ */
+static JsonParseErrorType
+dependencies_array_start(void *state)
+{
+ DependenciesParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case DEPS_EXPECT_ATTNUM_LIST:
+ parse->state = DEPS_EXPECT_ATTNUM;
+ break;
+ case DEPS_EXPECT_START:
+ parse->state = DEPS_EXPECT_ITEM;
+ break;
+ default:
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Array found in unexpected place."));
+ return JSON_SEM_ACTION_FAILED;
+ break;
+ }
+
+ return JSON_SUCCESS;
+}
+
+/*
+ * Either the end of an attnum list or the whole object.
+ */
+static JsonParseErrorType
+dependencies_array_end(void *state)
+{
+ DependenciesParseState *parse = state;
+
+ switch (parse->state)
+ {
+ case DEPS_EXPECT_ATTNUM:
+ if (parse->attnum_list != NIL)
+ {
+ parse->state = DEPS_EXPECT_KEY;
+ return JSON_SUCCESS;
+ }
+
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("The \"%s\" key must be an non-empty array.",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES));
+ break;
+
+ case DEPS_EXPECT_ITEM:
+ if (parse->dependency_list != NIL)
+ {
+ parse->state = DEPS_PARSE_COMPLETE;
+ return JSON_SUCCESS;
+ }
+
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Item array cannot be empty."));
+ break;
+
+ default:
+ /*
+ * This can only happen if a case was missed in depenenceies_array_start()
+ */
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Array found in unexpected place."));
+ break;
+ }
+ return JSON_SEM_ACTION_FAILED;
+}
+
+/*
+ * The valid keys for the MVDependency object are:
+ * - attributes
+ * - depeendency
+ * - degree
+ */
+static JsonParseErrorType
+dependencies_object_field_start(void *state, char *fname, bool isnull)
+{
+ DependenciesParseState *parse = state;
+
+ if (strcmp(fname, PG_DEPENDENCIES_KEY_ATTRIBUTES) == 0)
+ {
+ if (parse->found_attributes)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Multiple \"%s\" keys are not allowed.",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ parse->found_attributes = true;
+ parse->state = DEPS_EXPECT_ATTNUM_LIST;
+ return JSON_SUCCESS;
+ }
+
+ if (strcmp(fname, PG_DEPENDENCIES_KEY_DEPENDENCY) == 0)
+ {
+ if (parse->found_dependency)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Multiple \"%s\" keys are not allowed.",
+ PG_DEPENDENCIES_KEY_DEPENDENCY));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ parse->found_dependency = true;
+ parse->state = DEPS_EXPECT_DEPENDENCY;
+ return JSON_SUCCESS;
+ }
+
+ if (strcmp(fname, PG_DEPENDENCIES_KEY_DEGREE) == 0)
+ {
+ if (parse->found_degree)
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Multiple \"%s\" keys are not allowed.",
+ PG_DEPENDENCIES_KEY_DEGREE));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ parse->found_degree = true;
+ parse->state = DEPS_EXPECT_DEGREE;
+ return JSON_SUCCESS;
+ }
+
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Only allowed keys are \"%s\", \"%s\" and \"%s\".",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES,
+ PG_DEPENDENCIES_KEY_DEPENDENCY,
+ PG_DEPENDENCIES_KEY_DEGREE));
+ return JSON_SEM_ACTION_FAILED;
+}
+
+/*
+ * pg_dependencies input format does not have arrays, so any array elements
+ * encountered are an error.
+ */
+static JsonParseErrorType
+dependencies_array_element_start(void *state, bool isnull)
+{
+ DependenciesParseState *parse = state;
+
+ switch(parse->state)
+ {
+ case DEPS_EXPECT_ATTNUM:
+ if (!isnull)
+ return JSON_SUCCESS;
+
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Attribute number array cannot be null."));
+
+ return JSON_SEM_ACTION_FAILED;
+ break;
+
+ case DEPS_EXPECT_ITEM:
+ if (!isnull)
+ return JSON_SUCCESS;
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Item list elements cannot be null."));
+
+ return JSON_SEM_ACTION_FAILED;
+ break;
+
+ default:
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Unexpected array element."));
+ break;
+ }
+
+ return JSON_SEM_ACTION_FAILED;
+}
+
+/*
+ * Test for valid subsequent attribute number.
+ *
+ * If the previous value is positive, then current value must either be
+ * greater than the previous value, or negative.
+ *
+ * If the previous value is negative, then the value must be less than
+ * the previous value.
+ *
+ * Duplicate values are obviously not allowed, but that is already covered
+ * by the rules listed above.
+ */
+static bool
+valid_subsequent_attnum(const AttrNumber prev, const AttrNumber cur)
+{
+ Assert(prev != 0);
+
+ if (prev > 0)
+ return ((cur > prev) || (cur < 0));
+
+ return (cur < prev);
+}
+
+/*
+ * Handle scalar events from the dependencies input parser.
+ *
+ * There is only one case where we will encounter a scalar, and that is the
+ * dependency degree for the previous object key.
+ */
+static JsonParseErrorType
+dependencies_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+ DependenciesParseState *parse = state;
+ AttrNumber attnum;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ switch(parse->state)
+ {
+ case DEPS_EXPECT_ATTNUM:
+ attnum = pg_strtoint16_safe(token, (Node *) &escontext);
+
+ if (SOFT_ERROR_OCCURRED(&escontext))
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Invalid \"%s\" value.", PG_DEPENDENCIES_KEY_ATTRIBUTES));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ /*
+ * The attnum cannot be zero a negative number beyond the number of the
+ * possible expressions.
+ */
+ if (attnum == 0 || attnum < (0-STATS_MAX_DIMENSIONS))
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Invalid \"%s\" element: %d.",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES, attnum));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ if (parse->attnum_list != NIL)
+ {
+ const AttrNumber prev = llast_int(parse->attnum_list);
+
+ if (!valid_subsequent_attnum(prev, attnum))
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Invalid \"%s\" element: %d cannot follow %d.",
+ PG_DEPENDENCIES_KEY_ATTRIBUTES, attnum, prev));
+ return JSON_SEM_ACTION_FAILED;
+ }
+ }
+
+ parse->attnum_list = lappend_int(parse->attnum_list, (int) attnum);
+ return JSON_SUCCESS;
+ break;
+
+ case DEPS_EXPECT_DEPENDENCY:
+ parse->dependency = (AttrNumber)
+ pg_strtoint16_safe(token, (Node *) &escontext);
+
+ if (SOFT_ERROR_OCCURRED(&escontext))
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Invalid \"%s\" value.", PG_DEPENDENCIES_KEY_DEPENDENCY));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ parse->state = DEPS_EXPECT_KEY;
+ return JSON_SUCCESS;
+ break;
+
+ case DEPS_EXPECT_DEGREE:
+ parse->degree = float8in_internal(token, NULL, "double",
+ token, (Node *) &escontext);
+
+ if (SOFT_ERROR_OCCURRED(&escontext))
+ {
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Invalid \"%s\" value.", PG_DEPENDENCIES_KEY_DEGREE));
+ return JSON_SEM_ACTION_FAILED;
+ }
+
+ parse->state = DEPS_EXPECT_KEY;
+ return JSON_SUCCESS;
+ break;
+
+ default:
+ errsave(parse->escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+ errdetail("Unexpected scalar."));
+ break;
+ }
+
+ return JSON_SEM_ACTION_FAILED;
+}
+
+/*
+ * Compare the attribute arrays of two MVDependency values,
+ * looking for duplicate sets.
+ */
+static bool
+has_duplicate_attributes(const MVDependency *a, const MVDependency *b)
+{
+ int i;
+
+ if (a->nattributes != b->nattributes)
+ return false;
+
+ for (i = 0; i < a->nattributes; i++)
+ {
+ if (a->attributes[i] != b->attributes[i])
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Ensure that an attnum appears as one of the attnums in a given
+ * MVDependency.
+ */
+static bool
+dep_has_attnum(const MVDependency *item, AttrNumber attnum)
+{
+ for (int i = 0; i < item->nattributes; i++)
+ {
+ if (attnum == item->attributes[i])
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Ensure that the attributes of one MVDependency A are a proper subset
+ * of the reference MVDependency B.
+ */
+static bool
+dep_is_attnum_subset(const MVDependency *item,
+ const MVDependency *refitem)
+{
+ for (int i = 0; i < item->nattributes; i++)
+ {
+ if (!dep_has_attnum(refitem,item->attributes[i]))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Generate a string representing an array of attnums. Internally, the
+ * dependency attribute is the last element, so we leave that off.
+ *
+ *
+ * Freeing the allocated string is responsibility of the caller.
+ */
+static const char *
+dep_attnum_list(const MVDependency *item)
+{
+ StringInfoData str;
+
+ initStringInfo(&str);
+
+ appendStringInfo(&str, "%d", item->attributes[0]);
+
+ for (int i = 1; i < item->nattributes - 1; i++)
+ appendStringInfo(&str, ", %d", item->attributes[i]);
+
+ return str.data;
+}
+
+/*
+ * Return the dependency, which is the last attribute element.
+ */
+static const AttrNumber
+dep_attnum_dependency(const MVDependency *item)
+{
+ return item->attributes[item->nattributes - 1];
+}
+
/*
* pg_dependencies_in - input routine for type pg_dependencies.
*
- * pg_dependencies is real enough to be a table column, but it has no operations
- * of its own, and disallows input too
+ * This format is valid JSON, with the expected format:
+ * [{"attributes": [1,2], "dependency": -1, "degree": 1.0000},
+ * {"attributes": [1,-1], "dependency": 2, "degree": 0.0000},
+ * {"attributes": [2,-1], "dependency": 1, "degree": 1.0000}]
+ *
*/
Datum
pg_dependencies_in(PG_FUNCTION_ARGS)
{
- /*
- * pg_node_list stores the data in binary form and parsing text input is
- * not needed, so disallow this.
- */
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot accept a value of type %s", "pg_dependencies")));
+ char *str = PG_GETARG_CSTRING(0);
- PG_RETURN_VOID(); /* keep compiler quiet */
+ DependenciesParseState parse_state;
+ JsonParseErrorType result;
+ JsonLexContext *lex;
+ JsonSemAction sem_action;
+
+ /* initialize the semantic state */
+ parse_state.str = str;
+ parse_state.state = DEPS_EXPECT_START;
+ parse_state.dependency_list = NIL;
+ parse_state.attnum_list = NIL;
+ parse_state.dependency = 0;
+ parse_state.degree = 0.0;
+ parse_state.found_attributes = false;
+ parse_state.found_dependency = false;
+ parse_state.found_degree = false;
+ parse_state.escontext = fcinfo->context;
+
+ /* set callbacks */
+ sem_action.semstate = (void *) &parse_state;
+ sem_action.object_start = dependencies_object_start;
+ sem_action.object_end = dependencies_object_end;
+ sem_action.array_start = dependencies_array_start;
+ sem_action.array_end = dependencies_array_end;
+ sem_action.array_element_start = dependencies_array_element_start;
+ sem_action.array_element_end = NULL;
+ sem_action.object_field_start = dependencies_object_field_start;
+ sem_action.object_field_end = NULL;
+ sem_action.scalar = dependencies_scalar;
+
+ lex = makeJsonLexContextCstringLen(NULL, str, strlen(str), PG_UTF8, true);
+
+ result = pg_parse_json(lex, &sem_action);
+ freeJsonLexContext(lex);
+
+ if (result == JSON_SUCCESS)
+ {
+ List *list = parse_state.dependency_list;
+ int ndeps = list->length;
+ MVDependencies *mvdeps;
+ bytea *bytes;
+
+ int dep_most_attrs = 0;
+ int dep_most_attrs_idx = 0;
+
+ switch(parse_state.state)
+ {
+ case DEPS_PARSE_COMPLETE:
+ /*
+ * Parse ended in the expected place. We should have a list of items,
+ * but if we don't it is because there are bugs in other parse steps.
+ */
+ if (parse_state.dependency_list == NIL)
+ elog(ERROR,
+ "pg_dependencies parssing claims success with an empty item list.");
+
+ break;
+
+ case DEPS_EXPECT_START:
+ /* blank */
+ errsave(parse_state.escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", str),
+ errdetail("Value cannot be empty."));
+ PG_RETURN_NULL();
+ break;
+
+ default:
+ /* Unexpected end-state. TODO: Is this an elog()? */
+ errsave(parse_state.escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", str),
+ errdetail("Unexpected end state %d.", parse_state.state));
+ PG_RETURN_NULL();
+ break;
+ }
+
+ mvdeps = palloc0(offsetof(MVDependencies, deps) + ndeps * sizeof(MVDependency));
+ mvdeps->magic = STATS_DEPS_MAGIC;
+ mvdeps->type = STATS_DEPS_TYPE_BASIC;
+ mvdeps->ndeps = ndeps;
+
+ /* copy MVDependency structs out of the list into the MVDependencies */
+ for (int i = 0; i < ndeps; i++)
+ {
+ mvdeps->deps[i] = (MVDependency *) list_nth(list, i);
+
+ /*
+ * Ensure that this item does not duplicate the attributes of any
+ * pre-existing item.
+ */
+ for (int j = 0; j < i; j++)
+ {
+ if (has_duplicate_attributes(mvdeps->deps[i], mvdeps->deps[j]))
+ {
+ MVDependency *dep = mvdeps->deps[i];
+
+ errsave(parse_state.escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", str),
+ errdetail("Duplicate \"" PG_DEPENDENCIES_KEY_ATTRIBUTES "\" array: [%s]"
+ " with \"" PG_DEPENDENCIES_KEY_DEPENDENCY "\": %d.",
+ dep_attnum_list(dep), dep_attnum_dependency(dep)));
+ PG_RETURN_NULL();
+ }
+ }
+
+ /*
+ * Keep track of the first longest attribute list. All other attribute
+ * lists must be a subset of this list.
+ */
+ if (mvdeps->deps[i]->nattributes > dep_most_attrs)
+ {
+ dep_most_attrs = mvdeps->deps[i]->nattributes;
+ dep_most_attrs_idx = i;
+ }
+ }
+
+ /*
+ * Verify that all attnum sets are a proper subset of the first longest
+ * attnum set.
+ */
+ for (int i = 0; i < ndeps; i++)
+ {
+ if (i == dep_most_attrs_idx)
+ continue;
+
+ if (!dep_is_attnum_subset(mvdeps->deps[i],
+ mvdeps->deps[dep_most_attrs_idx]))
+ {
+ MVDependency *dep = mvdeps->deps[i];
+ MVDependency *refdep = mvdeps->deps[dep_most_attrs_idx];
+ const char *dep_list = dep_attnum_list(dep);
+ const char *refdep_list = dep_attnum_list(refdep);
+
+ errsave(parse_state.escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", str),
+ errdetail("\"" PG_DEPENDENCIES_KEY_ATTRIBUTES "\" array: [%s]"
+ " with dependency %d must be a subset of array: [%s]"
+ " with dependency %d.",
+ dep_list, dep_attnum_dependency(dep),
+ refdep_list, dep_attnum_dependency(refdep)));
+ PG_RETURN_NULL();
+ }
+ }
+ bytes = statext_dependencies_serialize(mvdeps);
+
+ list_free(list);
+ for (int i = 0; i < ndeps; i++)
+ pfree(mvdeps->deps[i]);
+ pfree(mvdeps);
+
+ PG_RETURN_BYTEA_P(bytes);
+ }
+
+ /*
+ * If escontext already set, just use that.
+ * Anything else is a generic JSON parse error.
+ */
+ if (!SOFT_ERROR_OCCURRED(parse_state.escontext))
+ errsave(parse_state.escontext,
+ errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed pg_dependencies: \"%s\"", str),
+ errdetail("Must be valid JSON."));
+
+ PG_RETURN_NULL(); /* keep compiler quiet */
}
/*
diff --git a/src/test/regress/expected/pg_dependencies.out b/src/test/regress/expected/pg_dependencies.out
new file mode 100644
index 00000000000..c263c133f08
--- /dev/null
+++ b/src/test/regress/expected/pg_dependencies.out
@@ -0,0 +1,376 @@
+-- Tests for type pg_distinct
+-- Invalid inputs
+SELECT 'null'::pg_dependencies;
+ERROR: malformed pg_dependencies: "null"
+LINE 1: SELECT 'null'::pg_dependencies;
+ ^
+DETAIL: Unexpected scalar.
+SELECT '{"a": 1}'::pg_dependencies;
+ERROR: malformed pg_dependencies: "{"a": 1}"
+LINE 1: SELECT '{"a": 1}'::pg_dependencies;
+ ^
+DETAIL: Initial element must be an array.
+SELECT '[]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[]"
+LINE 1: SELECT '[]'::pg_dependencies;
+ ^
+DETAIL: Item array cannot be empty.
+SELECT '{}'::pg_dependencies;
+ERROR: malformed pg_dependencies: "{}"
+LINE 1: SELECT '{}'::pg_dependencies;
+ ^
+DETAIL: Initial element must be an array.
+SELECT '[null]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[null]"
+LINE 1: SELECT '[null]'::pg_dependencies;
+ ^
+DETAIL: Item list elements cannot be null.
+SELECT * FROM pg_input_error_info('null', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-----------------------------------+--------------------+------+----------------
+ malformed pg_dependencies: "null" | Unexpected scalar. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('{"a": 1}', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------------+-----------------------------------+------+----------------
+ malformed pg_dependencies: "{"a": 1}" | Initial element must be an array. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------+-----------------------------+------+----------------
+ malformed pg_dependencies: "[]" | Item array cannot be empty. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('{}', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------+-----------------------------------+------+----------------
+ malformed pg_dependencies: "{}" | Initial element must be an array. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[null]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-------------------------------------+------------------------------------+------+----------------
+ malformed pg_dependencies: "[null]" | Item list elements cannot be null. | | 22P02
+(1 row)
+
+-- Invalid keys
+SELECT '[{"attributes_invalid" : [2,3], "dependency" : 4}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes_invalid" : [2,3], "dependency" : 4}]"
+LINE 1: SELECT '[{"attributes_invalid" : [2,3], "dependency" : 4}]':...
+ ^
+DETAIL: Only allowed keys are "attributes", "dependency" and "degree".
+SELECT '[{"attributes" : [2,3], "invalid" : 3, "dependency" : 4}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "invalid" : 3, "dependency" : 4}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "invalid" : 3, "dependency" ...
+ ^
+DETAIL: Only allowed keys are "attributes", "dependency" and "degree".
+SELECT * FROM pg_input_error_info('[{"attributes_invalid" : [2,3], "dependency" : 4}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------------------------------------------------------+----------------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes_invalid" : [2,3], "dependency" : 4}]" | Only allowed keys are "attributes", "dependency" and "degree". | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "invalid" : 3, "dependency" : 4}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+----------------------------------------------------------------------------------------+----------------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "invalid" : 3, "dependency" : 4}]" | Only allowed keys are "attributes", "dependency" and "degree". | | 22P02
+(1 row)
+
+-- Missing keys
+SELECT '[{"attributes" : [2,3], "dependency" : 4}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : 4}]'::pg_depe...
+ ^
+DETAIL: Item must contain "degree" key.
+SELECT '[{"attributes" : [2,3], "degree" : 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "degree" : 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "degree" : 1.000}]'::pg_depe...
+ ^
+DETAIL: Item must contain "dependency" key.
+SELECT '[{"attributes" : [2,3], "dependency" : 4}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : 4}]'::pg_depe...
+ ^
+DETAIL: Item must contain "degree" key.
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-------------------------------------------------------------------------+---------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4}]" | Item must contain "degree" key. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "degree" : 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-------------------------------------------------------------------------+-------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "degree" : 1.000}]" | Item must contain "dependency" key. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-------------------------------------------------------------------------+---------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4}]" | Item must contain "degree" key. | | 22P02
+(1 row)
+
+-- Valid keys, too many attributes
+SELECT '[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4...
+ ^
+DETAIL: The "attributes" key must contain an array of at least 1 and no than 7 elements.
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4, "degree": 1.000}]" | The "attributes" key must contain an array of at least 1 and no than 7 elements. | | 22P02
+(1 row)
+
+-- Valid keys, invalid values
+SELECT '[{"attributes" : null, "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : null, "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : null, "dependency" : 4, "degree": 1...
+ ^
+DETAIL: Unexpected scalar.
+SELECT '[{"attributes" : [2,null], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,null], "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,null], "dependency" : 4, "degree...
+ ^
+DETAIL: Attribute number array cannot be null.
+SELECT '[{"attributes" : [2,3], "dependency" : null, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : null, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : null, "degree...
+ ^
+DETAIL: Invalid "dependency" value.
+SELECT '[{"attributes" : [2,"a"], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,"a"], "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,"a"], "dependency" : 4, "degree"...
+ ^
+DETAIL: Invalid "attributes" value.
+SELECT '[{"attributes" : [2,3], "dependency" : "a", "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : "a", "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : "a", "degree"...
+ ^
+DETAIL: Invalid "dependency" value.
+SELECT '[{"attributes" : [2,3], "dependency" : [], "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : [], "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : [], "degree":...
+ ^
+DETAIL: Array found in unexpected place.
+SELECT '[{"attributes" : [2,3], "dependency" : [null], "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : [null], "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : [null], "degr...
+ ^
+DETAIL: Array found in unexpected place.
+SELECT '[{"attributes" : [2,3], "dependency" : [1,null], "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : [1,null], "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : [1,null], "de...
+ ^
+DETAIL: Array found in unexpected place.
+SELECT '[{"attributes" : 1, "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : 1, "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : 1, "dependency" : 4, "degree": 1.00...
+ ^
+DETAIL: Unexpected scalar.
+SELECT '[{"attributes" : "a", "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : "a", "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : "a", "dependency" : 4, "degree": 1....
+ ^
+DETAIL: Unexpected scalar.
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": NaN}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "degree": NaN}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": ...
+ ^
+DETAIL: Must be valid JSON.
+SELECT * FROM pg_input_error_info('[{"attributes" : null, "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-----------------------------------------------------------------------------------------+--------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : null, "dependency" : 4, "degree": 1.000}]" | Unexpected scalar. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,null], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------------------------------------------------------------------+----------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,null], "dependency" : 4, "degree": 1.000}]" | Attribute number array cannot be null. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : null, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------------------------------------------------------------------+-----------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : null, "degree": 1.000}]" | Invalid "dependency" value. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,"a"], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+--------------------------------------------------------------------------------------------+-----------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,"a"], "dependency" : 4, "degree": 1.000}]" | Invalid "attributes" value. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : "a", "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+--------------------------------------------------------------------------------------------+-----------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : "a", "degree": 1.000}]" | Invalid "dependency" value. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : [], "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-------------------------------------------------------------------------------------------+----------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : [], "degree": 1.000}]" | Array found in unexpected place. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : [null], "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-----------------------------------------------------------------------------------------------+----------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : [null], "degree": 1.000}]" | Array found in unexpected place. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : [1,null], "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-------------------------------------------------------------------------------------------------+----------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : [1,null], "degree": 1.000}]" | Array found in unexpected place. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : 1, "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+--------------------------------------------------------------------------------------+--------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : 1, "dependency" : 4, "degree": 1.000}]" | Unexpected scalar. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : "a", "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+----------------------------------------------------------------------------------------+--------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : "a", "dependency" : 4, "degree": 1.000}]" | Unexpected scalar. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": NaN}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+----------------------------------------------------------------------------------------+---------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "degree": NaN}]" | Must be valid JSON. | | 22P02
+(1 row)
+
+-- Duplicated keys
+SELECT '[{"attributes" : [2,3], "attributes": [1,2], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "attributes": [1,2], "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "attributes": [1,2], "depend...
+ ^
+DETAIL: Multiple "attributes" keys are not allowed.
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "dependency": 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "dependency": 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : 4, "dependenc...
+ ^
+DETAIL: Multiple "dependency" keys are not allowed.
+SELECT '[{"attributes" : [2,3], "dependency": 4, "degree": 1.000, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency": 4, "degree": 1.000, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency": 4, "degree": 1...
+ ^
+DETAIL: Multiple "degree" keys are not allowed.
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "attributes": [1,2], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------------------------------------------------------------------------------------------------------------+---------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "attributes": [1,2], "dependency" : 4, "degree": 1.000}]" | Multiple "attributes" keys are not allowed. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "dependency": 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-----------------------------------------------------------------------------------------------------------+---------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "dependency": 4, "degree": 1.000}]" | Multiple "dependency" keys are not allowed. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency": 4, "degree": 1.000, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+----------------------------------------------------------------------------------------------------------+-----------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency": 4, "degree": 1.000, "degree": 1.000}]" | Multiple "degree" keys are not allowed. | | 22P02
+(1 row)
+
+-- Invalid attnums
+SELECT '[{"attributes" : [0,2], "dependency" : 4, "degree": 0.500}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [0,2], "dependency" : 4, "degree": 0.500}]"
+LINE 1: SELECT '[{"attributes" : [0,2], "dependency" : 4, "degree": ...
+ ^
+DETAIL: Invalid "attributes" element: 0.
+SELECT '[{"attributes" : [-7,-9], "dependency" : 4, "degree": 0.500}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [-7,-9], "dependency" : 4, "degree": 0.500}]"
+LINE 1: SELECT '[{"attributes" : [-7,-9], "dependency" : 4, "degree"...
+ ^
+DETAIL: Invalid "attributes" element: -9.
+SELECT * FROM pg_input_error_info('[{"attributes" : [0,2], "dependency" : 4, "degree": 0.500}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+------------------------------------------------------------------------------------------+----------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [0,2], "dependency" : 4, "degree": 0.500}]" | Invalid "attributes" element: 0. | | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [-7,-9], "dependency" : 4, "degree": 0.500}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+--------------------------------------------------------------------------------------------+-----------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [-7,-9], "dependency" : 4, "degree": 0.500}]" | Invalid "attributes" element: -9. | | 22P02
+(1 row)
+
+-- Duplicated attributes
+SELECT '[{"attributes" : [2,2], "dependency" : 4, "degree": 0.500}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,2], "dependency" : 4, "degree": 0.500}]"
+LINE 1: SELECT '[{"attributes" : [2,2], "dependency" : 4, "degree": ...
+ ^
+DETAIL: Invalid "attributes" element: 2 cannot follow 2.
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,2], "dependency" : 4, "degree": 0.500}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+------------------------------------------------------------------------------------------+--------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,2], "dependency" : 4, "degree": 0.500}]" | Invalid "attributes" element: 2 cannot follow 2. | | 22P02
+(1 row)
+
+-- Duplicated attribute lists.
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3], "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": ...
+ ^
+DETAIL: Duplicate "attributes" array: [2, 3] with "dependency": 4.
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-----------------------------------------------------------------------------------------+------------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},+| Duplicate "attributes" array: [2, 3] with "dependency": 4. | | 22P02
+ {"attributes" : [2,3], "dependency" : 4, "degree": 1.000}]" | | |
+(1 row)
+
+-- Partially-covered attribute lists.
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [1,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ERROR: malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [1,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]"
+LINE 1: SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": ...
+ ^
+DETAIL: "attributes" array: [1, -1] with dependency 4 must be a subset of array: [2, 3, -1, -2] with dependency 4.
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [1,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+-----------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},+| "attributes" array: [1, -1] with dependency 4 must be a subset of array: [2, 3, -1, -2] with dependency 4. | | 22P02
+ {"attributes" : [1,-1], "dependency" : 4, "degree": 1.000}, +| | |
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 1.000}, +| | |
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]" | | |
+(1 row)
+
+-- Valid inputs
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": 0.250},
+ {"attributes" : [2,-1], "dependency" : 4, "degree": 0.500},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 0.750},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+ pg_dependencies
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"attributes": [2, 3], "dependency": 4, "degree": 0.250000}, {"attributes": [2, -1], "dependency": 4, "degree": 0.500000}, {"attributes": [2, 3, -1], "dependency": 4, "degree": 0.750000}, {"attributes": [2, 3, -1, -2], "dependency": 4, "degree": 1.000000}]
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": 0.250},
+ {"attributes" : [2,-1], "dependency" : 4, "degree": 0.500},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 0.750},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+ message | detail | hint | sql_error_code
+---------+--------+------+----------------
+ | | |
+(1 row)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index f3f0b5f2f31..cc6d799bcea 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -28,7 +28,7 @@ test: strings md5 numerology point lseg line box path polygon circle date time t
# geometry depends on point, lseg, line, box, path, polygon, circle
# horology depends on date, time, timetz, timestamp, timestamptz, interval
# ----------
-test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import pg_ndistinct
+test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import pg_ndistinct pg_dependencies
# ----------
# Load huge amounts of data
diff --git a/src/test/regress/sql/pg_dependencies.sql b/src/test/regress/sql/pg_dependencies.sql
new file mode 100644
index 00000000000..0dda9f76b1c
--- /dev/null
+++ b/src/test/regress/sql/pg_dependencies.sql
@@ -0,0 +1,99 @@
+-- Tests for type pg_distinct
+
+-- Invalid inputs
+SELECT 'null'::pg_dependencies;
+SELECT '{"a": 1}'::pg_dependencies;
+SELECT '[]'::pg_dependencies;
+SELECT '{}'::pg_dependencies;
+SELECT '[null]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('null', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('{"a": 1}', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('{}', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[null]', 'pg_dependencies');
+
+-- Invalid keys
+SELECT '[{"attributes_invalid" : [2,3], "dependency" : 4}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "invalid" : 3, "dependency" : 4}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes_invalid" : [2,3], "dependency" : 4}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "invalid" : 3, "dependency" : 4}]', 'pg_dependencies');
+
+-- Missing keys
+SELECT '[{"attributes" : [2,3], "dependency" : 4}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "degree" : 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : 4}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "degree" : 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4}]', 'pg_dependencies');
+
+-- Valid keys, too many attributes
+SELECT '[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2,3,4,5,6,7,8], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+
+-- Valid keys, invalid values
+SELECT '[{"attributes" : null, "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,null], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : null, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,"a"], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : "a", "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : [], "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : [null], "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : [1,null], "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : 1, "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : "a", "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": NaN}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : null, "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,null], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : null, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,"a"], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : "a", "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : [], "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : [null], "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : [1,null], "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : 1, "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : "a", "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": NaN}]', 'pg_dependencies');
+
+-- Duplicated keys
+SELECT '[{"attributes" : [2,3], "attributes": [1,2], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "dependency": 4, "degree": 1.000}]'::pg_dependencies;
+SELECT '[{"attributes" : [2,3], "dependency": 4, "degree": 1.000, "degree": 1.000}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "attributes": [1,2], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "dependency": 4, "degree": 1.000}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency": 4, "degree": 1.000, "degree": 1.000}]', 'pg_dependencies');
+
+-- Invalid attnums
+SELECT '[{"attributes" : [0,2], "dependency" : 4, "degree": 0.500}]'::pg_dependencies;
+SELECT '[{"attributes" : [-7,-9], "dependency" : 4, "degree": 0.500}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [0,2], "dependency" : 4, "degree": 0.500}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [-7,-9], "dependency" : 4, "degree": 0.500}]', 'pg_dependencies');
+
+-- Duplicated attributes
+SELECT '[{"attributes" : [2,2], "dependency" : 4, "degree": 0.500}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,2], "dependency" : 4, "degree": 0.500}]', 'pg_dependencies');
+
+-- Duplicated attribute lists.
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+
+-- Partially-covered attribute lists.
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [1,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [1,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 1.000},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
+
+-- Valid inputs
+SELECT '[{"attributes" : [2,3], "dependency" : 4, "degree": 0.250},
+ {"attributes" : [2,-1], "dependency" : 4, "degree": 0.500},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 0.750},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes" : [2,3], "dependency" : 4, "degree": 0.250},
+ {"attributes" : [2,-1], "dependency" : 4, "degree": 0.500},
+ {"attributes" : [2,3,-1], "dependency" : 4, "degree": 0.750},
+ {"attributes" : [2,3,-1,-2], "dependency" : 4, "degree": 1.000}]', 'pg_dependencies');
--
2.51.1