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

Generated by: LCOV version 1.14