LCOV - code coverage report
Current view: top level - contrib/ltree - ltxtquery_io.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.0 % 295 230
Test Date: 2026-05-25 14:16:17 Functions: 86.7 % 15 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * txtquery io
       3              :  * Teodor Sigaev <teodor@stack.net>
       4              :  * contrib/ltree/ltxtquery_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 "miscadmin.h"
      14              : #include "nodes/miscnodes.h"
      15              : #include "varatt.h"
      16              : 
      17              : 
      18              : /* parser's states */
      19              : #define WAITOPERAND 1
      20              : #define INOPERAND 2
      21              : #define WAITOPERATOR    3
      22              : 
      23              : /*
      24              :  * node of query tree, also used
      25              :  * for storing polish notation in parser
      26              :  */
      27              : typedef struct NODE
      28              : {
      29              :     int32       type;
      30              :     int32       val;
      31              :     int16       distance;
      32              :     int16       length;
      33              :     uint16      flag;
      34              :     struct NODE *next;
      35              : } NODE;
      36              : 
      37              : typedef struct
      38              : {
      39              :     char       *buf;
      40              :     int32       state;
      41              :     int32       count;
      42              :     struct Node *escontext;
      43              :     /* reverse polish notation in list (for temporary usage) */
      44              :     NODE       *str;
      45              :     /* number in str */
      46              :     int32       num;
      47              : 
      48              :     /* user-friendly operand */
      49              :     int32       lenop;
      50              :     int32       sumlen;
      51              :     char       *op;
      52              :     char       *curop;
      53              : } QPRS_STATE;
      54              : 
      55              : /*
      56              :  * get token from query string
      57              :  *
      58              :  * caller needs to check if a soft-error was set if the result is ERR.
      59              :  */
      60              : static int32
      61        65644 : gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
      62              : {
      63              :     int         charlen;
      64              : 
      65              :     for (;;)
      66              :     {
      67        82286 :         charlen = pg_mblen_cstr(state->buf);
      68              : 
      69        82286 :         switch (state->state)
      70              :         {
      71        32851 :             case WAITOPERAND:
      72        32851 :                 if (t_iseq(state->buf, '!'))
      73              :                 {
      74            6 :                     (state->buf)++;
      75            6 :                     *val = (int32) '!';
      76            6 :                     return OPR;
      77              :                 }
      78        32845 :                 else if (t_iseq(state->buf, '('))
      79              :                 {
      80        16383 :                     state->count++;
      81        16383 :                     (state->buf)++;
      82        16383 :                     return OPEN;
      83              :                 }
      84        16462 :                 else if (ISLABEL(state->buf))
      85              :                 {
      86        16435 :                     state->state = INOPERAND;
      87        16435 :                     *strval = state->buf;
      88        16435 :                     *lenval = charlen;
      89        16435 :                     *flag = 0;
      90              :                 }
      91           27 :                 else if (!isspace((unsigned char) *state->buf))
      92            2 :                     ereturn(state->escontext, ERR,
      93              :                             (errcode(ERRCODE_SYNTAX_ERROR),
      94              :                              errmsg("operand syntax error")));
      95        16460 :                 break;
      96        16592 :             case INOPERAND:
      97        16592 :                 if (ISLABEL(state->buf))
      98              :                 {
      99          127 :                     if (*flag)
     100            0 :                         ereturn(state->escontext, ERR,
     101              :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     102              :                                  errmsg("modifiers syntax error")));
     103          127 :                     *lenval += charlen;
     104              :                 }
     105        16465 :                 else if (t_iseq(state->buf, '%'))
     106            4 :                     *flag |= LVAR_SUBLEXEME;
     107        16461 :                 else if (t_iseq(state->buf, '@'))
     108           12 :                     *flag |= LVAR_INCASE;
     109        16449 :                 else if (t_iseq(state->buf, '*'))
     110           14 :                     *flag |= LVAR_ANYEND;
     111              :                 else
     112              :                 {
     113        16435 :                     state->state = WAITOPERATOR;
     114        16435 :                     return VAL;
     115              :                 }
     116          157 :                 break;
     117        32843 :             case WAITOPERATOR:
     118        32843 :                 if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|'))
     119              :                 {
     120        16409 :                     state->state = WAITOPERAND;
     121        16409 :                     *val = (int32) *(state->buf);
     122        16409 :                     (state->buf)++;
     123        16409 :                     return OPR;
     124              :                 }
     125        16434 :                 else if (t_iseq(state->buf, ')'))
     126              :                 {
     127        16383 :                     (state->buf)++;
     128        16383 :                     state->count--;
     129        16383 :                     return (state->count < 0) ? ERR : CLOSE;
     130              :                 }
     131           51 :                 else if (*(state->buf) == '\0')
     132              :                 {
     133           26 :                     return (state->count) ? ERR : END;
     134              :                 }
     135           25 :                 else if (!t_iseq(state->buf, ' '))
     136              :                 {
     137            0 :                     return ERR;
     138              :                 }
     139           25 :                 break;
     140            0 :             default:
     141            0 :                 return ERR;
     142              :                 break;
     143              :         }
     144              : 
     145        16642 :         state->buf += charlen;
     146              :     }
     147              : 
     148              :     /* should not get here */
     149              : }
     150              : 
     151              : /*
     152              :  * push new one in polish notation reverse view
     153              :  */
     154              : static bool
     155        32850 : pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
     156              : {
     157        32850 :     NODE       *tmp = palloc_object(NODE);
     158              : 
     159        32850 :     tmp->type = type;
     160        32850 :     tmp->val = val;
     161        32850 :     tmp->flag = flag;
     162        32850 :     if (distance > 0xffff)
     163            0 :         ereturn(state->escontext, false,
     164              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     165              :                  errmsg("value is too big")));
     166        32850 :     if (lenval > 0xff)
     167            0 :         ereturn(state->escontext, false,
     168              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     169              :                  errmsg("operand is too long")));
     170        32850 :     tmp->distance = distance;
     171        32850 :     tmp->length = lenval;
     172        32850 :     tmp->next = state->str;
     173        32850 :     state->str = tmp;
     174        32850 :     state->num++;
     175        32850 :     return true;
     176              : }
     177              : 
     178              : /*
     179              :  * This function is used for query text parsing
     180              :  */
     181              : static bool
     182        16435 : pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
     183              : {
     184        16435 :     if (lenval > 0xffff)
     185            0 :         ereturn(state->escontext, false,
     186              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     187              :                  errmsg("word is too long")));
     188              : 
     189        16435 :     if (!pushquery(state, type, ltree_crc32_sz(strval, lenval),
     190        16435 :                    state->curop - state->op, lenval, flag))
     191            0 :         return false;
     192              : 
     193        16445 :     while (state->curop - state->op + lenval + 1 >= state->lenop)
     194              :     {
     195           10 :         int32       tmp = state->curop - state->op;
     196              : 
     197           10 :         state->lenop *= 2;
     198           10 :         state->op = (char *) repalloc(state->op, state->lenop);
     199           10 :         state->curop = state->op + tmp;
     200              :     }
     201        16435 :     memcpy(state->curop, strval, lenval);
     202        16435 :     state->curop += lenval;
     203        16435 :     *(state->curop) = '\0';
     204        16435 :     state->curop++;
     205        16435 :     state->sumlen += lenval + 1;
     206        16435 :     return true;
     207              : }
     208              : 
     209              : #define STACKDEPTH      32
     210              : /*
     211              :  * make polish notation of query
     212              :  */
     213              : static int32
     214        16411 : makepol(QPRS_STATE *state)
     215              : {
     216        16411 :     int32       val = 0,
     217              :                 type;
     218        16411 :     int32       lenval = 0;
     219        16411 :     char       *strval = NULL;
     220              :     int32       stack[STACKDEPTH];
     221        16411 :     int32       lenstack = 0;
     222        16411 :     uint16      flag = 0;
     223              : 
     224              :     /* since this function recurses, it could be driven to stack overflow */
     225        16411 :     check_stack_depth();
     226              : 
     227        65644 :     while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
     228              :     {
     229        65618 :         switch (type)
     230              :         {
     231        16435 :             case VAL:
     232        16435 :                 if (!pushval_asis(state, VAL, strval, lenval, flag))
     233            0 :                     return ERR;
     234        24656 :                 while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
     235            8 :                                     stack[lenstack - 1] == (int32) '!'))
     236              :                 {
     237         8221 :                     lenstack--;
     238         8221 :                     if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     239            0 :                         return ERR;
     240              :                 }
     241        16435 :                 break;
     242        16415 :             case OPR:
     243        16415 :                 if (lenstack && val == (int32) '|')
     244              :                 {
     245            0 :                     if (!pushquery(state, OPR, val, 0, 0, 0))
     246            0 :                         return ERR;
     247              :                 }
     248              :                 else
     249              :                 {
     250        16415 :                     if (lenstack == STACKDEPTH)
     251              :                         /* internal error */
     252            0 :                         elog(ERROR, "stack too short");
     253        16415 :                     stack[lenstack] = val;
     254        16415 :                     lenstack++;
     255              :                 }
     256        16415 :                 break;
     257        16383 :             case OPEN:
     258        16383 :                 if (makepol(state) == ERR)
     259            0 :                     return ERR;
     260        24574 :                 while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
     261            1 :                                     stack[lenstack - 1] == (int32) '!'))
     262              :                 {
     263         8191 :                     lenstack--;
     264         8191 :                     if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     265            0 :                         return ERR;
     266              :                 }
     267        16383 :                 break;
     268        16383 :             case CLOSE:
     269        16383 :                 while (lenstack)
     270              :                 {
     271            0 :                     lenstack--;
     272            0 :                     if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     273            0 :                         return ERR;
     274              :                 };
     275        16383 :                 return END;
     276              :                 break;
     277            2 :             case ERR:
     278            2 :                 if (SOFT_ERROR_OCCURRED(state->escontext))
     279            2 :                     return ERR;
     280              :                 pg_fallthrough;
     281              :             default:
     282            0 :                 ereturn(state->escontext, ERR,
     283              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     284              :                          errmsg("syntax error")));
     285              : 
     286              :         }
     287              :     }
     288           29 :     while (lenstack)
     289              :     {
     290            3 :         lenstack--;
     291            3 :         if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     292            0 :             return ERR;
     293              :     };
     294           26 :     return END;
     295              : }
     296              : 
     297              : /*
     298              :  * Recursively fill the "left" fields of an ITEM array that represents
     299              :  * a valid postfix tree.
     300              :  *
     301              :  *  state: only needed for error reporting
     302              :  *  ptr: starting element of array
     303              :  *  pos: in/out argument, the array index this call is responsible to fill
     304              :  *
     305              :  * At exit, *pos has been incremented to point after the sub-tree whose
     306              :  * top is the entry-time value of *pos.
     307              :  *
     308              :  * Returns true if okay, false if error (the only possible error is
     309              :  * overflow of a "left" field).
     310              :  */
     311              : static bool
     312        32849 : findoprnd(QPRS_STATE *state, ITEM *ptr, int32 *pos)
     313              : {
     314              :     int32       mypos;
     315              : 
     316              :     /* since this function recurses, it could be driven to stack overflow. */
     317        32849 :     check_stack_depth();
     318              : 
     319              :     /* get the position this call is supposed to update */
     320        32849 :     mypos = *pos;
     321              : 
     322              :     /* in all cases, we should increment *pos to advance over this item */
     323        32849 :     (*pos)++;
     324              : 
     325        32849 :     if (ptr[mypos].type == VAL || ptr[mypos].type == VALTRUE)
     326              :     {
     327              :         /* base case: a VAL has no operand, so just set its left to zero */
     328        16434 :         ptr[mypos].left = 0;
     329              :     }
     330        16415 :     else if (ptr[mypos].val == (int32) '!')
     331              :     {
     332              :         /* unary operator, likewise easy: operand is just after it */
     333            6 :         ptr[mypos].left = 1;
     334              :         /* recurse to scan operand */
     335            6 :         if (!findoprnd(state, ptr, pos))
     336            0 :             return false;
     337              :     }
     338              :     else
     339              :     {
     340              :         /* binary operator */
     341              :         int32       delta;
     342              : 
     343              :         /* recurse to scan right operand */
     344        16409 :         if (!findoprnd(state, ptr, pos))
     345            0 :             return false;
     346              :         /* we must fill left with offset to left operand's top */
     347              :         /* delta can't overflow, see LTXTQUERY_TOO_BIG ... */
     348        16409 :         delta = *pos - mypos;
     349              :         /* ... but it might be too large to fit in the 16-bit left field */
     350              :         Assert(delta > 0);
     351        16409 :         if (unlikely(delta > PG_INT16_MAX))
     352            1 :             ereturn(state->escontext, false,
     353              :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     354              :                      errmsg("ltxtquery is too large")));
     355        16408 :         ptr[mypos].left = (int16) delta;
     356              :         /* recurse to scan left operand */
     357        16408 :         if (!findoprnd(state, ptr, pos))
     358            0 :             return false;
     359              :     }
     360              : 
     361        32848 :     return true;
     362              : }
     363              : 
     364              : 
     365              : /*
     366              :  * input
     367              :  */
     368              : static ltxtquery *
     369           28 : queryin(char *buf, struct Node *escontext)
     370              : {
     371              :     QPRS_STATE  state;
     372              :     int32       i;
     373              :     ltxtquery  *query;
     374              :     int32       commonlen;
     375              :     ITEM       *ptr;
     376              :     NODE       *tmp;
     377           28 :     int32       pos = 0;
     378              : 
     379              :     /* init state */
     380           28 :     state.buf = buf;
     381           28 :     state.state = WAITOPERAND;
     382           28 :     state.count = 0;
     383           28 :     state.num = 0;
     384           28 :     state.str = NULL;
     385           28 :     state.escontext = escontext;
     386              : 
     387              :     /* init list of operand */
     388           28 :     state.sumlen = 0;
     389           28 :     state.lenop = 64;
     390           28 :     state.curop = state.op = (char *) palloc(state.lenop);
     391           28 :     *(state.curop) = '\0';
     392              : 
     393              :     /* parse query & make polish notation (postfix, but in reverse order) */
     394           28 :     if (makepol(&state) == ERR)
     395            2 :         return NULL;
     396           26 :     if (!state.num)
     397            0 :         ereturn(escontext, NULL,
     398              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     399              :                  errmsg("syntax error"),
     400              :                  errdetail("Empty query.")));
     401              : 
     402           26 :     if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
     403            0 :         ereturn(escontext, NULL,
     404              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     405              :                  errmsg("ltxtquery is too large")));
     406           26 :     commonlen = COMPUTESIZE(state.num, state.sumlen);
     407              : 
     408           26 :     query = (ltxtquery *) palloc0(commonlen);
     409           26 :     SET_VARSIZE(query, commonlen);
     410           26 :     query->size = state.num;
     411           26 :     ptr = GETQUERY(query);
     412              : 
     413              :     /* set item in polish notation */
     414        32876 :     for (i = 0; i < state.num; i++)
     415              :     {
     416        32850 :         ptr[i].type = state.str->type;
     417        32850 :         ptr[i].val = state.str->val;
     418        32850 :         ptr[i].distance = state.str->distance;
     419        32850 :         ptr[i].length = state.str->length;
     420        32850 :         ptr[i].flag = state.str->flag;
     421        32850 :         tmp = state.str->next;
     422        32850 :         pfree(state.str);
     423        32850 :         state.str = tmp;
     424              :     }
     425              : 
     426              :     /* set user-friendly operand view */
     427           26 :     memcpy(GETOPERAND(query), state.op, state.sumlen);
     428           26 :     pfree(state.op);
     429              : 
     430              :     /* set left operand's position for every operator */
     431           26 :     pos = 0;
     432           26 :     if (!findoprnd(&state, ptr, &pos))
     433            0 :         return NULL;
     434              :     /* if successful, findoprnd should have scanned the whole array */
     435              :     Assert(pos == state.num);
     436              : 
     437           25 :     return query;
     438              : }
     439              : 
     440              : /*
     441              :  * in without morphology
     442              :  */
     443            3 : PG_FUNCTION_INFO_V1(ltxtq_in);
     444              : Datum
     445           28 : ltxtq_in(PG_FUNCTION_ARGS)
     446              : {
     447              :     ltxtquery  *res;
     448              : 
     449           28 :     if ((res = queryin(PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
     450            2 :         PG_RETURN_NULL();
     451           25 :     PG_RETURN_POINTER(res);
     452              : }
     453              : 
     454              : /*
     455              :  * ltxtquery type recv function
     456              :  *
     457              :  * The type is sent as text in binary mode, so this is almost the same
     458              :  * as the input function, but it's prefixed with a version number so we
     459              :  * can change the binary format sent in future if necessary. For now,
     460              :  * only version 1 is supported.
     461              :  */
     462            2 : PG_FUNCTION_INFO_V1(ltxtq_recv);
     463              : Datum
     464            0 : ltxtq_recv(PG_FUNCTION_ARGS)
     465              : {
     466            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     467            0 :     int         version = pq_getmsgint(buf, 1);
     468              :     char       *str;
     469              :     int         nbytes;
     470              :     ltxtquery  *res;
     471              : 
     472            0 :     if (version != 1)
     473            0 :         elog(ERROR, "unsupported ltxtquery version number %d", version);
     474              : 
     475            0 :     str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     476            0 :     res = queryin(str, NULL);
     477            0 :     pfree(str);
     478              : 
     479            0 :     PG_RETURN_POINTER(res);
     480              : }
     481              : 
     482              : /*
     483              :  * out function
     484              :  */
     485              : typedef struct
     486              : {
     487              :     ITEM       *curpol;
     488              :     char       *buf;
     489              :     char       *cur;
     490              :     char       *op;
     491              :     int32       buflen;
     492              : } INFIX;
     493              : 
     494              : #define RESIZEBUF(inf,addsize) \
     495              : while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
     496              : { \
     497              :     int32 len = (inf)->cur - (inf)->buf; \
     498              :     (inf)->buflen *= 2; \
     499              :     (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
     500              :     (inf)->cur = (inf)->buf + len; \
     501              : }
     502              : 
     503              : /*
     504              :  * recursive walk on tree and print it in
     505              :  * infix (human-readable) view
     506              :  */
     507              : static void
     508           10 : infix(INFIX *in, bool first)
     509              : {
     510              :     /* since this function recurses, it could be driven to stack overflow. */
     511           10 :     check_stack_depth();
     512              : 
     513           10 :     if (in->curpol->type == VAL)
     514              :     {
     515            6 :         char       *op = in->op + in->curpol->distance;
     516              : 
     517            8 :         RESIZEBUF(in, in->curpol->length * 2 + 5);
     518           32 :         while (*op)
     519              :         {
     520           26 :             *(in->cur) = *op;
     521           26 :             op++;
     522           26 :             in->cur++;
     523              :         }
     524            6 :         if (in->curpol->flag & LVAR_SUBLEXEME)
     525              :         {
     526            2 :             *(in->cur) = '%';
     527            2 :             in->cur++;
     528              :         }
     529            6 :         if (in->curpol->flag & LVAR_INCASE)
     530              :         {
     531            1 :             *(in->cur) = '@';
     532            1 :             in->cur++;
     533              :         }
     534            6 :         if (in->curpol->flag & LVAR_ANYEND)
     535              :         {
     536            3 :             *(in->cur) = '*';
     537            3 :             in->cur++;
     538              :         }
     539            6 :         *(in->cur) = '\0';
     540            6 :         in->curpol++;
     541              :     }
     542            4 :     else if (in->curpol->val == (int32) '!')
     543              :     {
     544            1 :         bool        isopr = false;
     545              : 
     546            1 :         RESIZEBUF(in, 1);
     547            1 :         *(in->cur) = '!';
     548            1 :         in->cur++;
     549            1 :         *(in->cur) = '\0';
     550            1 :         in->curpol++;
     551            1 :         if (in->curpol->type == OPR)
     552              :         {
     553            0 :             isopr = true;
     554            0 :             RESIZEBUF(in, 2);
     555            0 :             sprintf(in->cur, "( ");
     556            0 :             in->cur = strchr(in->cur, '\0');
     557              :         }
     558            1 :         infix(in, isopr);
     559            1 :         if (isopr)
     560              :         {
     561            0 :             RESIZEBUF(in, 2);
     562            0 :             sprintf(in->cur, " )");
     563            0 :             in->cur = strchr(in->cur, '\0');
     564              :         }
     565              :     }
     566              :     else
     567              :     {
     568            3 :         int32       op = in->curpol->val;
     569              :         INFIX       nrm;
     570              : 
     571            3 :         in->curpol++;
     572            3 :         if (op == (int32) '|' && !first)
     573              :         {
     574            0 :             RESIZEBUF(in, 2);
     575            0 :             sprintf(in->cur, "( ");
     576            0 :             in->cur = strchr(in->cur, '\0');
     577              :         }
     578              : 
     579            3 :         nrm.curpol = in->curpol;
     580            3 :         nrm.op = in->op;
     581            3 :         nrm.buflen = 16;
     582            3 :         nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
     583              : 
     584              :         /* get right operand */
     585            3 :         infix(&nrm, false);
     586              : 
     587              :         /* get & print left operand */
     588            3 :         in->curpol = nrm.curpol;
     589            3 :         infix(in, false);
     590              : 
     591              :         /* print operator & right operand */
     592            3 :         RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
     593            3 :         sprintf(in->cur, " %c %s", op, nrm.buf);
     594            3 :         in->cur = strchr(in->cur, '\0');
     595            3 :         pfree(nrm.buf);
     596              : 
     597            3 :         if (op == (int32) '|' && !first)
     598              :         {
     599            0 :             RESIZEBUF(in, 2);
     600            0 :             sprintf(in->cur, " )");
     601            0 :             in->cur = strchr(in->cur, '\0');
     602              :         }
     603              :     }
     604           10 : }
     605              : 
     606            3 : PG_FUNCTION_INFO_V1(ltxtq_out);
     607              : Datum
     608            3 : ltxtq_out(PG_FUNCTION_ARGS)
     609              : {
     610            3 :     ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
     611              :     INFIX       nrm;
     612              : 
     613            3 :     if (query->size == 0)
     614            0 :         ereport(ERROR,
     615              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     616              :                  errmsg("syntax error"),
     617              :                  errdetail("Empty query.")));
     618              : 
     619            3 :     nrm.curpol = GETQUERY(query);
     620            3 :     nrm.buflen = 32;
     621            3 :     nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
     622            3 :     *(nrm.cur) = '\0';
     623            3 :     nrm.op = GETOPERAND(query);
     624            3 :     infix(&nrm, true);
     625              : 
     626            3 :     PG_RETURN_POINTER(nrm.buf);
     627              : }
     628              : 
     629              : /*
     630              :  * ltxtquery type send function
     631              :  *
     632              :  * The type is sent as text in binary mode, so this is almost the same
     633              :  * as the output function, but it's prefixed with a version number so we
     634              :  * can change the binary format sent in future if necessary. For now,
     635              :  * only version 1 is supported.
     636              :  */
     637            2 : PG_FUNCTION_INFO_V1(ltxtq_send);
     638              : Datum
     639            0 : ltxtq_send(PG_FUNCTION_ARGS)
     640              : {
     641            0 :     ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
     642              :     StringInfoData buf;
     643            0 :     int         version = 1;
     644              :     INFIX       nrm;
     645              : 
     646            0 :     if (query->size == 0)
     647            0 :         ereport(ERROR,
     648              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     649              :                  errmsg("syntax error"),
     650              :                  errdetail("Empty query.")));
     651              : 
     652            0 :     nrm.curpol = GETQUERY(query);
     653            0 :     nrm.buflen = 32;
     654            0 :     nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
     655            0 :     *(nrm.cur) = '\0';
     656            0 :     nrm.op = GETOPERAND(query);
     657            0 :     infix(&nrm, true);
     658              : 
     659            0 :     pq_begintypsend(&buf);
     660            0 :     pq_sendint8(&buf, version);
     661            0 :     pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
     662            0 :     pfree(nrm.buf);
     663              : 
     664            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     665              : }
        

Generated by: LCOV version 2.0-1