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

Generated by: LCOV version 2.0-1