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 *
parse_ltree(const char * buf)36 parse_ltree(const char *buf)
37 {
38 const char *ptr;
39 nodeitem *list,
40 *lptr;
41 int num = 0,
42 totallen = 0;
43 int state = LTPRS_WAITNAME;
44 ltree *result;
45 ltree_level *curlevel;
46 int charlen;
47 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 ptr = buf;
55 while (*ptr)
56 {
57 charlen = pg_mblen(ptr);
58 if (t_iseq(ptr, '.'))
59 num++;
60 ptr += charlen;
61 }
62
63 if (num + 1 > LTREE_MAX_LEVELS)
64 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 list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
69 ptr = buf;
70 while (*ptr)
71 {
72 charlen = pg_mblen(ptr);
73
74 switch (state)
75 {
76 case LTPRS_WAITNAME:
77 if (ISALNUM(ptr))
78 {
79 lptr->start = ptr;
80 lptr->wlen = 0;
81 state = LTPRS_WAITDELIM;
82 }
83 else
84 UNCHAR;
85 break;
86 case LTPRS_WAITDELIM:
87 if (t_iseq(ptr, '.'))
88 {
89 finish_nodeitem(lptr, ptr, false, pos);
90 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
91 lptr++;
92 state = LTPRS_WAITNAME;
93 }
94 else if (!ISALNUM(ptr))
95 UNCHAR;
96 break;
97 default:
98 elog(ERROR, "internal error in ltree parser");
99 }
100
101 ptr += charlen;
102 lptr->wlen++;
103 pos++;
104 }
105
106 if (state == LTPRS_WAITDELIM)
107 {
108 finish_nodeitem(lptr, ptr, false, pos);
109 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
110 lptr++;
111 }
112 else if (!(state == LTPRS_WAITNAME && lptr == list))
113 ereport(ERROR,
114 (errcode(ERRCODE_SYNTAX_ERROR),
115 errmsg("ltree syntax error"),
116 errdetail("Unexpected end of input.")));
117
118 result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
119 SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
120 result->numlevel = lptr - list;
121 curlevel = LTREE_FIRST(result);
122 lptr = list;
123 while (lptr - list < result->numlevel)
124 {
125 curlevel->len = (uint16) lptr->len;
126 memcpy(curlevel->name, lptr->start, lptr->len);
127 curlevel = LEVEL_NEXT(curlevel);
128 lptr++;
129 }
130
131 pfree(list);
132 return result;
133
134 #undef UNCHAR
135 }
136
137 /*
138 * expects an ltree
139 * returns a null terminated string
140 */
141 static char *
deparse_ltree(const ltree * in)142 deparse_ltree(const ltree *in)
143 {
144 char *buf,
145 *ptr;
146 int i;
147 ltree_level *curlevel;
148
149 ptr = buf = (char *) palloc(VARSIZE(in));
150 curlevel = LTREE_FIRST(in);
151 for (i = 0; i < in->numlevel; i++)
152 {
153 if (i != 0)
154 {
155 *ptr = '.';
156 ptr++;
157 }
158 memcpy(ptr, curlevel->name, curlevel->len);
159 ptr += curlevel->len;
160 curlevel = LEVEL_NEXT(curlevel);
161 }
162
163 *ptr = '\0';
164 return buf;
165 }
166
167 /*
168 * Basic ltree I/O functions
169 */
170 PG_FUNCTION_INFO_V1(ltree_in);
171 Datum
ltree_in(PG_FUNCTION_ARGS)172 ltree_in(PG_FUNCTION_ARGS)
173 {
174 char *buf = (char *) PG_GETARG_POINTER(0);
175
176 PG_RETURN_POINTER(parse_ltree(buf));
177 }
178
179 PG_FUNCTION_INFO_V1(ltree_out);
180 Datum
ltree_out(PG_FUNCTION_ARGS)181 ltree_out(PG_FUNCTION_ARGS)
182 {
183 ltree *in = PG_GETARG_LTREE_P(0);
184
185 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 PG_FUNCTION_INFO_V1(ltree_send);
197 Datum
ltree_send(PG_FUNCTION_ARGS)198 ltree_send(PG_FUNCTION_ARGS)
199 {
200 ltree *in = PG_GETARG_LTREE_P(0);
201 StringInfoData buf;
202 int version = 1;
203 char *res = deparse_ltree(in);
204
205 pq_begintypsend(&buf);
206 pq_sendint8(&buf, version);
207 pq_sendtext(&buf, res, strlen(res));
208 pfree(res);
209
210 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 PG_FUNCTION_INFO_V1(ltree_recv);
222 Datum
ltree_recv(PG_FUNCTION_ARGS)223 ltree_recv(PG_FUNCTION_ARGS)
224 {
225 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
226 int version = pq_getmsgint(buf, 1);
227 char *str;
228 int nbytes;
229 ltree *res;
230
231 if (version != 1)
232 elog(ERROR, "unsupported ltree version number %d", version);
233
234 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
235 res = parse_ltree(str);
236 pfree(str);
237
238 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 *
parse_lquery(const char * buf)262 parse_lquery(const char *buf)
263 {
264 const char *ptr;
265 int num = 0,
266 totallen = 0,
267 numOR = 0;
268 int state = LQPRS_WAITLEVEL;
269 lquery *result;
270 nodeitem *lptr = NULL;
271 lquery_level *cur,
272 *curqlevel,
273 *tmpql;
274 lquery_variant *lrptr = NULL;
275 bool hasnot = false;
276 bool wasbad = false;
277 int charlen;
278 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 ptr = buf;
286 while (*ptr)
287 {
288 charlen = pg_mblen(ptr);
289
290 if (t_iseq(ptr, '.'))
291 num++;
292 else if (t_iseq(ptr, '|'))
293 numOR++;
294
295 ptr += charlen;
296 }
297
298 num++;
299 if (num > LQUERY_MAX_LEVELS)
300 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 curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
305 ptr = buf;
306 while (*ptr)
307 {
308 charlen = pg_mblen(ptr);
309
310 switch (state)
311 {
312 case LQPRS_WAITLEVEL:
313 if (ISALNUM(ptr))
314 {
315 GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
316 lptr->start = ptr;
317 state = LQPRS_WAITDELIM;
318 curqlevel->numvar = 1;
319 }
320 else if (t_iseq(ptr, '!'))
321 {
322 GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
323 lptr->start = ptr + 1;
324 lptr->wlen = -1; /* compensate for counting ! below */
325 state = LQPRS_WAITDELIM;
326 curqlevel->numvar = 1;
327 curqlevel->flag |= LQL_NOT;
328 hasnot = true;
329 }
330 else if (t_iseq(ptr, '*'))
331 state = LQPRS_WAITOPEN;
332 else
333 UNCHAR;
334 break;
335 case LQPRS_WAITVAR:
336 if (ISALNUM(ptr))
337 {
338 lptr++;
339 lptr->start = ptr;
340 state = LQPRS_WAITDELIM;
341 curqlevel->numvar++;
342 }
343 else
344 UNCHAR;
345 break;
346 case LQPRS_WAITDELIM:
347 if (t_iseq(ptr, '@'))
348 {
349 lptr->flag |= LVAR_INCASE;
350 curqlevel->flag |= LVAR_INCASE;
351 }
352 else if (t_iseq(ptr, '*'))
353 {
354 lptr->flag |= LVAR_ANYEND;
355 curqlevel->flag |= LVAR_ANYEND;
356 }
357 else if (t_iseq(ptr, '%'))
358 {
359 lptr->flag |= LVAR_SUBLEXEME;
360 curqlevel->flag |= LVAR_SUBLEXEME;
361 }
362 else if (t_iseq(ptr, '|'))
363 {
364 finish_nodeitem(lptr, ptr, true, pos);
365 state = LQPRS_WAITVAR;
366 }
367 else if (t_iseq(ptr, '{'))
368 {
369 finish_nodeitem(lptr, ptr, true, pos);
370 curqlevel->flag |= LQL_COUNT;
371 state = LQPRS_WAITFNUM;
372 }
373 else if (t_iseq(ptr, '.'))
374 {
375 finish_nodeitem(lptr, ptr, true, pos);
376 state = LQPRS_WAITLEVEL;
377 curqlevel = NEXTLEV(curqlevel);
378 }
379 else if (ISALNUM(ptr))
380 {
381 /* disallow more chars after a flag */
382 if (lptr->flag)
383 UNCHAR;
384 }
385 else
386 UNCHAR;
387 break;
388 case LQPRS_WAITOPEN:
389 if (t_iseq(ptr, '{'))
390 state = LQPRS_WAITFNUM;
391 else if (t_iseq(ptr, '.'))
392 {
393 /* We only get here for '*', so these are correct defaults */
394 curqlevel->low = 0;
395 curqlevel->high = LTREE_MAX_LEVELS;
396 curqlevel = NEXTLEV(curqlevel);
397 state = LQPRS_WAITLEVEL;
398 }
399 else
400 UNCHAR;
401 break;
402 case LQPRS_WAITFNUM:
403 if (t_iseq(ptr, ','))
404 state = LQPRS_WAITSNUM;
405 else if (t_isdigit(ptr))
406 {
407 int low = atoi(ptr);
408
409 if (low < 0 || low > LTREE_MAX_LEVELS)
410 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 curqlevel->low = (uint16) low;
417 state = LQPRS_WAITND;
418 }
419 else
420 UNCHAR;
421 break;
422 case LQPRS_WAITSNUM:
423 if (t_isdigit(ptr))
424 {
425 int high = atoi(ptr);
426
427 if (high < 0 || high > LTREE_MAX_LEVELS)
428 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 else if (curqlevel->low > high)
434 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 curqlevel->high = (uint16) high;
441 state = LQPRS_WAITCLOSE;
442 }
443 else if (t_iseq(ptr, '}'))
444 {
445 curqlevel->high = LTREE_MAX_LEVELS;
446 state = LQPRS_WAITEND;
447 }
448 else
449 UNCHAR;
450 break;
451 case LQPRS_WAITCLOSE:
452 if (t_iseq(ptr, '}'))
453 state = LQPRS_WAITEND;
454 else if (!t_isdigit(ptr))
455 UNCHAR;
456 break;
457 case LQPRS_WAITND:
458 if (t_iseq(ptr, '}'))
459 {
460 curqlevel->high = curqlevel->low;
461 state = LQPRS_WAITEND;
462 }
463 else if (t_iseq(ptr, ','))
464 state = LQPRS_WAITSNUM;
465 else if (!t_isdigit(ptr))
466 UNCHAR;
467 break;
468 case LQPRS_WAITEND:
469 if (t_iseq(ptr, '.'))
470 {
471 state = LQPRS_WAITLEVEL;
472 curqlevel = NEXTLEV(curqlevel);
473 }
474 else
475 UNCHAR;
476 break;
477 default:
478 elog(ERROR, "internal error in lquery parser");
479 }
480
481 ptr += charlen;
482 if (state == LQPRS_WAITDELIM)
483 lptr->wlen++;
484 pos++;
485 }
486
487 if (state == LQPRS_WAITDELIM)
488 finish_nodeitem(lptr, ptr, true, pos);
489 else if (state == LQPRS_WAITOPEN)
490 curqlevel->high = LTREE_MAX_LEVELS;
491 else if (state != LQPRS_WAITEND)
492 ereport(ERROR,
493 (errcode(ERRCODE_SYNTAX_ERROR),
494 errmsg("lquery syntax error"),
495 errdetail("Unexpected end of input.")));
496
497 curqlevel = tmpql;
498 totallen = LQUERY_HDRSIZE;
499 while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
500 {
501 totallen += LQL_HDRSIZE;
502 if (curqlevel->numvar)
503 {
504 lptr = GETVAR(curqlevel);
505 while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
506 {
507 totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
508 lptr++;
509 }
510 }
511 curqlevel = NEXTLEV(curqlevel);
512 }
513
514 result = (lquery *) palloc0(totallen);
515 SET_VARSIZE(result, totallen);
516 result->numlevel = num;
517 result->firstgood = 0;
518 result->flag = 0;
519 if (hasnot)
520 result->flag |= LQUERY_HASNOT;
521 cur = LQUERY_FIRST(result);
522 curqlevel = tmpql;
523 while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
524 {
525 memcpy(cur, curqlevel, LQL_HDRSIZE);
526 cur->totallen = LQL_HDRSIZE;
527 if (curqlevel->numvar)
528 {
529 lrptr = LQL_FIRST(cur);
530 lptr = GETVAR(curqlevel);
531 while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
532 {
533 cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
534 lrptr->len = lptr->len;
535 lrptr->flag = lptr->flag;
536 lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
537 memcpy(lrptr->name, lptr->start, lptr->len);
538 lptr++;
539 lrptr = LVAR_NEXT(lrptr);
540 }
541 pfree(GETVAR(curqlevel));
542 if (cur->numvar > 1 || cur->flag != 0)
543 {
544 /* Not a simple match */
545 wasbad = true;
546 }
547 else if (wasbad == false)
548 {
549 /* count leading simple matches */
550 (result->firstgood)++;
551 }
552 }
553 else
554 {
555 /* '*', so this isn't a simple match */
556 wasbad = true;
557 }
558 curqlevel = NEXTLEV(curqlevel);
559 cur = LQL_NEXT(cur);
560 }
561
562 pfree(tmpql);
563 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
finish_nodeitem(nodeitem * lptr,const char * ptr,bool is_lquery,int pos)573 finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos)
574 {
575 if (is_lquery)
576 {
577 /*
578 * Back up over any flag characters, and discount them from length and
579 * position.
580 */
581 while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL)
582 {
583 ptr--;
584 lptr->wlen--;
585 pos--;
586 }
587 }
588
589 /* Now compute the byte length, which we weren't tracking before. */
590 lptr->len = ptr - lptr->start;
591
592 /* Complain if it's empty or too long */
593 if (lptr->len == 0)
594 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 if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
601 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 }
607
608 /*
609 * expects an lquery
610 * returns a null terminated string
611 */
612 static char *
deparse_lquery(const lquery * in)613 deparse_lquery(const lquery *in)
614 {
615 char *buf,
616 *ptr;
617 int i,
618 j,
619 totallen = 1;
620 lquery_level *curqlevel;
621 lquery_variant *curtlevel;
622
623 curqlevel = LQUERY_FIRST(in);
624 for (i = 0; i < in->numlevel; i++)
625 {
626 totallen++;
627 if (curqlevel->numvar)
628 {
629 totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
630 if (curqlevel->flag & LQL_COUNT)
631 totallen += 2 * 11 + 3;
632 }
633 else
634 totallen += 2 * 11 + 4;
635 curqlevel = LQL_NEXT(curqlevel);
636 }
637
638 ptr = buf = (char *) palloc(totallen);
639 curqlevel = LQUERY_FIRST(in);
640 for (i = 0; i < in->numlevel; i++)
641 {
642 if (i != 0)
643 {
644 *ptr = '.';
645 ptr++;
646 }
647 if (curqlevel->numvar)
648 {
649 if (curqlevel->flag & LQL_NOT)
650 {
651 *ptr = '!';
652 ptr++;
653 }
654 curtlevel = LQL_FIRST(curqlevel);
655 for (j = 0; j < curqlevel->numvar; j++)
656 {
657 if (j != 0)
658 {
659 *ptr = '|';
660 ptr++;
661 }
662 memcpy(ptr, curtlevel->name, curtlevel->len);
663 ptr += curtlevel->len;
664 if ((curtlevel->flag & LVAR_SUBLEXEME))
665 {
666 *ptr = '%';
667 ptr++;
668 }
669 if ((curtlevel->flag & LVAR_INCASE))
670 {
671 *ptr = '@';
672 ptr++;
673 }
674 if ((curtlevel->flag & LVAR_ANYEND))
675 {
676 *ptr = '*';
677 ptr++;
678 }
679 curtlevel = LVAR_NEXT(curtlevel);
680 }
681 }
682 else
683 {
684 *ptr = '*';
685 ptr++;
686 }
687
688 if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
689 {
690 if (curqlevel->low == curqlevel->high)
691 {
692 sprintf(ptr, "{%d}", curqlevel->low);
693 }
694 else if (curqlevel->low == 0)
695 {
696 if (curqlevel->high == LTREE_MAX_LEVELS)
697 {
698 if (curqlevel->numvar == 0)
699 {
700 /* This is default for '*', so print nothing */
701 *ptr = '\0';
702 }
703 else
704 sprintf(ptr, "{,}");
705 }
706 else
707 sprintf(ptr, "{,%d}", curqlevel->high);
708 }
709 else if (curqlevel->high == LTREE_MAX_LEVELS)
710 {
711 sprintf(ptr, "{%d,}", curqlevel->low);
712 }
713 else
714 sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
715 ptr = strchr(ptr, '\0');
716 }
717
718 curqlevel = LQL_NEXT(curqlevel);
719 }
720
721 *ptr = '\0';
722 return buf;
723 }
724
725 /*
726 * Basic lquery I/O functions
727 */
728 PG_FUNCTION_INFO_V1(lquery_in);
729 Datum
lquery_in(PG_FUNCTION_ARGS)730 lquery_in(PG_FUNCTION_ARGS)
731 {
732 char *buf = (char *) PG_GETARG_POINTER(0);
733
734 PG_RETURN_POINTER(parse_lquery(buf));
735 }
736
737 PG_FUNCTION_INFO_V1(lquery_out);
738 Datum
lquery_out(PG_FUNCTION_ARGS)739 lquery_out(PG_FUNCTION_ARGS)
740 {
741 lquery *in = PG_GETARG_LQUERY_P(0);
742
743 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 PG_FUNCTION_INFO_V1(lquery_send);
755 Datum
lquery_send(PG_FUNCTION_ARGS)756 lquery_send(PG_FUNCTION_ARGS)
757 {
758 lquery *in = PG_GETARG_LQUERY_P(0);
759 StringInfoData buf;
760 int version = 1;
761 char *res = deparse_lquery(in);
762
763 pq_begintypsend(&buf);
764 pq_sendint8(&buf, version);
765 pq_sendtext(&buf, res, strlen(res));
766 pfree(res);
767
768 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 PG_FUNCTION_INFO_V1(lquery_recv);
780 Datum
lquery_recv(PG_FUNCTION_ARGS)781 lquery_recv(PG_FUNCTION_ARGS)
782 {
783 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
784 int version = pq_getmsgint(buf, 1);
785 char *str;
786 int nbytes;
787 lquery *res;
788
789 if (version != 1)
790 elog(ERROR, "unsupported lquery version number %d", version);
791
792 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
793 res = parse_lquery(str);
794 pfree(str);
795
796 PG_RETURN_POINTER(res);
797 }
798