LCOV - code coverage report
Current view: top level - contrib/ltree - ltree_io.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 86.1 % 396 341
Test Date: 2026-07-03 19:57:34 Functions: 81.0 % 21 17
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 75.3 % 283 213

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

Generated by: LCOV version 2.0-1