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