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