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

Generated by: LCOV version 2.0-1