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

Generated by: LCOV version 1.16