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 "common/int.h"
11 : #include "crc32.h"
12 : #include "libpq/pqformat.h"
13 : #include "ltree.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 4860 : parse_ltree(const char *buf, struct Node *escontext)
38 : {
39 : const char *ptr;
40 : nodeitem *list,
41 : *lptr;
42 4860 : int num = 0,
43 4860 : totallen = 0;
44 4860 : int state = LTPRS_WAITNAME;
45 : ltree *result;
46 : ltree_level *curlevel;
47 : int charlen;
48 4860 : 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 4860 : ptr = buf;
56 479128 : while (*ptr)
57 : {
58 474268 : charlen = pg_mblen_cstr(ptr);
59 474268 : if (t_iseq(ptr, '.'))
60 222773 : num++;
61 474268 : ptr += charlen;
62 : }
63 :
64 4860 : if (num + 1 > LTREE_MAX_LEVELS)
65 1 : 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 4859 : list = lptr = palloc_array(nodeitem, num + 1);
70 4859 : ptr = buf;
71 348021 : while (*ptr)
72 : {
73 343167 : charlen = pg_mblen_cstr(ptr);
74 :
75 343167 : switch (state)
76 : {
77 162080 : case LTPRS_WAITNAME:
78 162080 : if (ISLABEL(ptr))
79 : {
80 162075 : lptr->start = ptr;
81 162075 : lptr->wlen = 0;
82 162075 : state = LTPRS_WAITDELIM;
83 : }
84 : else
85 5 : UNCHAR;
86 162075 : break;
87 181087 : case LTPRS_WAITDELIM:
88 181087 : if (t_iseq(ptr, '.'))
89 : {
90 157231 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
91 0 : return NULL;
92 157231 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
93 157231 : lptr++;
94 157231 : state = LTPRS_WAITNAME;
95 : }
96 23856 : else if (!ISLABEL(ptr))
97 0 : UNCHAR;
98 181087 : break;
99 0 : default:
100 0 : elog(ERROR, "internal error in ltree parser");
101 : }
102 :
103 343162 : ptr += charlen;
104 343162 : lptr->wlen++;
105 343162 : pos++;
106 : }
107 :
108 4854 : if (state == LTPRS_WAITDELIM)
109 : {
110 4844 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
111 0 : return NULL;
112 4843 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
113 4843 : lptr++;
114 : }
115 10 : else if (!(state == LTPRS_WAITNAME && lptr == list))
116 3 : ereturn(escontext, NULL,
117 : (errcode(ERRCODE_SYNTAX_ERROR),
118 : errmsg("ltree syntax error"),
119 : errdetail("Unexpected end of input.")));
120 :
121 4850 : result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
122 4850 : SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
123 4850 : result->numlevel = lptr - list;
124 4850 : curlevel = LTREE_FIRST(result);
125 4850 : lptr = list;
126 166917 : while (lptr - list < result->numlevel)
127 : {
128 162067 : curlevel->len = (uint16) lptr->len;
129 162067 : memcpy(curlevel->name, lptr->start, lptr->len);
130 162067 : curlevel = LEVEL_NEXT(curlevel);
131 162067 : lptr++;
132 : }
133 :
134 4850 : pfree(list);
135 4850 : return result;
136 :
137 : #undef UNCHAR
138 : }
139 :
140 : /*
141 : * expects an ltree
142 : * returns a null terminated string
143 : */
144 : static char *
145 6263 : deparse_ltree(const ltree *in)
146 : {
147 : char *buf,
148 : *ptr;
149 : int i;
150 : ltree_level *curlevel;
151 :
152 6263 : ptr = buf = (char *) palloc(VARSIZE(in));
153 6263 : curlevel = LTREE_FIRST(in);
154 47260 : for (i = 0; i < in->numlevel; i++)
155 : {
156 40997 : if (i != 0)
157 : {
158 34748 : *ptr = '.';
159 34748 : ptr++;
160 : }
161 40997 : memcpy(ptr, curlevel->name, curlevel->len);
162 40997 : ptr += curlevel->len;
163 40997 : curlevel = LEVEL_NEXT(curlevel);
164 : }
165 :
166 6263 : *ptr = '\0';
167 6263 : return buf;
168 : }
169 :
170 : /*
171 : * Basic ltree I/O functions
172 : */
173 4 : PG_FUNCTION_INFO_V1(ltree_in);
174 : Datum
175 4860 : ltree_in(PG_FUNCTION_ARGS)
176 : {
177 4860 : char *buf = (char *) PG_GETARG_POINTER(0);
178 : ltree *res;
179 :
180 4860 : if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
181 4 : PG_RETURN_NULL();
182 :
183 4850 : PG_RETURN_POINTER(res);
184 : }
185 :
186 3 : PG_FUNCTION_INFO_V1(ltree_out);
187 : Datum
188 6263 : ltree_out(PG_FUNCTION_ARGS)
189 : {
190 6263 : ltree *in = PG_GETARG_LTREE_P(0);
191 :
192 6263 : 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 2 : 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 2 : 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 193 : parse_lquery(const char *buf, struct Node *escontext)
270 : {
271 : const char *ptr;
272 193 : int num = 0,
273 193 : totallen = 0,
274 193 : numOR = 0;
275 193 : int state = LQPRS_WAITLEVEL;
276 : lquery *result;
277 193 : nodeitem *lptr = NULL;
278 : lquery_level *cur,
279 : *curqlevel,
280 : *tmpql;
281 193 : lquery_variant *lrptr = NULL;
282 193 : bool hasnot = false;
283 193 : bool wasbad = false;
284 : int charlen;
285 193 : 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 193 : ptr = buf;
293 464698 : while (*ptr)
294 : {
295 464505 : charlen = pg_mblen_cstr(ptr);
296 :
297 464505 : if (t_iseq(ptr, '.'))
298 131474 : num++;
299 333031 : else if (t_iseq(ptr, '|'))
300 65825 : numOR++;
301 :
302 464505 : ptr += charlen;
303 : }
304 :
305 193 : num++;
306 193 : if (num > LQUERY_MAX_LEVELS)
307 1 : 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 192 : curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
312 192 : ptr = buf;
313 333569 : while (*ptr)
314 : {
315 333393 : charlen = pg_mblen_cstr(ptr);
316 :
317 333393 : switch (state)
318 : {
319 66114 : case LQPRS_WAITLEVEL:
320 66114 : if (ISLABEL(ptr))
321 : {
322 65841 : GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
323 65841 : lptr->start = ptr;
324 65841 : state = LQPRS_WAITDELIM;
325 65841 : curqlevel->numvar = 1;
326 : }
327 273 : else if (t_iseq(ptr, '!'))
328 : {
329 78 : GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
330 78 : lptr->start = ptr + 1;
331 78 : lptr->wlen = -1; /* compensate for counting ! below */
332 78 : state = LQPRS_WAITDELIM;
333 78 : curqlevel->numvar = 1;
334 78 : curqlevel->flag |= LQL_NOT;
335 78 : hasnot = true;
336 : }
337 195 : else if (t_iseq(ptr, '*'))
338 186 : state = LQPRS_WAITOPEN;
339 : else
340 9 : UNCHAR;
341 66105 : break;
342 65825 : case LQPRS_WAITVAR:
343 65825 : if (ISLABEL(ptr))
344 : {
345 65824 : lptr++;
346 65824 : lptr->start = ptr;
347 65824 : state = LQPRS_WAITDELIM;
348 65824 : if (pg_add_u16_overflow(curqlevel->numvar, 1, &curqlevel->numvar))
349 1 : ereturn(escontext, NULL,
350 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
351 : errmsg("lquery level has too many variants"),
352 : errdetail("Number of variants exceeds the maximum allowed (%d).",
353 : PG_UINT16_MAX)));
354 : }
355 : else
356 1 : UNCHAR;
357 65823 : break;
358 201076 : case LQPRS_WAITDELIM:
359 201076 : if (t_iseq(ptr, '@'))
360 : {
361 19 : lptr->flag |= LVAR_INCASE;
362 19 : curqlevel->flag |= LVAR_INCASE;
363 : }
364 201057 : else if (t_iseq(ptr, '*'))
365 : {
366 19 : lptr->flag |= LVAR_ANYEND;
367 19 : curqlevel->flag |= LVAR_ANYEND;
368 : }
369 201038 : else if (t_iseq(ptr, '%'))
370 : {
371 6 : lptr->flag |= LVAR_SUBLEXEME;
372 6 : curqlevel->flag |= LVAR_SUBLEXEME;
373 : }
374 201032 : else if (t_iseq(ptr, '|'))
375 : {
376 65825 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
377 0 : return NULL;
378 65825 : state = LQPRS_WAITVAR;
379 : }
380 135207 : else if (t_iseq(ptr, '{'))
381 : {
382 20 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
383 0 : return NULL;
384 20 : curqlevel->flag |= LQL_COUNT;
385 20 : state = LQPRS_WAITFNUM;
386 : }
387 135187 : else if (t_iseq(ptr, '.'))
388 : {
389 65789 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
390 0 : return NULL;
391 65787 : state = LQPRS_WAITLEVEL;
392 65787 : curqlevel = NEXTLEV(curqlevel);
393 : }
394 69398 : else if (ISLABEL(ptr))
395 : {
396 : /* disallow more chars after a flag */
397 69398 : if (lptr->flag)
398 0 : UNCHAR;
399 : }
400 : else
401 0 : UNCHAR;
402 201074 : break;
403 140 : case LQPRS_WAITOPEN:
404 140 : if (t_iseq(ptr, '{'))
405 50 : state = LQPRS_WAITFNUM;
406 90 : else if (t_iseq(ptr, '.'))
407 : {
408 : /* We only get here for '*', so these are correct defaults */
409 90 : curqlevel->low = 0;
410 90 : curqlevel->high = LTREE_MAX_LEVELS;
411 90 : curqlevel = NEXTLEV(curqlevel);
412 90 : state = LQPRS_WAITLEVEL;
413 : }
414 : else
415 0 : UNCHAR;
416 140 : break;
417 70 : case LQPRS_WAITFNUM:
418 70 : if (t_iseq(ptr, ','))
419 16 : state = LQPRS_WAITSNUM;
420 54 : else if (isdigit((unsigned char) *ptr))
421 : {
422 54 : int low = atoi(ptr);
423 :
424 54 : if (low < 0 || low > LTREE_MAX_LEVELS)
425 1 : ereturn(escontext, NULL,
426 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
427 : errmsg("lquery syntax error"),
428 : errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.",
429 : low, LTREE_MAX_LEVELS, pos)));
430 :
431 53 : curqlevel->low = (uint16) low;
432 53 : state = LQPRS_WAITND;
433 : }
434 : else
435 0 : UNCHAR;
436 69 : break;
437 37 : case LQPRS_WAITSNUM:
438 37 : if (isdigit((unsigned char) *ptr))
439 : {
440 21 : int high = atoi(ptr);
441 :
442 21 : if (high < 0 || high > LTREE_MAX_LEVELS)
443 1 : ereturn(escontext, NULL,
444 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
445 : errmsg("lquery syntax error"),
446 : errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.",
447 : high, LTREE_MAX_LEVELS, pos)));
448 20 : else if (curqlevel->low > high)
449 1 : ereturn(escontext, NULL,
450 : (errcode(ERRCODE_SYNTAX_ERROR),
451 : errmsg("lquery syntax error"),
452 : errdetail("Low limit (%d) is greater than high limit (%d), at character %d.",
453 : curqlevel->low, high, pos)));
454 :
455 19 : curqlevel->high = (uint16) high;
456 19 : state = LQPRS_WAITCLOSE;
457 : }
458 16 : else if (t_iseq(ptr, '}'))
459 : {
460 16 : curqlevel->high = LTREE_MAX_LEVELS;
461 16 : state = LQPRS_WAITEND;
462 : }
463 : else
464 0 : UNCHAR;
465 35 : break;
466 28 : case LQPRS_WAITCLOSE:
467 28 : if (t_iseq(ptr, '}'))
468 19 : state = LQPRS_WAITEND;
469 9 : else if (!isdigit((unsigned char) *ptr))
470 0 : UNCHAR;
471 28 : break;
472 57 : case LQPRS_WAITND:
473 57 : if (t_iseq(ptr, '}'))
474 : {
475 32 : curqlevel->high = curqlevel->low;
476 32 : state = LQPRS_WAITEND;
477 : }
478 25 : else if (t_iseq(ptr, ','))
479 21 : state = LQPRS_WAITSNUM;
480 4 : else if (!isdigit((unsigned char) *ptr))
481 0 : UNCHAR;
482 57 : break;
483 46 : case LQPRS_WAITEND:
484 46 : if (t_iseq(ptr, '.'))
485 : {
486 46 : state = LQPRS_WAITLEVEL;
487 46 : curqlevel = NEXTLEV(curqlevel);
488 : }
489 : else
490 0 : UNCHAR;
491 46 : break;
492 0 : default:
493 0 : elog(ERROR, "internal error in lquery parser");
494 : }
495 :
496 333377 : ptr += charlen;
497 333377 : if (state == LQPRS_WAITDELIM)
498 201184 : lptr->wlen++;
499 333377 : pos++;
500 : }
501 :
502 176 : if (state == LQPRS_WAITDELIM)
503 : {
504 108 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
505 0 : return NULL;
506 : }
507 68 : else if (state == LQPRS_WAITOPEN)
508 46 : curqlevel->high = LTREE_MAX_LEVELS;
509 22 : else if (state != LQPRS_WAITEND)
510 1 : ereturn(escontext, NULL,
511 : (errcode(ERRCODE_SYNTAX_ERROR),
512 : errmsg("lquery syntax error"),
513 : errdetail("Unexpected end of input.")));
514 :
515 172 : curqlevel = tmpql;
516 172 : totallen = LQUERY_HDRSIZE;
517 66256 : while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
518 : {
519 66084 : totallen += LQL_HDRSIZE;
520 66084 : if (curqlevel->numvar)
521 : {
522 65901 : lptr = GETVAR(curqlevel);
523 132091 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
524 : {
525 66190 : totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
526 66190 : lptr++;
527 : }
528 : }
529 66084 : curqlevel = NEXTLEV(curqlevel);
530 : }
531 :
532 172 : result = (lquery *) palloc0(totallen);
533 172 : SET_VARSIZE(result, totallen);
534 172 : result->numlevel = num;
535 172 : result->firstgood = 0;
536 172 : result->flag = 0;
537 172 : if (hasnot)
538 52 : result->flag |= LQUERY_HASNOT;
539 172 : cur = LQUERY_FIRST(result);
540 172 : curqlevel = tmpql;
541 66255 : while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
542 : {
543 66084 : memcpy(cur, curqlevel, LQL_HDRSIZE);
544 66084 : cur->totallen = LQL_HDRSIZE;
545 66084 : if (curqlevel->numvar)
546 : {
547 65901 : lrptr = LQL_FIRST(cur);
548 65901 : lptr = GETVAR(curqlevel);
549 132082 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
550 : {
551 66182 : int newlen = cur->totallen + MAXALIGN(LVAR_HDRSIZE + lptr->len);
552 :
553 66182 : if (newlen > PG_UINT16_MAX)
554 1 : ereturn(escontext, NULL,
555 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
556 : errmsg("lquery level is too large"),
557 : errdetail("Total size of level exceeds the maximum allowed (%d bytes).",
558 : PG_UINT16_MAX)));
559 66181 : cur->totallen = (uint16) newlen;
560 :
561 66181 : lrptr->len = lptr->len;
562 66181 : lrptr->flag = lptr->flag;
563 66181 : lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
564 66181 : memcpy(lrptr->name, lptr->start, lptr->len);
565 66181 : lptr++;
566 66181 : lrptr = LVAR_NEXT(lrptr);
567 : }
568 65900 : pfree(GETVAR(curqlevel));
569 65900 : if (cur->numvar > 1 || cur->flag != 0)
570 : {
571 : /* Not a simple match */
572 112 : wasbad = true;
573 : }
574 65788 : else if (wasbad == false)
575 : {
576 : /* count leading simple matches */
577 65674 : (result->firstgood)++;
578 : }
579 : }
580 : else
581 : {
582 : /* '*', so this isn't a simple match */
583 183 : wasbad = true;
584 : }
585 66083 : curqlevel = NEXTLEV(curqlevel);
586 66083 : cur = LQL_NEXT(cur);
587 : }
588 :
589 171 : pfree(tmpql);
590 171 : return result;
591 :
592 : #undef UNCHAR
593 : }
594 :
595 : /*
596 : * Close out parsing an ltree or lquery nodeitem:
597 : * compute the correct length, and complain if it's not OK
598 : */
599 : static bool
600 293817 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
601 : struct Node *escontext)
602 : {
603 293817 : if (is_lquery)
604 : {
605 : /*
606 : * Back up over any flag characters, and discount them from length and
607 : * position.
608 : */
609 131786 : while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL)
610 : {
611 44 : ptr--;
612 44 : lptr->wlen--;
613 44 : pos--;
614 : }
615 : }
616 :
617 : /* Now compute the byte length, which we weren't tracking before. */
618 293817 : lptr->len = ptr - lptr->start;
619 :
620 : /* Complain if it's empty or too long */
621 293817 : if (lptr->len == 0)
622 3 : ereturn(escontext, false,
623 : (errcode(ERRCODE_SYNTAX_ERROR),
624 : is_lquery ?
625 : errmsg("lquery syntax error at character %d", pos) :
626 : errmsg("ltree syntax error at character %d", pos),
627 : errdetail("Empty labels are not allowed.")));
628 293814 : if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
629 3 : ereturn(escontext, false,
630 : (errcode(ERRCODE_NAME_TOO_LONG),
631 : errmsg("label string is too long"),
632 : errdetail("Label length is %d, must be at most %d, at character %d.",
633 : lptr->wlen, LTREE_LABEL_MAX_CHARS, pos)));
634 293811 : return true;
635 : }
636 :
637 : /*
638 : * expects an lquery
639 : * returns a null terminated string
640 : */
641 : static char *
642 30 : deparse_lquery(const lquery *in)
643 : {
644 : char *buf,
645 : *ptr;
646 : int i,
647 : j,
648 30 : totallen = 1;
649 : lquery_level *curqlevel;
650 : lquery_variant *curtlevel;
651 :
652 30 : curqlevel = LQUERY_FIRST(in);
653 106 : for (i = 0; i < in->numlevel; i++)
654 : {
655 76 : totallen++;
656 76 : if (curqlevel->numvar)
657 : {
658 51 : totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
659 51 : if (curqlevel->flag & LQL_COUNT)
660 4 : totallen += 2 * 11 + 3;
661 : }
662 : else
663 25 : totallen += 2 * 11 + 4;
664 76 : curqlevel = LQL_NEXT(curqlevel);
665 : }
666 :
667 30 : ptr = buf = (char *) palloc(totallen);
668 30 : curqlevel = LQUERY_FIRST(in);
669 106 : for (i = 0; i < in->numlevel; i++)
670 : {
671 76 : if (i != 0)
672 : {
673 46 : *ptr = '.';
674 46 : ptr++;
675 : }
676 76 : if (curqlevel->numvar)
677 : {
678 51 : if (curqlevel->flag & LQL_NOT)
679 : {
680 2 : *ptr = '!';
681 2 : ptr++;
682 : }
683 51 : curtlevel = LQL_FIRST(curqlevel);
684 131 : for (j = 0; j < curqlevel->numvar; j++)
685 : {
686 80 : if (j != 0)
687 : {
688 29 : *ptr = '|';
689 29 : ptr++;
690 : }
691 80 : memcpy(ptr, curtlevel->name, curtlevel->len);
692 80 : ptr += curtlevel->len;
693 80 : if ((curtlevel->flag & LVAR_SUBLEXEME))
694 : {
695 1 : *ptr = '%';
696 1 : ptr++;
697 : }
698 80 : if ((curtlevel->flag & LVAR_INCASE))
699 : {
700 3 : *ptr = '@';
701 3 : ptr++;
702 : }
703 80 : if ((curtlevel->flag & LVAR_ANYEND))
704 : {
705 4 : *ptr = '*';
706 4 : ptr++;
707 : }
708 80 : curtlevel = LVAR_NEXT(curtlevel);
709 : }
710 : }
711 : else
712 : {
713 25 : *ptr = '*';
714 25 : ptr++;
715 : }
716 :
717 76 : if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
718 : {
719 29 : if (curqlevel->low == curqlevel->high)
720 : {
721 2 : sprintf(ptr, "{%d}", curqlevel->low);
722 : }
723 27 : else if (curqlevel->low == 0)
724 : {
725 23 : if (curqlevel->high == LTREE_MAX_LEVELS)
726 : {
727 20 : if (curqlevel->numvar == 0)
728 : {
729 : /* This is default for '*', so print nothing */
730 19 : *ptr = '\0';
731 : }
732 : else
733 1 : sprintf(ptr, "{,}");
734 : }
735 : else
736 3 : sprintf(ptr, "{,%d}", curqlevel->high);
737 : }
738 4 : else if (curqlevel->high == LTREE_MAX_LEVELS)
739 : {
740 2 : sprintf(ptr, "{%d,}", curqlevel->low);
741 : }
742 : else
743 2 : sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
744 29 : ptr = strchr(ptr, '\0');
745 : }
746 :
747 76 : curqlevel = LQL_NEXT(curqlevel);
748 : }
749 :
750 30 : *ptr = '\0';
751 30 : return buf;
752 : }
753 :
754 : /*
755 : * Basic lquery I/O functions
756 : */
757 3 : PG_FUNCTION_INFO_V1(lquery_in);
758 : Datum
759 193 : lquery_in(PG_FUNCTION_ARGS)
760 : {
761 193 : char *buf = (char *) PG_GETARG_POINTER(0);
762 : lquery *res;
763 :
764 193 : if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
765 4 : PG_RETURN_NULL();
766 :
767 171 : PG_RETURN_POINTER(res);
768 : }
769 :
770 3 : PG_FUNCTION_INFO_V1(lquery_out);
771 : Datum
772 30 : lquery_out(PG_FUNCTION_ARGS)
773 : {
774 30 : lquery *in = PG_GETARG_LQUERY_P(0);
775 :
776 30 : PG_RETURN_POINTER(deparse_lquery(in));
777 : }
778 :
779 : /*
780 : * lquery type send function
781 : *
782 : * The type is sent as text in binary mode, so this is almost the same
783 : * as the output function, but it's prefixed with a version number so we
784 : * can change the binary format sent in future if necessary. For now,
785 : * only version 1 is supported.
786 : */
787 2 : PG_FUNCTION_INFO_V1(lquery_send);
788 : Datum
789 0 : lquery_send(PG_FUNCTION_ARGS)
790 : {
791 0 : lquery *in = PG_GETARG_LQUERY_P(0);
792 : StringInfoData buf;
793 0 : int version = 1;
794 0 : char *res = deparse_lquery(in);
795 :
796 0 : pq_begintypsend(&buf);
797 0 : pq_sendint8(&buf, version);
798 0 : pq_sendtext(&buf, res, strlen(res));
799 0 : pfree(res);
800 :
801 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
802 : }
803 :
804 : /*
805 : * lquery type recv function
806 : *
807 : * The type is sent as text in binary mode, so this is almost the same
808 : * as the input function, but it's prefixed with a version number so we
809 : * can change the binary format sent in future if necessary. For now,
810 : * only version 1 is supported.
811 : */
812 2 : PG_FUNCTION_INFO_V1(lquery_recv);
813 : Datum
814 0 : lquery_recv(PG_FUNCTION_ARGS)
815 : {
816 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
817 0 : int version = pq_getmsgint(buf, 1);
818 : char *str;
819 : int nbytes;
820 : lquery *res;
821 :
822 0 : if (version != 1)
823 0 : elog(ERROR, "unsupported lquery version number %d", version);
824 :
825 0 : str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
826 0 : res = parse_lquery(str, NULL);
827 0 : pfree(str);
828 :
829 0 : PG_RETURN_POINTER(res);
830 : }
|