LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_scan.l (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 116 136 85.3 %
Date: 2025-01-18 04:15:08 Functions: 13 15 86.7 %
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-2025, 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             :                                     addstring(false, yytext, yyleng, yyscanner);
     124             :                                 }
     125             : 
     126             : <xnq>{blank}+                 {
     127             :                                     yylval->str = yyextra->scanstring;
     128             :                                     BEGIN INITIAL;
     129             :                                     return checkKeyword(yyscanner);
     130             :                                 }
     131             : 
     132             : <xnq>\/\*                     {
     133             :                                     yylval->str = yyextra->scanstring;
     134             :                                     BEGIN xc;
     135             :                                 }
     136             : 
     137             : <xnq>({special}|\")              {
     138             :                                     yylval->str = yyextra->scanstring;
     139             :                                     yyless(0);
     140             :                                     BEGIN INITIAL;
     141             :                                     return checkKeyword(yyscanner);
     142             :                                 }
     143             : 
     144             : <xnq><<EOF>>                  {
     145             :                                     yylval->str = yyextra->scanstring;
     146             :                                     BEGIN INITIAL;
     147             :                                     return checkKeyword(yyscanner);
     148             :                                 }
     149             : 
     150             : <xnq,xq,xvq>\\b               { addchar(false, '\b', yyscanner); }
     151             : 
     152             : <xnq,xq,xvq>\\f               { addchar(false, '\f', yyscanner); }
     153             : 
     154             : <xnq,xq,xvq>\\n               { addchar(false, '\n', yyscanner); }
     155             : 
     156             : <xnq,xq,xvq>\\r               { addchar(false, '\r', yyscanner); }
     157             : 
     158             : <xnq,xq,xvq>\\t               { addchar(false, '\t', yyscanner); }
     159             : 
     160             : <xnq,xq,xvq>\\v               { addchar(false, '\v', yyscanner); }
     161             : 
     162             : <xnq,xq,xvq>{unicode}+        {
     163             :                                 if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
     164             :                                     yyterminate();
     165             :                             }
     166             : 
     167             : <xnq,xq,xvq>{hex_char}        {
     168             :                                 if (!parseHexChar(yytext, escontext, yyscanner))
     169             :                                     yyterminate();
     170             :                             }
     171             : 
     172             : <xnq,xq,xvq>{unicode}*{unicodefail} {
     173             :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     174             :                                                  "invalid Unicode escape sequence");
     175             :                                 yyterminate();
     176             :                             }
     177             : 
     178             : <xnq,xq,xvq>{hex_fail}        {
     179             :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     180             :                                                  "invalid hexadecimal character sequence");
     181             :                                 yyterminate();
     182             :                             }
     183             : 
     184             : <xnq,xq,xvq>{unicode}+\\  {
     185             :                                 /* throw back the \\, and treat as unicode */
     186             :                                 yyless(yyleng - 1);
     187             :                                 if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
     188             :                                     yyterminate();
     189             :                             }
     190             : 
     191             : <xnq,xq,xvq>\\.               { addchar(false, yytext[1], yyscanner); }
     192             : 
     193             : <xnq,xq,xvq>\\                {
     194             :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     195             :                                                  "unexpected end after backslash");
     196             :                                 yyterminate();
     197             :                             }
     198             : 
     199             : <xq,xvq><<EOF>>               {
     200             :                                 jsonpath_yyerror(NULL, escontext, yyscanner,
     201             :                                                  "unterminated quoted string");
     202             :                                 yyterminate();
     203             :                             }
     204             : 
     205             : <xq>\"                           {
     206             :                                     yylval->str = yyextra->scanstring;
     207             :                                     BEGIN INITIAL;
     208             :                                     return STRING_P;
     209             :                                 }
     210             : 
     211             : <xvq>\"                          {
     212             :                                     yylval->str = yyextra->scanstring;
     213             :                                     BEGIN INITIAL;
     214             :                                     return VARIABLE_P;
     215             :                                 }
     216             : 
     217             : <xq,xvq>[^\\\"]+             { addstring(false, yytext, yyleng, yyscanner); }
     218             : 
     219             : <xc>\*\/                      { BEGIN INITIAL; }
     220             : 
     221             : <xc>[^\*]+                        { }
     222             : 
     223             : <xc>\*                            { }
     224             : 
     225             : <xc><<EOF>>                       {
     226             :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     227             :                                                      "unexpected end of comment");
     228             :                                     yyterminate();
     229             :                                 }
     230             : \&\&                            { return AND_P; }
     231             : 
     232             : \|\|                            { return OR_P; }
     233             : 
     234             : \!                              { return NOT_P; }
     235             : 
     236             : \*\*                            { return ANY_P; }
     237             : 
     238             : \<                               { return LESS_P; }
     239             : 
     240             : \<\=                         { return LESSEQUAL_P; }
     241             : 
     242             : \=\=                            { return EQUAL_P; }
     243             : 
     244             : \<\>                          { return NOTEQUAL_P; }
     245             : 
     246             : \!\=                            { return NOTEQUAL_P; }
     247             : 
     248             : \>\=                         { return GREATEREQUAL_P; }
     249             : 
     250             : \>                               { return GREATER_P; }
     251             : 
     252             : \${other}+                      {
     253             :                                     addstring(true, yytext + 1, yyleng - 1, yyscanner);
     254             :                                     addchar(false, '\0', yyscanner);
     255             :                                     yylval->str = yyextra->scanstring;
     256             :                                     return VARIABLE_P;
     257             :                                 }
     258             : 
     259             : \$\"                           {
     260             :                                     addchar(true, '\0', yyscanner);
     261             :                                     BEGIN xvq;
     262             :                                 }
     263             : 
     264             : {special}                       { return *yytext; }
     265             : 
     266             : {blank}+                        { /* ignore */ }
     267             : 
     268             : \/\*                            {
     269             :                                     addchar(true, '\0', yyscanner);
     270             :                                     BEGIN xc;
     271             :                                 }
     272             : 
     273             : {real}                          {
     274             :                                     addstring(true, yytext, yyleng, yyscanner);
     275             :                                     addchar(false, '\0', yyscanner);
     276             :                                     yylval->str = yyextra->scanstring;
     277             :                                     return NUMERIC_P;
     278             :                                 }
     279             : 
     280             : {decimal}                       {
     281             :                                     addstring(true, yytext, yyleng, yyscanner);
     282             :                                     addchar(false, '\0', yyscanner);
     283             :                                     yylval->str = yyextra->scanstring;
     284             :                                     return NUMERIC_P;
     285             :                                 }
     286             : 
     287             : {decinteger}                    {
     288             :                                     addstring(true, yytext, yyleng, yyscanner);
     289             :                                     addchar(false, '\0', yyscanner);
     290             :                                     yylval->str = yyextra->scanstring;
     291             :                                     return INT_P;
     292             :                                 }
     293             : 
     294             : {hexinteger}                    {
     295             :                                     addstring(true, yytext, yyleng, yyscanner);
     296             :                                     addchar(false, '\0', yyscanner);
     297             :                                     yylval->str = yyextra->scanstring;
     298             :                                     return INT_P;
     299             :                                 }
     300             : 
     301             : {octinteger}                    {
     302             :                                     addstring(true, yytext, yyleng, yyscanner);
     303             :                                     addchar(false, '\0', yyscanner);
     304             :                                     yylval->str = yyextra->scanstring;
     305             :                                     return INT_P;
     306             :                                 }
     307             : 
     308             : {bininteger}                    {
     309             :                                     addstring(true, yytext, yyleng, yyscanner);
     310             :                                     addchar(false, '\0', yyscanner);
     311             :                                     yylval->str = yyextra->scanstring;
     312             :                                     return INT_P;
     313             :                                 }
     314             : 
     315             : {realfail}                      {
     316             :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     317             :                                                      "invalid numeric literal");
     318             :                                     yyterminate();
     319             :                                 }
     320             : {decinteger_junk}               {
     321             :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     322             :                                                      "trailing junk after numeric literal");
     323             :                                     yyterminate();
     324             :                                 }
     325             : {decimal_junk}                  {
     326             :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     327             :                                                      "trailing junk after numeric literal");
     328             :                                     yyterminate();
     329             :                                 }
     330             : {real_junk}                     {
     331             :                                     jsonpath_yyerror(NULL, escontext, yyscanner,
     332             :                                                      "trailing junk after numeric literal");
     333             :                                     yyterminate();
     334             :                                 }
     335             : \"                             {
     336             :                                     addchar(true, '\0', yyscanner);
     337             :                                     BEGIN xq;
     338             :                                 }
     339             : 
     340             : \\                              {
     341             :                                     yyless(0);
     342             :                                     addchar(true, '\0', yyscanner);
     343             :                                     BEGIN xnq;
     344             :                                 }
     345             : 
     346             : {other}+                        {
     347             :                                     addstring(true, yytext, yyleng, yyscanner);
     348             :                                     BEGIN xnq;
     349             :                                 }
     350             : 
     351             : <<EOF>>                         { yyterminate(); }
     352             : 
     353             : %%
     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         324 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
     363             :                  yyscan_t yyscanner,
     364             :                  const char *message)
     365             : {
     366         324 :     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         324 :     if (SOFT_ERROR_OCCURRED(escontext))
     371          18 :         return;
     372             : 
     373         306 :     if (*yytext == YY_END_OF_BUFFER_CHAR)
     374             :     {
     375          60 :         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         246 :         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       11010 : checkKeyword(yyscan_t yyscanner)
     443             : {
     444       11010 :     int         res = IDENT_P;
     445             :     int         diff;
     446       11010 :     const JsonPathKeyword *StopLow = keywords,
     447       11010 :                *StopHigh = keywords + lengthof(keywords),
     448             :                *StopMiddle;
     449             : 
     450       11010 :     if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
     451           6 :         return res;
     452             : 
     453       59136 :     while (StopLow < StopHigh)
     454             :     {
     455       55362 :         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
     456             : 
     457       55362 :         if (StopMiddle->len == yyextra->scanstring.len)
     458       20490 :             diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
     459       20490 :                                   yyextra->scanstring.len);
     460             :         else
     461       34872 :             diff = StopMiddle->len - yyextra->scanstring.len;
     462             : 
     463       55362 :         if (diff < 0)
     464       15114 :             StopLow = StopMiddle + 1;
     465       40248 :         else if (diff > 0)
     466       33018 :             StopHigh = StopMiddle;
     467             :         else
     468             :         {
     469        7230 :             if (StopMiddle->lowercase)
     470         294 :                 diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
     471         294 :                                yyextra->scanstring.len);
     472             : 
     473        7230 :             if (diff == 0)
     474        7230 :                 res = StopMiddle->val;
     475             : 
     476        7230 :             break;
     477             :         }
     478             :     }
     479             : 
     480       11004 :     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       22264 : resizeString(bool init, int appendLen, yyscan_t yyscanner)
     489             : {
     490       22264 :     if (init)
     491             :     {
     492       16508 :         yyextra->scanstring.total = Max(32, appendLen);
     493       16508 :         yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
     494       16508 :         yyextra->scanstring.len = 0;
     495             :     }
     496             :     else
     497             :     {
     498        5756 :         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       22264 : }
     506             : 
     507             : /* Add set of bytes at "s" of length "l" to scanstring */
     508             : static void
     509       16598 : addstring(bool init, char *s, int l, yyscan_t yyscanner)
     510             : {
     511       16598 :     resizeString(init, l + 1, yyscanner);
     512       16598 :     memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
     513       16598 :     yyextra->scanstring.len += l;
     514       16598 : }
     515             : 
     516             : /* Add single byte "c" to scanstring */
     517             : static void
     518        5666 : addchar(bool init, char c, yyscan_t yyscanner)
     519             : {
     520        5666 :     resizeString(init, 1, yyscanner);
     521        5666 :     yyextra->scanstring.val[yyextra->scanstring.len] = c;
     522        5666 :     if (c != '\0')
     523         168 :         yyextra->scanstring.len++;
     524        5666 : }
     525             : 
     526             : /* Interface to jsonpath parser */
     527             : JsonPathParseResult *
     528       10718 : 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       10718 :     if (jsonpath_yylex_init(&scanner) != 0)
     535           0 :         elog(ERROR, "yylex_init() failed: %m");
     536             : 
     537       10718 :     yyset_extra(&yyext, scanner);
     538             : 
     539       10718 :     if (len <= 0)
     540           6 :         len = strlen(str);
     541             : 
     542       10718 :     jsonpath_yy_scan_bytes(str, len, scanner);
     543             : 
     544       10718 :     if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
     545          18 :         jsonpath_yyerror(NULL, escontext, scanner, "invalid input");  /* shouldn't happen */
     546             : 
     547       10352 :     jsonpath_yylex_destroy(scanner);
     548             : 
     549       10352 :     return parseresult;
     550             : }
     551             : 
     552             : /* Turn hex character into integer */
     553             : static bool
     554         876 : hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
     555             : {
     556         876 :     if (c >= '0' && c <= '9')
     557             :     {
     558         588 :         *result = c - '0';
     559         588 :         return true;
     560             :     }
     561         288 :     if (c >= 'a' && c <= 'f')
     562             :     {
     563         252 :         *result = c - 'a' + 0xA;
     564         252 :         return true;
     565             :     }
     566          36 :     if (c >= 'A' && c <= 'F')
     567             :     {
     568          36 :         *result = c - 'A' + 0xA;
     569          36 :         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         144 : addUnicodeChar(int ch, struct Node *escontext, yyscan_t yyscanner)
     578             : {
     579         144 :     if (ch == 0)
     580             :     {
     581             :         /* We can't allow this, since our TEXT type doesn't */
     582          24 :         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         120 :         if (!escontext || !IsA(escontext, ErrorSaveContext))
     598         120 :             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         120 :         addstring(false, cbuf, strlen(cbuf), yyscanner);
     604             :     }
     605         120 :     return true;
     606             : }
     607             : 
     608             : /* Add unicode character, processing any surrogate pairs */
     609             : static bool
     610         216 : addUnicode(int ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
     611             : {
     612         216 :     if (is_utf16_surrogate_first(ch))
     613             :     {
     614          60 :         if (*hi_surrogate != -1)
     615          12 :             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          48 :         *hi_surrogate = ch;
     621          48 :         return true;
     622             :     }
     623         156 :     else if (is_utf16_surrogate_second(ch))
     624             :     {
     625          48 :         if (*hi_surrogate == -1)
     626          24 :             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          24 :         ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
     632          24 :         *hi_surrogate = -1;
     633             :     }
     634         108 :     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         132 :     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         132 : parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
     652             : {
     653         132 :     int         i = 2;
     654         132 :     int         hi_surrogate = -1;
     655             : 
     656         288 :     for (i = 2; i < l; i += 2)   /* skip '\u' */
     657             :     {
     658         216 :         int         ch = 0;
     659             :         int         j,
     660             :                     si;
     661             : 
     662         216 :         if (s[i] == '{')        /* parse '\u{XX...}' */
     663             :         {
     664         168 :             while (s[++i] != '}' && i < l)
     665             :             {
     666         132 :                 if (!hexval(s[i], &si, escontext, yyscanner))
     667           0 :                     return false;
     668         132 :                 ch = (ch << 4) | si;
     669             :             }
     670          36 :             i++;                /* skip '}' */
     671             :         }
     672             :         else                    /* parse '\uXXXX' */
     673             :         {
     674         900 :             for (j = 0; j < 4 && i < l; j++)
     675             :             {
     676         720 :                 if (!hexval(s[i++], &si, escontext, yyscanner))
     677           0 :                     return false;
     678         720 :                 ch = (ch << 4) | si;
     679             :             }
     680             :         }
     681             : 
     682         216 :         if (!addUnicode(ch, &hi_surrogate, escontext, yyscanner))
     683           0 :             return false;
     684             :     }
     685             : 
     686          72 :     if (hi_surrogate != -1)
     687             :     {
     688          12 :         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          60 :     return true;
     696             : }
     697             : 
     698             : /* Parse sequence of hex-encoded characters */
     699             : static bool
     700          12 : parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
     701             : {
     702             :     int         s2,
     703             :                 s3,
     704             :                 ch;
     705             : 
     706          12 :     if (!hexval(s[2], &s2, escontext, yyscanner))
     707           0 :         return false;
     708          12 :     if (!hexval(s[3], &s3, escontext, yyscanner))
     709           0 :         return false;
     710             : 
     711          12 :     ch = (s2 << 4) | s3;
     712             : 
     713          12 :     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       42872 : jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
     723             : {
     724       42872 :     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       51760 : jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
     738             : {
     739       51760 :     if (ptr)
     740       41408 :         pfree(ptr);
     741       51760 : }

Generated by: LCOV version 1.14