LCOV - code coverage report
Current view: top level - src/backend/utils/adt - pg_dependencies.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 263 285 92.3 %
Date: 2025-12-23 12:18:01 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_dependencies.c
       4             :  *      pg_dependencies data type support.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/pg_dependencies.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "common/int.h"
      18             : #include "common/jsonapi.h"
      19             : #include "lib/stringinfo.h"
      20             : #include "mb/pg_wchar.h"
      21             : #include "nodes/miscnodes.h"
      22             : #include "statistics/extended_stats_internal.h"
      23             : #include "statistics/statistics_format.h"
      24             : #include "utils/builtins.h"
      25             : #include "utils/float.h"
      26             : #include "utils/fmgrprotos.h"
      27             : 
      28             : typedef enum
      29             : {
      30             :     DEPS_EXPECT_START = 0,
      31             :     DEPS_EXPECT_ITEM,
      32             :     DEPS_EXPECT_KEY,
      33             :     DEPS_EXPECT_ATTNUM_LIST,
      34             :     DEPS_EXPECT_ATTNUM,
      35             :     DEPS_EXPECT_DEPENDENCY,
      36             :     DEPS_EXPECT_DEGREE,
      37             :     DEPS_PARSE_COMPLETE,
      38             : } DependenciesSemanticState;
      39             : 
      40             : typedef struct
      41             : {
      42             :     const char *str;
      43             :     DependenciesSemanticState state;
      44             : 
      45             :     List       *dependency_list;
      46             :     Node       *escontext;
      47             : 
      48             :     bool        found_attributes;   /* Item has an attributes key */
      49             :     bool        found_dependency;   /* Item has an dependency key */
      50             :     bool        found_degree;   /* Item has degree key */
      51             :     List       *attnum_list;    /* Accumulated attribute numbers */
      52             :     AttrNumber  dependency;
      53             :     double      degree;
      54             : } DependenciesParseState;
      55             : 
      56             : /*
      57             :  * Invoked at the start of each MVDependency object.
      58             :  *
      59             :  * The entire JSON document should be one array of MVDependency objects.
      60             :  *
      61             :  * If we are anywhere else in the document, it's an error.
      62             :  */
      63             : static JsonParseErrorType
      64         642 : dependencies_object_start(void *state)
      65             : {
      66         642 :     DependenciesParseState *parse = state;
      67             : 
      68         642 :     switch (parse->state)
      69             :     {
      70         570 :         case DEPS_EXPECT_ITEM:
      71             :             /* Now we expect to see attributes/dependency/degree keys */
      72         570 :             parse->state = DEPS_EXPECT_KEY;
      73         570 :             return JSON_SUCCESS;
      74             : 
      75          24 :         case DEPS_EXPECT_START:
      76             :             /* pg_dependencies must begin with a '[' */
      77          24 :             errsave(parse->escontext,
      78             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      79             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
      80             :                     errdetail("Initial element must be an array."));
      81          12 :             break;
      82             : 
      83           0 :         case DEPS_EXPECT_KEY:
      84             :             /* In an object, expecting key */
      85           0 :             errsave(parse->escontext,
      86             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      87             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
      88             :                     errdetail("A key was expected."));
      89           0 :             break;
      90             : 
      91          12 :         case DEPS_EXPECT_ATTNUM_LIST:
      92             :             /* Just followed an "attributes": key */
      93          12 :             errsave(parse->escontext,
      94             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      95             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
      96             :                     errdetail("Value of \"%s\" must be an array of attribute numbers.",
      97             :                               PG_DEPENDENCIES_KEY_ATTRIBUTES));
      98           6 :             break;
      99             : 
     100          12 :         case DEPS_EXPECT_ATTNUM:
     101             :             /* In an attribute number list, expect only scalar integers */
     102          12 :             errsave(parse->escontext,
     103             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     104             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     105             :                     errdetail("Attribute lists can only contain attribute numbers."));
     106           6 :             break;
     107             : 
     108          12 :         case DEPS_EXPECT_DEPENDENCY:
     109             :             /* Just followed a "dependency" key */
     110          12 :             errsave(parse->escontext,
     111             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     112             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     113             :                     errdetail("Value of \"%s\" must be an integer.",
     114             :                               PG_DEPENDENCIES_KEY_DEPENDENCY));
     115           6 :             break;
     116             : 
     117          12 :         case DEPS_EXPECT_DEGREE:
     118             :             /* Just followed a "degree" key */
     119          12 :             errsave(parse->escontext,
     120             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     121             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     122             :                     errdetail("Value of \"%s\" must be an integer.",
     123             :                               PG_DEPENDENCIES_KEY_DEGREE));
     124           6 :             break;
     125             : 
     126           0 :         default:
     127           0 :             elog(ERROR,
     128             :                  "object start of \"%s\" found in unexpected parse state: %d.",
     129             :                  "pg_dependencies", (int) parse->state);
     130             :             break;
     131             :     }
     132             : 
     133          36 :     return JSON_SEM_ACTION_FAILED;
     134             : }
     135             : 
     136             : /*
     137             :  * Invoked at the end of an object.
     138             :  *
     139             :  * Handle the end of an MVDependency object's JSON representation.
     140             :  */
     141             : static JsonParseErrorType
     142         192 : dependencies_object_end(void *state)
     143             : {
     144         192 :     DependenciesParseState *parse = state;
     145             : 
     146             :     MVDependency *dep;
     147             : 
     148         192 :     int         natts = 0;
     149             : 
     150         192 :     if (parse->state != DEPS_EXPECT_KEY)
     151           0 :         elog(ERROR,
     152             :              "object end of \"%s\" found in unexpected parse state: %d.",
     153             :              "pg_dependencies", (int) parse->state);
     154             : 
     155         192 :     if (!parse->found_attributes)
     156             :     {
     157          12 :         errsave(parse->escontext,
     158             :                 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     159             :                 errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     160             :                 errdetail("Item must contain \"%s\" key.",
     161             :                           PG_DEPENDENCIES_KEY_ATTRIBUTES));
     162           6 :         return JSON_SEM_ACTION_FAILED;
     163             :     }
     164             : 
     165         180 :     if (!parse->found_dependency)
     166             :     {
     167          12 :         errsave(parse->escontext,
     168             :                 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     169             :                 errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     170             :                 errdetail("Item must contain \"%s\" key.",
     171             :                           PG_DEPENDENCIES_KEY_DEPENDENCY));
     172           6 :         return JSON_SEM_ACTION_FAILED;
     173             :     }
     174             : 
     175         168 :     if (!parse->found_degree)
     176             :     {
     177          24 :         errsave(parse->escontext,
     178             :                 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     179             :                 errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     180             :                 errdetail("Item must contain \"%s\" key.",
     181             :                           PG_DEPENDENCIES_KEY_DEGREE));
     182          12 :         return JSON_SEM_ACTION_FAILED;
     183             :     }
     184             : 
     185             :     /*
     186             :      * We need at least one attribute number in a dependencies item, anything
     187             :      * less is malformed.
     188             :      */
     189         144 :     natts = list_length(parse->attnum_list);
     190         144 :     if ((natts < 1) || (natts > (STATS_MAX_DIMENSIONS - 1)))
     191             :     {
     192          12 :         errsave(parse->escontext,
     193             :                 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     194             :                 errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     195             :                 errdetail("The \"%s\" key must contain an array of at least %d and no more than %d elements.",
     196             :                           PG_DEPENDENCIES_KEY_ATTRIBUTES, 1,
     197             :                           STATS_MAX_DIMENSIONS - 1));
     198           6 :         return JSON_SEM_ACTION_FAILED;
     199             :     }
     200             : 
     201             :     /*
     202             :      * Allocate enough space for the dependency, the attribute numbers in the
     203             :      * list and the final attribute number for the dependency.
     204             :      */
     205         132 :     dep = palloc0(offsetof(MVDependency, attributes) + ((natts + 1) * sizeof(AttrNumber)));
     206         132 :     dep->nattributes = natts + 1;
     207             : 
     208         132 :     dep->attributes[natts] = parse->dependency;
     209         132 :     dep->degree = parse->degree;
     210             : 
     211             :     /*
     212             :      * Assign attribute numbers to the attributes array, comparing each one
     213             :      * against the dependency attribute to ensure that there there are no
     214             :      * matches.
     215             :      */
     216         414 :     for (int i = 0; i < natts; i++)
     217             :     {
     218         294 :         dep->attributes[i] = (AttrNumber) list_nth_int(parse->attnum_list, i);
     219         294 :         if (dep->attributes[i] == parse->dependency)
     220             :         {
     221          12 :             errsave(parse->escontext,
     222             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     223             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     224             :                     errdetail("Item \"%s\" with value %d has been found in the \"%s\" list.",
     225             :                               PG_DEPENDENCIES_KEY_DEPENDENCY, parse->dependency,
     226             :                               PG_DEPENDENCIES_KEY_ATTRIBUTES));
     227           6 :             return JSON_SEM_ACTION_FAILED;
     228             :         }
     229             :     }
     230             : 
     231         120 :     parse->dependency_list = lappend(parse->dependency_list, (void *) dep);
     232             : 
     233             :     /*
     234             :      * Reset dependency item state variables to look for the next
     235             :      * MVDependency.
     236             :      */
     237         120 :     list_free(parse->attnum_list);
     238         120 :     parse->attnum_list = NIL;
     239         120 :     parse->dependency = 0;
     240         120 :     parse->degree = 0.0;
     241         120 :     parse->found_attributes = false;
     242         120 :     parse->found_dependency = false;
     243         120 :     parse->found_degree = false;
     244         120 :     parse->state = DEPS_EXPECT_ITEM;
     245             : 
     246         120 :     return JSON_SUCCESS;
     247             : }
     248             : 
     249             : /*
     250             :  * Invoked at the start of an array.
     251             :  *
     252             :  * Dependency input format does not have arrays, so any array elements
     253             :  * encountered are an error.
     254             :  */
     255             : static JsonParseErrorType
     256        1050 : dependencies_array_start(void *state)
     257             : {
     258        1050 :     DependenciesParseState *parse = state;
     259             : 
     260        1050 :     switch (parse->state)
     261             :     {
     262         486 :         case DEPS_EXPECT_ATTNUM_LIST:
     263         486 :             parse->state = DEPS_EXPECT_ATTNUM;
     264         486 :             break;
     265         528 :         case DEPS_EXPECT_START:
     266         528 :             parse->state = DEPS_EXPECT_ITEM;
     267         528 :             break;
     268          36 :         default:
     269          36 :             errsave(parse->escontext,
     270             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     271             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     272             :                     errdetail("Array has been found at an unexpected location."));
     273          18 :             return JSON_SEM_ACTION_FAILED;
     274             :     }
     275             : 
     276        1014 :     return JSON_SUCCESS;
     277             : }
     278             : 
     279             : /*
     280             :  * Invoked at the end of an array.
     281             :  *
     282             :  * Either the end of an attribute number list or the whole object.
     283             :  */
     284             : static JsonParseErrorType
     285         468 : dependencies_array_end(void *state)
     286             : {
     287         468 :     DependenciesParseState *parse = state;
     288             : 
     289         468 :     switch (parse->state)
     290             :     {
     291         402 :         case DEPS_EXPECT_ATTNUM:
     292         402 :             if (list_length(parse->attnum_list) > 0)
     293             :             {
     294         390 :                 parse->state = DEPS_EXPECT_KEY;
     295         390 :                 return JSON_SUCCESS;
     296             :             }
     297             : 
     298          12 :             errsave(parse->escontext,
     299             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     300             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     301             :                     errdetail("The \"%s\" key must be an non-empty array.",
     302             :                               PG_DEPENDENCIES_KEY_ATTRIBUTES));
     303           6 :             break;
     304             : 
     305          66 :         case DEPS_EXPECT_ITEM:
     306          66 :             if (list_length(parse->dependency_list) > 0)
     307             :             {
     308          54 :                 parse->state = DEPS_PARSE_COMPLETE;
     309          54 :                 return JSON_SUCCESS;
     310             :             }
     311             : 
     312          12 :             errsave(parse->escontext,
     313             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     314             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     315             :                     errdetail("Item array cannot be empty."));
     316           6 :             break;
     317             : 
     318           0 :         default:
     319             : 
     320             :             /*
     321             :              * This can only happen if a case was missed in
     322             :              * dependencies_array_start().
     323             :              */
     324           0 :             elog(ERROR,
     325             :                  "array end of \"%s\" found in unexpected parse state: %d.",
     326             :                  "pg_dependencies", (int) parse->state);
     327             :             break;
     328             :     }
     329          12 :     return JSON_SEM_ACTION_FAILED;
     330             : }
     331             : 
     332             : /*
     333             :  * Invoked at the start of a key/value field.
     334             :  *
     335             :  * The valid keys for the MVDependency object are:
     336             :  *   - attributes
     337             :  *   - dependency
     338             :  *   - degree
     339             :  */
     340             : static JsonParseErrorType
     341        1152 : dependencies_object_field_start(void *state, char *fname, bool isnull)
     342             : {
     343        1152 :     DependenciesParseState *parse = state;
     344             : 
     345        1152 :     if (strcmp(fname, PG_DEPENDENCIES_KEY_ATTRIBUTES) == 0)
     346             :     {
     347         546 :         if (parse->found_attributes)
     348             :         {
     349          12 :             errsave(parse->escontext,
     350             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     351             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     352             :                     errdetail("Multiple \"%s\" keys are not allowed.",
     353             :                               PG_DEPENDENCIES_KEY_ATTRIBUTES));
     354           6 :             return JSON_SEM_ACTION_FAILED;
     355             :         }
     356             : 
     357         534 :         parse->found_attributes = true;
     358         534 :         parse->state = DEPS_EXPECT_ATTNUM_LIST;
     359         534 :         return JSON_SUCCESS;
     360             :     }
     361             : 
     362         606 :     if (strcmp(fname, PG_DEPENDENCIES_KEY_DEPENDENCY) == 0)
     363             :     {
     364         366 :         if (parse->found_dependency)
     365             :         {
     366          12 :             errsave(parse->escontext,
     367             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     368             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     369             :                     errdetail("Multiple \"%s\" keys are not allowed.",
     370             :                               PG_DEPENDENCIES_KEY_DEPENDENCY));
     371           6 :             return JSON_SEM_ACTION_FAILED;
     372             :         }
     373             : 
     374         354 :         parse->found_dependency = true;
     375         354 :         parse->state = DEPS_EXPECT_DEPENDENCY;
     376         354 :         return JSON_SUCCESS;
     377             :     }
     378             : 
     379         240 :     if (strcmp(fname, PG_DEPENDENCIES_KEY_DEGREE) == 0)
     380             :     {
     381         216 :         if (parse->found_degree)
     382             :         {
     383          12 :             errsave(parse->escontext,
     384             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     385             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     386             :                     errdetail("Multiple \"%s\" keys are not allowed.",
     387             :                               PG_DEPENDENCIES_KEY_DEGREE));
     388           6 :             return JSON_SEM_ACTION_FAILED;
     389             :         }
     390             : 
     391         204 :         parse->found_degree = true;
     392         204 :         parse->state = DEPS_EXPECT_DEGREE;
     393         204 :         return JSON_SUCCESS;
     394             :     }
     395             : 
     396          24 :     errsave(parse->escontext,
     397             :             errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     398             :             errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     399             :             errdetail("Only allowed keys are \"%s\", \"%s\" and \"%s\".",
     400             :                       PG_DEPENDENCIES_KEY_ATTRIBUTES,
     401             :                       PG_DEPENDENCIES_KEY_DEPENDENCY,
     402             :                       PG_DEPENDENCIES_KEY_DEGREE));
     403          12 :     return JSON_SEM_ACTION_FAILED;
     404             : }
     405             : 
     406             : /*
     407             :  * Invoked at the start of an array element.
     408             :  *
     409             :  * pg_dependencies input format does not have arrays, so any array elements
     410             :  * encountered are an error.
     411             :  */
     412             : static JsonParseErrorType
     413        1710 : dependencies_array_element_start(void *state, bool isnull)
     414             : {
     415        1710 :     DependenciesParseState *parse = state;
     416             : 
     417        1710 :     switch (parse->state)
     418             :     {
     419        1128 :         case DEPS_EXPECT_ATTNUM:
     420        1128 :             if (!isnull)
     421        1116 :                 return JSON_SUCCESS;
     422             : 
     423          12 :             errsave(parse->escontext,
     424             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     425             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     426             :                     errdetail("Attribute number array cannot be null."));
     427           6 :             break;
     428             : 
     429         582 :         case DEPS_EXPECT_ITEM:
     430         582 :             if (!isnull)
     431         570 :                 return JSON_SUCCESS;
     432             : 
     433          12 :             errsave(parse->escontext,
     434             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     435             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     436             :                     errdetail("Item list elements cannot be null."));
     437           6 :             break;
     438             : 
     439           0 :         default:
     440           0 :             elog(ERROR,
     441             :                  "array element start of \"%s\" found in unexpected parse state: %d.",
     442             :                  "pg_dependencies", (int) parse->state);
     443             :             break;
     444             :     }
     445             : 
     446          12 :     return JSON_SEM_ACTION_FAILED;
     447             : }
     448             : 
     449             : /*
     450             :  * Test for valid subsequent attribute number.
     451             :  *
     452             :  * If the previous value is positive, then current value must either be
     453             :  * greater than the previous value, or negative.
     454             :  *
     455             :  * If the previous value is negative, then the value must be less than
     456             :  * the previous value.
     457             :  *
     458             :  * Duplicate values are not allowed; that is already covered by the rules
     459             :  * described above.
     460             :  */
     461             : static bool
     462         618 : valid_subsequent_attnum(const AttrNumber prev, const AttrNumber cur)
     463             : {
     464             :     Assert(prev != 0);
     465             : 
     466         618 :     if (prev > 0)
     467         600 :         return ((cur > prev) || (cur < 0));
     468             : 
     469          18 :     return (cur < prev);
     470             : }
     471             : 
     472             : /*
     473             :  * Handle scalar events from the dependencies input parser.
     474             :  *
     475             :  * There is only one case where we will encounter a scalar, and that is the
     476             :  * dependency degree for the previous object key.
     477             :  */
     478             : static JsonParseErrorType
     479        1650 : dependencies_scalar(void *state, char *token, JsonTokenType tokentype)
     480             : {
     481        1650 :     DependenciesParseState *parse = state;
     482             :     AttrNumber  attnum;
     483        1650 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     484             : 
     485        1650 :     switch (parse->state)
     486             :     {
     487        1104 :         case DEPS_EXPECT_ATTNUM:
     488        1104 :             attnum = pg_strtoint16_safe(token, (Node *) &escontext);
     489             : 
     490        1104 :             if (escontext.error_occurred)
     491             :             {
     492          12 :                 errsave(parse->escontext,
     493             :                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     494             :                         errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     495             :                         errdetail("Key \"%s\" has an incorrect value.", PG_DEPENDENCIES_KEY_ATTRIBUTES));
     496           6 :                 return JSON_SEM_ACTION_FAILED;
     497             :             }
     498             : 
     499             :             /*
     500             :              * An attribute number cannot be zero or a negative number beyond
     501             :              * the number of the possible expressions.
     502             :              */
     503        1092 :             if (attnum == 0 || attnum < (0 - STATS_MAX_DIMENSIONS))
     504             :             {
     505          24 :                 errsave(parse->escontext,
     506             :                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     507             :                         errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     508             :                         errdetail("Invalid \"%s\" element has been found: %d.",
     509             :                                   PG_DEPENDENCIES_KEY_ATTRIBUTES, attnum));
     510          12 :                 return JSON_SEM_ACTION_FAILED;
     511             :             }
     512             : 
     513        1068 :             if (parse->attnum_list != NIL)
     514             :             {
     515         618 :                 const AttrNumber prev = llast_int(parse->attnum_list);
     516             : 
     517         618 :                 if (!valid_subsequent_attnum(prev, attnum))
     518             :                 {
     519          12 :                     errsave(parse->escontext,
     520             :                             errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     521             :                             errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     522             :                             errdetail("Invalid \"%s\" element has been found: %d cannot follow %d.",
     523             :                                       PG_DEPENDENCIES_KEY_ATTRIBUTES, attnum, prev));
     524           6 :                     return JSON_SEM_ACTION_FAILED;
     525             :                 }
     526             :             }
     527             : 
     528        1056 :             parse->attnum_list = lappend_int(parse->attnum_list, (int) attnum);
     529        1056 :             return JSON_SUCCESS;
     530             : 
     531         306 :         case DEPS_EXPECT_DEPENDENCY:
     532         306 :             parse->dependency = (AttrNumber)
     533         306 :                 pg_strtoint16_safe(token, (Node *) &escontext);
     534             : 
     535         306 :             if (escontext.error_occurred)
     536             :             {
     537          24 :                 errsave(parse->escontext,
     538             :                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     539             :                         errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     540             :                         errdetail("Key \"%s\" has an incorrect value.", PG_DEPENDENCIES_KEY_DEPENDENCY));
     541          12 :                 return JSON_SEM_ACTION_FAILED;
     542             :             }
     543             : 
     544             :             /*
     545             :              * The dependency attribute number cannot be zero or a negative
     546             :              * number beyond the number of the possible expressions.
     547             :              */
     548         282 :             if (parse->dependency == 0 || parse->dependency < (0 - STATS_MAX_DIMENSIONS))
     549             :             {
     550          24 :                 errsave(parse->escontext,
     551             :                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     552             :                         errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     553             :                         errdetail("Key \"%s\" has an incorrect value: %d.",
     554             :                                   PG_DEPENDENCIES_KEY_DEPENDENCY, parse->dependency));
     555          12 :                 return JSON_SEM_ACTION_FAILED;
     556             :             }
     557             : 
     558         258 :             parse->state = DEPS_EXPECT_KEY;
     559         258 :             return JSON_SUCCESS;
     560             : 
     561         192 :         case DEPS_EXPECT_DEGREE:
     562         192 :             parse->degree = float8in_internal(token, NULL, "double",
     563             :                                               token, (Node *) &escontext);
     564             : 
     565         192 :             if (escontext.error_occurred)
     566             :             {
     567          12 :                 errsave(parse->escontext,
     568             :                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     569             :                         errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     570             :                         errdetail("Key \"%s\" has an incorrect value.", PG_DEPENDENCIES_KEY_DEGREE));
     571           6 :                 return JSON_SEM_ACTION_FAILED;
     572             :             }
     573             : 
     574         180 :             parse->state = DEPS_EXPECT_KEY;
     575         180 :             return JSON_SUCCESS;
     576             : 
     577          48 :         default:
     578          48 :             errsave(parse->escontext,
     579             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     580             :                     errmsg("malformed pg_dependencies: \"%s\"", parse->str),
     581             :                     errdetail("Unexpected scalar has been found."));
     582          24 :             break;
     583             :     }
     584             : 
     585          24 :     return JSON_SEM_ACTION_FAILED;
     586             : }
     587             : 
     588             : /*
     589             :  * Compare the attribute arrays of two MVDependency values,
     590             :  * looking for duplicated sets.
     591             :  */
     592             : static bool
     593         120 : dep_attributes_eq(const MVDependency *a, const MVDependency *b)
     594             : {
     595             :     int         i;
     596             : 
     597         120 :     if (a->nattributes != b->nattributes)
     598          90 :         return false;
     599             : 
     600          78 :     for (i = 0; i < a->nattributes; i++)
     601             :     {
     602          66 :         if (a->attributes[i] != b->attributes[i])
     603          18 :             return false;
     604             :     }
     605             : 
     606          12 :     return true;
     607             : }
     608             : 
     609             : /*
     610             :  * Generate a string representing an array of attribute numbers.
     611             :  * Internally, the dependency attribute is the last element, so we
     612             :  * leave that off.
     613             :  *
     614             :  * Freeing the allocated string is the responsibility of the caller.
     615             :  */
     616             : static char *
     617          12 : dep_attnum_list(const MVDependency *item)
     618             : {
     619             :     StringInfoData str;
     620             : 
     621          12 :     initStringInfo(&str);
     622             : 
     623          12 :     appendStringInfo(&str, "%d", item->attributes[0]);
     624             : 
     625          24 :     for (int i = 1; i < item->nattributes - 1; i++)
     626          12 :         appendStringInfo(&str, ", %d", item->attributes[i]);
     627             : 
     628          12 :     return str.data;
     629             : }
     630             : 
     631             : /*
     632             :  * Return the dependency, which is the last attribute element.
     633             :  */
     634             : static AttrNumber
     635          12 : dep_attnum_dependency(const MVDependency *item)
     636             : {
     637          12 :     return item->attributes[item->nattributes - 1];
     638             : }
     639             : 
     640             : /*
     641             :  * Attempt to build and serialize the MVDependencies object.
     642             :  *
     643             :  * This can only be executed after the completion of the JSON parsing.
     644             :  *
     645             :  * In the event of an error, set the error context and return NULL.
     646             :  */
     647             : static bytea *
     648          54 : build_mvdependencies(DependenciesParseState *parse, char *str)
     649             : {
     650          54 :     int         ndeps = list_length(parse->dependency_list);
     651             : 
     652             :     MVDependencies *mvdeps;
     653             :     bytea      *bytes;
     654             : 
     655          54 :     switch (parse->state)
     656             :     {
     657          54 :         case DEPS_PARSE_COMPLETE:
     658             : 
     659             :             /*
     660             :              * Parse ended in the expected place.  We should have a list of
     661             :              * items, but if we do not there is an issue with one of the
     662             :              * earlier parse steps.
     663             :              */
     664          54 :             if (ndeps == 0)
     665           0 :                 elog(ERROR,
     666             :                      "pg_dependencies parsing claims success with an empty item list.");
     667          54 :             break;
     668             : 
     669           0 :         case DEPS_EXPECT_START:
     670             :             /* blank */
     671           0 :             errsave(parse->escontext,
     672             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     673             :                     errmsg("malformed pg_dependencies: \"%s\"", str),
     674             :                     errdetail("Value cannot be empty."));
     675           0 :             return NULL;
     676             : 
     677           0 :         default:
     678             :             /* Unexpected end-state. */
     679           0 :             errsave(parse->escontext,
     680             :                     errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     681             :                     errmsg("malformed pg_dependencies: \"%s\"", str),
     682             :                     errdetail("Unexpected end state has been found: %d.", parse->state));
     683           0 :             return NULL;
     684             :     }
     685             : 
     686          54 :     mvdeps = palloc0(offsetof(MVDependencies, deps)
     687          54 :                      + (ndeps * sizeof(MVDependency *)));
     688          54 :     mvdeps->magic = STATS_DEPS_MAGIC;
     689          54 :     mvdeps->type = STATS_DEPS_TYPE_BASIC;
     690          54 :     mvdeps->ndeps = ndeps;
     691             : 
     692         162 :     for (int i = 0; i < ndeps; i++)
     693             :     {
     694             :         /*
     695             :          * Use the MVDependency objects in the dependency_list.
     696             :          *
     697             :          * Because we free the dependency_list after parsing is done, we
     698             :          * cannot free it here.
     699             :          */
     700         120 :         mvdeps->deps[i] = list_nth(parse->dependency_list, i);
     701             : 
     702             :         /*
     703             :          * Ensure that this item does not duplicate the attributes of any
     704             :          * pre-existing item.
     705             :          */
     706         228 :         for (int j = 0; j < i; j++)
     707             :         {
     708         120 :             if (dep_attributes_eq(mvdeps->deps[i], mvdeps->deps[j]))
     709             :             {
     710          12 :                 MVDependency *dep = mvdeps->deps[i];
     711          12 :                 char       *attnum_list = dep_attnum_list(dep);
     712          12 :                 AttrNumber  attnum_dep = dep_attnum_dependency(dep);
     713             : 
     714          12 :                 errsave(parse->escontext,
     715             :                         errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     716             :                         errmsg("malformed pg_dependencies: \"%s\"", str),
     717             :                         errdetail("Duplicated \"%s\" array has been found: [%s] for key \"%s\" and value %d.",
     718             :                                   PG_DEPENDENCIES_KEY_ATTRIBUTES, attnum_list,
     719             :                                   PG_DEPENDENCIES_KEY_DEPENDENCY, attnum_dep));
     720           6 :                 pfree(mvdeps);
     721           6 :                 return NULL;
     722             :             }
     723             :         }
     724             :     }
     725             : 
     726          42 :     bytes = statext_dependencies_serialize(mvdeps);
     727             : 
     728             :     /*
     729             :      * No need to free the individual MVDependency objects, because they are
     730             :      * still in the dependency_list, and will be freed with that.
     731             :      */
     732          42 :     pfree(mvdeps);
     733             : 
     734          42 :     return bytes;
     735             : }
     736             : 
     737             : 
     738             : /*
     739             :  * pg_dependencies_in       - input routine for type pg_dependencies.
     740             :  *
     741             :  * This format is valid JSON, with the expected format:
     742             :  *    [{"attributes": [1,2], "dependency": -1, "degree": 1.0000},
     743             :  *     {"attributes": [1,-1], "dependency": 2, "degree": 0.0000},
     744             :  *     {"attributes": [2,-1], "dependency": 1, "degree": 1.0000}]
     745             :  *
     746             :  */
     747             : Datum
     748         564 : pg_dependencies_in(PG_FUNCTION_ARGS)
     749             : {
     750         564 :     char       *str = PG_GETARG_CSTRING(0);
     751         564 :     bytea      *bytes = NULL;
     752             : 
     753             :     DependenciesParseState parse_state;
     754             :     JsonParseErrorType result;
     755             :     JsonLexContext *lex;
     756             :     JsonSemAction sem_action;
     757             : 
     758             :     /* initialize the semantic state */
     759         564 :     parse_state.str = str;
     760         564 :     parse_state.state = DEPS_EXPECT_START;
     761         564 :     parse_state.dependency_list = NIL;
     762         564 :     parse_state.attnum_list = NIL;
     763         564 :     parse_state.dependency = 0;
     764         564 :     parse_state.degree = 0.0;
     765         564 :     parse_state.found_attributes = false;
     766         564 :     parse_state.found_dependency = false;
     767         564 :     parse_state.found_degree = false;
     768         564 :     parse_state.escontext = fcinfo->context;
     769             : 
     770             :     /* set callbacks */
     771         564 :     sem_action.semstate = (void *) &parse_state;
     772         564 :     sem_action.object_start = dependencies_object_start;
     773         564 :     sem_action.object_end = dependencies_object_end;
     774         564 :     sem_action.array_start = dependencies_array_start;
     775         564 :     sem_action.array_end = dependencies_array_end;
     776         564 :     sem_action.array_element_start = dependencies_array_element_start;
     777         564 :     sem_action.array_element_end = NULL;
     778         564 :     sem_action.object_field_start = dependencies_object_field_start;
     779         564 :     sem_action.object_field_end = NULL;
     780         564 :     sem_action.scalar = dependencies_scalar;
     781             : 
     782         564 :     lex = makeJsonLexContextCstringLen(NULL, str, strlen(str), PG_UTF8, true);
     783             : 
     784         564 :     result = pg_parse_json(lex, &sem_action);
     785         342 :     freeJsonLexContext(lex);
     786             : 
     787         342 :     if (result == JSON_SUCCESS)
     788          54 :         bytes = build_mvdependencies(&parse_state, str);
     789             : 
     790         336 :     list_free_deep(parse_state.dependency_list);
     791         336 :     list_free(parse_state.attnum_list);
     792             : 
     793         336 :     if (bytes)
     794          42 :         PG_RETURN_BYTEA_P(bytes);
     795             : 
     796             :     /*
     797             :      * If escontext already set, just use that. Anything else is a generic
     798             :      * JSON parse error.
     799             :      */
     800         294 :     if (!SOFT_ERROR_OCCURRED(parse_state.escontext))
     801          66 :         errsave(parse_state.escontext,
     802             :                 errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     803             :                 errmsg("malformed pg_dependencies: \"%s\"", str),
     804             :                 errdetail("Input data must be valid JSON."));
     805             : 
     806         258 :     PG_RETURN_NULL();
     807             : }
     808             : 
     809             : 
     810             : /*
     811             :  * pg_dependencies_out      - output routine for type pg_dependencies.
     812             :  */
     813             : Datum
     814          52 : pg_dependencies_out(PG_FUNCTION_ARGS)
     815             : {
     816          52 :     bytea      *data = PG_GETARG_BYTEA_PP(0);
     817          52 :     MVDependencies *dependencies = statext_dependencies_deserialize(data);
     818             :     StringInfoData str;
     819             : 
     820          52 :     initStringInfo(&str);
     821          52 :     appendStringInfoChar(&str, '[');
     822             : 
     823         204 :     for (int i = 0; i < dependencies->ndeps; i++)
     824             :     {
     825         152 :         MVDependency *dependency = dependencies->deps[i];
     826             : 
     827         152 :         if (i > 0)
     828         100 :             appendStringInfoString(&str, ", ");
     829             : 
     830         152 :         if (dependency->nattributes <= 1)
     831           0 :             elog(ERROR, "invalid zero-length nattributes array in MVDependencies");
     832             : 
     833         152 :         appendStringInfo(&str, "{\"" PG_DEPENDENCIES_KEY_ATTRIBUTES "\": [%d",
     834         152 :                          dependency->attributes[0]);
     835             : 
     836         268 :         for (int j = 1; j < dependency->nattributes - 1; j++)
     837         116 :             appendStringInfo(&str, ", %d", dependency->attributes[j]);
     838             : 
     839         152 :         appendStringInfo(&str, "], \"" PG_DEPENDENCIES_KEY_DEPENDENCY "\": %d, "
     840             :                          "\"" PG_DEPENDENCIES_KEY_DEGREE "\": %f}",
     841         152 :                          dependency->attributes[dependency->nattributes - 1],
     842             :                          dependency->degree);
     843             :     }
     844             : 
     845          52 :     appendStringInfoChar(&str, ']');
     846             : 
     847          52 :     PG_RETURN_CSTRING(str.data);
     848             : }
     849             : 
     850             : /*
     851             :  * pg_dependencies_recv     - binary input routine for type pg_dependencies.
     852             :  */
     853             : Datum
     854           0 : pg_dependencies_recv(PG_FUNCTION_ARGS)
     855             : {
     856           0 :     ereport(ERROR,
     857             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     858             :              errmsg("cannot accept a value of type %s", "pg_dependencies")));
     859             : 
     860             :     PG_RETURN_VOID();           /* keep compiler quiet */
     861             : }
     862             : 
     863             : /*
     864             :  * pg_dependencies_send     - binary output routine for type pg_dependencies.
     865             :  *
     866             :  * Functional dependencies are serialized in a bytea value (although the type
     867             :  * is named differently), so let's just send that.
     868             :  */
     869             : Datum
     870           0 : pg_dependencies_send(PG_FUNCTION_ARGS)
     871             : {
     872           0 :     return byteasend(fcinfo);
     873             : }

Generated by: LCOV version 1.16