LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_scan.l (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 126 145 86.9 %
Date: 2024-11-21 08:14: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-2024, 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         324 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
     364             :                  const char *message)
     365             : {
     366             :     /* don't overwrite escontext if it's already been set */
     367         324 :     if (SOFT_ERROR_OCCURRED(escontext))
     368          18 :         return;
     369             : 
     370         306 :     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         246 :         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, DATE_P,     "date"},
     405             :     { 4, false, FLAG_P,     "flag"},
     406             :     { 4, false, LAST_P,     "last"},
     407             :     { 4, true,  NULL_P,     "null"},
     408             :     { 4, false, SIZE_P,     "size"},
     409             :     { 4, false, TIME_P,     "time"},
     410             :     { 4, true,  TRUE_P,     "true"},
     411             :     { 4, false, TYPE_P,     "type"},
     412             :     { 4, false, WITH_P,     "with"},
     413             :     { 5, true,  FALSE_P,    "false"},
     414             :     { 5, false, FLOOR_P,    "floor"},
     415             :     { 6, false, BIGINT_P,   "bigint"},
     416             :     { 6, false, DOUBLE_P,   "double"},
     417             :     { 6, false, EXISTS_P,   "exists"},
     418             :     { 6, false, NUMBER_P,   "number"},
     419             :     { 6, false, STARTS_P,   "starts"},
     420             :     { 6, false, STRICT_P,   "strict"},
     421             :     { 6, false, STRINGFUNC_P, "string"},
     422             :     { 7, false, BOOLEAN_P,  "boolean"},
     423             :     { 7, false, CEILING_P,  "ceiling"},
     424             :     { 7, false, DECIMAL_P,  "decimal"},
     425             :     { 7, false, INTEGER_P,  "integer"},
     426             :     { 7, false, TIME_TZ_P,  "time_tz"},
     427             :     { 7, false, UNKNOWN_P,  "unknown"},
     428             :     { 8, false, DATETIME_P, "datetime"},
     429             :     { 8, false, KEYVALUE_P, "keyvalue"},
     430             :     { 9, false, TIMESTAMP_P, "timestamp"},
     431             :     { 10,false, LIKE_REGEX_P, "like_regex"},
     432             :     { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
     433             : };
     434             : 
     435             : /* Check if current scanstring value is a keyword */
     436             : static enum yytokentype
     437       11010 : checkKeyword()
     438             : {
     439       11010 :     int         res = IDENT_P;
     440             :     int         diff;
     441       11010 :     const JsonPathKeyword  *StopLow = keywords,
     442       11010 :                            *StopHigh = keywords + lengthof(keywords),
     443             :                            *StopMiddle;
     444             : 
     445       11010 :     if (scanstring.len > keywords[lengthof(keywords) - 1].len)
     446           6 :         return res;
     447             : 
     448       59136 :     while (StopLow < StopHigh)
     449             :     {
     450       55362 :         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
     451             : 
     452       55362 :         if (StopMiddle->len == scanstring.len)
     453       20490 :             diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
     454       20490 :                                   scanstring.len);
     455             :         else
     456       34872 :             diff = StopMiddle->len - scanstring.len;
     457             : 
     458       55362 :         if (diff < 0)
     459       15114 :             StopLow = StopMiddle + 1;
     460       40248 :         else if (diff > 0)
     461       33018 :             StopHigh = StopMiddle;
     462             :         else
     463             :         {
     464        7230 :             if (StopMiddle->lowercase)
     465         294 :                 diff = strncmp(StopMiddle->keyword, scanstring.val,
     466         294 :                                scanstring.len);
     467             : 
     468        7230 :             if (diff == 0)
     469        7230 :                 res = StopMiddle->val;
     470             : 
     471        7230 :             break;
     472             :         }
     473             :     }
     474             : 
     475       11004 :     return res;
     476             : }
     477             : 
     478             : /*
     479             :  * Called before any actual parsing is done
     480             :  */
     481             : static void
     482       10700 : jsonpath_scanner_init(const char *str, int slen)
     483             : {
     484       10700 :     if (slen <= 0)
     485           6 :         slen = strlen(str);
     486             : 
     487             :     /*
     488             :      * Might be left over after ereport()
     489             :      */
     490       10700 :     yy_init_globals();
     491             : 
     492             :     /*
     493             :      * Make a scan buffer with special termination needed by flex.
     494             :      */
     495             : 
     496       10700 :     scanbuflen = slen;
     497       10700 :     scanbuf = palloc(slen + 2);
     498       10700 :     memcpy(scanbuf, str, slen);
     499       10700 :     scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
     500       10700 :     scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
     501             : 
     502       10700 :     BEGIN(INITIAL);
     503       10700 : }
     504             : 
     505             : 
     506             : /*
     507             :  * Called after parsing is done to clean up after jsonpath_scanner_init()
     508             :  */
     509             : static void
     510       10334 : jsonpath_scanner_finish(void)
     511             : {
     512       10334 :     yy_delete_buffer(scanbufhandle);
     513       10334 :     pfree(scanbuf);
     514       10334 : }
     515             : 
     516             : /*
     517             :  * Resize scanstring so that it can append string of given length.
     518             :  * Reinitialize if required.
     519             :  */
     520             : static void
     521       22228 : resizeString(bool init, int appendLen)
     522             : {
     523       22228 :     if (init)
     524             :     {
     525       16490 :         scanstring.total = Max(32, appendLen);
     526       16490 :         scanstring.val = (char *) palloc(scanstring.total);
     527       16490 :         scanstring.len = 0;
     528             :     }
     529             :     else
     530             :     {
     531        5738 :         if (scanstring.len + appendLen >= scanstring.total)
     532             :         {
     533           0 :             while (scanstring.len + appendLen >= scanstring.total)
     534           0 :                 scanstring.total *= 2;
     535           0 :             scanstring.val = repalloc(scanstring.val, scanstring.total);
     536             :         }
     537             :     }
     538       22228 : }
     539             : 
     540             : /* Add set of bytes at "s" of length "l" to scanstring */
     541             : static void
     542       16580 : addstring(bool init, char *s, int l)
     543             : {
     544       16580 :     resizeString(init, l + 1);
     545       16580 :     memcpy(scanstring.val + scanstring.len, s, l);
     546       16580 :     scanstring.len += l;
     547       16580 : }
     548             : 
     549             : /* Add single byte "c" to scanstring */
     550             : static void
     551        5648 : addchar(bool init, char c)
     552             : {
     553        5648 :     resizeString(init, 1);
     554        5648 :     scanstring.val[scanstring.len] = c;
     555        5648 :     if (c != '\0')
     556         168 :         scanstring.len++;
     557        5648 : }
     558             : 
     559             : /* Interface to jsonpath parser */
     560             : JsonPathParseResult *
     561       10700 : parsejsonpath(const char *str, int len, struct Node *escontext)
     562             : {
     563             :     JsonPathParseResult *parseresult;
     564             : 
     565       10700 :     jsonpath_scanner_init(str, len);
     566             : 
     567       10700 :     if (jsonpath_yyparse((void *) &parseresult, escontext) != 0)
     568          18 :         jsonpath_yyerror(NULL, escontext, "invalid input"); /* shouldn't happen */
     569             : 
     570       10334 :     jsonpath_scanner_finish();
     571             : 
     572       10334 :     return parseresult;
     573             : }
     574             : 
     575             : /* Turn hex character into integer */
     576             : static bool
     577         876 : hexval(char c, int *result, struct Node *escontext)
     578             : {
     579         876 :     if (c >= '0' && c <= '9')
     580             :     {
     581         588 :         *result = c - '0';
     582         588 :         return true;
     583             :     }
     584         288 :     if (c >= 'a' && c <= 'f')
     585             :     {
     586         252 :         *result = c - 'a' + 0xA;
     587         252 :         return true;
     588             :     }
     589          36 :     if (c >= 'A' && c <= 'F')
     590             :     {
     591          36 :         *result = c - 'A' + 0xA;
     592          36 :         return true;
     593             :     }
     594           0 :     jsonpath_yyerror(NULL, escontext, "invalid hexadecimal digit");
     595           0 :     return false;
     596             : }
     597             : 
     598             : /* Add given unicode character to scanstring */
     599             : static bool
     600         144 : addUnicodeChar(int ch, struct Node *escontext)
     601             : {
     602         144 :     if (ch == 0)
     603             :     {
     604             :         /* We can't allow this, since our TEXT type doesn't */
     605          24 :         ereturn(escontext, false,
     606             :                 (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
     607             :                  errmsg("unsupported Unicode escape sequence"),
     608             :                   errdetail("\\u0000 cannot be converted to text.")));
     609             :     }
     610             :     else
     611             :     {
     612             :         char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
     613             : 
     614             :         /*
     615             :          * If we're trapping the error status, call the noerror form of the
     616             :          * conversion function. Otherwise call the normal form which provides
     617             :          * more detailed errors.
     618             :          */
     619             : 
     620         120 :         if (! escontext  || ! IsA(escontext, ErrorSaveContext))
     621         120 :             pg_unicode_to_server(ch, (unsigned char *) cbuf);
     622           0 :         else if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf))
     623           0 :             ereturn(escontext, false,
     624             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     625             :                      errmsg("could not convert Unicode to server encoding")));
     626         120 :         addstring(false, cbuf, strlen(cbuf));
     627             :     }
     628         120 :     return true;
     629             : }
     630             : 
     631             : /* Add unicode character, processing any surrogate pairs */
     632             : static bool
     633         216 : addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
     634             : {
     635         216 :     if (is_utf16_surrogate_first(ch))
     636             :     {
     637          60 :         if (*hi_surrogate != -1)
     638          12 :             ereturn(escontext, false,
     639             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     640             :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     641             :                      errdetail("Unicode high surrogate must not follow "
     642             :                                "a high surrogate.")));
     643          48 :         *hi_surrogate = ch;
     644          48 :         return true;
     645             :     }
     646         156 :     else if (is_utf16_surrogate_second(ch))
     647             :     {
     648          48 :         if (*hi_surrogate == -1)
     649          24 :             ereturn(escontext, false,
     650             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     651             :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     652             :                      errdetail("Unicode low surrogate must follow a high "
     653             :                                "surrogate.")));
     654          24 :         ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
     655          24 :         *hi_surrogate = -1;
     656             :     }
     657         108 :     else if (*hi_surrogate != -1)
     658             :     {
     659           0 :         ereturn(escontext, false,
     660             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     661             :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     662             :                  errdetail("Unicode low surrogate must follow a high "
     663             :                            "surrogate.")));
     664             :     }
     665             : 
     666         132 :     return addUnicodeChar(ch, escontext);
     667             : }
     668             : 
     669             : /*
     670             :  * parseUnicode was adopted from json_lex_string() in
     671             :  * src/backend/utils/adt/json.c
     672             :  */
     673             : static bool
     674         132 : parseUnicode(char *s, int l, struct Node *escontext)
     675             : {
     676         132 :     int         i = 2;
     677         132 :     int         hi_surrogate = -1;
     678             : 
     679         288 :     for (i = 2; i < l; i += 2)   /* skip '\u' */
     680             :     {
     681         216 :         int         ch = 0;
     682             :         int         j, si;
     683             : 
     684         216 :         if (s[i] == '{')    /* parse '\u{XX...}' */
     685             :         {
     686         168 :             while (s[++i] != '}' && i < l)
     687             :             {
     688         132 :                 if (!hexval(s[i], &si, escontext))
     689           0 :                     return false;
     690         132 :                 ch = (ch << 4) | si;
     691             :             }
     692          36 :             i++;    /* skip '}' */
     693             :         }
     694             :         else        /* parse '\uXXXX' */
     695             :         {
     696         900 :             for (j = 0; j < 4 && i < l; j++)
     697             :             {
     698         720 :                 if (!hexval(s[i++], &si, escontext))
     699           0 :                     return false;
     700         720 :                 ch = (ch << 4) | si;
     701             :             }
     702             :         }
     703             : 
     704         216 :         if (! addUnicode(ch, &hi_surrogate, escontext))
     705           0 :             return false;
     706             :     }
     707             : 
     708          72 :     if (hi_surrogate != -1)
     709             :     {
     710          12 :         ereturn(escontext, false,
     711             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     712             :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     713             :                  errdetail("Unicode low surrogate must follow a high "
     714             :                            "surrogate.")));
     715             :     }
     716             : 
     717          60 :     return true;
     718             : }
     719             : 
     720             : /* Parse sequence of hex-encoded characters */
     721             : static bool
     722          12 : parseHexChar(char *s, struct Node *escontext)
     723             : {
     724             :     int s2, s3, ch;
     725          12 :     if (!hexval(s[2], &s2, escontext))
     726           0 :         return false;
     727          12 :     if (!hexval(s[3], &s3, escontext))
     728           0 :         return false;
     729             : 
     730          12 :     ch = (s2 << 4) | s3;
     731             : 
     732          12 :     return addUnicodeChar(ch, escontext);
     733             : }
     734             : 
     735             : /*
     736             :  * Interface functions to make flex use palloc() instead of malloc().
     737             :  * It'd be better to make these static, but flex insists otherwise.
     738             :  */
     739             : 
     740             : void *
     741       21400 : jsonpath_yyalloc(yy_size_t bytes)
     742             : {
     743       21400 :     return palloc(bytes);
     744             : }
     745             : 
     746             : void *
     747           0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
     748             : {
     749           0 :     if (ptr)
     750           0 :         return repalloc(ptr, bytes);
     751             :     else
     752           0 :         return palloc(bytes);
     753             : }
     754             : 
     755             : void
     756       10334 : jsonpath_yyfree(void *ptr)
     757             : {
     758       10334 :     if (ptr)
     759       10334 :         pfree(ptr);
     760       10334 : }

Generated by: LCOV version 1.14