LCOV - differential code coverage report
Current view: top level - contrib/ltree - ltree_io.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 86.0 % 392 337 8 13 26 8 6 144 30 157 39 154 2 13
Current Date: 2023-04-08 15:15:32 Functions: 81.0 % 21 17 4 14 3 4 17
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * in/out function for ltree and lquery
       3                 :  * Teodor Sigaev <teodor@stack.net>
       4                 :  * contrib/ltree/ltree_io.c
       5                 :  */
       6                 : #include "postgres.h"
       7                 : 
       8                 : #include <ctype.h>
       9                 : 
      10                 : #include "crc32.h"
      11                 : #include "libpq/pqformat.h"
      12                 : #include "ltree.h"
      13                 : #include "utils/memutils.h"
      14                 : #include "varatt.h"
      15                 : 
      16                 : 
      17                 : typedef struct
      18                 : {
      19                 :     const char *start;
      20                 :     int         len;            /* length in bytes */
      21                 :     int         flag;
      22                 :     int         wlen;           /* length in characters */
      23                 : } nodeitem;
      24                 : 
      25                 : #define LTPRS_WAITNAME  0
      26                 : #define LTPRS_WAITDELIM 1
      27                 : 
      28                 : static bool finish_nodeitem(nodeitem *lptr, const char *ptr,
      29                 :                             bool is_lquery, int pos, struct Node *escontext);
      30                 : 
      31                 : 
      32                 : /*
      33                 :  * expects a null terminated string
      34                 :  * returns an ltree
      35                 :  */
      36                 : static ltree *
      37 GNC        4851 : parse_ltree(const char *buf, struct Node *escontext)
      38 ECB             : {
      39                 :     const char *ptr;
      40                 :     nodeitem   *list,
      41                 :                *lptr;
      42 GIC        4851 :     int         num = 0,
      43 CBC        4851 :                 totallen = 0;
      44            4851 :     int         state = LTPRS_WAITNAME;
      45 ECB             :     ltree      *result;
      46                 :     ltree_level *curlevel;
      47                 :     int         charlen;
      48 GIC        4851 :     int         pos = 1;        /* character position for error messages */
      49 ECB             : 
      50                 : #define UNCHAR ereturn(escontext, NULL,\
      51                 :                        errcode(ERRCODE_SYNTAX_ERROR), \
      52                 :                        errmsg("ltree syntax error at character %d", \
      53                 :                               pos))
      54                 : 
      55 GIC        4851 :     ptr = buf;
      56 CBC      479073 :     while (*ptr)
      57 ECB             :     {
      58 GIC      474222 :         charlen = pg_mblen(ptr);
      59 CBC      474222 :         if (t_iseq(ptr, '.'))
      60          222765 :             num++;
      61          474222 :         ptr += charlen;
      62 ECB             :     }
      63                 : 
      64 GIC        4851 :     if (num + 1 > LTREE_MAX_LEVELS)
      65 GNC           1 :         ereturn(escontext, NULL,
      66 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
      67                 :                  errmsg("number of ltree labels (%d) exceeds the maximum allowed (%d)",
      68                 :                         num + 1, LTREE_MAX_LEVELS)));
      69 GIC        4850 :     list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
      70 CBC        4850 :     ptr = buf;
      71          347966 :     while (*ptr)
      72 ECB             :     {
      73 GIC      343121 :         charlen = pg_mblen(ptr);
      74 ECB             : 
      75 GIC      343121 :         switch (state)
      76 ECB             :         {
      77 GIC      162064 :             case LTPRS_WAITNAME:
      78 GNC      162064 :                 if (ISLABEL(ptr))
      79 ECB             :                 {
      80 GIC      162059 :                     lptr->start = ptr;
      81 CBC      162059 :                     lptr->wlen = 0;
      82          162059 :                     state = LTPRS_WAITDELIM;
      83 ECB             :                 }
      84                 :                 else
      85 GIC           5 :                     UNCHAR;
      86 CBC      162059 :                 break;
      87          181057 :             case LTPRS_WAITDELIM:
      88          181057 :                 if (t_iseq(ptr, '.'))
      89 ECB             :                 {
      90 GNC      157223 :                     if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
      91 UNC           0 :                         return NULL;
      92 CBC      157223 :                     totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
      93 GBC      157223 :                     lptr++;
      94 CBC      157223 :                     state = LTPRS_WAITNAME;
      95 ECB             :                 }
      96 GNC       23834 :                 else if (!ISLABEL(ptr))
      97 UIC           0 :                     UNCHAR;
      98 CBC      181057 :                 break;
      99 UBC           0 :             default:
     100 LBC           0 :                 elog(ERROR, "internal error in ltree parser");
     101 EUB             :         }
     102                 : 
     103 GIC      343116 :         ptr += charlen;
     104          343116 :         lptr->wlen++;
     105 CBC      343116 :         pos++;
     106 ECB             :     }
     107                 : 
     108 GIC        4845 :     if (state == LTPRS_WAITDELIM)
     109                 :     {
     110 GNC        4836 :         if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
     111 UNC           0 :             return NULL;
     112 GIC        4835 :         totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
     113 CBC        4835 :         lptr++;
     114 EUB             :     }
     115 CBC           9 :     else if (!(state == LTPRS_WAITNAME && lptr == list))
     116 GNC           3 :         ereturn(escontext, NULL,
     117                 :                 (errcode(ERRCODE_SYNTAX_ERROR),
     118 ECB             :                  errmsg("ltree syntax error"),
     119                 :                  errdetail("Unexpected end of input.")));
     120                 : 
     121 GIC        4841 :     result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
     122            4841 :     SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
     123            4841 :     result->numlevel = lptr - list;
     124 CBC        4841 :     curlevel = LTREE_FIRST(result);
     125            4841 :     lptr = list;
     126          166892 :     while (lptr - list < result->numlevel)
     127 ECB             :     {
     128 CBC      162051 :         curlevel->len = (uint16) lptr->len;
     129          162051 :         memcpy(curlevel->name, lptr->start, lptr->len);
     130 GIC      162051 :         curlevel = LEVEL_NEXT(curlevel);
     131 CBC      162051 :         lptr++;
     132 ECB             :     }
     133                 : 
     134 CBC        4841 :     pfree(list);
     135 GIC        4841 :     return result;
     136                 : 
     137 ECB             : #undef UNCHAR
     138                 : }
     139                 : 
     140                 : /*
     141                 :  * expects an ltree
     142                 :  * returns a null terminated string
     143                 :  */
     144                 : static char *
     145 GIC        6261 : deparse_ltree(const ltree *in)
     146                 : {
     147                 :     char       *buf,
     148 ECB             :                *ptr;
     149                 :     int         i;
     150                 :     ltree_level *curlevel;
     151                 : 
     152 GIC        6261 :     ptr = buf = (char *) palloc(VARSIZE(in));
     153            6261 :     curlevel = LTREE_FIRST(in);
     154           47254 :     for (i = 0; i < in->numlevel; i++)
     155 ECB             :     {
     156 CBC       40993 :         if (i != 0)
     157 ECB             :         {
     158 GIC       34746 :             *ptr = '.';
     159 CBC       34746 :             ptr++;
     160                 :         }
     161           40993 :         memcpy(ptr, curlevel->name, curlevel->len);
     162           40993 :         ptr += curlevel->len;
     163 GIC       40993 :         curlevel = LEVEL_NEXT(curlevel);
     164 ECB             :     }
     165                 : 
     166 CBC        6261 :     *ptr = '\0';
     167 GIC        6261 :     return buf;
     168                 : }
     169 ECB             : 
     170                 : /*
     171                 :  * Basic ltree I/O functions
     172                 :  */
     173 GIC           4 : PG_FUNCTION_INFO_V1(ltree_in);
     174                 : Datum
     175            4851 : ltree_in(PG_FUNCTION_ARGS)
     176 ECB             : {
     177 GIC        4851 :     char       *buf = (char *) PG_GETARG_POINTER(0);
     178                 :     ltree      *res;
     179 ECB             : 
     180 GNC        4851 :     if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
     181               4 :         PG_RETURN_NULL();
     182                 : 
     183            4841 :     PG_RETURN_POINTER(res);
     184 ECB             : }
     185                 : 
     186 GIC           3 : PG_FUNCTION_INFO_V1(ltree_out);
     187 ECB             : Datum
     188 CBC        6261 : ltree_out(PG_FUNCTION_ARGS)
     189                 : {
     190            6261 :     ltree      *in = PG_GETARG_LTREE_P(0);
     191                 : 
     192 GIC        6261 :     PG_RETURN_POINTER(deparse_ltree(in));
     193 ECB             : }
     194                 : 
     195                 : /*
     196                 :  * ltree type send function
     197                 :  *
     198                 :  * The type is sent as text in binary mode, so this is almost the same
     199                 :  * as the output function, but it's prefixed with a version number so we
     200                 :  * can change the binary format sent in future if necessary. For now,
     201                 :  * only version 1 is supported.
     202                 :  */
     203 GIC           2 : PG_FUNCTION_INFO_V1(ltree_send);
     204                 : Datum
     205 UIC           0 : ltree_send(PG_FUNCTION_ARGS)
     206                 : {
     207               0 :     ltree      *in = PG_GETARG_LTREE_P(0);
     208                 :     StringInfoData buf;
     209               0 :     int         version = 1;
     210 LBC           0 :     char       *res = deparse_ltree(in);
     211                 : 
     212 UBC           0 :     pq_begintypsend(&buf);
     213 UIC           0 :     pq_sendint8(&buf, version);
     214 UBC           0 :     pq_sendtext(&buf, res, strlen(res));
     215 UIC           0 :     pfree(res);
     216 EUB             : 
     217 UBC           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     218                 : }
     219 EUB             : 
     220                 : /*
     221                 :  * ltree type recv function
     222                 :  *
     223                 :  * The type is sent as text in binary mode, so this is almost the same
     224                 :  * as the input function, but it's prefixed with a version number so we
     225                 :  * can change the binary format sent in future if necessary. For now,
     226                 :  * only version 1 is supported.
     227                 :  */
     228 GIC           2 : PG_FUNCTION_INFO_V1(ltree_recv);
     229                 : Datum
     230 UIC           0 : ltree_recv(PG_FUNCTION_ARGS)
     231                 : {
     232               0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     233               0 :     int         version = pq_getmsgint(buf, 1);
     234                 :     char       *str;
     235 ECB             :     int         nbytes;
     236                 :     ltree      *res;
     237 EUB             : 
     238 UIC           0 :     if (version != 1)
     239 UBC           0 :         elog(ERROR, "unsupported ltree version number %d", version);
     240 EUB             : 
     241 UIC           0 :     str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     242 UNC           0 :     res = parse_ltree(str, NULL);
     243 UIC           0 :     pfree(str);
     244                 : 
     245 UBC           0 :     PG_RETURN_POINTER(res);
     246 EUB             : }
     247                 : 
     248                 : 
     249                 : #define LQPRS_WAITLEVEL 0
     250                 : #define LQPRS_WAITDELIM 1
     251                 : #define LQPRS_WAITOPEN  2
     252                 : #define LQPRS_WAITFNUM  3
     253                 : #define LQPRS_WAITSNUM  4
     254                 : #define LQPRS_WAITND    5
     255                 : #define LQPRS_WAITCLOSE 6
     256                 : #define LQPRS_WAITEND   7
     257                 : #define LQPRS_WAITVAR   8
     258                 : 
     259                 : 
     260                 : #define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
     261                 : #define ITEMSIZE    MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
     262                 : #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
     263                 : 
     264                 : /*
     265                 :  * expects a null terminated string
     266                 :  * returns an lquery
     267                 :  */
     268                 : static lquery *
     269 GNC         191 : parse_lquery(const char *buf, struct Node *escontext)
     270                 : {
     271                 :     const char *ptr;
     272 GIC         191 :     int         num = 0,
     273             191 :                 totallen = 0,
     274             191 :                 numOR = 0;
     275             191 :     int         state = LQPRS_WAITLEVEL;
     276 ECB             :     lquery     *result;
     277 GIC         191 :     nodeitem   *lptr = NULL;
     278                 :     lquery_level *cur,
     279 ECB             :                *curqlevel,
     280                 :                *tmpql;
     281 CBC         191 :     lquery_variant *lrptr = NULL;
     282             191 :     bool        hasnot = false;
     283 GIC         191 :     bool        wasbad = false;
     284 ECB             :     int         charlen;
     285 GIC         191 :     int         pos = 1;        /* character position for error messages */
     286                 : 
     287                 : #define UNCHAR ereturn(escontext, NULL,\
     288 ECB             :                        errcode(ERRCODE_SYNTAX_ERROR), \
     289                 :                        errmsg("lquery syntax error at character %d", \
     290                 :                               pos))
     291                 : 
     292 CBC         191 :     ptr = buf;
     293 GIC      267834 :     while (*ptr)
     294                 :     {
     295          267643 :         charlen = pg_mblen(ptr);
     296                 : 
     297          267643 :         if (t_iseq(ptr, '.'))
     298          131474 :             num++;
     299 CBC      136169 :         else if (t_iseq(ptr, '|'))
     300              34 :             numOR++;
     301                 : 
     302          267643 :         ptr += charlen;
     303                 :     }
     304 ECB             : 
     305 CBC         191 :     num++;
     306             191 :     if (num > LQUERY_MAX_LEVELS)
     307 GNC           1 :         ereturn(escontext, NULL,
     308                 :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     309 ECB             :                  errmsg("number of lquery items (%d) exceeds the maximum allowed (%d)",
     310                 :                         num, LQUERY_MAX_LEVELS)));
     311 GIC         190 :     curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
     312 CBC         190 :     ptr = buf;
     313          136706 :     while (*ptr)
     314 ECB             :     {
     315 GIC      136531 :         charlen = pg_mblen(ptr);
     316                 : 
     317          136531 :         switch (state)
     318 ECB             :         {
     319 CBC       66112 :             case LQPRS_WAITLEVEL:
     320 GNC       66112 :                 if (ISLABEL(ptr))
     321                 :                 {
     322 CBC       65839 :                     GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
     323 GIC       65839 :                     lptr->start = ptr;
     324 CBC       65839 :                     state = LQPRS_WAITDELIM;
     325 GIC       65839 :                     curqlevel->numvar = 1;
     326 ECB             :                 }
     327 CBC         273 :                 else if (t_iseq(ptr, '!'))
     328                 :                 {
     329              78 :                     GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
     330              78 :                     lptr->start = ptr + 1;
     331              78 :                     lptr->wlen = -1; /* compensate for counting ! below */
     332              78 :                     state = LQPRS_WAITDELIM;
     333 GIC          78 :                     curqlevel->numvar = 1;
     334 CBC          78 :                     curqlevel->flag |= LQL_NOT;
     335 GIC          78 :                     hasnot = true;
     336 ECB             :                 }
     337 CBC         195 :                 else if (t_iseq(ptr, '*'))
     338             186 :                     state = LQPRS_WAITOPEN;
     339 ECB             :                 else
     340 CBC           9 :                     UNCHAR;
     341           66103 :                 break;
     342              34 :             case LQPRS_WAITVAR:
     343 GNC          34 :                 if (ISLABEL(ptr))
     344 ECB             :                 {
     345 CBC          33 :                     lptr++;
     346 GIC          33 :                     lptr->start = ptr;
     347 CBC          33 :                     state = LQPRS_WAITDELIM;
     348              33 :                     curqlevel->numvar++;
     349 ECB             :                 }
     350                 :                 else
     351 GIC           1 :                     UNCHAR;
     352 CBC          33 :                 break;
     353           70007 :             case LQPRS_WAITDELIM:
     354           70007 :                 if (t_iseq(ptr, '@'))
     355 ECB             :                 {
     356 GIC          19 :                     lptr->flag |= LVAR_INCASE;
     357              19 :                     curqlevel->flag |= LVAR_INCASE;
     358 ECB             :                 }
     359 CBC       69988 :                 else if (t_iseq(ptr, '*'))
     360 ECB             :                 {
     361 CBC          19 :                     lptr->flag |= LVAR_ANYEND;
     362 GIC          19 :                     curqlevel->flag |= LVAR_ANYEND;
     363 ECB             :                 }
     364 CBC       69969 :                 else if (t_iseq(ptr, '%'))
     365                 :                 {
     366               6 :                     lptr->flag |= LVAR_SUBLEXEME;
     367 GIC           6 :                     curqlevel->flag |= LVAR_SUBLEXEME;
     368 ECB             :                 }
     369 CBC       69963 :                 else if (t_iseq(ptr, '|'))
     370                 :                 {
     371 GNC          34 :                     if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     372 UNC           0 :                         return NULL;
     373 GIC          34 :                     state = LQPRS_WAITVAR;
     374 ECB             :                 }
     375 CBC       69929 :                 else if (t_iseq(ptr, '{'))
     376                 :                 {
     377 GNC          20 :                     if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     378 UNC           0 :                         return NULL;
     379 GIC          20 :                     curqlevel->flag |= LQL_COUNT;
     380 CBC          20 :                     state = LQPRS_WAITFNUM;
     381 EUB             :                 }
     382 CBC       69909 :                 else if (t_iseq(ptr, '.'))
     383                 :                 {
     384 GNC       65789 :                     if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     385 UNC           0 :                         return NULL;
     386 GIC       65787 :                     state = LQPRS_WAITLEVEL;
     387 CBC       65787 :                     curqlevel = NEXTLEV(curqlevel);
     388 EUB             :                 }
     389 GNC        4120 :                 else if (ISLABEL(ptr))
     390 ECB             :                 {
     391                 :                     /* disallow more chars after a flag */
     392 CBC        4120 :                     if (lptr->flag)
     393 UIC           0 :                         UNCHAR;
     394 ECB             :                 }
     395 EUB             :                 else
     396 LBC           0 :                     UNCHAR;
     397 CBC       70005 :                 break;
     398 GIC         140 :             case LQPRS_WAITOPEN:
     399 CBC         140 :                 if (t_iseq(ptr, '{'))
     400 GIC          50 :                     state = LQPRS_WAITFNUM;
     401              90 :                 else if (t_iseq(ptr, '.'))
     402 ECB             :                 {
     403 EUB             :                     /* We only get here for '*', so these are correct defaults */
     404 GIC          90 :                     curqlevel->low = 0;
     405              90 :                     curqlevel->high = LTREE_MAX_LEVELS;
     406 GBC          90 :                     curqlevel = NEXTLEV(curqlevel);
     407 CBC          90 :                     state = LQPRS_WAITLEVEL;
     408 ECB             :                 }
     409                 :                 else
     410 LBC           0 :                     UNCHAR;
     411 CBC         140 :                 break;
     412 GIC          70 :             case LQPRS_WAITFNUM:
     413              70 :                 if (t_iseq(ptr, ','))
     414 CBC          16 :                     state = LQPRS_WAITSNUM;
     415              54 :                 else if (t_isdigit(ptr))
     416 ECB             :                 {
     417 CBC          54 :                     int         low = atoi(ptr);
     418                 : 
     419 GIC          54 :                     if (low < 0 || low > LTREE_MAX_LEVELS)
     420 GNC           1 :                         ereturn(escontext, NULL,
     421 ECB             :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     422                 :                                  errmsg("lquery syntax error"),
     423                 :                                  errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.",
     424                 :                                            low, LTREE_MAX_LEVELS, pos)));
     425                 : 
     426 GIC          53 :                     curqlevel->low = (uint16) low;
     427 CBC          53 :                     state = LQPRS_WAITND;
     428                 :                 }
     429 ECB             :                 else
     430 LBC           0 :                     UNCHAR;
     431 GIC          69 :                 break;
     432              37 :             case LQPRS_WAITSNUM:
     433              37 :                 if (t_isdigit(ptr))
     434                 :                 {
     435              21 :                     int         high = atoi(ptr);
     436 ECB             : 
     437 CBC          21 :                     if (high < 0 || high > LTREE_MAX_LEVELS)
     438 GNC           1 :                         ereturn(escontext, NULL,
     439                 :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     440 EUB             :                                  errmsg("lquery syntax error"),
     441 ECB             :                                  errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.",
     442                 :                                            high, LTREE_MAX_LEVELS, pos)));
     443 CBC          20 :                     else if (curqlevel->low > high)
     444 GNC           1 :                         ereturn(escontext, NULL,
     445 ECB             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     446                 :                                  errmsg("lquery syntax error"),
     447                 :                                  errdetail("Low limit (%d) is greater than high limit (%d), at character %d.",
     448                 :                                            curqlevel->low, high, pos)));
     449                 : 
     450 GIC          19 :                     curqlevel->high = (uint16) high;
     451              19 :                     state = LQPRS_WAITCLOSE;
     452                 :                 }
     453 CBC          16 :                 else if (t_iseq(ptr, '}'))
     454 ECB             :                 {
     455 GIC          16 :                     curqlevel->high = LTREE_MAX_LEVELS;
     456              16 :                     state = LQPRS_WAITEND;
     457                 :                 }
     458                 :                 else
     459 UIC           0 :                     UNCHAR;
     460 CBC          35 :                 break;
     461              28 :             case LQPRS_WAITCLOSE:
     462 GIC          28 :                 if (t_iseq(ptr, '}'))
     463 CBC          19 :                     state = LQPRS_WAITEND;
     464 GIC           9 :                 else if (!t_isdigit(ptr))
     465 LBC           0 :                     UNCHAR;
     466 CBC          28 :                 break;
     467 GIC          57 :             case LQPRS_WAITND:
     468              57 :                 if (t_iseq(ptr, '}'))
     469 EUB             :                 {
     470 CBC          32 :                     curqlevel->high = curqlevel->low;
     471              32 :                     state = LQPRS_WAITEND;
     472 ECB             :                 }
     473 CBC          25 :                 else if (t_iseq(ptr, ','))
     474              21 :                     state = LQPRS_WAITSNUM;
     475 GBC           4 :                 else if (!t_isdigit(ptr))
     476 LBC           0 :                     UNCHAR;
     477 CBC          57 :                 break;
     478              46 :             case LQPRS_WAITEND:
     479 GIC          46 :                 if (t_iseq(ptr, '.'))
     480 ECB             :                 {
     481 CBC          46 :                     state = LQPRS_WAITLEVEL;
     482 GIC          46 :                     curqlevel = NEXTLEV(curqlevel);
     483 ECB             :                 }
     484                 :                 else
     485 LBC           0 :                     UNCHAR;
     486 GBC          46 :                 break;
     487 LBC           0 :             default:
     488               0 :                 elog(ERROR, "internal error in lquery parser");
     489 ECB             :         }
     490                 : 
     491 CBC      136516 :         ptr += charlen;
     492          136516 :         if (state == LQPRS_WAITDELIM)
     493 GIC       70114 :             lptr->wlen++;
     494          136516 :         pos++;
     495 EUB             :     }
     496 ECB             : 
     497 GBC         175 :     if (state == LQPRS_WAITDELIM)
     498                 :     {
     499 GNC         107 :         if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     500 UNC           0 :             return NULL;
     501                 :     }
     502 GIC          68 :     else if (state == LQPRS_WAITOPEN)
     503              46 :         curqlevel->high = LTREE_MAX_LEVELS;
     504 CBC          22 :     else if (state != LQPRS_WAITEND)
     505 GNC           1 :         ereturn(escontext, NULL,
     506 ECB             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     507                 :                  errmsg("lquery syntax error"),
     508                 :                  errdetail("Unexpected end of input.")));
     509                 : 
     510 CBC         171 :     curqlevel = tmpql;
     511 GIC         171 :     totallen = LQUERY_HDRSIZE;
     512 CBC       66254 :     while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
     513 EUB             :     {
     514 GIC       66083 :         totallen += LQL_HDRSIZE;
     515 CBC       66083 :         if (curqlevel->numvar)
     516 ECB             :         {
     517 CBC       65900 :             lptr = GETVAR(curqlevel);
     518          131833 :             while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
     519                 :             {
     520 GIC       65933 :                 totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
     521           65933 :                 lptr++;
     522                 :             }
     523 ECB             :         }
     524 CBC       66083 :         curqlevel = NEXTLEV(curqlevel);
     525 ECB             :     }
     526                 : 
     527 CBC         171 :     result = (lquery *) palloc0(totallen);
     528             171 :     SET_VARSIZE(result, totallen);
     529 GIC         171 :     result->numlevel = num;
     530 CBC         171 :     result->firstgood = 0;
     531             171 :     result->flag = 0;
     532 GIC         171 :     if (hasnot)
     533 CBC          52 :         result->flag |= LQUERY_HASNOT;
     534             171 :     cur = LQUERY_FIRST(result);
     535 GIC         171 :     curqlevel = tmpql;
     536           66254 :     while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
     537 ECB             :     {
     538 GIC       66083 :         memcpy(cur, curqlevel, LQL_HDRSIZE);
     539           66083 :         cur->totallen = LQL_HDRSIZE;
     540 CBC       66083 :         if (curqlevel->numvar)
     541 ECB             :         {
     542 CBC       65900 :             lrptr = LQL_FIRST(cur);
     543           65900 :             lptr = GETVAR(curqlevel);
     544          131833 :             while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
     545 ECB             :             {
     546 CBC       65933 :                 cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
     547           65933 :                 lrptr->len = lptr->len;
     548           65933 :                 lrptr->flag = lptr->flag;
     549           65933 :                 lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
     550 GIC       65933 :                 memcpy(lrptr->name, lptr->start, lptr->len);
     551 CBC       65933 :                 lptr++;
     552           65933 :                 lrptr = LVAR_NEXT(lrptr);
     553 ECB             :             }
     554 GIC       65900 :             pfree(GETVAR(curqlevel));
     555 CBC       65900 :             if (cur->numvar > 1 || cur->flag != 0)
     556 ECB             :             {
     557                 :                 /* Not a simple match */
     558 GIC         112 :                 wasbad = true;
     559 ECB             :             }
     560 CBC       65788 :             else if (wasbad == false)
     561 ECB             :             {
     562                 :                 /* count leading simple matches */
     563 CBC       65674 :                 (result->firstgood)++;
     564 ECB             :             }
     565                 :         }
     566                 :         else
     567                 :         {
     568                 :             /* '*', so this isn't a simple match */
     569 GIC         183 :             wasbad = true;
     570                 :         }
     571 CBC       66083 :         curqlevel = NEXTLEV(curqlevel);
     572 GIC       66083 :         cur = LQL_NEXT(cur);
     573 ECB             :     }
     574                 : 
     575 GIC         171 :     pfree(tmpql);
     576 CBC         171 :     return result;
     577                 : 
     578                 : #undef UNCHAR
     579                 : }
     580                 : 
     581                 : /*
     582 ECB             :  * Close out parsing an ltree or lquery nodeitem:
     583                 :  * compute the correct length, and complain if it's not OK
     584                 :  */
     585                 : static bool
     586 GNC      228009 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
     587                 :     struct Node *escontext)
     588                 : {
     589 CBC      228009 :     if (is_lquery)
     590 ECB             :     {
     591                 :         /*
     592                 :          * Back up over any flag characters, and discount them from length and
     593                 :          * position.
     594                 :          */
     595 GIC       65994 :         while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL)
     596                 :         {
     597              44 :             ptr--;
     598              44 :             lptr->wlen--;
     599              44 :             pos--;
     600 ECB             :         }
     601                 :     }
     602                 : 
     603                 :     /* Now compute the byte length, which we weren't tracking before. */
     604 GIC      228009 :     lptr->len = ptr - lptr->start;
     605                 : 
     606                 :     /* Complain if it's empty or too long */
     607          228009 :     if (lptr->len == 0)
     608 GNC           3 :         ereturn(escontext, false,
     609 ECB             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     610                 :                  is_lquery ?
     611                 :                  errmsg("lquery syntax error at character %d", pos) :
     612                 :                  errmsg("ltree syntax error at character %d", pos),
     613                 :                  errdetail("Empty labels are not allowed.")));
     614 GIC      228006 :     if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
     615 GNC           3 :         ereturn(escontext, false,
     616                 :                 (errcode(ERRCODE_NAME_TOO_LONG),
     617                 :                  errmsg("label string is too long"),
     618 ECB             :                  errdetail("Label length is %d, must be at most %d, at character %d.",
     619                 :                            lptr->wlen, LTREE_LABEL_MAX_CHARS, pos)));
     620 GNC      228003 :     return true;
     621                 : }
     622 ECB             : 
     623                 : /*
     624                 :  * expects an lquery
     625                 :  * returns a null terminated string
     626                 :  */
     627                 : static char *
     628 GIC          30 : deparse_lquery(const lquery *in)
     629 ECB             : {
     630                 :     char       *buf,
     631                 :                *ptr;
     632                 :     int         i,
     633                 :                 j,
     634 GIC          30 :                 totallen = 1;
     635 ECB             :     lquery_level *curqlevel;
     636                 :     lquery_variant *curtlevel;
     637                 : 
     638 GIC          30 :     curqlevel = LQUERY_FIRST(in);
     639             106 :     for (i = 0; i < in->numlevel; i++)
     640                 :     {
     641              76 :         totallen++;
     642              76 :         if (curqlevel->numvar)
     643 ECB             :         {
     644 GIC          51 :             totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
     645              51 :             if (curqlevel->flag & LQL_COUNT)
     646               4 :                 totallen += 2 * 11 + 3;
     647                 :         }
     648                 :         else
     649 CBC          25 :             totallen += 2 * 11 + 4;
     650 GIC          76 :         curqlevel = LQL_NEXT(curqlevel);
     651                 :     }
     652                 : 
     653 CBC          30 :     ptr = buf = (char *) palloc(totallen);
     654              30 :     curqlevel = LQUERY_FIRST(in);
     655 GIC         106 :     for (i = 0; i < in->numlevel; i++)
     656 ECB             :     {
     657 CBC          76 :         if (i != 0)
     658                 :         {
     659              46 :             *ptr = '.';
     660              46 :             ptr++;
     661 ECB             :         }
     662 GIC          76 :         if (curqlevel->numvar)
     663                 :         {
     664 CBC          51 :             if (curqlevel->flag & LQL_NOT)
     665 ECB             :             {
     666 GIC           2 :                 *ptr = '!';
     667               2 :                 ptr++;
     668 ECB             :             }
     669 CBC          51 :             curtlevel = LQL_FIRST(curqlevel);
     670             131 :             for (j = 0; j < curqlevel->numvar; j++)
     671                 :             {
     672              80 :                 if (j != 0)
     673                 :                 {
     674              29 :                     *ptr = '|';
     675              29 :                     ptr++;
     676                 :                 }
     677              80 :                 memcpy(ptr, curtlevel->name, curtlevel->len);
     678 GIC          80 :                 ptr += curtlevel->len;
     679 CBC          80 :                 if ((curtlevel->flag & LVAR_SUBLEXEME))
     680                 :                 {
     681               1 :                     *ptr = '%';
     682               1 :                     ptr++;
     683                 :                 }
     684              80 :                 if ((curtlevel->flag & LVAR_INCASE))
     685 ECB             :                 {
     686 GIC           3 :                     *ptr = '@';
     687 CBC           3 :                     ptr++;
     688                 :                 }
     689              80 :                 if ((curtlevel->flag & LVAR_ANYEND))
     690 ECB             :                 {
     691 GIC           4 :                     *ptr = '*';
     692 CBC           4 :                     ptr++;
     693 ECB             :                 }
     694 CBC          80 :                 curtlevel = LVAR_NEXT(curtlevel);
     695                 :             }
     696 ECB             :         }
     697                 :         else
     698                 :         {
     699 CBC          25 :             *ptr = '*';
     700 GIC          25 :             ptr++;
     701 ECB             :         }
     702                 : 
     703 GIC          76 :         if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
     704 ECB             :         {
     705 GIC          29 :             if (curqlevel->low == curqlevel->high)
     706 ECB             :             {
     707 CBC           2 :                 sprintf(ptr, "{%d}", curqlevel->low);
     708                 :             }
     709              27 :             else if (curqlevel->low == 0)
     710                 :             {
     711 GIC          23 :                 if (curqlevel->high == LTREE_MAX_LEVELS)
     712                 :                 {
     713              20 :                     if (curqlevel->numvar == 0)
     714 ECB             :                     {
     715                 :                         /* This is default for '*', so print nothing */
     716 GIC          19 :                         *ptr = '\0';
     717                 :                     }
     718 ECB             :                     else
     719 GIC           1 :                         sprintf(ptr, "{,}");
     720 ECB             :                 }
     721                 :                 else
     722 CBC           3 :                     sprintf(ptr, "{,%d}", curqlevel->high);
     723                 :             }
     724               4 :             else if (curqlevel->high == LTREE_MAX_LEVELS)
     725                 :             {
     726               2 :                 sprintf(ptr, "{%d,}", curqlevel->low);
     727                 :             }
     728 ECB             :             else
     729 GIC           2 :                 sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
     730              29 :             ptr = strchr(ptr, '\0');
     731 ECB             :         }
     732                 : 
     733 GIC          76 :         curqlevel = LQL_NEXT(curqlevel);
     734 ECB             :     }
     735                 : 
     736 GIC          30 :     *ptr = '\0';
     737 CBC          30 :     return buf;
     738                 : }
     739 ECB             : 
     740                 : /*
     741                 :  * Basic lquery I/O functions
     742                 :  */
     743 GIC           3 : PG_FUNCTION_INFO_V1(lquery_in);
     744 ECB             : Datum
     745 CBC         191 : lquery_in(PG_FUNCTION_ARGS)
     746                 : {
     747 GIC         191 :     char       *buf = (char *) PG_GETARG_POINTER(0);
     748                 :     lquery     *res;
     749 ECB             : 
     750 GNC         191 :     if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
     751               4 :         PG_RETURN_NULL();
     752                 : 
     753             171 :     PG_RETURN_POINTER(res);
     754                 : }
     755 ECB             : 
     756 CBC           3 : PG_FUNCTION_INFO_V1(lquery_out);
     757                 : Datum
     758 GIC          30 : lquery_out(PG_FUNCTION_ARGS)
     759                 : {
     760              30 :     lquery     *in = PG_GETARG_LQUERY_P(0);
     761                 : 
     762 CBC          30 :     PG_RETURN_POINTER(deparse_lquery(in));
     763                 : }
     764 ECB             : 
     765                 : /*
     766                 :  * lquery type send function
     767                 :  *
     768                 :  * The type is sent as text in binary mode, so this is almost the same
     769                 :  * as the output function, but it's prefixed with a version number so we
     770                 :  * can change the binary format sent in future if necessary. For now,
     771                 :  * only version 1 is supported.
     772                 :  */
     773 GIC           2 : PG_FUNCTION_INFO_V1(lquery_send);
     774                 : Datum
     775 LBC           0 : lquery_send(PG_FUNCTION_ARGS)
     776                 : {
     777               0 :     lquery     *in = PG_GETARG_LQUERY_P(0);
     778                 :     StringInfoData buf;
     779               0 :     int         version = 1;
     780 UIC           0 :     char       *res = deparse_lquery(in);
     781 ECB             : 
     782 UIC           0 :     pq_begintypsend(&buf);
     783               0 :     pq_sendint8(&buf, version);
     784               0 :     pq_sendtext(&buf, res, strlen(res));
     785               0 :     pfree(res);
     786                 : 
     787               0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     788                 : }
     789                 : 
     790                 : /*
     791                 :  * lquery type recv function
     792 ECB             :  *
     793                 :  * The type is sent as text in binary mode, so this is almost the same
     794 EUB             :  * as the input function, but it's prefixed with a version number so we
     795                 :  * can change the binary format sent in future if necessary. For now,
     796                 :  * only version 1 is supported.
     797                 :  */
     798 GBC           2 : PG_FUNCTION_INFO_V1(lquery_recv);
     799 EUB             : Datum
     800 UIC           0 : lquery_recv(PG_FUNCTION_ARGS)
     801 EUB             : {
     802 UBC           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     803               0 :     int         version = pq_getmsgint(buf, 1);
     804 EUB             :     char       *str;
     805                 :     int         nbytes;
     806                 :     lquery     *res;
     807                 : 
     808 UIC           0 :     if (version != 1)
     809               0 :         elog(ERROR, "unsupported lquery version number %d", version);
     810                 : 
     811               0 :     str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     812 UNC           0 :     res = parse_lquery(str, NULL);
     813 UIC           0 :     pfree(str);
     814                 : 
     815               0 :     PG_RETURN_POINTER(res);
     816                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a