LCOV - code coverage report
Current view: top level - contrib/ltree - ltree_io.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 464 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 21 0
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            0 : parse_ltree(const char *buf, struct Node *escontext)
      37              : {
      38            0 :         const char *ptr;
      39            0 :         nodeitem   *list,
      40              :                            *lptr;
      41            0 :         int                     num = 0,
      42            0 :                                 totallen = 0;
      43            0 :         int                     state = LTPRS_WAITNAME;
      44            0 :         ltree      *result;
      45            0 :         ltree_level *curlevel;
      46            0 :         int                     charlen;
      47            0 :         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            0 :         ptr = buf;
      55            0 :         while (*ptr)
      56              :         {
      57            0 :                 charlen = pg_mblen(ptr);
      58            0 :                 if (t_iseq(ptr, '.'))
      59            0 :                         num++;
      60            0 :                 ptr += charlen;
      61              :         }
      62              : 
      63            0 :         if (num + 1 > LTREE_MAX_LEVELS)
      64            0 :                 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            0 :         list = lptr = palloc_array(nodeitem, num + 1);
      69            0 :         ptr = buf;
      70            0 :         while (*ptr)
      71              :         {
      72            0 :                 charlen = pg_mblen(ptr);
      73              : 
      74            0 :                 switch (state)
      75              :                 {
      76              :                         case LTPRS_WAITNAME:
      77            0 :                                 if (ISLABEL(ptr))
      78              :                                 {
      79            0 :                                         lptr->start = ptr;
      80            0 :                                         lptr->wlen = 0;
      81            0 :                                         state = LTPRS_WAITDELIM;
      82            0 :                                 }
      83              :                                 else
      84            0 :                                         UNCHAR;
      85            0 :                                 break;
      86              :                         case LTPRS_WAITDELIM:
      87            0 :                                 if (t_iseq(ptr, '.'))
      88              :                                 {
      89            0 :                                         if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
      90            0 :                                                 return NULL;
      91            0 :                                         totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
      92            0 :                                         lptr++;
      93            0 :                                         state = LTPRS_WAITNAME;
      94            0 :                                 }
      95            0 :                                 else if (!ISLABEL(ptr))
      96            0 :                                         UNCHAR;
      97            0 :                                 break;
      98              :                         default:
      99            0 :                                 elog(ERROR, "internal error in ltree parser");
     100            0 :                 }
     101              : 
     102            0 :                 ptr += charlen;
     103            0 :                 lptr->wlen++;
     104            0 :                 pos++;
     105              :         }
     106              : 
     107            0 :         if (state == LTPRS_WAITDELIM)
     108              :         {
     109            0 :                 if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
     110            0 :                         return NULL;
     111            0 :                 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
     112            0 :                 lptr++;
     113            0 :         }
     114            0 :         else if (!(state == LTPRS_WAITNAME && lptr == list))
     115            0 :                 ereturn(escontext, NULL,
     116              :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     117              :                                  errmsg("ltree syntax error"),
     118              :                                  errdetail("Unexpected end of input.")));
     119              : 
     120            0 :         result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
     121            0 :         SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
     122            0 :         result->numlevel = lptr - list;
     123            0 :         curlevel = LTREE_FIRST(result);
     124            0 :         lptr = list;
     125            0 :         while (lptr - list < result->numlevel)
     126              :         {
     127            0 :                 curlevel->len = (uint16) lptr->len;
     128            0 :                 memcpy(curlevel->name, lptr->start, lptr->len);
     129            0 :                 curlevel = LEVEL_NEXT(curlevel);
     130            0 :                 lptr++;
     131              :         }
     132              : 
     133            0 :         pfree(list);
     134            0 :         return result;
     135              : 
     136              : #undef UNCHAR
     137            0 : }
     138              : 
     139              : /*
     140              :  * expects an ltree
     141              :  * returns a null terminated string
     142              :  */
     143              : static char *
     144            0 : deparse_ltree(const ltree *in)
     145              : {
     146            0 :         char       *buf,
     147              :                            *ptr;
     148            0 :         int                     i;
     149            0 :         ltree_level *curlevel;
     150              : 
     151            0 :         ptr = buf = (char *) palloc(VARSIZE(in));
     152            0 :         curlevel = LTREE_FIRST(in);
     153            0 :         for (i = 0; i < in->numlevel; i++)
     154              :         {
     155            0 :                 if (i != 0)
     156              :                 {
     157            0 :                         *ptr = '.';
     158            0 :                         ptr++;
     159            0 :                 }
     160            0 :                 memcpy(ptr, curlevel->name, curlevel->len);
     161            0 :                 ptr += curlevel->len;
     162            0 :                 curlevel = LEVEL_NEXT(curlevel);
     163            0 :         }
     164              : 
     165            0 :         *ptr = '\0';
     166            0 :         return buf;
     167            0 : }
     168              : 
     169              : /*
     170              :  * Basic ltree I/O functions
     171              :  */
     172            0 : PG_FUNCTION_INFO_V1(ltree_in);
     173              : Datum
     174            0 : ltree_in(PG_FUNCTION_ARGS)
     175              : {
     176            0 :         char       *buf = (char *) PG_GETARG_POINTER(0);
     177            0 :         ltree      *res;
     178              : 
     179            0 :         if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
     180            0 :                 PG_RETURN_NULL();
     181              : 
     182            0 :         PG_RETURN_POINTER(res);
     183            0 : }
     184              : 
     185            0 : PG_FUNCTION_INFO_V1(ltree_out);
     186              : Datum
     187            0 : ltree_out(PG_FUNCTION_ARGS)
     188              : {
     189            0 :         ltree      *in = PG_GETARG_LTREE_P(0);
     190              : 
     191            0 :         PG_RETURN_POINTER(deparse_ltree(in));
     192            0 : }
     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            0 : 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            0 :         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            0 : }
     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            0 : 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            0 :         char       *str;
     234            0 :         int                     nbytes;
     235            0 :         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            0 : }
     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            0 : parse_lquery(const char *buf, struct Node *escontext)
     269              : {
     270            0 :         const char *ptr;
     271            0 :         int                     num = 0,
     272            0 :                                 totallen = 0,
     273            0 :                                 numOR = 0;
     274            0 :         int                     state = LQPRS_WAITLEVEL;
     275            0 :         lquery     *result;
     276            0 :         nodeitem   *lptr = NULL;
     277            0 :         lquery_level *cur,
     278              :                            *curqlevel,
     279              :                            *tmpql;
     280            0 :         lquery_variant *lrptr = NULL;
     281            0 :         bool            hasnot = false;
     282            0 :         bool            wasbad = false;
     283            0 :         int                     charlen;
     284            0 :         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            0 :         ptr = buf;
     292            0 :         while (*ptr)
     293              :         {
     294            0 :                 charlen = pg_mblen(ptr);
     295              : 
     296            0 :                 if (t_iseq(ptr, '.'))
     297            0 :                         num++;
     298            0 :                 else if (t_iseq(ptr, '|'))
     299            0 :                         numOR++;
     300              : 
     301            0 :                 ptr += charlen;
     302              :         }
     303              : 
     304            0 :         num++;
     305            0 :         if (num > LQUERY_MAX_LEVELS)
     306            0 :                 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            0 :         curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
     311            0 :         ptr = buf;
     312            0 :         while (*ptr)
     313              :         {
     314            0 :                 charlen = pg_mblen(ptr);
     315              : 
     316            0 :                 switch (state)
     317              :                 {
     318              :                         case LQPRS_WAITLEVEL:
     319            0 :                                 if (ISLABEL(ptr))
     320              :                                 {
     321            0 :                                         GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
     322            0 :                                         lptr->start = ptr;
     323            0 :                                         state = LQPRS_WAITDELIM;
     324            0 :                                         curqlevel->numvar = 1;
     325            0 :                                 }
     326            0 :                                 else if (t_iseq(ptr, '!'))
     327              :                                 {
     328            0 :                                         GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
     329            0 :                                         lptr->start = ptr + 1;
     330            0 :                                         lptr->wlen = -1;     /* compensate for counting ! below */
     331            0 :                                         state = LQPRS_WAITDELIM;
     332            0 :                                         curqlevel->numvar = 1;
     333            0 :                                         curqlevel->flag |= LQL_NOT;
     334            0 :                                         hasnot = true;
     335            0 :                                 }
     336            0 :                                 else if (t_iseq(ptr, '*'))
     337            0 :                                         state = LQPRS_WAITOPEN;
     338              :                                 else
     339            0 :                                         UNCHAR;
     340            0 :                                 break;
     341              :                         case LQPRS_WAITVAR:
     342            0 :                                 if (ISLABEL(ptr))
     343              :                                 {
     344            0 :                                         lptr++;
     345            0 :                                         lptr->start = ptr;
     346            0 :                                         state = LQPRS_WAITDELIM;
     347            0 :                                         curqlevel->numvar++;
     348            0 :                                 }
     349              :                                 else
     350            0 :                                         UNCHAR;
     351            0 :                                 break;
     352              :                         case LQPRS_WAITDELIM:
     353            0 :                                 if (t_iseq(ptr, '@'))
     354              :                                 {
     355            0 :                                         lptr->flag |= LVAR_INCASE;
     356            0 :                                         curqlevel->flag |= LVAR_INCASE;
     357            0 :                                 }
     358            0 :                                 else if (t_iseq(ptr, '*'))
     359              :                                 {
     360            0 :                                         lptr->flag |= LVAR_ANYEND;
     361            0 :                                         curqlevel->flag |= LVAR_ANYEND;
     362            0 :                                 }
     363            0 :                                 else if (t_iseq(ptr, '%'))
     364              :                                 {
     365            0 :                                         lptr->flag |= LVAR_SUBLEXEME;
     366            0 :                                         curqlevel->flag |= LVAR_SUBLEXEME;
     367            0 :                                 }
     368            0 :                                 else if (t_iseq(ptr, '|'))
     369              :                                 {
     370            0 :                                         if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     371            0 :                                                 return NULL;
     372            0 :                                         state = LQPRS_WAITVAR;
     373            0 :                                 }
     374            0 :                                 else if (t_iseq(ptr, '{'))
     375              :                                 {
     376            0 :                                         if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     377            0 :                                                 return NULL;
     378            0 :                                         curqlevel->flag |= LQL_COUNT;
     379            0 :                                         state = LQPRS_WAITFNUM;
     380            0 :                                 }
     381            0 :                                 else if (t_iseq(ptr, '.'))
     382              :                                 {
     383            0 :                                         if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     384            0 :                                                 return NULL;
     385            0 :                                         state = LQPRS_WAITLEVEL;
     386            0 :                                         curqlevel = NEXTLEV(curqlevel);
     387            0 :                                 }
     388            0 :                                 else if (ISLABEL(ptr))
     389              :                                 {
     390              :                                         /* disallow more chars after a flag */
     391            0 :                                         if (lptr->flag)
     392            0 :                                                 UNCHAR;
     393            0 :                                 }
     394              :                                 else
     395            0 :                                         UNCHAR;
     396            0 :                                 break;
     397              :                         case LQPRS_WAITOPEN:
     398            0 :                                 if (t_iseq(ptr, '{'))
     399            0 :                                         state = LQPRS_WAITFNUM;
     400            0 :                                 else if (t_iseq(ptr, '.'))
     401              :                                 {
     402              :                                         /* We only get here for '*', so these are correct defaults */
     403            0 :                                         curqlevel->low = 0;
     404            0 :                                         curqlevel->high = LTREE_MAX_LEVELS;
     405            0 :                                         curqlevel = NEXTLEV(curqlevel);
     406            0 :                                         state = LQPRS_WAITLEVEL;
     407            0 :                                 }
     408              :                                 else
     409            0 :                                         UNCHAR;
     410            0 :                                 break;
     411              :                         case LQPRS_WAITFNUM:
     412            0 :                                 if (t_iseq(ptr, ','))
     413            0 :                                         state = LQPRS_WAITSNUM;
     414            0 :                                 else if (isdigit((unsigned char) *ptr))
     415              :                                 {
     416            0 :                                         int                     low = atoi(ptr);
     417              : 
     418            0 :                                         if (low < 0 || low > LTREE_MAX_LEVELS)
     419            0 :                                                 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            0 :                                         curqlevel->low = (uint16) low;
     426            0 :                                         state = LQPRS_WAITND;
     427            0 :                                 }
     428              :                                 else
     429            0 :                                         UNCHAR;
     430            0 :                                 break;
     431              :                         case LQPRS_WAITSNUM:
     432            0 :                                 if (isdigit((unsigned char) *ptr))
     433              :                                 {
     434            0 :                                         int                     high = atoi(ptr);
     435              : 
     436            0 :                                         if (high < 0 || high > LTREE_MAX_LEVELS)
     437            0 :                                                 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            0 :                                         else if (curqlevel->low > high)
     443            0 :                                                 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            0 :                                         curqlevel->high = (uint16) high;
     450            0 :                                         state = LQPRS_WAITCLOSE;
     451            0 :                                 }
     452            0 :                                 else if (t_iseq(ptr, '}'))
     453              :                                 {
     454            0 :                                         curqlevel->high = LTREE_MAX_LEVELS;
     455            0 :                                         state = LQPRS_WAITEND;
     456            0 :                                 }
     457              :                                 else
     458            0 :                                         UNCHAR;
     459            0 :                                 break;
     460              :                         case LQPRS_WAITCLOSE:
     461            0 :                                 if (t_iseq(ptr, '}'))
     462            0 :                                         state = LQPRS_WAITEND;
     463            0 :                                 else if (!isdigit((unsigned char) *ptr))
     464            0 :                                         UNCHAR;
     465            0 :                                 break;
     466              :                         case LQPRS_WAITND:
     467            0 :                                 if (t_iseq(ptr, '}'))
     468              :                                 {
     469            0 :                                         curqlevel->high = curqlevel->low;
     470            0 :                                         state = LQPRS_WAITEND;
     471            0 :                                 }
     472            0 :                                 else if (t_iseq(ptr, ','))
     473            0 :                                         state = LQPRS_WAITSNUM;
     474            0 :                                 else if (!isdigit((unsigned char) *ptr))
     475            0 :                                         UNCHAR;
     476            0 :                                 break;
     477              :                         case LQPRS_WAITEND:
     478            0 :                                 if (t_iseq(ptr, '.'))
     479              :                                 {
     480            0 :                                         state = LQPRS_WAITLEVEL;
     481            0 :                                         curqlevel = NEXTLEV(curqlevel);
     482            0 :                                 }
     483              :                                 else
     484            0 :                                         UNCHAR;
     485            0 :                                 break;
     486              :                         default:
     487            0 :                                 elog(ERROR, "internal error in lquery parser");
     488            0 :                 }
     489              : 
     490            0 :                 ptr += charlen;
     491            0 :                 if (state == LQPRS_WAITDELIM)
     492            0 :                         lptr->wlen++;
     493            0 :                 pos++;
     494              :         }
     495              : 
     496            0 :         if (state == LQPRS_WAITDELIM)
     497              :         {
     498            0 :                 if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
     499            0 :                         return NULL;
     500            0 :         }
     501            0 :         else if (state == LQPRS_WAITOPEN)
     502            0 :                 curqlevel->high = LTREE_MAX_LEVELS;
     503            0 :         else if (state != LQPRS_WAITEND)
     504            0 :                 ereturn(escontext, NULL,
     505              :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     506              :                                  errmsg("lquery syntax error"),
     507              :                                  errdetail("Unexpected end of input.")));
     508              : 
     509            0 :         curqlevel = tmpql;
     510            0 :         totallen = LQUERY_HDRSIZE;
     511            0 :         while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
     512              :         {
     513            0 :                 totallen += LQL_HDRSIZE;
     514            0 :                 if (curqlevel->numvar)
     515              :                 {
     516            0 :                         lptr = GETVAR(curqlevel);
     517            0 :                         while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
     518              :                         {
     519            0 :                                 totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
     520            0 :                                 lptr++;
     521              :                         }
     522            0 :                 }
     523            0 :                 curqlevel = NEXTLEV(curqlevel);
     524              :         }
     525              : 
     526            0 :         result = (lquery *) palloc0(totallen);
     527            0 :         SET_VARSIZE(result, totallen);
     528            0 :         result->numlevel = num;
     529            0 :         result->firstgood = 0;
     530            0 :         result->flag = 0;
     531            0 :         if (hasnot)
     532            0 :                 result->flag |= LQUERY_HASNOT;
     533            0 :         cur = LQUERY_FIRST(result);
     534            0 :         curqlevel = tmpql;
     535            0 :         while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
     536              :         {
     537            0 :                 memcpy(cur, curqlevel, LQL_HDRSIZE);
     538            0 :                 cur->totallen = LQL_HDRSIZE;
     539            0 :                 if (curqlevel->numvar)
     540              :                 {
     541            0 :                         lrptr = LQL_FIRST(cur);
     542            0 :                         lptr = GETVAR(curqlevel);
     543            0 :                         while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
     544              :                         {
     545            0 :                                 cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
     546            0 :                                 lrptr->len = lptr->len;
     547            0 :                                 lrptr->flag = lptr->flag;
     548            0 :                                 lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
     549            0 :                                 memcpy(lrptr->name, lptr->start, lptr->len);
     550            0 :                                 lptr++;
     551            0 :                                 lrptr = LVAR_NEXT(lrptr);
     552              :                         }
     553            0 :                         pfree(GETVAR(curqlevel));
     554            0 :                         if (cur->numvar > 1 || cur->flag != 0)
     555              :                         {
     556              :                                 /* Not a simple match */
     557            0 :                                 wasbad = true;
     558            0 :                         }
     559            0 :                         else if (wasbad == false)
     560              :                         {
     561              :                                 /* count leading simple matches */
     562            0 :                                 (result->firstgood)++;
     563            0 :                         }
     564            0 :                 }
     565              :                 else
     566              :                 {
     567              :                         /* '*', so this isn't a simple match */
     568            0 :                         wasbad = true;
     569              :                 }
     570            0 :                 curqlevel = NEXTLEV(curqlevel);
     571            0 :                 cur = LQL_NEXT(cur);
     572              :         }
     573              : 
     574            0 :         pfree(tmpql);
     575            0 :         return result;
     576              : 
     577              : #undef UNCHAR
     578            0 : }
     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            0 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
     586              :                                 struct Node *escontext)
     587              : {
     588            0 :         if (is_lquery)
     589              :         {
     590              :                 /*
     591              :                  * Back up over any flag characters, and discount them from length and
     592              :                  * position.
     593              :                  */
     594            0 :                 while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL)
     595              :                 {
     596            0 :                         ptr--;
     597            0 :                         lptr->wlen--;
     598            0 :                         pos--;
     599              :                 }
     600            0 :         }
     601              : 
     602              :         /* Now compute the byte length, which we weren't tracking before. */
     603            0 :         lptr->len = ptr - lptr->start;
     604              : 
     605              :         /* Complain if it's empty or too long */
     606            0 :         if (lptr->len == 0)
     607            0 :                 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            0 :         if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
     614            0 :                 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            0 :         return true;
     620            0 : }
     621              : 
     622              : /*
     623              :  * expects an lquery
     624              :  * returns a null terminated string
     625              :  */
     626              : static char *
     627            0 : deparse_lquery(const lquery *in)
     628              : {
     629            0 :         char       *buf,
     630              :                            *ptr;
     631            0 :         int                     i,
     632              :                                 j,
     633            0 :                                 totallen = 1;
     634            0 :         lquery_level *curqlevel;
     635            0 :         lquery_variant *curtlevel;
     636              : 
     637            0 :         curqlevel = LQUERY_FIRST(in);
     638            0 :         for (i = 0; i < in->numlevel; i++)
     639              :         {
     640            0 :                 totallen++;
     641            0 :                 if (curqlevel->numvar)
     642              :                 {
     643            0 :                         totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
     644            0 :                         if (curqlevel->flag & LQL_COUNT)
     645            0 :                                 totallen += 2 * 11 + 3;
     646            0 :                 }
     647              :                 else
     648            0 :                         totallen += 2 * 11 + 4;
     649            0 :                 curqlevel = LQL_NEXT(curqlevel);
     650            0 :         }
     651              : 
     652            0 :         ptr = buf = (char *) palloc(totallen);
     653            0 :         curqlevel = LQUERY_FIRST(in);
     654            0 :         for (i = 0; i < in->numlevel; i++)
     655              :         {
     656            0 :                 if (i != 0)
     657              :                 {
     658            0 :                         *ptr = '.';
     659            0 :                         ptr++;
     660            0 :                 }
     661            0 :                 if (curqlevel->numvar)
     662              :                 {
     663            0 :                         if (curqlevel->flag & LQL_NOT)
     664              :                         {
     665            0 :                                 *ptr = '!';
     666            0 :                                 ptr++;
     667            0 :                         }
     668            0 :                         curtlevel = LQL_FIRST(curqlevel);
     669            0 :                         for (j = 0; j < curqlevel->numvar; j++)
     670              :                         {
     671            0 :                                 if (j != 0)
     672              :                                 {
     673            0 :                                         *ptr = '|';
     674            0 :                                         ptr++;
     675            0 :                                 }
     676            0 :                                 memcpy(ptr, curtlevel->name, curtlevel->len);
     677            0 :                                 ptr += curtlevel->len;
     678            0 :                                 if ((curtlevel->flag & LVAR_SUBLEXEME))
     679              :                                 {
     680            0 :                                         *ptr = '%';
     681            0 :                                         ptr++;
     682            0 :                                 }
     683            0 :                                 if ((curtlevel->flag & LVAR_INCASE))
     684              :                                 {
     685            0 :                                         *ptr = '@';
     686            0 :                                         ptr++;
     687            0 :                                 }
     688            0 :                                 if ((curtlevel->flag & LVAR_ANYEND))
     689              :                                 {
     690            0 :                                         *ptr = '*';
     691            0 :                                         ptr++;
     692            0 :                                 }
     693            0 :                                 curtlevel = LVAR_NEXT(curtlevel);
     694            0 :                         }
     695            0 :                 }
     696              :                 else
     697              :                 {
     698            0 :                         *ptr = '*';
     699            0 :                         ptr++;
     700              :                 }
     701              : 
     702            0 :                 if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
     703              :                 {
     704            0 :                         if (curqlevel->low == curqlevel->high)
     705              :                         {
     706            0 :                                 sprintf(ptr, "{%d}", curqlevel->low);
     707            0 :                         }
     708            0 :                         else if (curqlevel->low == 0)
     709              :                         {
     710            0 :                                 if (curqlevel->high == LTREE_MAX_LEVELS)
     711              :                                 {
     712            0 :                                         if (curqlevel->numvar == 0)
     713              :                                         {
     714              :                                                 /* This is default for '*', so print nothing */
     715            0 :                                                 *ptr = '\0';
     716            0 :                                         }
     717              :                                         else
     718            0 :                                                 sprintf(ptr, "{,}");
     719            0 :                                 }
     720              :                                 else
     721            0 :                                         sprintf(ptr, "{,%d}", curqlevel->high);
     722            0 :                         }
     723            0 :                         else if (curqlevel->high == LTREE_MAX_LEVELS)
     724              :                         {
     725            0 :                                 sprintf(ptr, "{%d,}", curqlevel->low);
     726            0 :                         }
     727              :                         else
     728            0 :                                 sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
     729            0 :                         ptr = strchr(ptr, '\0');
     730            0 :                 }
     731              : 
     732            0 :                 curqlevel = LQL_NEXT(curqlevel);
     733            0 :         }
     734              : 
     735            0 :         *ptr = '\0';
     736            0 :         return buf;
     737            0 : }
     738              : 
     739              : /*
     740              :  * Basic lquery I/O functions
     741              :  */
     742            0 : PG_FUNCTION_INFO_V1(lquery_in);
     743              : Datum
     744            0 : lquery_in(PG_FUNCTION_ARGS)
     745              : {
     746            0 :         char       *buf = (char *) PG_GETARG_POINTER(0);
     747            0 :         lquery     *res;
     748              : 
     749            0 :         if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
     750            0 :                 PG_RETURN_NULL();
     751              : 
     752            0 :         PG_RETURN_POINTER(res);
     753            0 : }
     754              : 
     755            0 : PG_FUNCTION_INFO_V1(lquery_out);
     756              : Datum
     757            0 : lquery_out(PG_FUNCTION_ARGS)
     758              : {
     759            0 :         lquery     *in = PG_GETARG_LQUERY_P(0);
     760              : 
     761            0 :         PG_RETURN_POINTER(deparse_lquery(in));
     762            0 : }
     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            0 : 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            0 :         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            0 : }
     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            0 : 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            0 :         char       *str;
     804            0 :         int                     nbytes;
     805            0 :         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            0 : }
        

Generated by: LCOV version 2.3.2-1