LCOV - code coverage report
Current view: top level - contrib/ltree - ltree_io.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 86.1 % 396 341
Test Date: 2026-05-17 14:16:24 Functions: 81.0 % 21 17
Legend: Lines:     hit not hit

            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         4860 : parse_ltree(const char *buf, struct Node *escontext)
      38              : {
      39              :     const char *ptr;
      40              :     nodeitem   *list,
      41              :                *lptr;
      42         4860 :     int         num = 0,
      43         4860 :                 totallen = 0;
      44         4860 :     int         state = LTPRS_WAITNAME;
      45              :     ltree      *result;
      46              :     ltree_level *curlevel;
      47              :     int         charlen;
      48         4860 :     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         4860 :     ptr = buf;
      56       479128 :     while (*ptr)
      57              :     {
      58       474268 :         charlen = pg_mblen_cstr(ptr);
      59       474268 :         if (t_iseq(ptr, '.'))
      60       222773 :             num++;
      61       474268 :         ptr += charlen;
      62              :     }
      63              : 
      64         4860 :     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         4859 :     list = lptr = palloc_array(nodeitem, num + 1);
      70         4859 :     ptr = buf;
      71       348021 :     while (*ptr)
      72              :     {
      73       343167 :         charlen = pg_mblen_cstr(ptr);
      74              : 
      75       343167 :         switch (state)
      76              :         {
      77       162080 :             case LTPRS_WAITNAME:
      78       162080 :                 if (ISLABEL(ptr))
      79              :                 {
      80       162075 :                     lptr->start = ptr;
      81       162075 :                     lptr->wlen = 0;
      82       162075 :                     state = LTPRS_WAITDELIM;
      83              :                 }
      84              :                 else
      85            5 :                     UNCHAR;
      86       162075 :                 break;
      87       181087 :             case LTPRS_WAITDELIM:
      88       181087 :                 if (t_iseq(ptr, '.'))
      89              :                 {
      90       157231 :                     if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
      91            0 :                         return NULL;
      92       157231 :                     totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
      93       157231 :                     lptr++;
      94       157231 :                     state = LTPRS_WAITNAME;
      95              :                 }
      96        23856 :                 else if (!ISLABEL(ptr))
      97            0 :                     UNCHAR;
      98       181087 :                 break;
      99            0 :             default:
     100            0 :                 elog(ERROR, "internal error in ltree parser");
     101              :         }
     102              : 
     103       343162 :         ptr += charlen;
     104       343162 :         lptr->wlen++;
     105       343162 :         pos++;
     106              :     }
     107              : 
     108         4854 :     if (state == LTPRS_WAITDELIM)
     109              :     {
     110         4844 :         if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
     111            0 :             return NULL;
     112         4843 :         totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
     113         4843 :         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         4850 :     result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
     122         4850 :     SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
     123         4850 :     result->numlevel = lptr - list;
     124         4850 :     curlevel = LTREE_FIRST(result);
     125         4850 :     lptr = list;
     126       166917 :     while (lptr - list < result->numlevel)
     127              :     {
     128       162067 :         curlevel->len = (uint16) lptr->len;
     129       162067 :         memcpy(curlevel->name, lptr->start, lptr->len);
     130       162067 :         curlevel = LEVEL_NEXT(curlevel);
     131       162067 :         lptr++;
     132              :     }
     133              : 
     134         4850 :     pfree(list);
     135         4850 :     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         4860 : ltree_in(PG_FUNCTION_ARGS)
     176              : {
     177         4860 :     char       *buf = (char *) PG_GETARG_POINTER(0);
     178              :     ltree      *res;
     179              : 
     180         4860 :     if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
     181            4 :         PG_RETURN_NULL();
     182              : 
     183         4850 :     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       293817 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
     601              :                 struct Node *escontext)
     602              : {
     603       293817 :     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       293817 :     lptr->len = ptr - lptr->start;
     619              : 
     620              :     /* Complain if it's empty or too long */
     621       293817 :     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       293814 :     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       293811 :     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