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