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

Generated by: LCOV version 2.0-1