LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_scan.l (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.9 % 294 232
Test Date: 2026-04-07 14:16:30 Functions: 86.7 % 15 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : %top{
       2              : /*-------------------------------------------------------------------------
       3              :  *
       4              :  * jsonpath_scan.l
       5              :  *  Lexical parser for jsonpath datatype
       6              :  *
       7              :  * Splits jsonpath string into tokens represented as JsonPathString structs.
       8              :  * Decodes unicode and hex escaped strings.
       9              :  *
      10              :  * Copyright (c) 2019-2026, PostgreSQL Global Development Group
      11              :  *
      12              :  * IDENTIFICATION
      13              :  *  src/backend/utils/adt/jsonpath_scan.l
      14              :  *
      15              :  *-------------------------------------------------------------------------
      16              :  */
      17              : 
      18              : #include "postgres.h"
      19              : 
      20              : /*
      21              :  * NB: include jsonpath_gram.h only AFTER including jsonpath_internal.h,
      22              :  * because jsonpath_internal.h contains the declaration for JsonPathString.
      23              :  */
      24              : #include "jsonpath_internal.h"
      25              : #include "jsonpath_gram.h"
      26              : 
      27              : #include "mb/pg_wchar.h"
      28              : #include "nodes/miscnodes.h"
      29              : #include "nodes/pg_list.h"
      30              : }
      31              : 
      32              : %{
      33              : struct jsonpath_yy_extra_type
      34              : {
      35              :     JsonPathString scanstring;
      36              : };
      37              : 
      38              : static void addstring(bool init, char *s, int l, yyscan_t yyscanner);
      39              : static void addchar(bool init, char c, yyscan_t yyscanner);
      40              : static enum yytokentype checkKeyword(yyscan_t yyscanner);
      41              : static bool parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner);
      42              : static bool parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner);
      43              : 
      44              : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
      45              : #undef fprintf
      46              : #define fprintf(file, fmt, msg)  fprintf_to_ereport(fmt, msg)
      47              : 
      48              : static void
      49            0 : fprintf_to_ereport(const char *fmt, const char *msg)
      50              : {
      51            0 :     ereport(ERROR, (errmsg_internal("%s", msg)));
      52              : }
      53              : 
      54              : /* LCOV_EXCL_START */
      55              : 
      56              : %}
      57              : 
      58              : %option 8bit
      59              : %option never-interactive
      60              : %option nodefault
      61              : %option noinput
      62              : %option nounput
      63              : %option noyywrap
      64              : %option warn
      65              : %option prefix="jsonpath_yy"
      66              : %option extra-type="struct jsonpath_yy_extra_type *"
      67              : %option reentrant
      68              : %option bison-bridge
      69              : %option noyyalloc
      70              : %option noyyrealloc
      71              : %option noyyfree
      72              : 
      73              : /*
      74              :  * We use exclusive states for quoted and non-quoted strings,
      75              :  * quoted variable names and C-style comments.
      76              :  * Exclusive states:
      77              :  *  <xq> - quoted strings
      78              :  *  <xnq> - non-quoted strings
      79              :  *  <xvq> - quoted variable names
      80              :  *  <xc> - C-style comment
      81              :  */
      82              : 
      83              : %x xq
      84              : %x xnq
      85              : %x xvq
      86              : %x xc
      87              : 
      88              : special     [\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/]
      89              : blank       [ \t\n\r\f]
      90              : /* "other" means anything that's not special, blank, or '\' or '"' */
      91              : other       [^\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/\\\" \t\n\r\f]
      92              : 
      93              : decdigit    [0-9]
      94              : hexdigit    [0-9A-Fa-f]
      95              : octdigit    [0-7]
      96              : bindigit    [0-1]
      97              : 
      98              : /* DecimalInteger in ECMAScript; must not start with 0 unless it's exactly 0 */
      99              : decinteger  (0|[1-9](_?{decdigit})*)
     100              : /* DecimalDigits in ECMAScript; only used as part of other rules */
     101              : decdigits   {decdigit}(_?{decdigit})*
     102              : /* Non-decimal integers; in ECMAScript, these must not have underscore after prefix */
     103              : hexinteger  0[xX]{hexdigit}(_?{hexdigit})*
     104              : octinteger  0[oO]{octdigit}(_?{octdigit})*
     105              : bininteger  0[bB]{bindigit}(_?{bindigit})*
     106              : 
     107              : decimal     ({decinteger}\.{decdigits}?|\.{decdigits})
     108              : real        ({decinteger}|{decimal})[Ee][-+]?{decdigits}
     109              : realfail    ({decinteger}|{decimal})[Ee][-+]
     110              : 
     111              : decinteger_junk {decinteger}{other}
     112              : decimal_junk    {decimal}{other}
     113              : real_junk       {real}{other}
     114              : 
     115              : unicode     \\u({hexdigit}{4}|\{{hexdigit}{1,6}\})
     116              : unicodefail \\u({hexdigit}{0,3}|\{{hexdigit}{0,6})
     117              : hex_char    \\x{hexdigit}{2}
     118              : hex_fail    \\x{hexdigit}{0,1}
     119              : 
     120              : %%
     121              : 
     122              : <xnq>{other}+                 {
     123            4 :                                     addstring(false, yytext, yyleng, yyscanner);
     124              :                                 }
     125            4 : 
     126         2580 : <xnq>{blank}+                 {
     127         2580 :                                     yylval->str = yyextra->scanstring;
     128         2580 :                                     BEGIN INITIAL;
     129         2580 :                                     return checkKeyword(yyscanner);
     130              :                                 }
     131              : 
     132            0 : <xnq>\/\*                     {
     133            0 :                                     yylval->str = yyextra->scanstring;
     134            0 :                                     BEGIN xc;
     135              :                                 }
     136            0 : 
     137         4544 : <xnq>({special}|\")              {
     138         4544 :                                     yylval->str = yyextra->scanstring;
     139         4544 :                                     yyless(0);
     140         4544 :                                     BEGIN INITIAL;
     141         4544 :                                     return checkKeyword(yyscanner);
     142              :                                 }
     143              : 
     144          938 : <xnq><<EOF>>                  {
     145          938 :                                     yylval->str = yyextra->scanstring;
     146          938 :                                     BEGIN INITIAL;
     147          938 :                                     return checkKeyword(yyscanner);
     148              :                                 }
     149              : 
     150            4 : <xnq,xq,xvq>\\b               { addchar(false, '\b', yyscanner); }
     151            4 : 
     152            4 : <xnq,xq,xvq>\\f               { addchar(false, '\f', yyscanner); }
     153            4 : 
     154            4 : <xnq,xq,xvq>\\n               { addchar(false, '\n', yyscanner); }
     155            4 : 
     156            4 : <xnq,xq,xvq>\\r               { addchar(false, '\r', yyscanner); }
     157            4 : 
     158            8 : <xnq,xq,xvq>\\t               { addchar(false, '\t', yyscanner); }
     159            8 : 
     160            4 : <xnq,xq,xvq>\\v               { addchar(false, '\v', yyscanner); }
     161            4 : 
     162           84 : <xnq,xq,xvq>{unicode}+        {
     163           84 :                                 if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
     164            0 :                                     yyterminate();
     165              :                             }
     166           36 : 
     167            8 : <xnq,xq,xvq>{hex_char}        {
     168            8 :                                 if (!parseHexChar(yytext, escontext, yyscanner))
     169            0 :                                     yyterminate();
     170              :                             }
     171            8 : 
     172           24 : <xnq,xq,xvq>{unicode}*{unicodefail} {
     173           24 :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     174              :                                                  "invalid Unicode escape sequence");
     175            0 :                                 yyterminate();
     176              :                             }
     177              : 
     178            0 : <xnq,xq,xvq>{hex_fail}        {
     179            0 :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     180              :                                                  "invalid hexadecimal character sequence");
     181            0 :                                 yyterminate();
     182              :                             }
     183              : 
     184            4 : <xnq,xq,xvq>{unicode}+\\  {
     185              :                                 /* throw back the \\, and treat as unicode */
     186            4 :                                 yyless(yyleng - 1);
     187            4 :                                 if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
     188            0 :                                     yyterminate();
     189              :                             }
     190            4 : 
     191           84 : <xnq,xq,xvq>\\.               { addchar(false, yytext[1], yyscanner); }
     192           84 : 
     193            0 : <xnq,xq,xvq>\\                {
     194            0 :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     195              :                                                  "unexpected end after backslash");
     196            0 :                                 yyterminate();
     197              :                             }
     198              : 
     199            0 : <xq,xvq><<EOF>>               {
     200            0 :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     201              :                                                  "unterminated quoted string");
     202            0 :                                 yyterminate();
     203              :                             }
     204              : 
     205         1448 : <xq>\"                           {
     206         1448 :                                     yylval->str = yyextra->scanstring;
     207         1448 :                                     BEGIN INITIAL;
     208         1448 :                                     return STRING_P;
     209              :                                 }
     210              : 
     211           68 : <xvq>\"                          {
     212           68 :                                     yylval->str = yyextra->scanstring;
     213           68 :                                     BEGIN INITIAL;
     214           68 :                                     return VARIABLE_P;
     215              :                                 }
     216              : 
     217         1564 : <xq,xvq>[^\\\"]+             { addstring(false, yytext, yyleng, yyscanner); }
     218         1564 : 
     219            0 : <xc>\*\/                      { BEGIN INITIAL; }
     220            0 : 
     221            0 : <xc>[^\*]+                        { }
     222            0 : 
     223            0 : <xc>\*                            { }
     224            0 : 
     225            0 : <xc><<EOF>>                       {
     226            0 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     227              :                                                      "unexpected end of comment");
     228            0 :                                     yyterminate();
     229              :                                 }
     230          124 : \&\&                            { return AND_P; }
     231          124 : 
     232           72 : \|\|                            { return OR_P; }
     233              : 
     234           16 : \!                              { return NOT_P; }
     235              : 
     236          236 : \*\*                            { return ANY_P; }
     237              : 
     238          504 : \<                               { return LESS_P; }
     239              : 
     240           28 : \<\=                         { return LESSEQUAL_P; }
     241              : 
     242          668 : \=\=                            { return EQUAL_P; }
     243              : 
     244            0 : \<\>                          { return NOTEQUAL_P; }
     245              : 
     246            8 : \!\=                            { return NOTEQUAL_P; }
     247              : 
     248          224 : \>\=                         { return GREATEREQUAL_P; }
     249              : 
     250          296 : \>                               { return GREATER_P; }
     251              : 
     252          408 : \${other}+                      {
     253          408 :                                     addstring(true, yytext + 1, yyleng - 1, yyscanner);
     254          408 :                                     addchar(false, '\0', yyscanner);
     255          408 :                                     yylval->str = yyextra->scanstring;
     256          408 :                                     return VARIABLE_P;
     257              :                                 }
     258              : 
     259           68 : \$\"                           {
     260           68 :                                     addchar(true, '\0', yyscanner);
     261           68 :                                     BEGIN xvq;
     262              :                                 }
     263           68 : 
     264        34120 : {special}                       { return *yytext; }
     265              : 
     266         6876 : {blank}+                        { /* ignore */ }
     267         6876 : 
     268            0 : \/\*                            {
     269            0 :                                     addchar(true, '\0', yyscanner);
     270            0 :                                     BEGIN xc;
     271              :                                 }
     272            0 : 
     273          200 : {real}                          {
     274          200 :                                     addstring(true, yytext, yyleng, yyscanner);
     275          200 :                                     addchar(false, '\0', yyscanner);
     276          200 :                                     yylval->str = yyextra->scanstring;
     277          200 :                                     return NUMERIC_P;
     278              :                                 }
     279              : 
     280          172 : {decimal}                       {
     281          172 :                                     addstring(true, yytext, yyleng, yyscanner);
     282          172 :                                     addchar(false, '\0', yyscanner);
     283          172 :                                     yylval->str = yyextra->scanstring;
     284          172 :                                     return NUMERIC_P;
     285              :                                 }
     286              : 
     287         1548 : {decinteger}                    {
     288         1548 :                                     addstring(true, yytext, yyleng, yyscanner);
     289         1548 :                                     addchar(false, '\0', yyscanner);
     290         1548 :                                     yylval->str = yyextra->scanstring;
     291         1548 :                                     return INT_P;
     292              :                                 }
     293              : 
     294            8 : {hexinteger}                    {
     295            8 :                                     addstring(true, yytext, yyleng, yyscanner);
     296            8 :                                     addchar(false, '\0', yyscanner);
     297            8 :                                     yylval->str = yyextra->scanstring;
     298            8 :                                     return INT_P;
     299              :                                 }
     300              : 
     301            8 : {octinteger}                    {
     302            8 :                                     addstring(true, yytext, yyleng, yyscanner);
     303            8 :                                     addchar(false, '\0', yyscanner);
     304            8 :                                     yylval->str = yyextra->scanstring;
     305            8 :                                     return INT_P;
     306              :                                 }
     307              : 
     308            8 : {bininteger}                    {
     309            8 :                                     addstring(true, yytext, yyleng, yyscanner);
     310            8 :                                     addchar(false, '\0', yyscanner);
     311            8 :                                     yylval->str = yyextra->scanstring;
     312            8 :                                     return INT_P;
     313              :                                 }
     314              : 
     315            0 : {realfail}                      {
     316            0 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     317              :                                                      "invalid numeric literal");
     318            0 :                                     yyterminate();
     319              :                                 }
     320              : {decinteger_junk}               {
     321           60 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     322              :                                                      "trailing junk after numeric literal");
     323           16 :                                     yyterminate();
     324              :                                 }
     325              : {decimal_junk}                  {
     326           28 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     327              :                                                      "trailing junk after numeric literal");
     328            0 :                                     yyterminate();
     329              :                                 }
     330              : {real_junk}                     {
     331            4 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     332              :                                                      "trailing junk after numeric literal");
     333            0 :                                     yyterminate();
     334              :                                 }
     335              : \"                             {
     336         1520 :                                     addchar(true, '\0', yyscanner);
     337         1520 :                                     BEGIN xq;
     338              :                                 }
     339         1520 : 
     340            0 : \\                              {
     341            0 :                                     yyless(0);
     342            0 :                                     addchar(true, '\0', yyscanner);
     343            0 :                                     BEGIN xnq;
     344              :                                 }
     345            0 : 
     346         8062 : {other}+                        {
     347         8062 :                                     addstring(true, yytext, yyleng, yyscanner);
     348         8062 :                                     BEGIN xnq;
     349              :                                 }
     350         8062 : 
     351         7370 : <<EOF>>                         { yyterminate(); }
     352              : 
     353            0 : %%
     354              : 
     355              : /* LCOV_EXCL_STOP */
     356              : 
     357              : /* see scan.l */
     358              : #undef yyextra
     359              : #define yyextra  (((struct yyguts_t *) yyscanner)->yyextra_r)
     360              : 
     361              : void
     362          280 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
     363              :                  yyscan_t yyscanner,
     364              :                  const char *message)
     365              : {
     366          280 :     struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;   /* needed for yytext
     367              :                                                              * macro */
     368              : 
     369              :     /* don't overwrite escontext if it's already been set */
     370          280 :     if (SOFT_ERROR_OCCURRED(escontext))
     371           12 :         return;
     372              : 
     373          268 :     if (*yytext == YY_END_OF_BUFFER_CHAR)
     374              :     {
     375           40 :         errsave(escontext,
     376              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     377              :         /* translator: %s is typically "syntax error" */
     378              :                  errmsg("%s at end of jsonpath input", _(message))));
     379              :     }
     380              :     else
     381              :     {
     382          228 :         errsave(escontext,
     383              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     384              :         /* translator: first %s is typically "syntax error" */
     385              :                  errmsg("%s at or near \"%s\" of jsonpath input",
     386              :                         _(message), yytext)));
     387              :     }
     388              : }
     389              : 
     390              : typedef struct JsonPathKeyword
     391              : {
     392              :     int16       len;
     393              :     bool        lowercase;
     394              :     int         val;
     395              :     const char *keyword;
     396              : } JsonPathKeyword;
     397              : 
     398              : /*
     399              :  * Array of key words should be sorted by length and then
     400              :  * alphabetical order
     401              :  */
     402              : static const JsonPathKeyword keywords[] = {
     403              :     {2, false, IS_P, "is"},
     404              :     {2, false, TO_P, "to"},
     405              :     {3, false, ABS_P, "abs"},
     406              :     {3, false, LAX_P, "lax"},
     407              :     {4, false, DATE_P, "date"},
     408              :     {4, false, FLAG_P, "flag"},
     409              :     {4, false, LAST_P, "last"},
     410              :     {4, true, NULL_P, "null"},
     411              :     {4, false, SIZE_P, "size"},
     412              :     {4, false, TIME_P, "time"},
     413              :     {4, true, TRUE_P, "true"},
     414              :     {4, false, TYPE_P, "type"},
     415              :     {4, false, WITH_P, "with"},
     416              :     {5, false, STR_BTRIM_P, "btrim"},
     417              :     {5, true, FALSE_P, "false"},
     418              :     {5, false, FLOOR_P, "floor"},
     419              :     {5, false, STR_LOWER_P, "lower"},
     420              :     {5, false, STR_LTRIM_P, "ltrim"},
     421              :     {5, false, STR_RTRIM_P, "rtrim"},
     422              :     {5, false, STR_UPPER_P, "upper"},
     423              :     {6, false, BIGINT_P, "bigint"},
     424              :     {6, false, DOUBLE_P, "double"},
     425              :     {6, false, EXISTS_P, "exists"},
     426              :     {6, false, NUMBER_P, "number"},
     427              :     {6, false, STARTS_P, "starts"},
     428              :     {6, false, STRICT_P, "strict"},
     429              :     {6, false, STRINGFUNC_P, "string"},
     430              :     {7, false, BOOLEAN_P, "boolean"},
     431              :     {7, false, CEILING_P, "ceiling"},
     432              :     {7, false, DECIMAL_P, "decimal"},
     433              :     {7, false, STR_INITCAP_P, "initcap"},
     434              :     {7, false, INTEGER_P, "integer"},
     435              :     {7, false, STR_REPLACE_P, "replace"},
     436              :     {7, false, TIME_TZ_P, "time_tz"},
     437              :     {7, false, UNKNOWN_P, "unknown"},
     438              :     {8, false, DATETIME_P, "datetime"},
     439              :     {8, false, KEYVALUE_P, "keyvalue"},
     440              :     {9, false, TIMESTAMP_P, "timestamp"},
     441              :     {10, false, LIKE_REGEX_P, "like_regex"},
     442              :     {10, false, STR_SPLIT_PART_P, "split_part"},
     443              :     {12, false, TIMESTAMP_TZ_P, "timestamp_tz"},
     444              : };
     445              : 
     446              : /*
     447              :  * Check if current scanstring value is a keyword
     448              :  */
     449              : static enum yytokentype
     450         8062 : checkKeyword(yyscan_t yyscanner)
     451              : {
     452         8062 :     int         res = IDENT_P;
     453              :     int         diff;
     454         8062 :     const JsonPathKeyword *StopLow = keywords,
     455         8062 :                *StopHigh = keywords + lengthof(keywords),
     456              :                *StopMiddle;
     457              : 
     458         8062 :     if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
     459            4 :         return res;
     460              : 
     461        44582 :     while (StopLow < StopHigh)
     462              :     {
     463        42020 :         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
     464              : 
     465        42020 :         if (StopMiddle->len == yyextra->scanstring.len)
     466        15856 :             diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
     467        15856 :                                   yyextra->scanstring.len);
     468              :         else
     469        26164 :             diff = StopMiddle->len - yyextra->scanstring.len;
     470              : 
     471        42020 :         if (diff < 0)
     472        11036 :             StopLow = StopMiddle + 1;
     473        30984 :         else if (diff > 0)
     474        25488 :             StopHigh = StopMiddle;
     475              :         else
     476              :         {
     477         5496 :             if (StopMiddle->lowercase)
     478          196 :                 diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
     479          196 :                                yyextra->scanstring.len);
     480              : 
     481         5496 :             if (diff == 0)
     482         5496 :                 res = StopMiddle->val;
     483              : 
     484         5496 :             break;
     485              :         }
     486              :     }
     487              : 
     488         8058 :     return res;
     489              : }
     490              : 
     491              : /*
     492              :  * Resize scanstring so that it can append string of given length.
     493              :  * Reinitialize if required.
     494              :  */
     495              : static void
     496        16114 : resizeString(bool init, int appendLen, yyscan_t yyscanner)
     497              : {
     498        16114 :     if (init)
     499              :     {
     500        12002 :         yyextra->scanstring.total = Max(32, appendLen);
     501        12002 :         yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
     502        12002 :         yyextra->scanstring.len = 0;
     503              :     }
     504              :     else
     505              :     {
     506         4112 :         if (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
     507              :         {
     508            0 :             while (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
     509            0 :                 yyextra->scanstring.total *= 2;
     510            0 :             yyextra->scanstring.val = repalloc(yyextra->scanstring.val, yyextra->scanstring.total);
     511              :         }
     512              :     }
     513        16114 : }
     514              : 
     515              : /* Add set of bytes at "s" of length "l" to scanstring */
     516              : static void
     517        12062 : addstring(bool init, char *s, int l, yyscan_t yyscanner)
     518              : {
     519        12062 :     resizeString(init, l + 1, yyscanner);
     520        12062 :     memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
     521        12062 :     yyextra->scanstring.len += l;
     522        12062 : }
     523              : 
     524              : /* Add single byte "c" to scanstring */
     525              : static void
     526         4052 : addchar(bool init, char c, yyscan_t yyscanner)
     527              : {
     528         4052 :     resizeString(init, 1, yyscanner);
     529         4052 :     yyextra->scanstring.val[yyextra->scanstring.len] = c;
     530         4052 :     if (c != '\0')
     531          112 :         yyextra->scanstring.len++;
     532         4052 : }
     533              : 
     534              : /* Interface to jsonpath parser */
     535              : JsonPathParseResult *
     536         7706 : parsejsonpath(const char *str, int len, struct Node *escontext)
     537              : {
     538              :     JsonPathParseResult *parseresult;
     539              :     yyscan_t    scanner;
     540              :     struct jsonpath_yy_extra_type yyext;
     541              : 
     542         7706 :     if (jsonpath_yylex_init(&scanner) != 0)
     543            0 :         elog(ERROR, "yylex_init() failed: %m");
     544              : 
     545         7706 :     yyset_extra(&yyext, scanner);
     546              : 
     547         7706 :     if (len <= 0)
     548            4 :         len = strlen(str);
     549              : 
     550         7706 :     jsonpath_yy_scan_bytes(str, len, scanner);
     551              : 
     552         7706 :     if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
     553           12 :         jsonpath_yyerror(NULL, escontext, scanner, "invalid input");  /* shouldn't happen */
     554              : 
     555         7398 :     jsonpath_yylex_destroy(scanner);
     556              : 
     557         7398 :     return parseresult;
     558              : }
     559              : 
     560              : /* Turn hex character into integer */
     561              : static bool
     562          584 : hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
     563              : {
     564          584 :     if (c >= '0' && c <= '9')
     565              :     {
     566          392 :         *result = c - '0';
     567          392 :         return true;
     568              :     }
     569          192 :     if (c >= 'a' && c <= 'f')
     570              :     {
     571          168 :         *result = c - 'a' + 0xA;
     572          168 :         return true;
     573              :     }
     574           24 :     if (c >= 'A' && c <= 'F')
     575              :     {
     576           24 :         *result = c - 'A' + 0xA;
     577           24 :         return true;
     578              :     }
     579            0 :     jsonpath_yyerror(NULL, escontext, yyscanner, "invalid hexadecimal digit");
     580            0 :     return false;
     581              : }
     582              : 
     583              : /* Add given unicode character to scanstring */
     584              : static bool
     585           96 : addUnicodeChar(char32_t ch, struct Node *escontext, yyscan_t yyscanner)
     586              : {
     587           96 :     if (ch == 0)
     588              :     {
     589              :         /* We can't allow this, since our TEXT type doesn't */
     590           16 :         ereturn(escontext, false,
     591              :                 (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
     592              :                  errmsg("unsupported Unicode escape sequence"),
     593              :                  errdetail("\\u0000 cannot be converted to text.")));
     594              :     }
     595              :     else
     596              :     {
     597              :         char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
     598              : 
     599              :         /*
     600              :          * If we're trapping the error status, call the noerror form of the
     601              :          * conversion function. Otherwise call the normal form which provides
     602              :          * more detailed errors.
     603              :          */
     604              : 
     605           80 :         if (!escontext || !IsA(escontext, ErrorSaveContext))
     606           80 :             pg_unicode_to_server(ch, (unsigned char *) cbuf);
     607            0 :         else if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf))
     608            0 :             ereturn(escontext, false,
     609              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     610              :                      errmsg("could not convert Unicode to server encoding")));
     611           80 :         addstring(false, cbuf, strlen(cbuf), yyscanner);
     612              :     }
     613           80 :     return true;
     614              : }
     615              : 
     616              : /* Add unicode character, processing any surrogate pairs */
     617              : static bool
     618          144 : addUnicode(char32_t ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
     619              : {
     620          144 :     if (is_utf16_surrogate_first(ch))
     621              :     {
     622           40 :         if (*hi_surrogate != -1)
     623            8 :             ereturn(escontext, false,
     624              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     625              :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     626              :                      errdetail("Unicode high surrogate must not follow "
     627              :                                "a high surrogate.")));
     628           32 :         *hi_surrogate = ch;
     629           32 :         return true;
     630              :     }
     631          104 :     else if (is_utf16_surrogate_second(ch))
     632              :     {
     633           32 :         if (*hi_surrogate == -1)
     634           16 :             ereturn(escontext, false,
     635              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     636              :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     637              :                      errdetail("Unicode low surrogate must follow a high "
     638              :                                "surrogate.")));
     639           16 :         ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
     640           16 :         *hi_surrogate = -1;
     641              :     }
     642           72 :     else if (*hi_surrogate != -1)
     643              :     {
     644            0 :         ereturn(escontext, false,
     645              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     646              :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     647              :                  errdetail("Unicode low surrogate must follow a high "
     648              :                            "surrogate.")));
     649              :     }
     650              : 
     651           88 :     return addUnicodeChar(ch, escontext, yyscanner);
     652              : }
     653              : 
     654              : /*
     655              :  * parseUnicode was adopted from json_lex_string() in
     656              :  * src/backend/utils/adt/json.c
     657              :  */
     658              : static bool
     659           88 : parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
     660              : {
     661           88 :     int         i = 2;
     662           88 :     int         hi_surrogate = -1;
     663              : 
     664          192 :     for (i = 2; i < l; i += 2)   /* skip '\u' */
     665              :     {
     666          144 :         char32_t        ch = 0;
     667              :         int         j,
     668              :                     si;
     669              : 
     670          144 :         if (s[i] == '{')        /* parse '\u{XX...}' */
     671              :         {
     672          112 :             while (s[++i] != '}' && i < l)
     673              :             {
     674           88 :                 if (!hexval(s[i], &si, escontext, yyscanner))
     675            0 :                     return false;
     676           88 :                 ch = (ch << 4) | si;
     677              :             }
     678           24 :             i++;                /* skip '}' */
     679              :         }
     680              :         else                    /* parse '\uXXXX' */
     681              :         {
     682          600 :             for (j = 0; j < 4 && i < l; j++)
     683              :             {
     684          480 :                 if (!hexval(s[i++], &si, escontext, yyscanner))
     685            0 :                     return false;
     686          480 :                 ch = (ch << 4) | si;
     687              :             }
     688              :         }
     689              : 
     690          144 :         if (!addUnicode(ch, &hi_surrogate, escontext, yyscanner))
     691            0 :             return false;
     692              :     }
     693              : 
     694           48 :     if (hi_surrogate != -1)
     695              :     {
     696            8 :         ereturn(escontext, false,
     697              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     698              :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     699              :                  errdetail("Unicode low surrogate must follow a high "
     700              :                            "surrogate.")));
     701              :     }
     702              : 
     703           40 :     return true;
     704              : }
     705              : 
     706              : /* Parse sequence of hex-encoded characters */
     707              : static bool
     708            8 : parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
     709              : {
     710              :     int         s2,
     711              :                 s3,
     712              :                 ch;
     713              : 
     714            8 :     if (!hexval(s[2], &s2, escontext, yyscanner))
     715            0 :         return false;
     716            8 :     if (!hexval(s[3], &s3, escontext, yyscanner))
     717            0 :         return false;
     718              : 
     719            8 :     ch = (s2 << 4) | s3;
     720              : 
     721            8 :     return addUnicodeChar(ch, escontext, yyscanner);
     722              : }
     723              : 
     724              : /*
     725              :  * Interface functions to make flex use palloc() instead of malloc().
     726              :  * It'd be better to make these static, but flex insists otherwise.
     727              :  */
     728              : 
     729              : void *
     730        30824 : jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
     731              : {
     732        30824 :     return palloc(bytes);
     733              : }
     734              : 
     735              : void *
     736            0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner)
     737              : {
     738            0 :     if (ptr)
     739            0 :         return repalloc(ptr, bytes);
     740              :     else
     741            0 :         return palloc(bytes);
     742              : }
     743              : 
     744              : void
     745        36990 : jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
     746              : {
     747        36990 :     if (ptr)
     748        29592 :         pfree(ptr);
     749        36990 : }
        

Generated by: LCOV version 2.0-1