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