LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_scan.l (source / functions) Hit Total Coverage
Test: PostgreSQL 15beta1 Lines: 116 130 89.2 %
Date: 2022-05-18 02:09:37 Functions: 15 17 88.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : %{
       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-2022, 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             : #include "mb/pg_wchar.h"
      21             : #include "nodes/pg_list.h"
      22             : 
      23             : static JsonPathString scanstring;
      24             : 
      25             : /* Handles to the buffer that the lexer uses internally */
      26             : static YY_BUFFER_STATE scanbufhandle;
      27             : static char *scanbuf;
      28             : static int  scanbuflen;
      29             : 
      30             : static void addstring(bool init, char *s, int l);
      31             : static void addchar(bool init, char s);
      32             : static enum yytokentype checkKeyword(void);
      33             : static void parseUnicode(char *s, int l);
      34             : static void parseHexChar(char *s);
      35             : 
      36             : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
      37             : #undef fprintf
      38             : #define fprintf(file, fmt, msg)  fprintf_to_ereport(fmt, msg)
      39             : 
      40             : static void
      41           0 : fprintf_to_ereport(const char *fmt, const char *msg)
      42             : {
      43           0 :     ereport(ERROR, (errmsg_internal("%s", msg)));
      44             : }
      45             : 
      46             : /* LCOV_EXCL_START */
      47             : 
      48             : %}
      49             : 
      50             : %option 8bit
      51             : %option never-interactive
      52             : %option nodefault
      53             : %option noinput
      54             : %option nounput
      55             : %option noyywrap
      56             : %option warn
      57             : %option prefix="jsonpath_yy"
      58             : %option bison-bridge
      59             : %option noyyalloc
      60             : %option noyyrealloc
      61             : %option noyyfree
      62             : 
      63             : /*
      64             :  * We use exclusive states for quoted and non-quoted strings,
      65             :  * quoted variable names and C-style comments.
      66             :  * Exclusive states:
      67             :  *  <xq> - quoted strings
      68             :  *  <xnq> - non-quoted strings
      69             :  *  <xvq> - quoted variable names
      70             :  *  <xc> - C-style comment
      71             :  */
      72             : 
      73             : %x xq
      74             : %x xnq
      75             : %x xvq
      76             : %x xc
      77             : 
      78             : special     [\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/]
      79             : blank       [ \t\n\r\f]
      80             : /* "other" means anything that's not special, blank, or '\' or '"' */
      81             : other       [^\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/\\\" \t\n\r\f]
      82             : 
      83             : digit       [0-9]
      84             : integer     (0|[1-9]{digit}*)
      85             : decimal     ({integer}\.{digit}*|\.{digit}+)
      86             : real        ({integer}|{decimal})[Ee][-+]?{digit}+
      87             : realfail    ({integer}|{decimal})[Ee][-+]
      88             : 
      89             : integer_junk    {integer}{other}
      90             : decimal_junk    {decimal}{other}
      91             : real_junk       {real}{other}
      92             : 
      93             : hex_dig     [0-9A-Fa-f]
      94             : unicode     \\u({hex_dig}{4}|\{{hex_dig}{1,6}\})
      95             : unicodefail \\u({hex_dig}{0,3}|\{{hex_dig}{0,6})
      96             : hex_char    \\x{hex_dig}{2}
      97             : hex_fail    \\x{hex_dig}{0,1}
      98             : 
      99             : %%
     100             : 
     101             : <xnq>{other}+                 {
     102             :                                     addstring(false, yytext, yyleng);
     103             :                                 }
     104             : 
     105             : <xnq>{blank}+                 {
     106             :                                     yylval->str = scanstring;
     107             :                                     BEGIN INITIAL;
     108             :                                     return checkKeyword();
     109             :                                 }
     110             : 
     111             : <xnq>\/\*                     {
     112             :                                     yylval->str = scanstring;
     113             :                                     BEGIN xc;
     114             :                                 }
     115             : 
     116             : <xnq>({special}|\")              {
     117             :                                     yylval->str = scanstring;
     118             :                                     yyless(0);
     119             :                                     BEGIN INITIAL;
     120             :                                     return checkKeyword();
     121             :                                 }
     122             : 
     123             : <xnq><<EOF>>                  {
     124             :                                     yylval->str = scanstring;
     125             :                                     BEGIN INITIAL;
     126             :                                     return checkKeyword();
     127             :                                 }
     128             : 
     129             : <xnq,xq,xvq>\\b               { addchar(false, '\b'); }
     130             : 
     131             : <xnq,xq,xvq>\\f               { addchar(false, '\f'); }
     132             : 
     133             : <xnq,xq,xvq>\\n               { addchar(false, '\n'); }
     134             : 
     135             : <xnq,xq,xvq>\\r               { addchar(false, '\r'); }
     136             : 
     137             : <xnq,xq,xvq>\\t               { addchar(false, '\t'); }
     138             : 
     139             : <xnq,xq,xvq>\\v               { addchar(false, '\v'); }
     140             : 
     141             : <xnq,xq,xvq>{unicode}+        { parseUnicode(yytext, yyleng); }
     142             : 
     143             : <xnq,xq,xvq>{hex_char}        { parseHexChar(yytext); }
     144             : 
     145             : <xnq,xq,xvq>{unicode}*{unicodefail}   { yyerror(NULL, "invalid unicode sequence"); }
     146             : 
     147             : <xnq,xq,xvq>{hex_fail}        { yyerror(NULL, "invalid hex character sequence"); }
     148             : 
     149             : <xnq,xq,xvq>{unicode}+\\  {
     150             :                                 /* throw back the \\, and treat as unicode */
     151             :                                 yyless(yyleng - 1);
     152             :                                 parseUnicode(yytext, yyleng);
     153             :                             }
     154             : 
     155             : <xnq,xq,xvq>\\.               { addchar(false, yytext[1]); }
     156             : 
     157             : <xnq,xq,xvq>\\                { yyerror(NULL, "unexpected end after backslash"); }
     158             : 
     159             : <xq,xvq><<EOF>>               { yyerror(NULL, "unexpected end of quoted string"); }
     160             : 
     161             : <xq>\"                           {
     162             :                                     yylval->str = scanstring;
     163             :                                     BEGIN INITIAL;
     164             :                                     return STRING_P;
     165             :                                 }
     166             : 
     167             : <xvq>\"                          {
     168             :                                     yylval->str = scanstring;
     169             :                                     BEGIN INITIAL;
     170             :                                     return VARIABLE_P;
     171             :                                 }
     172             : 
     173             : <xq,xvq>[^\\\"]+             { addstring(false, yytext, yyleng); }
     174             : 
     175             : <xc>\*\/                      { BEGIN INITIAL; }
     176             : 
     177             : <xc>[^\*]+                        { }
     178             : 
     179             : <xc>\*                            { }
     180             : 
     181             : <xc><<EOF>>                       { yyerror(NULL, "unexpected end of comment"); }
     182             : 
     183             : \&\&                            { return AND_P; }
     184             : 
     185             : \|\|                            { return OR_P; }
     186             : 
     187             : \!                              { return NOT_P; }
     188             : 
     189             : \*\*                            { return ANY_P; }
     190             : 
     191             : \<                               { return LESS_P; }
     192             : 
     193             : \<\=                         { return LESSEQUAL_P; }
     194             : 
     195             : \=\=                            { return EQUAL_P; }
     196             : 
     197             : \<\>                          { return NOTEQUAL_P; }
     198             : 
     199             : \!\=                            { return NOTEQUAL_P; }
     200             : 
     201             : \>\=                         { return GREATEREQUAL_P; }
     202             : 
     203             : \>                               { return GREATER_P; }
     204             : 
     205             : \${other}+                      {
     206             :                                     addstring(true, yytext + 1, yyleng - 1);
     207             :                                     addchar(false, '\0');
     208             :                                     yylval->str = scanstring;
     209             :                                     return VARIABLE_P;
     210             :                                 }
     211             : 
     212             : \$\"                           {
     213             :                                     addchar(true, '\0');
     214             :                                     BEGIN xvq;
     215             :                                 }
     216             : 
     217             : {special}                       { return *yytext; }
     218             : 
     219             : {blank}+                        { /* ignore */ }
     220             : 
     221             : \/\*                            {
     222             :                                     addchar(true, '\0');
     223             :                                     BEGIN xc;
     224             :                                 }
     225             : 
     226             : {real}                          {
     227             :                                     addstring(true, yytext, yyleng);
     228             :                                     addchar(false, '\0');
     229             :                                     yylval->str = scanstring;
     230             :                                     return NUMERIC_P;
     231             :                                 }
     232             : 
     233             : {decimal}                       {
     234             :                                     addstring(true, yytext, yyleng);
     235             :                                     addchar(false, '\0');
     236             :                                     yylval->str = scanstring;
     237             :                                     return NUMERIC_P;
     238             :                                 }
     239             : 
     240             : {integer}                       {
     241             :                                     addstring(true, yytext, yyleng);
     242             :                                     addchar(false, '\0');
     243             :                                     yylval->str = scanstring;
     244             :                                     return INT_P;
     245             :                                 }
     246             : 
     247             : {realfail}                      { yyerror(NULL, "invalid numeric literal"); }
     248             : {integer_junk}                  { yyerror(NULL, "trailing junk after numeric literal"); }
     249             : {decimal_junk}                  { yyerror(NULL, "trailing junk after numeric literal"); }
     250             : {real_junk}                     { yyerror(NULL, "trailing junk after numeric literal"); }
     251             : 
     252             : \"                             {
     253             :                                     addchar(true, '\0');
     254             :                                     BEGIN xq;
     255             :                                 }
     256             : 
     257             : \\                              {
     258             :                                     yyless(0);
     259             :                                     addchar(true, '\0');
     260             :                                     BEGIN xnq;
     261             :                                 }
     262             : 
     263             : {other}+                        {
     264             :                                     addstring(true, yytext, yyleng);
     265             :                                     BEGIN xnq;
     266             :                                 }
     267             : 
     268             : <<EOF>>                         { yyterminate(); }
     269             : 
     270             : %%
     271             : 
     272             : /* LCOV_EXCL_STOP */
     273             : 
     274             : void
     275          90 : jsonpath_yyerror(JsonPathParseResult **result, const char *message)
     276             : {
     277          90 :     if (*yytext == YY_END_OF_BUFFER_CHAR)
     278             :     {
     279           0 :         ereport(ERROR,
     280             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     281             :                  /* translator: %s is typically "syntax error" */
     282             :                  errmsg("%s at end of jsonpath input", _(message))));
     283             :     }
     284             :     else
     285             :     {
     286          90 :         ereport(ERROR,
     287             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     288             :                  /* translator: first %s is typically "syntax error" */
     289             :                  errmsg("%s at or near \"%s\" of jsonpath input",
     290             :                         _(message), yytext)));
     291             :     }
     292             : }
     293             : 
     294             : typedef struct JsonPathKeyword
     295             : {
     296             :     int16       len;
     297             :     bool        lowercase;
     298             :     int         val;
     299             :     const char *keyword;
     300             : } JsonPathKeyword;
     301             : 
     302             : /*
     303             :  * Array of key words should be sorted by length and then
     304             :  * alphabetical order
     305             :  */
     306             : static const JsonPathKeyword keywords[] = {
     307             :     { 2, false, IS_P,       "is"},
     308             :     { 2, false, TO_P,       "to"},
     309             :     { 3, false, ABS_P,      "abs"},
     310             :     { 3, false, LAX_P,      "lax"},
     311             :     { 4, false, FLAG_P,     "flag"},
     312             :     { 4, false, LAST_P,     "last"},
     313             :     { 4, true,  NULL_P,     "null"},
     314             :     { 4, false, SIZE_P,     "size"},
     315             :     { 4, true,  TRUE_P,     "true"},
     316             :     { 4, false, TYPE_P,     "type"},
     317             :     { 4, false, WITH_P,     "with"},
     318             :     { 5, true,  FALSE_P,    "false"},
     319             :     { 5, false, FLOOR_P,    "floor"},
     320             :     { 6, false, DOUBLE_P,   "double"},
     321             :     { 6, false, EXISTS_P,   "exists"},
     322             :     { 6, false, STARTS_P,   "starts"},
     323             :     { 6, false, STRICT_P,   "strict"},
     324             :     { 7, false, CEILING_P,  "ceiling"},
     325             :     { 7, false, UNKNOWN_P,  "unknown"},
     326             :     { 8, false, DATETIME_P, "datetime"},
     327             :     { 8, false, KEYVALUE_P, "keyvalue"},
     328             :     { 10,false, LIKE_REGEX_P, "like_regex"},
     329             : };
     330             : 
     331             : /* Check if current scanstring value is a keyword */
     332             : static enum yytokentype
     333        7266 : checkKeyword()
     334             : {
     335        7266 :     int         res = IDENT_P;
     336             :     int         diff;
     337        7266 :     const JsonPathKeyword  *StopLow = keywords,
     338        7266 :                            *StopHigh = keywords + lengthof(keywords),
     339             :                            *StopMiddle;
     340             : 
     341        7266 :     if (scanstring.len > keywords[lengthof(keywords) - 1].len)
     342           6 :         return res;
     343             : 
     344       35334 :     while (StopLow < StopHigh)
     345             :     {
     346       32154 :         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
     347             : 
     348       32154 :         if (StopMiddle->len == scanstring.len)
     349        9378 :             diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
     350        9378 :                                   scanstring.len);
     351             :         else
     352       22776 :             diff = StopMiddle->len - scanstring.len;
     353             : 
     354       32154 :         if (diff < 0)
     355        7032 :             StopLow = StopMiddle + 1;
     356       25122 :         else if (diff > 0)
     357       21042 :             StopHigh = StopMiddle;
     358             :         else
     359             :         {
     360        4080 :             if (StopMiddle->lowercase)
     361         270 :                 diff = strncmp(StopMiddle->keyword, scanstring.val,
     362         270 :                                scanstring.len);
     363             : 
     364        4080 :             if (diff == 0)
     365        4080 :                 res = StopMiddle->val;
     366             : 
     367        4080 :             break;
     368             :         }
     369             :     }
     370             : 
     371        7260 :     return res;
     372             : }
     373             : 
     374             : /*
     375             :  * Called before any actual parsing is done
     376             :  */
     377             : static void
     378        6822 : jsonpath_scanner_init(const char *str, int slen)
     379             : {
     380        6822 :     if (slen <= 0)
     381           6 :         slen = strlen(str);
     382             : 
     383             :     /*
     384             :      * Might be left over after ereport()
     385             :      */
     386        6822 :     yy_init_globals();
     387             : 
     388             :     /*
     389             :      * Make a scan buffer with special termination needed by flex.
     390             :      */
     391             : 
     392        6822 :     scanbuflen = slen;
     393        6822 :     scanbuf = palloc(slen + 2);
     394        6822 :     memcpy(scanbuf, str, slen);
     395        6822 :     scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
     396        6822 :     scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
     397             : 
     398        6822 :     BEGIN(INITIAL);
     399        6822 : }
     400             : 
     401             : 
     402             : /*
     403             :  * Called after parsing is done to clean up after jsonpath_scanner_init()
     404             :  */
     405             : static void
     406        6642 : jsonpath_scanner_finish(void)
     407             : {
     408        6642 :     yy_delete_buffer(scanbufhandle);
     409        6642 :     pfree(scanbuf);
     410        6642 : }
     411             : 
     412             : /*
     413             :  * Resize scanstring so that it can append string of given length.
     414             :  * Reinitialize if required.
     415             :  */
     416             : static void
     417       15684 : resizeString(bool init, int appendLen)
     418             : {
     419       15684 :     if (init)
     420             :     {
     421       11346 :         scanstring.total = Max(32, appendLen);
     422       11346 :         scanstring.val = (char *) palloc(scanstring.total);
     423       11346 :         scanstring.len = 0;
     424             :     }
     425             :     else
     426             :     {
     427        4338 :         if (scanstring.len + appendLen >= scanstring.total)
     428             :         {
     429           0 :             while (scanstring.len + appendLen >= scanstring.total)
     430           0 :                 scanstring.total *= 2;
     431           0 :             scanstring.val = repalloc(scanstring.val, scanstring.total);
     432             :         }
     433             :     }
     434       15684 : }
     435             : 
     436             : /* Add set of bytes at "s" of length "l" to scanstring */
     437             : static void
     438       11436 : addstring(bool init, char *s, int l)
     439             : {
     440       11436 :     resizeString(init, l + 1);
     441       11436 :     memcpy(scanstring.val + scanstring.len, s, l);
     442       11436 :     scanstring.len += l;
     443       11436 : }
     444             : 
     445             : /* Add single byte "c" to scanstring */
     446             : static void
     447        4248 : addchar(bool init, char c)
     448             : {
     449        4248 :     resizeString(init, 1);
     450        4248 :     scanstring.val[scanstring.len] = c;
     451        4248 :     if (c != '\0')
     452         168 :         scanstring.len++;
     453        4248 : }
     454             : 
     455             : /* Interface to jsonpath parser */
     456             : JsonPathParseResult *
     457        6822 : parsejsonpath(const char *str, int len)
     458             : {
     459             :     JsonPathParseResult *parseresult;
     460             : 
     461        6822 :     jsonpath_scanner_init(str, len);
     462             : 
     463        6822 :     if (jsonpath_yyparse((void *) &parseresult) != 0)
     464           0 :         jsonpath_yyerror(NULL, "bogus input"); /* shouldn't happen */
     465             : 
     466        6642 :     jsonpath_scanner_finish();
     467             : 
     468        6642 :     return parseresult;
     469             : }
     470             : 
     471             : /* Turn hex character into integer */
     472             : static int
     473         876 : hexval(char c)
     474             : {
     475         876 :     if (c >= '0' && c <= '9')
     476         588 :         return c - '0';
     477         288 :     if (c >= 'a' && c <= 'f')
     478         252 :         return c - 'a' + 0xA;
     479          36 :     if (c >= 'A' && c <= 'F')
     480          36 :         return c - 'A' + 0xA;
     481           0 :     jsonpath_yyerror(NULL, "invalid hexadecimal digit");
     482           0 :     return 0; /* not reached */
     483             : }
     484             : 
     485             : /* Add given unicode character to scanstring */
     486             : static void
     487         144 : addUnicodeChar(int ch)
     488             : {
     489         144 :     if (ch == 0)
     490             :     {
     491             :         /* We can't allow this, since our TEXT type doesn't */
     492          24 :         ereport(ERROR,
     493             :                 (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
     494             :                  errmsg("unsupported Unicode escape sequence"),
     495             :                   errdetail("\\u0000 cannot be converted to text.")));
     496             :     }
     497             :     else
     498             :     {
     499             :         char        cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
     500             : 
     501         120 :         pg_unicode_to_server(ch, (unsigned char *) cbuf);
     502         120 :         addstring(false, cbuf, strlen(cbuf));
     503             :     }
     504         120 : }
     505             : 
     506             : /* Add unicode character, processing any surrogate pairs */
     507             : static void
     508         216 : addUnicode(int ch, int *hi_surrogate)
     509             : {
     510         216 :     if (is_utf16_surrogate_first(ch))
     511             :     {
     512          60 :         if (*hi_surrogate != -1)
     513          12 :             ereport(ERROR,
     514             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     515             :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     516             :                      errdetail("Unicode high surrogate must not follow "
     517             :                                "a high surrogate.")));
     518          48 :         *hi_surrogate = ch;
     519          48 :         return;
     520             :     }
     521         156 :     else if (is_utf16_surrogate_second(ch))
     522             :     {
     523          48 :         if (*hi_surrogate == -1)
     524          24 :             ereport(ERROR,
     525             :                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     526             :                      errmsg("invalid input syntax for type %s", "jsonpath"),
     527             :                      errdetail("Unicode low surrogate must follow a high "
     528             :                                "surrogate.")));
     529          24 :         ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
     530          24 :         *hi_surrogate = -1;
     531             :     }
     532         108 :     else if (*hi_surrogate != -1)
     533             :     {
     534           0 :         ereport(ERROR,
     535             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     536             :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     537             :                  errdetail("Unicode low surrogate must follow a high "
     538             :                            "surrogate.")));
     539             :     }
     540             : 
     541         132 :     addUnicodeChar(ch);
     542             : }
     543             : 
     544             : /*
     545             :  * parseUnicode was adopted from json_lex_string() in
     546             :  * src/backend/utils/adt/json.c
     547             :  */
     548             : static void
     549         132 : parseUnicode(char *s, int l)
     550             : {
     551         132 :     int         i = 2;
     552         132 :     int         hi_surrogate = -1;
     553             : 
     554         288 :     for (i = 2; i < l; i += 2)   /* skip '\u' */
     555             :     {
     556         216 :         int         ch = 0;
     557             :         int         j;
     558             : 
     559         216 :         if (s[i] == '{')    /* parse '\u{XX...}' */
     560             :         {
     561         168 :             while (s[++i] != '}' && i < l)
     562         132 :                 ch = (ch << 4) | hexval(s[i]);
     563          36 :             i++;    /* skip '}' */
     564             :         }
     565             :         else        /* parse '\uXXXX' */
     566             :         {
     567         900 :             for (j = 0; j < 4 && i < l; j++)
     568         720 :                 ch = (ch << 4) | hexval(s[i++]);
     569             :         }
     570             : 
     571         216 :         addUnicode(ch, &hi_surrogate);
     572             :     }
     573             : 
     574          72 :     if (hi_surrogate != -1)
     575             :     {
     576          12 :         ereport(ERROR,
     577             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     578             :                  errmsg("invalid input syntax for type %s", "jsonpath"),
     579             :                  errdetail("Unicode low surrogate must follow a high "
     580             :                            "surrogate.")));
     581             :     }
     582          60 : }
     583             : 
     584             : /* Parse sequence of hex-encoded characters */
     585             : static void
     586          12 : parseHexChar(char *s)
     587             : {
     588          12 :     int         ch = (hexval(s[2]) << 4) |
     589          12 :                       hexval(s[3]);
     590             : 
     591          12 :     addUnicodeChar(ch);
     592          12 : }
     593             : 
     594             : /*
     595             :  * Interface functions to make flex use palloc() instead of malloc().
     596             :  * It'd be better to make these static, but flex insists otherwise.
     597             :  */
     598             : 
     599             : void *
     600       13644 : jsonpath_yyalloc(yy_size_t bytes)
     601             : {
     602       13644 :     return palloc(bytes);
     603             : }
     604             : 
     605             : void *
     606           0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
     607             : {
     608           0 :     if (ptr)
     609           0 :         return repalloc(ptr, bytes);
     610             :     else
     611           0 :         return palloc(bytes);
     612             : }
     613             : 
     614             : void
     615        6642 : jsonpath_yyfree(void *ptr)
     616             : {
     617        6642 :     if (ptr)
     618        6642 :         pfree(ptr);
     619        6642 : }

Generated by: LCOV version 1.14