LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_scan.l (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 126 145 86.9 %
Date: 2023-12-01 18:10:44 Functions: 15 17 88.2 %
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-2023, 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             : static JsonPathString scanstring;
      34             : 
      35             : /* Handles to the buffer that the lexer uses internally */
      36             : static YY_BUFFER_STATE scanbufhandle;
      37             : static char *scanbuf;
      38             : static int  scanbuflen;
      39             : 
      40             : static void addstring(bool init, char *s, int l);
      41             : static void addchar(bool init, char c);
      42             : static enum yytokentype checkKeyword(void);
      43             : static bool parseUnicode(char *s, int l, struct Node *escontext);
      44             : static bool parseHexChar(char *s, struct Node *escontext);
      45             : 
      46             : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
      47             : #undef fprintf
      48             : #define fprintf(file, fmt, msg)  fprintf_to_ereport(fmt, msg)
      49             : 
      50             : static void
      51           0 : fprintf_to_ereport(const char *fmt, const char *msg)
      52             : {
      53           0 :     ereport(ERROR, (errmsg_internal("%s", msg)));
      54             : }
      55             : 
      56             : /* LCOV_EXCL_START */
      57             : 
      58             : %}
      59             : 
      60             : %option 8bit
      61             : %option never-interactive
      62             : %option nodefault
      63             : %option noinput
      64             : %option nounput
      65             : %option noyywrap
      66             : %option warn
      67             : %option prefix="jsonpath_yy"
      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);
     124             :                                 }
     125             : 
     126             : <xnq>{blank}+                 {
     127             :                                     yylval->str = scanstring;
     128             :                                     BEGIN INITIAL;
     129             :                                     return checkKeyword();
     130             :                                 }
     131             : 
     132             : <xnq>\/\*                     {
     133             :                                     yylval->str = scanstring;
     134             :                                     BEGIN xc;
     135             :                                 }
     136             : 
     137             : <xnq>({special}|\")              {
     138             :                                     yylval->str = scanstring;
     139             :                                     yyless(0);
     140             :                                     BEGIN INITIAL;
     141             :                                     return checkKeyword();
     142             :                                 }
     143             : 
     144             : <xnq><<EOF>>                  {
     145             :                                     yylval->str = scanstring;
     146             :                                     BEGIN INITIAL;
     147             :                                     return checkKeyword();
     148             :                                 }
     149             : 
     150             : <xnq,xq,xvq>\\b               { addchar(false, '\b'); }
     151             : 
     152             : <xnq,xq,xvq>\\f               { addchar(false, '\f'); }
     153             : 
     154             : <xnq,xq,xvq>\\n               { addchar(false, '\n'); }
     155             : 
     156             : <xnq,xq,xvq>\\r               { addchar(false, '\r'); }
     157             : 
     158             : <xnq,xq,xvq>\\t               { addchar(false, '\t'); }
     159             : 
     160             : <xnq,xq,xvq>\\v               { addchar(false, '\v'); }
     161             : 
     162             : <xnq,xq,xvq>{unicode}+        {
     163             :                                 if (!parseUnicode(yytext, yyleng, escontext))
     164             :                                     yyterminate();
     165             :                             }
     166             : 
     167             : <xnq,xq,xvq>{hex_char}        {
     168             :                                 if (!parseHexChar(yytext, escontext))
     169             :                                     yyterminate();
     170             :                             }
     171             : 
     172             : <xnq,xq,xvq>{unicode}*{unicodefail} {
     173             :                                 jsonpath_yyerror(NULL, escontext,
     174             :                                                  "invalid Unicode escape sequence");
     175             :                                 yyterminate();
     176             :                             }
     177             : 
     178             : <xnq,xq,xvq>{hex_fail}        {
     179             :                                 jsonpath_yyerror(NULL, escontext,
     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))
     188             :                                     yyterminate();
     189             :                             }
     190             : 
     191             : <xnq,xq,xvq>\\.               { addchar(false, yytext[1]); }
     192             : 
     193             : <xnq,xq,xvq>\\                {
     194             :                               jsonpath_yyerror(NULL, escontext,
     195             :                                                "unexpected end after backslash");
     196             :                               yyterminate();
     197             :                             }
     198             : 
     199             : <xq,xvq><<EOF>>               {
     200             :                               jsonpath_yyerror(NULL, escontext,
     201             :                                                "unterminated quoted string");
     202             :                               yyterminate();
     203             :                             }
     204             : 
     205             : <xq>\"                           {
     206             :                                     yylval->str = scanstring;
     207             :                                     BEGIN INITIAL;
     208             :                                     return STRING_P;
     209             :                                 }
     210             : 
     211             : <xvq>\"                          {
     212             :                                     yylval->str = scanstring;
     213             :                                     BEGIN INITIAL;
     214             :                                     return VARIABLE_P;
     215             :                                 }
     216             : 
     217             : <xq,xvq>[^\\\"]+             { addstring(false, yytext, yyleng); }
     218             : 
     219             : <xc>\*\/                      { BEGIN INITIAL; }
     220             : 
     221             : <xc>[^\*]+                        { }
     222             : 
     223             : <xc>\*                            { }
     224             : 
     225             : <xc><<EOF>>                       {
     226             :                                     jsonpath_yyerror(
     227             :                                         NULL, escontext,
     228             :                                         "unexpected end of comment");
     229             :                                     yyterminate();
     230             :                                 }
     231             : \&\&                            { return AND_P; }
     232             : 
     233             : \|\|                            { return OR_P; }
     234             : 
     235             : \!                              { return NOT_P; }
     236             : 
     237             : \*\*                            { return ANY_P; }
     238             : 
     239             : \<                               { return LESS_P; }
     240             : 
     241             : \<\=                         { return LESSEQUAL_P; }
     242             : 
     243             : \=\=                            { return EQUAL_P; }
     244             : 
     245             : \<\>                          { return NOTEQUAL_P; }
     246             : 
     247             : \!\=                            { return NOTEQUAL_P; }
     248             : 
     249             : \>\=                         { return GREATEREQUAL_P; }
     250             : 
     251             : \>                               { return GREATER_P; }
     252             : 
     253             : \${other}+                      {
     254             :                                     addstring(true, yytext + 1, yyleng - 1);
     255             :                                     addchar(false, '\0');
     256             :                                     yylval->str = scanstring;
     257             :                                     return VARIABLE_P;
     258             :                                 }
     259             : 
     260             : \$\"                           {
     261             :                                     addchar(true, '\0');
     262             :                                     BEGIN xvq;
     263             :                                 }
     264             : 
     265             : {special}                       { return *yytext; }
     266             : 
     267             : {blank}+                        { /* ignore */ }
     268             : 
     269             : \/\*                            {
     270             :                                     addchar(true, '\0');
     271             :                                     BEGIN xc;
     272             :                                 }
     273             : 
     274             : {real}                          {
     275             :                                     addstring(true, yytext, yyleng);
     276             :                                     addchar(false, '\0');
     277             :                                     yylval->str = scanstring;
     278             :                                     return NUMERIC_P;
     279             :                                 }
     280             : 
     281             : {decimal}                       {
     282             :                                     addstring(true, yytext, yyleng);
     283             :                                     addchar(false, '\0');
     284             :                                     yylval->str = scanstring;
     285             :                                     return NUMERIC_P;
     286             :                                 }
     287             : 
     288             : {decinteger}                    {
     289             :                                     addstring(true, yytext, yyleng);
     290             :                                     addchar(false, '\0');
     291             :                                     yylval->str = scanstring;
     292             :                                     return INT_P;
     293             :                                 }
     294             : 
     295             : {hexinteger}                    {
     296             :                                     addstring(true, yytext, yyleng);
     297             :                                     addchar(false, '\0');
     298             :                                     yylval->str = scanstring;
     299             :                                     return INT_P;
     300             :                                 }
     301             : 
     302             : {octinteger}                    {
     303             :                                     addstring(true, yytext, yyleng);
     304             :                                     addchar(false, '\0');
     305             :                                     yylval->str = scanstring;
     306             :                                     return INT_P;
     307             :                                 }
     308             : 
     309             : {bininteger}                    {
     310             :                                     addstring(true, yytext, yyleng);
     311             :                                     addchar(false, '\0');
     312             :                                     yylval->str = scanstring;
     313             :                                     return INT_P;
     314             :                                 }
     315             : 
     316             : {realfail}                      {
     317             :                                     jsonpath_yyerror(
     318             :                                         NULL, escontext,
     319             :                                         "invalid numeric literal");
     320             :                                     yyterminate();
     321             :                                 }
     322             : {decinteger_junk}               {
     323             :                                     jsonpath_yyerror(
     324             :                                         NULL, escontext,
     325             :                                         "trailing junk after numeric literal");
     326             :                                     yyterminate();
     327             :                                 }
     328             : {decimal_junk}                  {
     329             :                                     jsonpath_yyerror(
     330             :                                         NULL, escontext,
     331             :                                         "trailing junk after numeric literal");
     332             :                                     yyterminate();
     333             :                                 }
     334             : {real_junk}                     {
     335             :                                     jsonpath_yyerror(
     336             :                                         NULL, escontext,
     337             :                                         "trailing junk after numeric literal");
     338             :                                     yyterminate();
     339             :                                 }
     340             : \"                             {
     341             :                                     addchar(true, '\0');
     342             :                                     BEGIN xq;
     343             :                                 }
     344             : 
     345             : \\                              {
     346             :                                     yyless(0);
     347             :                                     addchar(true, '\0');
     348             :                                     BEGIN xnq;
     349             :                                 }
     350             : 
     351             : {other}+                        {
     352             :                                     addstring(true, yytext, yyleng);
     353             :                                     BEGIN xnq;
     354             :                                 }
     355             : 
     356             : <<EOF>>                         { yyterminate(); }
     357             : 
     358             : %%
     359             : 
     360             : /* LCOV_EXCL_STOP */
     361             : 
     362             : void
     363         246 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
     364             :                  const char *message)
     365             : {
     366             :     /* don't overwrite escontext if it's already been set */
     367         246 :     if (SOFT_ERROR_OCCURRED(escontext))
     368          12 :         return;
     369             : 
     370         234 :     if (*yytext == YY_END_OF_BUFFER_CHAR)
     371             :     {
     372          60 :         errsave(escontext,
     373             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     374             :                  /* translator: %s is typically "syntax error" */
     375             :                  errmsg("%s at end of jsonpath input", _(message))));
     376             :     }
     377             :     else
     378             :     {
     379         174 :         errsave(escontext,
     380             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     381             :                  /* translator: first %s is typically "syntax error" */
     382             :                  errmsg("%s at or near \"%s\" of jsonpath input",
     383             :                         _(message), yytext)));
     384             :     }
     385             : }
     386             : 
     387             : typedef struct JsonPathKeyword
     388             : {
     389             :     int16       len;
     390             :     bool        lowercase;
     391             :     int         val;
     392             :     const char *keyword;
     393             : } JsonPathKeyword;
     394             : 
     395             : /*
     396             :  * Array of key words should be sorted by length and then
     397             :  * alphabetical order
     398             :  */
     399             : static const JsonPathKeyword keywords[] = {
     400             :     { 2, false, IS_P,       "is"},
     401             :     { 2, false, TO_P,       "to"},
     402             :     { 3, false, ABS_P,      "abs"},
     403             :     { 3, false, LAX_P,      "lax"},
     404             :     { 4, false, FLAG_P,     "flag"},
     405             :     { 4, false, LAST_P,     "last"},
     406             :     { 4, true,  NULL_P,     "null"},
     407             :     { 4, false, SIZE_P,     "size"},
     408             :     { 4, true,  TRUE_P,     "true"},
     409             :     { 4, false, TYPE_P,     "type"},
     410             :     { 4, false, WITH_P,     "with"},
     411             :     { 5, true,  FALSE_P,    "false"},
     412             :     { 5, false, FLOOR_P,    "floor"},
     413             :     { 6, false, DOUBLE_P,   "double"},
     414             :     { 6, false, EXISTS_P,   "exists"},
     415             :     { 6, false, STARTS_P,   "starts"},
     416             :     { 6, false, STRICT_P,   "strict"},
     417             :     { 7, false, CEILING_P,  "ceiling"},
     418             :     { 7, false, UNKNOWN_P,  "unknown"},
     419             :     { 8, false, DATETIME_P, "datetime"},
     420             :     { 8, false, KEYVALUE_P, "keyvalue"},
     421             :     { 10,false, LIKE_REGEX_P, "like_regex"},
     422             : };
     423             : 
     424             : /* Check if current scanstring value is a keyword */
     425             : static enum yytokentype
     426        6174 : checkKeyword()
     427             : {
     428        6174 :     int         res = IDENT_P;
     429             :     int         diff;
     430        6174 :     const JsonPathKeyword  *StopLow = keywords,
     431        6174 :                            *StopHigh = keywords + lengthof(keywords),
     432             :                            *StopMiddle;
     433             : 
     434        6174 :     if (scanstring.len > keywords[lengthof(keywords) - 1].len)
     435           6 :         return res;
     436             : 
     437       29646 :     while (StopLow < StopHigh)
     438             :     {
     439       27048 :         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
     440             : 
     441       27048 :         if (StopMiddle->len == scanstring.len)
     442        8370 :             diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
     443        8370 :                                   scanstring.len);
     444             :         else
     445       18678 :             diff = StopMiddle->len - scanstring.len;
     446             : 
     447       27048 :         if (diff < 0)
     448        6174 :             StopLow = StopMiddle + 1;
     449       20874 :         else if (diff > 0)
     450       17304 :             StopHigh = StopMiddle;
     451             :         else
     452             :         {
     453        3570 :             if (StopMiddle->lowercase)
     454         294 :                 diff = strncmp(StopMiddle->keyword, scanstring.val,
     455         294 :                                scanstring.len);
     456             : 
     457        3570 :             if (diff == 0)
     458        3570 :                 res = StopMiddle->val;
     459             : 
     460        3570 :             break;
     461             :         }
     462             :     }
     463             : 
     464        6168 :     return res;
     465             : }
     466             : 
     467             : /*
     468             :  * Called before any actual parsing is done
     469             :  */
     470             : static void
     471        4578 : jsonpath_scanner_init(const char *str, int slen)
     472             : {
     473        4578 :     if (slen <= 0)
     474           6 :         slen = strlen(str);
     475             : 
     476             :     /*
     477             :      * Might be left over after ereport()
     478             :      */
     479        4578 :     yy_init_globals();
     480             : 
     481             :     /*
     482             :      * Make a scan buffer with special termination needed by flex.
     483             :      */
     484             : 
     485        4578 :     scanbuflen = slen;
     486        4578 :     scanbuf = palloc(slen + 2);
     487        4578 :     memcpy(scanbuf, str, slen);
     488        4578 :     scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
     489        4578 :     scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
     490             : 
     491        4578 :     BEGIN(INITIAL);
     492        4578 : }
     493             : 
     494             : 
     495             : /*
     496             :  * Called after parsing is done to clean up after jsonpath_scanner_init()
     497             :  */
     498             : static void
     499        4254 : jsonpath_scanner_finish(void)
     500             : {
     501        4254 :     yy_delete_buffer(scanbufhandle);
     502        4254 :     pfree(scanbuf);
     503        4254 : }
     504             : 
     505             : /*
     506             :  * Resize scanstring so that it can append string of given length.
     507             :  * Reinitialize if required.
     508             :  */
     509             : static void
     510       13952 : resizeString(bool init, int appendLen)
     511             : {
     512       13952 :     if (init)
     513             :     {
     514        9954 :         scanstring.total = Max(32, appendLen);
     515        9954 :         scanstring.val = (char *) palloc(scanstring.total);
     516        9954 :         scanstring.len = 0;
     517             :     }
     518             :     else
     519             :     {
     520        3998 :         if (scanstring.len + appendLen >= scanstring.total)
     521             :         {
     522           0 :             while (scanstring.len + appendLen >= scanstring.total)
     523           0 :                 scanstring.total *= 2;
     524           0 :             scanstring.val = repalloc(scanstring.val, scanstring.total);
     525             :         }
     526             :     }
     527       13952 : }
     528             : 
     529             : /* Add set of bytes at "s" of length "l" to scanstring */
     530             : static void
     531       10004 : addstring(bool init, char *s, int l)
     532             : {
     533       10004 :     resizeString(init, l + 1);
     534       10004 :     memcpy(scanstring.val + scanstring.len, s, l);
     535       10004 :     scanstring.len += l;
     536       10004 : }
     537             : 
     538             : /* Add single byte "c" to scanstring */
     539             : static void
     540        3948 : addchar(bool init, char c)
     541             : {
     542        3948 :     resizeString(init, 1);
     543        3948 :     scanstring.val[scanstring.len] = c;
     544        3948 :     if (c != '\0')
     545         168 :         scanstring.len++;
     546        3948 : }
     547             : 
     548             : /* Interface to jsonpath parser */
     549             : JsonPathParseResult *
     550        4578 : parsejsonpath(const char *str, int len, struct Node *escontext)
     551             : {
     552             :     JsonPathParseResult *parseresult;
     553             : 
     554        4578 :     jsonpath_scanner_init(str, len);
     555             : 
     556        4578 :     if (jsonpath_yyparse((void *) &parseresult, escontext) != 0)
     557          12 :         jsonpath_yyerror(NULL, escontext, "invalid input"); /* shouldn't happen */
     558             : 
     559        4254 :     jsonpath_scanner_finish();
     560             : 
     561        4254 :     return parseresult;
     562             : }
     563             : 
     564             : /* Turn hex character into integer */
     565             : static bool
     566         812 : hexval(char c, int *result, struct Node *escontext)
     567             : {
     568         812 :     if (c >= '0' && c <= '9')
     569             :     {
     570         556 :         *result = c - '0';
     571         556 :         return true;
     572             :     }
     573         256 :     if (c >= 'a' && c <= 'f')
     574             :     {
     575         220 :         *result = c - 'a' + 0xA;
     576         220 :         return true;
     577             :     }
     578          36 :     if (c >= 'A' && c <= 'F')
     579             :     {
     580          36 :         *result = c - 'A' + 0xA;
     581          36 :         return true;
     582             :     }
     583           0 :     jsonpath_yyerror(NULL, escontext, "invalid hexadecimal digit");
     584           0 :     return false;
     585             : }
     586             : 
     587             : /* Add given unicode character to scanstring */
     588             : static bool
     589         136 : addUnicodeChar(int ch, struct Node *escontext)
     590             : {
     591         136 :     if (ch == 0)
     592             :     {
     593             :         /* We can't allow this, since our TEXT type doesn't */
     594          24 :         ereturn(escontext, false,
     595             :                 (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
     596             :                  errmsg("unsupported Unicode escape sequence"),
     597             :                   errdetail("\\u0000 cannot be converted to text.")));
     598             :     }
     599             :     else
     600             :     {
     601             :         char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
     602             : 
     603             :         /*
     604             :          * If we're trapping the error status, call the noerror form of the
     605             :          * conversion function. Otherwise call the normal form which provides
     606             :          * more detailed errors.
     607             :          */
     608             : 
     609         112 :         if (! escontext  || ! IsA(escontext, ErrorSaveContext))
     610         112 :             pg_unicode_to_server(ch, (unsigned char *) cbuf);
     611           0 :         else if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf))
     612           0 :             ereturn(escontext, false,
     613             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     614             :                      errmsg("could not convert Unicode to server encoding")));
     615          88 :         addstring(false, cbuf, strlen(cbuf));
     616             :     }
     617          88 :     return true;
     618             : }
     619             : 
     620             : /* Add unicode character, processing any surrogate pairs */
     621             : static bool
     622         200 : addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
     623             : {
     624         200 :     if (is_utf16_surrogate_first(ch))
     625             :     {
     626          52 :         if (*hi_surrogate != -1)
     627          12 :             ereturn(escontext, false,
     628             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     629             :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     630             :                      errdetail("Unicode high surrogate must not follow "
     631             :                                "a high surrogate.")));
     632          40 :         *hi_surrogate = ch;
     633          40 :         return true;
     634             :     }
     635         148 :     else if (is_utf16_surrogate_second(ch))
     636             :     {
     637          40 :         if (*hi_surrogate == -1)
     638          24 :             ereturn(escontext, false,
     639             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     640             :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     641             :                      errdetail("Unicode low surrogate must follow a high "
     642             :                                "surrogate.")));
     643          16 :         ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
     644          16 :         *hi_surrogate = -1;
     645             :     }
     646         108 :     else if (*hi_surrogate != -1)
     647             :     {
     648           0 :         ereturn(escontext, false,
     649             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     650             :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     651             :                  errdetail("Unicode low surrogate must follow a high "
     652             :                            "surrogate.")));
     653             :     }
     654             : 
     655         124 :     return addUnicodeChar(ch, escontext);
     656             : }
     657             : 
     658             : /*
     659             :  * parseUnicode was adopted from json_lex_string() in
     660             :  * src/backend/utils/adt/json.c
     661             :  */
     662             : static bool
     663         132 : parseUnicode(char *s, int l, struct Node *escontext)
     664             : {
     665         132 :     int         i = 2;
     666         132 :     int         hi_surrogate = -1;
     667             : 
     668         248 :     for (i = 2; i < l; i += 2)   /* skip '\u' */
     669             :     {
     670         200 :         int         ch = 0;
     671             :         int         j, si;
     672             : 
     673         200 :         if (s[i] == '{')    /* parse '\u{XX...}' */
     674             :         {
     675         168 :             while (s[++i] != '}' && i < l)
     676             :             {
     677         132 :                 if (!hexval(s[i], &si, escontext))
     678           0 :                     return false;
     679         132 :                 ch = (ch << 4) | si;
     680             :             }
     681          36 :             i++;    /* skip '}' */
     682             :         }
     683             :         else        /* parse '\uXXXX' */
     684             :         {
     685         820 :             for (j = 0; j < 4 && i < l; j++)
     686             :             {
     687         656 :                 if (!hexval(s[i++], &si, escontext))
     688           0 :                     return false;
     689         656 :                 ch = (ch << 4) | si;
     690             :             }
     691             :         }
     692             : 
     693         200 :         if (! addUnicode(ch, &hi_surrogate, escontext))
     694           0 :             return false;
     695             :     }
     696             : 
     697          48 :     if (hi_surrogate != -1)
     698             :     {
     699          12 :         ereturn(escontext, false,
     700             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     701             :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     702             :                  errdetail("Unicode low surrogate must follow a high "
     703             :                            "surrogate.")));
     704             :     }
     705             : 
     706          36 :     return true;
     707             : }
     708             : 
     709             : /* Parse sequence of hex-encoded characters */
     710             : static bool
     711          12 : parseHexChar(char *s, struct Node *escontext)
     712             : {
     713             :     int s2, s3, ch;
     714          12 :     if (!hexval(s[2], &s2, escontext))
     715           0 :         return false;
     716          12 :     if (!hexval(s[3], &s3, escontext))
     717           0 :         return false;
     718             : 
     719          12 :     ch = (s2 << 4) | s3;
     720             : 
     721          12 :     return addUnicodeChar(ch, escontext);
     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        9156 : jsonpath_yyalloc(yy_size_t bytes)
     731             : {
     732        9156 :     return palloc(bytes);
     733             : }
     734             : 
     735             : void *
     736           0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
     737             : {
     738           0 :     if (ptr)
     739           0 :         return repalloc(ptr, bytes);
     740             :     else
     741           0 :         return palloc(bytes);
     742             : }
     743             : 
     744             : void
     745        4254 : jsonpath_yyfree(void *ptr)
     746             : {
     747        4254 :     if (ptr)
     748        4254 :         pfree(ptr);
     749        4254 : }

Generated by: LCOV version 1.14