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 0 : parse_ltree(const char *buf, struct Node *escontext)
37 : {
38 0 : const char *ptr;
39 0 : nodeitem *list,
40 : *lptr;
41 0 : int num = 0,
42 0 : totallen = 0;
43 0 : int state = LTPRS_WAITNAME;
44 0 : ltree *result;
45 0 : ltree_level *curlevel;
46 0 : int charlen;
47 0 : 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 0 : ptr = buf;
55 0 : while (*ptr)
56 : {
57 0 : charlen = pg_mblen(ptr);
58 0 : if (t_iseq(ptr, '.'))
59 0 : num++;
60 0 : ptr += charlen;
61 : }
62 :
63 0 : if (num + 1 > LTREE_MAX_LEVELS)
64 0 : 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 0 : list = lptr = palloc_array(nodeitem, num + 1);
69 0 : ptr = buf;
70 0 : while (*ptr)
71 : {
72 0 : charlen = pg_mblen(ptr);
73 :
74 0 : switch (state)
75 : {
76 : case LTPRS_WAITNAME:
77 0 : if (ISLABEL(ptr))
78 : {
79 0 : lptr->start = ptr;
80 0 : lptr->wlen = 0;
81 0 : state = LTPRS_WAITDELIM;
82 0 : }
83 : else
84 0 : UNCHAR;
85 0 : break;
86 : case LTPRS_WAITDELIM:
87 0 : if (t_iseq(ptr, '.'))
88 : {
89 0 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
90 0 : return NULL;
91 0 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
92 0 : lptr++;
93 0 : state = LTPRS_WAITNAME;
94 0 : }
95 0 : else if (!ISLABEL(ptr))
96 0 : UNCHAR;
97 0 : break;
98 : default:
99 0 : elog(ERROR, "internal error in ltree parser");
100 0 : }
101 :
102 0 : ptr += charlen;
103 0 : lptr->wlen++;
104 0 : pos++;
105 : }
106 :
107 0 : if (state == LTPRS_WAITDELIM)
108 : {
109 0 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
110 0 : return NULL;
111 0 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
112 0 : lptr++;
113 0 : }
114 0 : else if (!(state == LTPRS_WAITNAME && lptr == list))
115 0 : ereturn(escontext, NULL,
116 : (errcode(ERRCODE_SYNTAX_ERROR),
117 : errmsg("ltree syntax error"),
118 : errdetail("Unexpected end of input.")));
119 :
120 0 : result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
121 0 : SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
122 0 : result->numlevel = lptr - list;
123 0 : curlevel = LTREE_FIRST(result);
124 0 : lptr = list;
125 0 : while (lptr - list < result->numlevel)
126 : {
127 0 : curlevel->len = (uint16) lptr->len;
128 0 : memcpy(curlevel->name, lptr->start, lptr->len);
129 0 : curlevel = LEVEL_NEXT(curlevel);
130 0 : lptr++;
131 : }
132 :
133 0 : pfree(list);
134 0 : return result;
135 :
136 : #undef UNCHAR
137 0 : }
138 :
139 : /*
140 : * expects an ltree
141 : * returns a null terminated string
142 : */
143 : static char *
144 0 : deparse_ltree(const ltree *in)
145 : {
146 0 : char *buf,
147 : *ptr;
148 0 : int i;
149 0 : ltree_level *curlevel;
150 :
151 0 : ptr = buf = (char *) palloc(VARSIZE(in));
152 0 : curlevel = LTREE_FIRST(in);
153 0 : for (i = 0; i < in->numlevel; i++)
154 : {
155 0 : if (i != 0)
156 : {
157 0 : *ptr = '.';
158 0 : ptr++;
159 0 : }
160 0 : memcpy(ptr, curlevel->name, curlevel->len);
161 0 : ptr += curlevel->len;
162 0 : curlevel = LEVEL_NEXT(curlevel);
163 0 : }
164 :
165 0 : *ptr = '\0';
166 0 : return buf;
167 0 : }
168 :
169 : /*
170 : * Basic ltree I/O functions
171 : */
172 0 : PG_FUNCTION_INFO_V1(ltree_in);
173 : Datum
174 0 : ltree_in(PG_FUNCTION_ARGS)
175 : {
176 0 : char *buf = (char *) PG_GETARG_POINTER(0);
177 0 : ltree *res;
178 :
179 0 : if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
180 0 : PG_RETURN_NULL();
181 :
182 0 : PG_RETURN_POINTER(res);
183 0 : }
184 :
185 0 : PG_FUNCTION_INFO_V1(ltree_out);
186 : Datum
187 0 : ltree_out(PG_FUNCTION_ARGS)
188 : {
189 0 : ltree *in = PG_GETARG_LTREE_P(0);
190 :
191 0 : PG_RETURN_POINTER(deparse_ltree(in));
192 0 : }
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 0 : 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 0 : 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 0 : }
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 0 : 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 0 : char *str;
234 0 : int nbytes;
235 0 : 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 0 : }
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 0 : parse_lquery(const char *buf, struct Node *escontext)
269 : {
270 0 : const char *ptr;
271 0 : int num = 0,
272 0 : totallen = 0,
273 0 : numOR = 0;
274 0 : int state = LQPRS_WAITLEVEL;
275 0 : lquery *result;
276 0 : nodeitem *lptr = NULL;
277 0 : lquery_level *cur,
278 : *curqlevel,
279 : *tmpql;
280 0 : lquery_variant *lrptr = NULL;
281 0 : bool hasnot = false;
282 0 : bool wasbad = false;
283 0 : int charlen;
284 0 : 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 0 : ptr = buf;
292 0 : while (*ptr)
293 : {
294 0 : charlen = pg_mblen(ptr);
295 :
296 0 : if (t_iseq(ptr, '.'))
297 0 : num++;
298 0 : else if (t_iseq(ptr, '|'))
299 0 : numOR++;
300 :
301 0 : ptr += charlen;
302 : }
303 :
304 0 : num++;
305 0 : if (num > LQUERY_MAX_LEVELS)
306 0 : 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 0 : curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
311 0 : ptr = buf;
312 0 : while (*ptr)
313 : {
314 0 : charlen = pg_mblen(ptr);
315 :
316 0 : switch (state)
317 : {
318 : case LQPRS_WAITLEVEL:
319 0 : if (ISLABEL(ptr))
320 : {
321 0 : GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
322 0 : lptr->start = ptr;
323 0 : state = LQPRS_WAITDELIM;
324 0 : curqlevel->numvar = 1;
325 0 : }
326 0 : else if (t_iseq(ptr, '!'))
327 : {
328 0 : GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
329 0 : lptr->start = ptr + 1;
330 0 : lptr->wlen = -1; /* compensate for counting ! below */
331 0 : state = LQPRS_WAITDELIM;
332 0 : curqlevel->numvar = 1;
333 0 : curqlevel->flag |= LQL_NOT;
334 0 : hasnot = true;
335 0 : }
336 0 : else if (t_iseq(ptr, '*'))
337 0 : state = LQPRS_WAITOPEN;
338 : else
339 0 : UNCHAR;
340 0 : break;
341 : case LQPRS_WAITVAR:
342 0 : if (ISLABEL(ptr))
343 : {
344 0 : lptr++;
345 0 : lptr->start = ptr;
346 0 : state = LQPRS_WAITDELIM;
347 0 : curqlevel->numvar++;
348 0 : }
349 : else
350 0 : UNCHAR;
351 0 : break;
352 : case LQPRS_WAITDELIM:
353 0 : if (t_iseq(ptr, '@'))
354 : {
355 0 : lptr->flag |= LVAR_INCASE;
356 0 : curqlevel->flag |= LVAR_INCASE;
357 0 : }
358 0 : else if (t_iseq(ptr, '*'))
359 : {
360 0 : lptr->flag |= LVAR_ANYEND;
361 0 : curqlevel->flag |= LVAR_ANYEND;
362 0 : }
363 0 : else if (t_iseq(ptr, '%'))
364 : {
365 0 : lptr->flag |= LVAR_SUBLEXEME;
366 0 : curqlevel->flag |= LVAR_SUBLEXEME;
367 0 : }
368 0 : else if (t_iseq(ptr, '|'))
369 : {
370 0 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
371 0 : return NULL;
372 0 : state = LQPRS_WAITVAR;
373 0 : }
374 0 : else if (t_iseq(ptr, '{'))
375 : {
376 0 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
377 0 : return NULL;
378 0 : curqlevel->flag |= LQL_COUNT;
379 0 : state = LQPRS_WAITFNUM;
380 0 : }
381 0 : else if (t_iseq(ptr, '.'))
382 : {
383 0 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
384 0 : return NULL;
385 0 : state = LQPRS_WAITLEVEL;
386 0 : curqlevel = NEXTLEV(curqlevel);
387 0 : }
388 0 : else if (ISLABEL(ptr))
389 : {
390 : /* disallow more chars after a flag */
391 0 : if (lptr->flag)
392 0 : UNCHAR;
393 0 : }
394 : else
395 0 : UNCHAR;
396 0 : break;
397 : case LQPRS_WAITOPEN:
398 0 : if (t_iseq(ptr, '{'))
399 0 : state = LQPRS_WAITFNUM;
400 0 : else if (t_iseq(ptr, '.'))
401 : {
402 : /* We only get here for '*', so these are correct defaults */
403 0 : curqlevel->low = 0;
404 0 : curqlevel->high = LTREE_MAX_LEVELS;
405 0 : curqlevel = NEXTLEV(curqlevel);
406 0 : state = LQPRS_WAITLEVEL;
407 0 : }
408 : else
409 0 : UNCHAR;
410 0 : break;
411 : case LQPRS_WAITFNUM:
412 0 : if (t_iseq(ptr, ','))
413 0 : state = LQPRS_WAITSNUM;
414 0 : else if (isdigit((unsigned char) *ptr))
415 : {
416 0 : int low = atoi(ptr);
417 :
418 0 : if (low < 0 || low > LTREE_MAX_LEVELS)
419 0 : 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 0 : curqlevel->low = (uint16) low;
426 0 : state = LQPRS_WAITND;
427 0 : }
428 : else
429 0 : UNCHAR;
430 0 : break;
431 : case LQPRS_WAITSNUM:
432 0 : if (isdigit((unsigned char) *ptr))
433 : {
434 0 : int high = atoi(ptr);
435 :
436 0 : if (high < 0 || high > LTREE_MAX_LEVELS)
437 0 : 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 0 : else if (curqlevel->low > high)
443 0 : 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 0 : curqlevel->high = (uint16) high;
450 0 : state = LQPRS_WAITCLOSE;
451 0 : }
452 0 : else if (t_iseq(ptr, '}'))
453 : {
454 0 : curqlevel->high = LTREE_MAX_LEVELS;
455 0 : state = LQPRS_WAITEND;
456 0 : }
457 : else
458 0 : UNCHAR;
459 0 : break;
460 : case LQPRS_WAITCLOSE:
461 0 : if (t_iseq(ptr, '}'))
462 0 : state = LQPRS_WAITEND;
463 0 : else if (!isdigit((unsigned char) *ptr))
464 0 : UNCHAR;
465 0 : break;
466 : case LQPRS_WAITND:
467 0 : if (t_iseq(ptr, '}'))
468 : {
469 0 : curqlevel->high = curqlevel->low;
470 0 : state = LQPRS_WAITEND;
471 0 : }
472 0 : else if (t_iseq(ptr, ','))
473 0 : state = LQPRS_WAITSNUM;
474 0 : else if (!isdigit((unsigned char) *ptr))
475 0 : UNCHAR;
476 0 : break;
477 : case LQPRS_WAITEND:
478 0 : if (t_iseq(ptr, '.'))
479 : {
480 0 : state = LQPRS_WAITLEVEL;
481 0 : curqlevel = NEXTLEV(curqlevel);
482 0 : }
483 : else
484 0 : UNCHAR;
485 0 : break;
486 : default:
487 0 : elog(ERROR, "internal error in lquery parser");
488 0 : }
489 :
490 0 : ptr += charlen;
491 0 : if (state == LQPRS_WAITDELIM)
492 0 : lptr->wlen++;
493 0 : pos++;
494 : }
495 :
496 0 : if (state == LQPRS_WAITDELIM)
497 : {
498 0 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
499 0 : return NULL;
500 0 : }
501 0 : else if (state == LQPRS_WAITOPEN)
502 0 : curqlevel->high = LTREE_MAX_LEVELS;
503 0 : else if (state != LQPRS_WAITEND)
504 0 : ereturn(escontext, NULL,
505 : (errcode(ERRCODE_SYNTAX_ERROR),
506 : errmsg("lquery syntax error"),
507 : errdetail("Unexpected end of input.")));
508 :
509 0 : curqlevel = tmpql;
510 0 : totallen = LQUERY_HDRSIZE;
511 0 : while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
512 : {
513 0 : totallen += LQL_HDRSIZE;
514 0 : if (curqlevel->numvar)
515 : {
516 0 : lptr = GETVAR(curqlevel);
517 0 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
518 : {
519 0 : totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
520 0 : lptr++;
521 : }
522 0 : }
523 0 : curqlevel = NEXTLEV(curqlevel);
524 : }
525 :
526 0 : result = (lquery *) palloc0(totallen);
527 0 : SET_VARSIZE(result, totallen);
528 0 : result->numlevel = num;
529 0 : result->firstgood = 0;
530 0 : result->flag = 0;
531 0 : if (hasnot)
532 0 : result->flag |= LQUERY_HASNOT;
533 0 : cur = LQUERY_FIRST(result);
534 0 : curqlevel = tmpql;
535 0 : while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
536 : {
537 0 : memcpy(cur, curqlevel, LQL_HDRSIZE);
538 0 : cur->totallen = LQL_HDRSIZE;
539 0 : if (curqlevel->numvar)
540 : {
541 0 : lrptr = LQL_FIRST(cur);
542 0 : lptr = GETVAR(curqlevel);
543 0 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
544 : {
545 0 : cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
546 0 : lrptr->len = lptr->len;
547 0 : lrptr->flag = lptr->flag;
548 0 : lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
549 0 : memcpy(lrptr->name, lptr->start, lptr->len);
550 0 : lptr++;
551 0 : lrptr = LVAR_NEXT(lrptr);
552 : }
553 0 : pfree(GETVAR(curqlevel));
554 0 : if (cur->numvar > 1 || cur->flag != 0)
555 : {
556 : /* Not a simple match */
557 0 : wasbad = true;
558 0 : }
559 0 : else if (wasbad == false)
560 : {
561 : /* count leading simple matches */
562 0 : (result->firstgood)++;
563 0 : }
564 0 : }
565 : else
566 : {
567 : /* '*', so this isn't a simple match */
568 0 : wasbad = true;
569 : }
570 0 : curqlevel = NEXTLEV(curqlevel);
571 0 : cur = LQL_NEXT(cur);
572 : }
573 :
574 0 : pfree(tmpql);
575 0 : return result;
576 :
577 : #undef UNCHAR
578 0 : }
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 0 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
586 : struct Node *escontext)
587 : {
588 0 : if (is_lquery)
589 : {
590 : /*
591 : * Back up over any flag characters, and discount them from length and
592 : * position.
593 : */
594 0 : while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL)
595 : {
596 0 : ptr--;
597 0 : lptr->wlen--;
598 0 : pos--;
599 : }
600 0 : }
601 :
602 : /* Now compute the byte length, which we weren't tracking before. */
603 0 : lptr->len = ptr - lptr->start;
604 :
605 : /* Complain if it's empty or too long */
606 0 : if (lptr->len == 0)
607 0 : 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 0 : if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
614 0 : 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 0 : return true;
620 0 : }
621 :
622 : /*
623 : * expects an lquery
624 : * returns a null terminated string
625 : */
626 : static char *
627 0 : deparse_lquery(const lquery *in)
628 : {
629 0 : char *buf,
630 : *ptr;
631 0 : int i,
632 : j,
633 0 : totallen = 1;
634 0 : lquery_level *curqlevel;
635 0 : lquery_variant *curtlevel;
636 :
637 0 : curqlevel = LQUERY_FIRST(in);
638 0 : for (i = 0; i < in->numlevel; i++)
639 : {
640 0 : totallen++;
641 0 : if (curqlevel->numvar)
642 : {
643 0 : totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
644 0 : if (curqlevel->flag & LQL_COUNT)
645 0 : totallen += 2 * 11 + 3;
646 0 : }
647 : else
648 0 : totallen += 2 * 11 + 4;
649 0 : curqlevel = LQL_NEXT(curqlevel);
650 0 : }
651 :
652 0 : ptr = buf = (char *) palloc(totallen);
653 0 : curqlevel = LQUERY_FIRST(in);
654 0 : for (i = 0; i < in->numlevel; i++)
655 : {
656 0 : if (i != 0)
657 : {
658 0 : *ptr = '.';
659 0 : ptr++;
660 0 : }
661 0 : if (curqlevel->numvar)
662 : {
663 0 : if (curqlevel->flag & LQL_NOT)
664 : {
665 0 : *ptr = '!';
666 0 : ptr++;
667 0 : }
668 0 : curtlevel = LQL_FIRST(curqlevel);
669 0 : for (j = 0; j < curqlevel->numvar; j++)
670 : {
671 0 : if (j != 0)
672 : {
673 0 : *ptr = '|';
674 0 : ptr++;
675 0 : }
676 0 : memcpy(ptr, curtlevel->name, curtlevel->len);
677 0 : ptr += curtlevel->len;
678 0 : if ((curtlevel->flag & LVAR_SUBLEXEME))
679 : {
680 0 : *ptr = '%';
681 0 : ptr++;
682 0 : }
683 0 : if ((curtlevel->flag & LVAR_INCASE))
684 : {
685 0 : *ptr = '@';
686 0 : ptr++;
687 0 : }
688 0 : if ((curtlevel->flag & LVAR_ANYEND))
689 : {
690 0 : *ptr = '*';
691 0 : ptr++;
692 0 : }
693 0 : curtlevel = LVAR_NEXT(curtlevel);
694 0 : }
695 0 : }
696 : else
697 : {
698 0 : *ptr = '*';
699 0 : ptr++;
700 : }
701 :
702 0 : if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
703 : {
704 0 : if (curqlevel->low == curqlevel->high)
705 : {
706 0 : sprintf(ptr, "{%d}", curqlevel->low);
707 0 : }
708 0 : else if (curqlevel->low == 0)
709 : {
710 0 : if (curqlevel->high == LTREE_MAX_LEVELS)
711 : {
712 0 : if (curqlevel->numvar == 0)
713 : {
714 : /* This is default for '*', so print nothing */
715 0 : *ptr = '\0';
716 0 : }
717 : else
718 0 : sprintf(ptr, "{,}");
719 0 : }
720 : else
721 0 : sprintf(ptr, "{,%d}", curqlevel->high);
722 0 : }
723 0 : else if (curqlevel->high == LTREE_MAX_LEVELS)
724 : {
725 0 : sprintf(ptr, "{%d,}", curqlevel->low);
726 0 : }
727 : else
728 0 : sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
729 0 : ptr = strchr(ptr, '\0');
730 0 : }
731 :
732 0 : curqlevel = LQL_NEXT(curqlevel);
733 0 : }
734 :
735 0 : *ptr = '\0';
736 0 : return buf;
737 0 : }
738 :
739 : /*
740 : * Basic lquery I/O functions
741 : */
742 0 : PG_FUNCTION_INFO_V1(lquery_in);
743 : Datum
744 0 : lquery_in(PG_FUNCTION_ARGS)
745 : {
746 0 : char *buf = (char *) PG_GETARG_POINTER(0);
747 0 : lquery *res;
748 :
749 0 : if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
750 0 : PG_RETURN_NULL();
751 :
752 0 : PG_RETURN_POINTER(res);
753 0 : }
754 :
755 0 : PG_FUNCTION_INFO_V1(lquery_out);
756 : Datum
757 0 : lquery_out(PG_FUNCTION_ARGS)
758 : {
759 0 : lquery *in = PG_GETARG_LQUERY_P(0);
760 :
761 0 : PG_RETURN_POINTER(deparse_lquery(in));
762 0 : }
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 0 : 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 0 : 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 0 : }
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 0 : 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 0 : char *str;
804 0 : int nbytes;
805 0 : 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 0 : }
|