From 29bc27036a1966dbc29c414d1dd6a1512e0ad524 Mon Sep 17 00:00:00 2001 From: Michael Paquier 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