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