From 29f98ba149aeb006f61bb2c8a562732c264875c0 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 25 Nov 2025 16:13:44 +0900
Subject: [PATCH v18 2/2] 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       | 785 +++++++++++++++++-
 src/test/regress/expected/pg_dependencies.out | 495 +++++++++++
 src/test/regress/parallel_schedule            |   2 +-
 src/test/regress/sql/pg_dependencies.sql      | 123 +++
 src/tools/pgindent/typedefs.list              |   2 +
 5 files changed, 1396 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 87181aa00e9a..0fd8a2476459 100644
--- a/src/backend/utils/adt/pg_dependencies.c
+++ b/src/backend/utils/adt/pg_dependencies.c
@@ -14,31 +14,796 @@
 
 #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,
+} DependenciesSemanticState;
+
+typedef struct
+{
+	const char *str;
+	DependenciesSemanticState 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 attribute numbers */
+	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;
+
+		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 attribute number 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;
+}
+
+/*
+ * Handle the end of an MVDependency object's JSON representation.
+ */
+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 in a dependencies item, anything
+	 * less is malformed.
+	 */
+	natts = list_length(parse->attnum_list);
+	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 more than %d elements.",
+						  PG_DEPENDENCIES_KEY_ATTRIBUTES, 1,
+						  STATS_MAX_DIMENSIONS - 1));
+		return JSON_SEM_ACTION_FAILED;
+	}
+
+	/*
+	 * Allocate enough space for the dependency, the attribute numbers in the
+	 * list and the final attribute number for the dependency.
+	 */
+	dep = palloc0(offsetof(MVDependency, attributes) + ((natts + 1) * sizeof(AttrNumber)));
+	dep->nattributes = natts + 1;
+
+	dep->attributes[natts] = parse->dependency;
+	dep->degree = parse->degree;
+
+	/*
+	 * Assign attribute numbers to the attributes array, comparing each one
+	 * against the dependency attribute to ensure that there there are no
+	 * matches.
+	 */
+	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 to look for the next
+	 * MVDependency.
+	 */
+	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;
+	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;
+	}
+
+	return JSON_SUCCESS;
+}
+
+/*
+ * Either the end of an attribute number list or the whole object.
+ */
+static JsonParseErrorType
+dependencies_array_end(void *state)
+{
+	DependenciesParseState *parse = state;
+
+	switch (parse->state)
+	{
+		case DEPS_EXPECT_ATTNUM:
+			if (list_length(parse->attnum_list) > 0)
+			{
+				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 (list_length(parse->dependency_list) > 0)
+			{
+				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
+			 * dependencies_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
+ *   - dependency
+ *   - 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."));
+			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."));
+			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 not allowed; that is already covered by the rules
+ * described 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;
+			}
+
+			/*
+			 * An attribute number cannot be zero or 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;
+
+		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;
+			}
+
+			/*
+			 * The dependency attribute number cannot be zero or a negative
+			 * number beyond the number of the possible expressions.
+			 */
+			if (parse->dependency == 0 || parse->dependency < (0 - STATS_MAX_DIMENSIONS))
+			{
+				errsave(parse->escontext,
+						errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						errmsg("malformed pg_dependencies: \"%s\"", parse->str),
+						errdetail("Invalid \"%s\" value: %d.",
+								  PG_DEPENDENCIES_KEY_DEPENDENCY, parse->dependency));
+				return JSON_SEM_ACTION_FAILED;
+			}
+
+			parse->state = DEPS_EXPECT_KEY;
+			return JSON_SUCCESS;
+
+		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;
+
+		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 duplicated sets.
+ */
+static bool
+dep_attributes_eq(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;
+}
+
+/*
+ * Generate a string representing an array of attribute numbers.
+ * Internally, the dependency attribute is the last element, so we
+ * leave that off.
+ *
+ * Freeing the allocated string is the responsibility of the caller.
+ */
+static 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 AttrNumber
+dep_attnum_dependency(const MVDependency *item)
+{
+	return item->attributes[item->nattributes - 1];
+}
+
+/*
+ * Attempt to build and serialize the MVDependencies object.
+ *
+ * This can only be executed after the completion of the JSON parsing.
+ *
+ * In the event of an error, set the error context and return NULL.
+ */
+static bytea *
+build_mvdependencies(DependenciesParseState *parse, char *str)
+{
+	int			ndeps = list_length(parse->dependency_list);
+
+	MVDependencies *mvdeps;
+	bytea	   *bytes;
+
+	switch (parse->state)
+	{
+		case DEPS_PARSE_COMPLETE:
+
+			/*
+			 * Parse ended in the expected place.  We should have a list of
+			 * items, but if we do not there is an issue with one of the
+			 * earlier parse steps.
+			 */
+			if (ndeps == 0)
+				elog(ERROR,
+					 "pg_dependencies parsing claims success with an empty item list.");
+			break;
+
+		case DEPS_EXPECT_START:
+			/* blank */
+			errsave(parse->escontext,
+					errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					errmsg("malformed pg_dependencies: \"%s\"", str),
+					errdetail("Value cannot be empty."));
+			return NULL;
+
+		default:
+			/* Unexpected end-state. */
+			errsave(parse->escontext,
+					errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					errmsg("malformed pg_dependencies: \"%s\"", str),
+					errdetail("Unexpected end state %d.", parse->state));
+			return NULL;
+	}
+
+	mvdeps = palloc0(offsetof(MVDependencies, deps)
+					 + (ndeps * sizeof(MVDependency *)));
+	mvdeps->magic = STATS_DEPS_MAGIC;
+	mvdeps->type = STATS_DEPS_TYPE_BASIC;
+	mvdeps->ndeps = ndeps;
+
+	for (int i = 0; i < ndeps; i++)
+	{
+		/*
+		 * Use the MVDependency objects in the dependency_list.
+		 *
+		 * Because we free the dependency_list after parsing is done, we
+		 * cannot free it here.
+		 */
+		mvdeps->deps[i] = list_nth(parse->dependency_list, i);
+
+		/*
+		 * Ensure that this item does not duplicate the attributes of any
+		 * pre-existing item.
+		 */
+		for (int j = 0; j < i; j++)
+		{
+			if (dep_attributes_eq(mvdeps->deps[i], mvdeps->deps[j]))
+			{
+				MVDependency *dep = mvdeps->deps[i];
+				char	   *attnum_list = dep_attnum_list(dep);
+				AttrNumber	attnum_dep = dep_attnum_dependency(dep);
+
+				errsave(parse->escontext,
+						errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+						errmsg("malformed pg_dependencies: \"%s\"", str),
+						errdetail("Duplicate \"%s\" array: [%s] with \"%s\": %d.",
+								  PG_DEPENDENCIES_KEY_ATTRIBUTES, attnum_list,
+								  PG_DEPENDENCIES_KEY_DEPENDENCY, attnum_dep));
+				pfree(mvdeps);
+				return NULL;
+			}
+		}
+	}
+
+	bytes = statext_dependencies_serialize(mvdeps);
+
+	/*
+	 * No need to free the individual MVDependency objects, because they are
+	 * still in the dependency_list, and will be freed with that.
+	 */
+	pfree(mvdeps);
+
+	return bytes;
+}
+
+
 /*
  * 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);
+	bytea	   *bytes = NULL;
 
-	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)
+		bytes = build_mvdependencies(&parse_state, str);
+
+	list_free_deep(parse_state.dependency_list);
+	list_free(parse_state.attnum_list);
+
+	if (bytes)
+		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();
 }
 
+
 /*
  * pg_dependencies_out		- output routine for type pg_dependencies.
  */
diff --git a/src/test/regress/expected/pg_dependencies.out b/src/test/regress/expected/pg_dependencies.out
new file mode 100644
index 000000000000..556e3dad00fb
--- /dev/null
+++ b/src/test/regress/expected/pg_dependencies.out
@@ -0,0 +1,495 @@
+-- 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 more 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 more 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)
+
+SELECT '[{"attributes": [], "dependency": 2, "degree": 1}]' ::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes": [], "dependency": 2, "degree": 1}]"
+LINE 1: SELECT '[{"attributes": [], "dependency": 2, "degree": 1}]' ...
+               ^
+DETAIL:  The "attributes" key must be an non-empty array.
+SELECT '[{"attributes" : {"a": 1}, "dependency" : 4, "degree": "1.2"}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : {"a": 1}, "dependency" : 4, "degree": "1.2"}]"
+LINE 1: SELECT '[{"attributes" : {"a": 1}, "dependency" : 4, "degree...
+               ^
+DETAIL:  Value of "attributes" must be an array of attribute numbers.
+SELECT * FROM pg_input_error_info('[{"attributes": [], "dependency": 2, "degree": 1}]', 'pg_dependencies');
+                                     message                                     |                      detail                      | hint | sql_error_code 
+---------------------------------------------------------------------------------+--------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes": [], "dependency": 2, "degree": 1}]" | The "attributes" key must be an non-empty array. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : {"a": 1}, "dependency" : 4, "degree": "1.2"}]', 'pg_dependencies');
+                                           message                                           |                            detail                            | hint | sql_error_code 
+---------------------------------------------------------------------------------------------+--------------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : {"a": 1}, "dependency" : 4, "degree": "1.2"}]" | Value of "attributes" must be an array of attribute numbers. |      | 22P02
+(1 row)
+
+SELECT '[{"dependency" : 4, "degree": "1.2"}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"dependency" : 4, "degree": "1.2"}]"
+LINE 1: SELECT '[{"dependency" : 4, "degree": "1.2"}]'::pg_dependenc...
+               ^
+DETAIL:  Item must contain "attributes" key
+SELECT '[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, "degree": "1.2"}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, "degree": "1.2"}]"
+LINE 1: SELECT '[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, ...
+               ^
+DETAIL:  Invalid "dependency" value: 0.
+SELECT '[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9, "degree": "1.2"}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9, "degree": "1.2"}]"
+LINE 1: SELECT '[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9,...
+               ^
+DETAIL:  Invalid "dependency" value: -9.
+SELECT '[{"attributes": [1,2], "dependency": 2, "degree": 1}]' ::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes": [1,2], "dependency": 2, "degree": 1}]"
+LINE 1: SELECT '[{"attributes": [1,2], "dependency": 2, "degree": 1}...
+               ^
+DETAIL:  Item "dependency" value 2 found in the "attributes" list.
+SELECT '[{"attributes" : [1, {}], "dependency" : 1, "degree": "1.2"}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : [1, {}], "dependency" : 1, "degree": "1.2"}]"
+LINE 1: SELECT '[{"attributes" : [1, {}], "dependency" : 1, "degree"...
+               ^
+DETAIL:  Attribute lists can only contain attribute numbers.
+SELECT '[{"attributes" : [1,2], "dependency" : {}, "degree": 1.0}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : [1,2], "dependency" : {}, "degree": 1.0}]"
+LINE 1: SELECT '[{"attributes" : [1,2], "dependency" : {}, "degree":...
+               ^
+DETAIL:  Value of "dependency" must be an integer.
+SELECT '[{"attributes" : [1,2], "dependency" : 3, "degree": {}}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : [1,2], "dependency" : 3, "degree": {}}]"
+LINE 1: SELECT '[{"attributes" : [1,2], "dependency" : 3, "degree": ...
+               ^
+DETAIL:  Value of "degree" must be an integer.
+SELECT '[{"attributes" : [1,2], "dependency" : 1, "degree": "a"}]'::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes" : [1,2], "dependency" : 1, "degree": "a"}]"
+LINE 1: SELECT '[{"attributes" : [1,2], "dependency" : 1, "degree": ...
+               ^
+DETAIL:  Invalid "degree" value.
+SELECT * FROM pg_input_error_info('[{"dependency" : 4, "degree": "1.2"}]', 'pg_dependencies');
+                              message                               |               detail               | hint | sql_error_code 
+--------------------------------------------------------------------+------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"dependency" : 4, "degree": "1.2"}]" | Item must contain "attributes" key |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, "degree": "1.2"}]', 'pg_dependencies');
+                                              message                                               |             detail             | hint | sql_error_code 
+----------------------------------------------------------------------------------------------------+--------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, "degree": "1.2"}]" | Invalid "dependency" value: 0. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9, "degree": "1.2"}]', 'pg_dependencies');
+                                               message                                               |             detail              | hint | sql_error_code 
+-----------------------------------------------------------------------------------------------------+---------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9, "degree": "1.2"}]" | Invalid "dependency" value: -9. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes": [1,2], "dependency": 2, "degree": 1}]' , 'pg_dependencies');
+                                      message                                       |                          detail                           | hint | sql_error_code 
+------------------------------------------------------------------------------------+-----------------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes": [1,2], "dependency": 2, "degree": 1}]" | Item "dependency" value 2 found in the "attributes" list. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [1, {}], "dependency" : 1, "degree": "1.2"}]', 'pg_dependencies');
+                                          message                                           |                       detail                        | hint | sql_error_code 
+--------------------------------------------------------------------------------------------+-----------------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1, {}], "dependency" : 1, "degree": "1.2"}]" | Attribute lists can only contain attribute numbers. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2], "dependency" : {}, "degree": 1.0}]', 'pg_dependencies');
+                                         message                                         |                  detail                   | hint | sql_error_code 
+-----------------------------------------------------------------------------------------+-------------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1,2], "dependency" : {}, "degree": 1.0}]" | Value of "dependency" must be an integer. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2], "dependency" : 3, "degree": {}}]', 'pg_dependencies');
+                                        message                                        |                detail                 | hint | sql_error_code 
+---------------------------------------------------------------------------------------+---------------------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1,2], "dependency" : 3, "degree": {}}]" | Value of "degree" must be an integer. |      | 22P02
+(1 row)
+
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2], "dependency" : 1, "degree": "a"}]', 'pg_dependencies');
+                                        message                                         |         detail          | hint | sql_error_code 
+----------------------------------------------------------------------------------------+-------------------------+------+----------------
+ malformed pg_dependencies: "[{"attributes" : [1,2], "dependency" : 1, "degree": "a"}]" | Invalid "degree" value. |      | 22P02
+(1 row)
+
+-- Funky degree values, which do not fail.
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "NaN"}]'::pg_dependencies;
+                    pg_dependencies                    
+-------------------------------------------------------
+ [{"attributes": [2], "dependency": 4, "degree": NaN}]
+(1 row)
+
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "-inf"}]'::pg_dependencies;
+                       pg_dependencies                       
+-------------------------------------------------------------
+ [{"attributes": [2], "dependency": 4, "degree": -Infinity}]
+(1 row)
+
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "inf"}]'::pg_dependencies;
+                      pg_dependencies                       
+------------------------------------------------------------
+ [{"attributes": [2], "dependency": 4, "degree": Infinity}]
+(1 row)
+
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "-inf"}]'::pg_dependencies::text::pg_dependencies;
+ERROR:  malformed pg_dependencies: "[{"attributes": [2], "dependency": 4, "degree": -Infinity}]"
+DETAIL:  Must be valid JSON.
+-- 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)
+
+-- 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)
+
+-- Partially-covered attribute lists, possible as items with a degree of 0
+-- are discarded.
+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;
+                                                                                                                          pg_dependencies                                                                                                                          
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"attributes": [2, 3], "dependency": 4, "degree": 1.000000}, {"attributes": [1, -1], "dependency": 4, "degree": 1.000000}, {"attributes": [2, 3, -1], "dependency": 4, "degree": 1.000000}, {"attributes": [2, 3, -1, -2], "dependency": 4, "degree": 1.000000}]
+(1 row)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index f3f0b5f2f317..cc6d799bceaf 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 000000000000..ccc8ee277126
--- /dev/null
+++ b/src/test/regress/sql/pg_dependencies.sql
@@ -0,0 +1,123 @@
+-- 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');
+
+SELECT '[{"attributes": [], "dependency": 2, "degree": 1}]' ::pg_dependencies;
+SELECT '[{"attributes" : {"a": 1}, "dependency" : 4, "degree": "1.2"}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"attributes": [], "dependency": 2, "degree": 1}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : {"a": 1}, "dependency" : 4, "degree": "1.2"}]', 'pg_dependencies');
+
+SELECT '[{"dependency" : 4, "degree": "1.2"}]'::pg_dependencies;
+SELECT '[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, "degree": "1.2"}]'::pg_dependencies;
+SELECT '[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9, "degree": "1.2"}]'::pg_dependencies;
+SELECT '[{"attributes": [1,2], "dependency": 2, "degree": 1}]' ::pg_dependencies;
+SELECT '[{"attributes" : [1, {}], "dependency" : 1, "degree": "1.2"}]'::pg_dependencies;
+SELECT '[{"attributes" : [1,2], "dependency" : {}, "degree": 1.0}]'::pg_dependencies;
+SELECT '[{"attributes" : [1,2], "dependency" : 3, "degree": {}}]'::pg_dependencies;
+SELECT '[{"attributes" : [1,2], "dependency" : 1, "degree": "a"}]'::pg_dependencies;
+SELECT * FROM pg_input_error_info('[{"dependency" : 4, "degree": "1.2"}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2,3,4,5,6,7], "dependency" : 0, "degree": "1.2"}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2,3,4,5,6,7], "dependency" : -9, "degree": "1.2"}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes": [1,2], "dependency": 2, "degree": 1}]' , 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [1, {}], "dependency" : 1, "degree": "1.2"}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2], "dependency" : {}, "degree": 1.0}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2], "dependency" : 3, "degree": {}}]', 'pg_dependencies');
+SELECT * FROM pg_input_error_info('[{"attributes" : [1,2], "dependency" : 1, "degree": "a"}]', 'pg_dependencies');
+
+-- Funky degree values, which do not fail.
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "NaN"}]'::pg_dependencies;
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "-inf"}]'::pg_dependencies;
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "inf"}]'::pg_dependencies;
+SELECT '[{"attributes" : [2], "dependency" : 4, "degree": "-inf"}]'::pg_dependencies::text::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');
+
+-- 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');
+-- Partially-covered attribute lists, possible as items with a degree of 0
+-- are discarded.
+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;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 17e2b40b9cb0..dfcd619bfee3 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -631,6 +631,8 @@ DefaultACLInfo
 DefineStmt
 DefnDumperPtr
 DeleteStmt
+DependenciesParseState
+DependenciesSemanticState
 DependencyGenerator
 DependencyGeneratorData
 DependencyType
-- 
2.51.0

