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