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-03-10 15:14:48 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            3 :                                     addstring(false, yytext, yyleng, yyscanner);
     124              :                                 }
     125            3 : 
     126         1890 : <xnq>{blank}+                 {
     127         1890 :                                     yylval->str = yyextra->scanstring;
     128         1890 :                                     BEGIN INITIAL;
     129         1890 :                                     return checkKeyword(yyscanner);
     130              :                                 }
     131              : 
     132            0 : <xnq>\/\*                     {
     133            0 :                                     yylval->str = yyextra->scanstring;
     134            0 :                                     BEGIN xc;
     135              :                                 }
     136            0 : 
     137         2964 : <xnq>({special}|\")              {
     138         2964 :                                     yylval->str = yyextra->scanstring;
     139         2964 :                                     yyless(0);
     140         2964 :                                     BEGIN INITIAL;
     141         2964 :                                     return checkKeyword(yyscanner);
     142              :                                 }
     143              : 
     144          675 : <xnq><<EOF>>                  {
     145          675 :                                     yylval->str = yyextra->scanstring;
     146          675 :                                     BEGIN INITIAL;
     147          675 :                                     return checkKeyword(yyscanner);
     148              :                                 }
     149              : 
     150            3 : <xnq,xq,xvq>\\b               { addchar(false, '\b', yyscanner); }
     151            3 : 
     152            3 : <xnq,xq,xvq>\\f               { addchar(false, '\f', yyscanner); }
     153            3 : 
     154            3 : <xnq,xq,xvq>\\n               { addchar(false, '\n', yyscanner); }
     155            3 : 
     156            3 : <xnq,xq,xvq>\\r               { addchar(false, '\r', yyscanner); }
     157            3 : 
     158            6 : <xnq,xq,xvq>\\t               { addchar(false, '\t', yyscanner); }
     159            6 : 
     160            3 : <xnq,xq,xvq>\\v               { addchar(false, '\v', yyscanner); }
     161            3 : 
     162           63 : <xnq,xq,xvq>{unicode}+        {
     163           63 :                                 if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
     164            0 :                                     yyterminate();
     165              :                             }
     166           27 : 
     167            6 : <xnq,xq,xvq>{hex_char}        {
     168            6 :                                 if (!parseHexChar(yytext, escontext, yyscanner))
     169            0 :                                     yyterminate();
     170              :                             }
     171            6 : 
     172           18 : <xnq,xq,xvq>{unicode}*{unicodefail} {
     173           18 :                                 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            3 : <xnq,xq,xvq>{unicode}+\\  {
     185              :                                 /* throw back the \\, and treat as unicode */
     186            3 :                                 yyless(yyleng - 1);
     187            3 :                                 if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
     188            0 :                                     yyterminate();
     189              :                             }
     190            3 : 
     191           63 : <xnq,xq,xvq>\\.               { addchar(false, yytext[1], yyscanner); }
     192           63 : 
     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          901 : <xq>\"                           {
     206          901 :                                     yylval->str = yyextra->scanstring;
     207          901 :                                     BEGIN INITIAL;
     208          901 :                                     return STRING_P;
     209              :                                 }
     210              : 
     211           51 : <xvq>\"                          {
     212           51 :                                     yylval->str = yyextra->scanstring;
     213           51 :                                     BEGIN INITIAL;
     214           51 :                                     return VARIABLE_P;
     215              :                                 }
     216              : 
     217          988 : <xq,xvq>[^\\\"]+             { addstring(false, yytext, yyleng, yyscanner); }
     218          988 : 
     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           93 : \&\&                            { return AND_P; }
     231           93 : 
     232           54 : \|\|                            { return OR_P; }
     233              : 
     234           12 : \!                              { return NOT_P; }
     235              : 
     236          177 : \*\*                            { return ANY_P; }
     237              : 
     238          378 : \<                               { return LESS_P; }
     239              : 
     240           21 : \<\=                         { return LESSEQUAL_P; }
     241              : 
     242          501 : \=\=                            { return EQUAL_P; }
     243              : 
     244            0 : \<\>                          { return NOTEQUAL_P; }
     245              : 
     246            6 : \!\=                            { return NOTEQUAL_P; }
     247              : 
     248          168 : \>\=                         { return GREATEREQUAL_P; }
     249              : 
     250          222 : \>                               { return GREATER_P; }
     251              : 
     252          306 : \${other}+                      {
     253          306 :                                     addstring(true, yytext + 1, yyleng - 1, yyscanner);
     254          306 :                                     addchar(false, '\0', yyscanner);
     255          306 :                                     yylval->str = yyextra->scanstring;
     256          306 :                                     return VARIABLE_P;
     257              :                                 }
     258              : 
     259           51 : \$\"                           {
     260           51 :                                     addchar(true, '\0', yyscanner);
     261           51 :                                     BEGIN xvq;
     262              :                                 }
     263           51 : 
     264        23735 : {special}                       { return *yytext; }
     265              : 
     266         5103 : {blank}+                        { /* ignore */ }
     267         5103 : 
     268            0 : \/\*                            {
     269            0 :                                     addchar(true, '\0', yyscanner);
     270            0 :                                     BEGIN xc;
     271              :                                 }
     272            0 : 
     273          150 : {real}                          {
     274          150 :                                     addstring(true, yytext, yyleng, yyscanner);
     275          150 :                                     addchar(false, '\0', yyscanner);
     276          150 :                                     yylval->str = yyextra->scanstring;
     277          150 :                                     return NUMERIC_P;
     278              :                                 }
     279              : 
     280          129 : {decimal}                       {
     281          129 :                                     addstring(true, yytext, yyleng, yyscanner);
     282          129 :                                     addchar(false, '\0', yyscanner);
     283          129 :                                     yylval->str = yyextra->scanstring;
     284          129 :                                     return NUMERIC_P;
     285              :                                 }
     286              : 
     287         1140 : {decinteger}                    {
     288         1140 :                                     addstring(true, yytext, yyleng, yyscanner);
     289         1140 :                                     addchar(false, '\0', yyscanner);
     290         1140 :                                     yylval->str = yyextra->scanstring;
     291         1140 :                                     return INT_P;
     292              :                                 }
     293              : 
     294            6 : {hexinteger}                    {
     295            6 :                                     addstring(true, yytext, yyleng, yyscanner);
     296            6 :                                     addchar(false, '\0', yyscanner);
     297            6 :                                     yylval->str = yyextra->scanstring;
     298            6 :                                     return INT_P;
     299              :                                 }
     300              : 
     301            6 : {octinteger}                    {
     302            6 :                                     addstring(true, yytext, yyleng, yyscanner);
     303            6 :                                     addchar(false, '\0', yyscanner);
     304            6 :                                     yylval->str = yyextra->scanstring;
     305            6 :                                     return INT_P;
     306              :                                 }
     307              : 
     308            6 : {bininteger}                    {
     309            6 :                                     addstring(true, yytext, yyleng, yyscanner);
     310            6 :                                     addchar(false, '\0', yyscanner);
     311            6 :                                     yylval->str = yyextra->scanstring;
     312            6 :                                     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           45 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     322              :                                                      "trailing junk after numeric literal");
     323           12 :                                     yyterminate();
     324              :                                 }
     325              : {decimal_junk}                  {
     326           21 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     327              :                                                      "trailing junk after numeric literal");
     328            0 :                                     yyterminate();
     329              :                                 }
     330              : {real_junk}                     {
     331            3 :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     332              :                                                      "trailing junk after numeric literal");
     333            0 :                                     yyterminate();
     334              :                                 }
     335              : \"                             {
     336          955 :                                     addchar(true, '\0', yyscanner);
     337          955 :                                     BEGIN xq;
     338              :                                 }
     339          955 : 
     340            0 : \\                              {
     341            0 :                                     yyless(0);
     342            0 :                                     addchar(true, '\0', yyscanner);
     343            0 :                                     BEGIN xnq;
     344              :                                 }
     345            0 : 
     346         5529 : {other}+                        {
     347         5529 :                                     addstring(true, yytext, yyleng, yyscanner);
     348         5529 :                                     BEGIN xnq;
     349              :                                 }
     350         5529 : 
     351         5179 : <<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          162 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
     363              :                  yyscan_t yyscanner,
     364              :                  const char *message)
     365              : {
     366          162 :     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          162 :     if (SOFT_ERROR_OCCURRED(escontext))
     371            9 :         return;
     372              : 
     373          153 :     if (*yytext == YY_END_OF_BUFFER_CHAR)
     374              :     {
     375           30 :         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          123 :         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, true, FALSE_P, "false"},
     417              :     {5, false, FLOOR_P, "floor"},
     418              :     {6, false, BIGINT_P, "bigint"},
     419              :     {6, false, DOUBLE_P, "double"},
     420              :     {6, false, EXISTS_P, "exists"},
     421              :     {6, false, NUMBER_P, "number"},
     422              :     {6, false, STARTS_P, "starts"},
     423              :     {6, false, STRICT_P, "strict"},
     424              :     {6, false, STRINGFUNC_P, "string"},
     425              :     {7, false, BOOLEAN_P, "boolean"},
     426              :     {7, false, CEILING_P, "ceiling"},
     427              :     {7, false, DECIMAL_P, "decimal"},
     428              :     {7, false, INTEGER_P, "integer"},
     429              :     {7, false, TIME_TZ_P, "time_tz"},
     430              :     {7, false, UNKNOWN_P, "unknown"},
     431              :     {8, false, DATETIME_P, "datetime"},
     432              :     {8, false, KEYVALUE_P, "keyvalue"},
     433              :     {9, false, TIMESTAMP_P, "timestamp"},
     434              :     {10, false, LIKE_REGEX_P, "like_regex"},
     435              :     {12, false, TIMESTAMP_TZ_P, "timestamp_tz"},
     436              : };
     437              : 
     438              : /*
     439              :  * Check if current scanstring value is a keyword
     440              :  */
     441              : static enum yytokentype
     442         5529 : checkKeyword(yyscan_t yyscanner)
     443              : {
     444         5529 :     int         res = IDENT_P;
     445              :     int         diff;
     446         5529 :     const JsonPathKeyword *StopLow = keywords,
     447         5529 :                *StopHigh = keywords + lengthof(keywords),
     448              :                *StopMiddle;
     449              : 
     450         5529 :     if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
     451            3 :         return res;
     452              : 
     453        29736 :     while (StopLow < StopHigh)
     454              :     {
     455        27825 :         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
     456              : 
     457        27825 :         if (StopMiddle->len == yyextra->scanstring.len)
     458        10245 :             diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
     459        10245 :                                   yyextra->scanstring.len);
     460              :         else
     461        17580 :             diff = StopMiddle->len - yyextra->scanstring.len;
     462              : 
     463        27825 :         if (diff < 0)
     464         7557 :             StopLow = StopMiddle + 1;
     465        20268 :         else if (diff > 0)
     466        16653 :             StopHigh = StopMiddle;
     467              :         else
     468              :         {
     469         3615 :             if (StopMiddle->lowercase)
     470          147 :                 diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
     471          147 :                                yyextra->scanstring.len);
     472              : 
     473         3615 :             if (diff == 0)
     474         3615 :                 res = StopMiddle->val;
     475              : 
     476         3615 :             break;
     477              :         }
     478              :     }
     479              : 
     480         5526 :     return res;
     481              : }
     482              : 
     483              : /*
     484              :  * Resize scanstring so that it can append string of given length.
     485              :  * Reinitialize if required.
     486              :  */
     487              : static void
     488        11156 : resizeString(bool init, int appendLen, yyscan_t yyscanner)
     489              : {
     490        11156 :     if (init)
     491              :     {
     492         8278 :         yyextra->scanstring.total = Max(32, appendLen);
     493         8278 :         yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
     494         8278 :         yyextra->scanstring.len = 0;
     495              :     }
     496              :     else
     497              :     {
     498         2878 :         if (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
     499              :         {
     500            0 :             while (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
     501            0 :                 yyextra->scanstring.total *= 2;
     502            0 :             yyextra->scanstring.val = repalloc(yyextra->scanstring.val, yyextra->scanstring.total);
     503              :         }
     504              :     }
     505        11156 : }
     506              : 
     507              : /* Add set of bytes at "s" of length "l" to scanstring */
     508              : static void
     509         8323 : addstring(bool init, char *s, int l, yyscan_t yyscanner)
     510              : {
     511         8323 :     resizeString(init, l + 1, yyscanner);
     512         8323 :     memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
     513         8323 :     yyextra->scanstring.len += l;
     514         8323 : }
     515              : 
     516              : /* Add single byte "c" to scanstring */
     517              : static void
     518         2833 : addchar(bool init, char c, yyscan_t yyscanner)
     519              : {
     520         2833 :     resizeString(init, 1, yyscanner);
     521         2833 :     yyextra->scanstring.val[yyextra->scanstring.len] = c;
     522         2833 :     if (c != '\0')
     523           84 :         yyextra->scanstring.len++;
     524         2833 : }
     525              : 
     526              : /* Interface to jsonpath parser */
     527              : JsonPathParseResult *
     528         5383 : parsejsonpath(const char *str, int len, struct Node *escontext)
     529              : {
     530              :     JsonPathParseResult *parseresult;
     531              :     yyscan_t    scanner;
     532              :     struct jsonpath_yy_extra_type yyext;
     533              : 
     534         5383 :     if (jsonpath_yylex_init(&scanner) != 0)
     535            0 :         elog(ERROR, "yylex_init() failed: %m");
     536              : 
     537         5383 :     yyset_extra(&yyext, scanner);
     538              : 
     539         5383 :     if (len <= 0)
     540            3 :         len = strlen(str);
     541              : 
     542         5383 :     jsonpath_yy_scan_bytes(str, len, scanner);
     543              : 
     544         5383 :     if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
     545            9 :         jsonpath_yyerror(NULL, escontext, scanner, "invalid input");  /* shouldn't happen */
     546              : 
     547         5200 :     jsonpath_yylex_destroy(scanner);
     548              : 
     549         5200 :     return parseresult;
     550              : }
     551              : 
     552              : /* Turn hex character into integer */
     553              : static bool
     554          438 : hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
     555              : {
     556          438 :     if (c >= '0' && c <= '9')
     557              :     {
     558          294 :         *result = c - '0';
     559          294 :         return true;
     560              :     }
     561          144 :     if (c >= 'a' && c <= 'f')
     562              :     {
     563          126 :         *result = c - 'a' + 0xA;
     564          126 :         return true;
     565              :     }
     566           18 :     if (c >= 'A' && c <= 'F')
     567              :     {
     568           18 :         *result = c - 'A' + 0xA;
     569           18 :         return true;
     570              :     }
     571            0 :     jsonpath_yyerror(NULL, escontext, yyscanner, "invalid hexadecimal digit");
     572            0 :     return false;
     573              : }
     574              : 
     575              : /* Add given unicode character to scanstring */
     576              : static bool
     577           72 : addUnicodeChar(char32_t ch, struct Node *escontext, yyscan_t yyscanner)
     578              : {
     579           72 :     if (ch == 0)
     580              :     {
     581              :         /* We can't allow this, since our TEXT type doesn't */
     582           12 :         ereturn(escontext, false,
     583              :                 (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
     584              :                  errmsg("unsupported Unicode escape sequence"),
     585              :                  errdetail("\\u0000 cannot be converted to text.")));
     586              :     }
     587              :     else
     588              :     {
     589              :         char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
     590              : 
     591              :         /*
     592              :          * If we're trapping the error status, call the noerror form of the
     593              :          * conversion function. Otherwise call the normal form which provides
     594              :          * more detailed errors.
     595              :          */
     596              : 
     597           60 :         if (!escontext || !IsA(escontext, ErrorSaveContext))
     598           60 :             pg_unicode_to_server(ch, (unsigned char *) cbuf);
     599            0 :         else if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf))
     600            0 :             ereturn(escontext, false,
     601              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     602              :                      errmsg("could not convert Unicode to server encoding")));
     603           60 :         addstring(false, cbuf, strlen(cbuf), yyscanner);
     604              :     }
     605           60 :     return true;
     606              : }
     607              : 
     608              : /* Add unicode character, processing any surrogate pairs */
     609              : static bool
     610          108 : addUnicode(char32_t ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
     611              : {
     612          108 :     if (is_utf16_surrogate_first(ch))
     613              :     {
     614           30 :         if (*hi_surrogate != -1)
     615            6 :             ereturn(escontext, false,
     616              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     617              :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     618              :                      errdetail("Unicode high surrogate must not follow "
     619              :                                "a high surrogate.")));
     620           24 :         *hi_surrogate = ch;
     621           24 :         return true;
     622              :     }
     623           78 :     else if (is_utf16_surrogate_second(ch))
     624              :     {
     625           24 :         if (*hi_surrogate == -1)
     626           12 :             ereturn(escontext, false,
     627              :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     628              :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     629              :                      errdetail("Unicode low surrogate must follow a high "
     630              :                                "surrogate.")));
     631           12 :         ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
     632           12 :         *hi_surrogate = -1;
     633              :     }
     634           54 :     else if (*hi_surrogate != -1)
     635              :     {
     636            0 :         ereturn(escontext, false,
     637              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     638              :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     639              :                  errdetail("Unicode low surrogate must follow a high "
     640              :                            "surrogate.")));
     641              :     }
     642              : 
     643           66 :     return addUnicodeChar(ch, escontext, yyscanner);
     644              : }
     645              : 
     646              : /*
     647              :  * parseUnicode was adopted from json_lex_string() in
     648              :  * src/backend/utils/adt/json.c
     649              :  */
     650              : static bool
     651           66 : parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
     652              : {
     653           66 :     int         i = 2;
     654           66 :     int         hi_surrogate = -1;
     655              : 
     656          144 :     for (i = 2; i < l; i += 2)   /* skip '\u' */
     657              :     {
     658          108 :         char32_t        ch = 0;
     659              :         int         j,
     660              :                     si;
     661              : 
     662          108 :         if (s[i] == '{')        /* parse '\u{XX...}' */
     663              :         {
     664           84 :             while (s[++i] != '}' && i < l)
     665              :             {
     666           66 :                 if (!hexval(s[i], &si, escontext, yyscanner))
     667            0 :                     return false;
     668           66 :                 ch = (ch << 4) | si;
     669              :             }
     670           18 :             i++;                /* skip '}' */
     671              :         }
     672              :         else                    /* parse '\uXXXX' */
     673              :         {
     674          450 :             for (j = 0; j < 4 && i < l; j++)
     675              :             {
     676          360 :                 if (!hexval(s[i++], &si, escontext, yyscanner))
     677            0 :                     return false;
     678          360 :                 ch = (ch << 4) | si;
     679              :             }
     680              :         }
     681              : 
     682          108 :         if (!addUnicode(ch, &hi_surrogate, escontext, yyscanner))
     683            0 :             return false;
     684              :     }
     685              : 
     686           36 :     if (hi_surrogate != -1)
     687              :     {
     688            6 :         ereturn(escontext, false,
     689              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     690              :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     691              :                  errdetail("Unicode low surrogate must follow a high "
     692              :                            "surrogate.")));
     693              :     }
     694              : 
     695           30 :     return true;
     696              : }
     697              : 
     698              : /* Parse sequence of hex-encoded characters */
     699              : static bool
     700            6 : parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
     701              : {
     702              :     int         s2,
     703              :                 s3,
     704              :                 ch;
     705              : 
     706            6 :     if (!hexval(s[2], &s2, escontext, yyscanner))
     707            0 :         return false;
     708            6 :     if (!hexval(s[3], &s3, escontext, yyscanner))
     709            0 :         return false;
     710              : 
     711            6 :     ch = (s2 << 4) | s3;
     712              : 
     713            6 :     return addUnicodeChar(ch, escontext, yyscanner);
     714              : }
     715              : 
     716              : /*
     717              :  * Interface functions to make flex use palloc() instead of malloc().
     718              :  * It'd be better to make these static, but flex insists otherwise.
     719              :  */
     720              : 
     721              : void *
     722        21532 : jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
     723              : {
     724        21532 :     return palloc(bytes);
     725              : }
     726              : 
     727              : void *
     728            0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner)
     729              : {
     730            0 :     if (ptr)
     731            0 :         return repalloc(ptr, bytes);
     732              :     else
     733            0 :         return palloc(bytes);
     734              : }
     735              : 
     736              : void
     737        26000 : jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
     738              : {
     739        26000 :     if (ptr)
     740        20800 :         pfree(ptr);
     741        26000 : }
        

Generated by: LCOV version 2.0-1