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

Generated by: LCOV version 1.14