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

Generated by: LCOV version 1.13