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 "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 *
983 andrew@dunslane.net 36 :CBC 4859 : parse_ltree(const char *buf, struct Node *escontext)
37 : : {
38 : : const char *ptr;
39 : : nodeitem *list,
40 : : *lptr;
8403 bruce@momjian.us 41 : 4859 : int num = 0,
42 : 4859 : totallen = 0;
43 : 4859 : int state = LTPRS_WAITNAME;
44 : : ltree *result;
45 : : ltree_level *curlevel;
46 : : int charlen;
1985 tgl@sss.pgh.pa.us 47 : 4859 : 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 : :
8403 bruce@momjian.us 54 : 4859 : ptr = buf;
55 [ + + ]: 479110 : while (*ptr)
56 : : {
6277 teodor@sigaev.ru 57 : 474251 : charlen = pg_mblen(ptr);
1984 tgl@sss.pgh.pa.us 58 [ + + ]: 474251 : if (t_iseq(ptr, '.'))
8439 bruce@momjian.us 59 : 222771 : num++;
5931 60 : 474251 : ptr += charlen;
61 : : }
62 : :
1988 tgl@sss.pgh.pa.us 63 [ + + ]: 4859 : if (num + 1 > LTREE_MAX_LEVELS)
983 andrew@dunslane.net 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)));
8403 bruce@momjian.us 68 : 4858 : list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
69 : 4858 : ptr = buf;
70 [ + + ]: 348003 : while (*ptr)
71 : : {
6277 teodor@sigaev.ru 72 : 343150 : charlen = pg_mblen(ptr);
73 : :
1984 tgl@sss.pgh.pa.us 74 [ + + - ]: 343150 : switch (state)
75 : : {
76 : 162077 : case LTPRS_WAITNAME:
974 andrew@dunslane.net 77 [ + + + + : 162077 : if (ISLABEL(ptr))
+ + ]
78 : : {
1984 tgl@sss.pgh.pa.us 79 : 162072 : lptr->start = ptr;
80 : 162072 : lptr->wlen = 0;
81 : 162072 : state = LTPRS_WAITDELIM;
82 : : }
83 : : else
84 [ + + ]: 5 : UNCHAR;
85 : 162072 : break;
86 : 181073 : case LTPRS_WAITDELIM:
87 [ + + ]: 181073 : if (t_iseq(ptr, '.'))
88 : : {
983 andrew@dunslane.net 89 [ - + ]: 157229 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
983 andrew@dunslane.net 90 :UBC 0 : return NULL;
1984 tgl@sss.pgh.pa.us 91 :CBC 157229 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
92 : 157229 : lptr++;
93 : 157229 : state = LTPRS_WAITNAME;
94 : : }
974 andrew@dunslane.net 95 [ + + - + : 23844 : else if (!ISLABEL(ptr))
- - ]
1984 tgl@sss.pgh.pa.us 96 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 97 :CBC 181073 : break;
1984 tgl@sss.pgh.pa.us 98 :UBC 0 : default:
99 [ # # ]: 0 : elog(ERROR, "internal error in ltree parser");
100 : : }
101 : :
5931 bruce@momjian.us 102 :CBC 343145 : ptr += charlen;
6277 teodor@sigaev.ru 103 : 343145 : lptr->wlen++;
104 : 343145 : pos++;
105 : : }
106 : :
8403 bruce@momjian.us 107 [ + + ]: 4853 : if (state == LTPRS_WAITDELIM)
108 : : {
983 andrew@dunslane.net 109 [ - + ]: 4843 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
983 andrew@dunslane.net 110 :UBC 0 : return NULL;
8434 bruce@momjian.us 111 :CBC 4842 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
8439 112 : 4842 : lptr++;
113 : : }
8403 114 [ + - + + ]: 10 : else if (!(state == LTPRS_WAITNAME && lptr == list))
983 andrew@dunslane.net 115 [ + + ]: 3 : ereturn(escontext, NULL,
116 : : (errcode(ERRCODE_SYNTAX_ERROR),
117 : : errmsg("ltree syntax error"),
118 : : errdetail("Unexpected end of input.")));
119 : :
6357 tgl@sss.pgh.pa.us 120 : 4849 : result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
6765 121 : 4849 : SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
8403 bruce@momjian.us 122 : 4849 : result->numlevel = lptr - list;
8439 123 : 4849 : curlevel = LTREE_FIRST(result);
8403 124 : 4849 : lptr = list;
125 [ + + ]: 166913 : while (lptr - list < result->numlevel)
126 : : {
6277 teodor@sigaev.ru 127 : 162064 : curlevel->len = (uint16) lptr->len;
8403 bruce@momjian.us 128 : 162064 : memcpy(curlevel->name, lptr->start, lptr->len);
129 : 162064 : curlevel = LEVEL_NEXT(curlevel);
8439 130 : 162064 : lptr++;
131 : : }
132 : :
133 : 4849 : pfree(list);
1984 tgl@sss.pgh.pa.us 134 : 4849 : 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 : :
6765 151 : 6263 : ptr = buf = (char *) palloc(VARSIZE(in));
8439 bruce@momjian.us 152 : 6263 : curlevel = LTREE_FIRST(in);
8403 153 [ + + ]: 47260 : for (i = 0; i < in->numlevel; i++)
154 : : {
155 [ + + ]: 40997 : if (i != 0)
156 : : {
8439 157 : 34748 : *ptr = '.';
158 : 34748 : ptr++;
159 : : }
8403 160 : 40997 : memcpy(ptr, curlevel->name, curlevel->len);
161 : 40997 : ptr += curlevel->len;
8439 162 : 40997 : curlevel = LEVEL_NEXT(curlevel);
163 : : }
164 : :
8403 165 : 6263 : *ptr = '\0';
1984 tgl@sss.pgh.pa.us 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 : 4859 : ltree_in(PG_FUNCTION_ARGS)
175 : : {
176 : 4859 : char *buf = (char *) PG_GETARG_POINTER(0);
177 : : ltree *res;
178 : :
983 andrew@dunslane.net 179 [ + + ]: 4859 : if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
180 : 4 : PG_RETURN_NULL();
181 : :
182 : 4849 : PG_RETURN_POINTER(res);
183 : : }
184 : :
1984 tgl@sss.pgh.pa.us 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
1984 tgl@sss.pgh.pa.us 204 :UBC 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 : : */
1984 tgl@sss.pgh.pa.us 227 :CBC 2 : PG_FUNCTION_INFO_V1(ltree_recv);
228 : : Datum
1984 tgl@sss.pgh.pa.us 229 :UBC 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);
983 andrew@dunslane.net 241 : 0 : res = parse_ltree(str, NULL);
1984 tgl@sss.pgh.pa.us 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 *
983 andrew@dunslane.net 268 :CBC 191 : parse_lquery(const char *buf, struct Node *escontext)
269 : : {
270 : : const char *ptr;
8403 bruce@momjian.us 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;
1985 tgl@sss.pgh.pa.us 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 : :
8403 bruce@momjian.us 291 : 191 : ptr = buf;
292 [ + + ]: 267834 : while (*ptr)
293 : : {
6277 teodor@sigaev.ru 294 : 267643 : charlen = pg_mblen(ptr);
295 : :
1984 tgl@sss.pgh.pa.us 296 [ + + ]: 267643 : if (t_iseq(ptr, '.'))
297 : 131474 : num++;
298 [ + + ]: 136169 : else if (t_iseq(ptr, '|'))
299 : 34 : numOR++;
300 : :
5931 bruce@momjian.us 301 : 267643 : ptr += charlen;
302 : : }
303 : :
8439 304 : 191 : num++;
1988 tgl@sss.pgh.pa.us 305 [ + + ]: 191 : if (num > LQUERY_MAX_LEVELS)
983 andrew@dunslane.net 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)));
6357 tgl@sss.pgh.pa.us 310 : 190 : curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
8403 bruce@momjian.us 311 : 190 : ptr = buf;
312 [ + + ]: 136706 : while (*ptr)
313 : : {
6277 teodor@sigaev.ru 314 : 136531 : charlen = pg_mblen(ptr);
315 : :
1984 tgl@sss.pgh.pa.us 316 [ + + + + : 136531 : switch (state)
+ + + + +
- ]
317 : : {
318 : 66112 : case LQPRS_WAITLEVEL:
974 andrew@dunslane.net 319 [ + + + - : 66112 : if (ISLABEL(ptr))
- + ]
320 : : {
1984 tgl@sss.pgh.pa.us 321 : 65839 : GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(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 = (nodeitem *) palloc0(sizeof(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
8439 bruce@momjian.us 339 [ + + ]: 9 : UNCHAR;
1984 tgl@sss.pgh.pa.us 340 : 66103 : break;
341 : 34 : case LQPRS_WAITVAR:
974 andrew@dunslane.net 342 [ + + + - : 34 : if (ISLABEL(ptr))
- + ]
343 : : {
1984 tgl@sss.pgh.pa.us 344 : 33 : lptr++;
345 : 33 : lptr->start = ptr;
346 : 33 : state = LQPRS_WAITDELIM;
347 : 33 : curqlevel->numvar++;
348 : : }
349 : : else
8439 bruce@momjian.us 350 [ + - ]: 1 : UNCHAR;
1984 tgl@sss.pgh.pa.us 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 : : {
983 andrew@dunslane.net 370 [ - + ]: 34 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
983 andrew@dunslane.net 371 :UBC 0 : return NULL;
1984 tgl@sss.pgh.pa.us 372 :CBC 34 : state = LQPRS_WAITVAR;
373 : : }
374 [ + + ]: 69929 : else if (t_iseq(ptr, '{'))
375 : : {
983 andrew@dunslane.net 376 [ - + ]: 20 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
983 andrew@dunslane.net 377 :UBC 0 : return NULL;
1984 tgl@sss.pgh.pa.us 378 :CBC 20 : curqlevel->flag |= LQL_COUNT;
379 : 20 : state = LQPRS_WAITFNUM;
380 : : }
381 [ + + ]: 69909 : else if (t_iseq(ptr, '.'))
382 : : {
983 andrew@dunslane.net 383 [ - + ]: 65789 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
983 andrew@dunslane.net 384 :UBC 0 : return NULL;
1984 tgl@sss.pgh.pa.us 385 :CBC 65787 : state = LQPRS_WAITLEVEL;
386 : 65787 : curqlevel = NEXTLEV(curqlevel);
387 : : }
974 andrew@dunslane.net 388 [ + + - + : 4120 : else if (ISLABEL(ptr))
- - ]
389 : : {
390 : : /* disallow more chars after a flag */
1984 tgl@sss.pgh.pa.us 391 [ - + ]: 4120 : if (lptr->flag)
1984 tgl@sss.pgh.pa.us 392 [ # # ]:UBC 0 : UNCHAR;
393 : : }
394 : : else
8439 bruce@momjian.us 395 [ # # ]: 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 396 :CBC 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
8439 bruce@momjian.us 409 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 410 :CBC 140 : break;
411 : 70 : case LQPRS_WAITFNUM:
412 [ + + ]: 70 : if (t_iseq(ptr, ','))
413 : 16 : state = LQPRS_WAITSNUM;
263 peter@eisentraut.org 414 [ + - ]: 54 : else if (isdigit((unsigned char) *ptr))
415 : : {
1984 tgl@sss.pgh.pa.us 416 : 54 : int low = atoi(ptr);
417 : :
418 [ + - + + ]: 54 : if (low < 0 || low > LTREE_MAX_LEVELS)
983 andrew@dunslane.net 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 : :
1984 tgl@sss.pgh.pa.us 425 : 53 : curqlevel->low = (uint16) low;
426 : 53 : state = LQPRS_WAITND;
427 : : }
428 : : else
1984 tgl@sss.pgh.pa.us 429 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 430 :CBC 69 : break;
431 : 37 : case LQPRS_WAITSNUM:
263 peter@eisentraut.org 432 [ + + ]: 37 : if (isdigit((unsigned char) *ptr))
433 : : {
1984 tgl@sss.pgh.pa.us 434 : 21 : int high = atoi(ptr);
435 : :
436 [ + - + + ]: 21 : if (high < 0 || high > LTREE_MAX_LEVELS)
983 andrew@dunslane.net 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)));
1984 tgl@sss.pgh.pa.us 442 [ + + ]: 20 : else if (curqlevel->low > high)
983 andrew@dunslane.net 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 : :
1984 tgl@sss.pgh.pa.us 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
1984 tgl@sss.pgh.pa.us 458 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 459 :CBC 35 : break;
460 : 28 : case LQPRS_WAITCLOSE:
461 [ + + ]: 28 : if (t_iseq(ptr, '}'))
462 : 19 : state = LQPRS_WAITEND;
263 peter@eisentraut.org 463 [ - + ]: 9 : else if (!isdigit((unsigned char) *ptr))
1984 tgl@sss.pgh.pa.us 464 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 465 :CBC 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;
263 peter@eisentraut.org 474 [ - + ]: 4 : else if (!isdigit((unsigned char) *ptr))
1984 tgl@sss.pgh.pa.us 475 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 476 :CBC 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
1984 tgl@sss.pgh.pa.us 484 [ # # ]:UBC 0 : UNCHAR;
1984 tgl@sss.pgh.pa.us 485 :CBC 46 : break;
1984 tgl@sss.pgh.pa.us 486 :UBC 0 : default:
487 [ # # ]: 0 : elog(ERROR, "internal error in lquery parser");
488 : : }
489 : :
5931 bruce@momjian.us 490 :CBC 136516 : ptr += charlen;
491 [ + + ]: 136516 : if (state == LQPRS_WAITDELIM)
6277 teodor@sigaev.ru 492 : 70114 : lptr->wlen++;
493 : 136516 : pos++;
494 : : }
495 : :
8403 bruce@momjian.us 496 [ + + ]: 175 : if (state == LQPRS_WAITDELIM)
497 : : {
983 andrew@dunslane.net 498 [ - + ]: 107 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
982 andrew@dunslane.net 499 :UBC 0 : return NULL;
500 : : }
8403 bruce@momjian.us 501 [ + + ]:CBC 68 : else if (state == LQPRS_WAITOPEN)
1988 tgl@sss.pgh.pa.us 502 : 46 : curqlevel->high = LTREE_MAX_LEVELS;
8403 bruce@momjian.us 503 [ + + ]: 22 : else if (state != LQPRS_WAITEND)
983 andrew@dunslane.net 504 [ + - ]: 1 : ereturn(escontext, NULL,
505 : : (errcode(ERRCODE_SYNTAX_ERROR),
506 : : errmsg("lquery syntax error"),
507 : : errdetail("Unexpected end of input.")));
508 : :
8439 bruce@momjian.us 509 : 171 : curqlevel = tmpql;
8403 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 : : {
8439 516 : 65900 : lptr = GETVAR(curqlevel);
8403 517 [ + + ]: 131833 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
518 : : {
8434 519 : 65933 : totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
8439 520 : 65933 : lptr++;
521 : : }
522 : : }
8434 523 : 66083 : curqlevel = NEXTLEV(curqlevel);
524 : : }
525 : :
6357 tgl@sss.pgh.pa.us 526 : 171 : result = (lquery *) palloc0(totallen);
6765 527 : 171 : SET_VARSIZE(result, totallen);
8439 bruce@momjian.us 528 : 171 : result->numlevel = num;
529 : 171 : result->firstgood = 0;
8403 530 : 171 : result->flag = 0;
531 [ + + ]: 171 : if (hasnot)
8439 532 : 52 : result->flag |= LQUERY_HASNOT;
533 : 171 : cur = LQUERY_FIRST(result);
534 : 171 : curqlevel = tmpql;
8403 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 : : {
8439 541 : 65900 : lrptr = LQL_FIRST(cur);
542 : 65900 : lptr = GETVAR(curqlevel);
8403 543 [ + + ]: 131833 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
544 : : {
8434 545 : 65933 : cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
8403 546 : 65933 : lrptr->len = lptr->len;
8439 547 : 65933 : lrptr->flag = lptr->flag;
7287 tgl@sss.pgh.pa.us 548 : 65933 : lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
8403 bruce@momjian.us 549 : 65933 : memcpy(lrptr->name, lptr->start, lptr->len);
8439 550 : 65933 : lptr++;
8403 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 : : }
8434 570 : 66083 : curqlevel = NEXTLEV(curqlevel);
8439 571 : 66083 : cur = LQL_NEXT(cur);
572 : : }
573 : :
574 : 171 : pfree(tmpql);
1984 tgl@sss.pgh.pa.us 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
983 andrew@dunslane.net 585 : 228022 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
586 : : struct Node *escontext)
587 : : {
1984 tgl@sss.pgh.pa.us 588 [ + + ]: 228022 : 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 : 228022 : lptr->len = ptr - lptr->start;
604 : :
605 : : /* Complain if it's empty or too long */
606 [ + + ]: 228022 : if (lptr->len == 0)
983 andrew@dunslane.net 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.")));
1984 tgl@sss.pgh.pa.us 613 [ + + ]: 228019 : if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
983 andrew@dunslane.net 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 : 228016 : return true;
620 : : }
621 : :
622 : : /*
623 : : * expects an lquery
624 : : * returns a null terminated string
625 : : */
626 : : static char *
1984 tgl@sss.pgh.pa.us 627 : 30 : deparse_lquery(const lquery *in)
628 : : {
629 : : char *buf,
630 : : *ptr;
631 : : int i,
632 : : j,
7934 633 : 30 : totallen = 1;
634 : : lquery_level *curqlevel;
635 : : lquery_variant *curtlevel;
636 : :
8439 bruce@momjian.us 637 : 30 : curqlevel = LQUERY_FIRST(in);
8403 638 [ + + ]: 106 : for (i = 0; i < in->numlevel; i++)
639 : : {
7934 tgl@sss.pgh.pa.us 640 : 76 : totallen++;
8403 bruce@momjian.us 641 [ + + ]: 76 : if (curqlevel->numvar)
642 : : {
7934 tgl@sss.pgh.pa.us 643 : 51 : totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
1985 644 [ + + ]: 51 : if (curqlevel->flag & LQL_COUNT)
645 : 4 : totallen += 2 * 11 + 3;
646 : : }
647 : : else
7934 648 : 25 : totallen += 2 * 11 + 4;
8439 bruce@momjian.us 649 : 76 : curqlevel = LQL_NEXT(curqlevel);
650 : : }
651 : :
8403 652 : 30 : ptr = buf = (char *) palloc(totallen);
8439 653 : 30 : curqlevel = LQUERY_FIRST(in);
8403 654 [ + + ]: 106 : for (i = 0; i < in->numlevel; i++)
655 : : {
656 [ + + ]: 76 : if (i != 0)
657 : : {
8439 658 : 46 : *ptr = '.';
659 : 46 : ptr++;
660 : : }
8403 661 [ + + ]: 76 : if (curqlevel->numvar)
662 : : {
663 [ + + ]: 51 : if (curqlevel->flag & LQL_NOT)
664 : : {
8439 665 : 2 : *ptr = '!';
666 : 2 : ptr++;
667 : : }
668 : 51 : curtlevel = LQL_FIRST(curqlevel);
8403 669 [ + + ]: 131 : for (j = 0; j < curqlevel->numvar; j++)
670 : : {
671 [ + + ]: 80 : if (j != 0)
672 : : {
8439 673 : 29 : *ptr = '|';
674 : 29 : ptr++;
675 : : }
8403 676 : 80 : memcpy(ptr, curtlevel->name, curtlevel->len);
677 : 80 : ptr += curtlevel->len;
7129 neilc@samurai.com 678 [ + + ]: 80 : if ((curtlevel->flag & LVAR_SUBLEXEME))
679 : : {
8439 bruce@momjian.us 680 : 1 : *ptr = '%';
681 : 1 : ptr++;
682 : : }
8403 683 [ + + ]: 80 : if ((curtlevel->flag & LVAR_INCASE))
684 : : {
8439 685 : 3 : *ptr = '@';
686 : 3 : ptr++;
687 : : }
8403 688 [ + + ]: 80 : if ((curtlevel->flag & LVAR_ANYEND))
689 : : {
8439 690 : 4 : *ptr = '*';
691 : 4 : ptr++;
692 : : }
693 : 80 : curtlevel = LVAR_NEXT(curtlevel);
694 : : }
695 : : }
696 : : else
697 : : {
1985 tgl@sss.pgh.pa.us 698 : 25 : *ptr = '*';
699 : 25 : ptr++;
700 : : }
701 : :
702 [ + + + + ]: 76 : if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
703 : : {
8403 bruce@momjian.us 704 [ + + ]: 29 : if (curqlevel->low == curqlevel->high)
705 : : {
1985 tgl@sss.pgh.pa.us 706 : 2 : sprintf(ptr, "{%d}", curqlevel->low);
707 : : }
8403 bruce@momjian.us 708 [ + + ]: 27 : else if (curqlevel->low == 0)
709 : : {
1988 tgl@sss.pgh.pa.us 710 [ + + ]: 23 : if (curqlevel->high == LTREE_MAX_LEVELS)
711 : : {
1985 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 : : }
1988 723 [ + + ]: 4 : else if (curqlevel->high == LTREE_MAX_LEVELS)
724 : : {
1985 725 : 2 : sprintf(ptr, "{%d,}", curqlevel->low);
726 : : }
727 : : else
728 : 2 : sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
8403 bruce@momjian.us 729 : 29 : ptr = strchr(ptr, '\0');
730 : : }
731 : :
8439 732 : 76 : curqlevel = LQL_NEXT(curqlevel);
733 : : }
734 : :
8403 735 : 30 : *ptr = '\0';
1984 tgl@sss.pgh.pa.us 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 : :
983 andrew@dunslane.net 749 [ + + ]: 191 : if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
750 : 4 : PG_RETURN_NULL();
751 : :
752 : 171 : PG_RETURN_POINTER(res);
753 : : }
754 : :
1984 tgl@sss.pgh.pa.us 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
1984 tgl@sss.pgh.pa.us 774 :UBC 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 : : */
1984 tgl@sss.pgh.pa.us 797 :CBC 2 : PG_FUNCTION_INFO_V1(lquery_recv);
798 : : Datum
1984 tgl@sss.pgh.pa.us 799 :UBC 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);
983 andrew@dunslane.net 811 : 0 : res = parse_lquery(str, NULL);
1984 tgl@sss.pgh.pa.us 812 : 0 : pfree(str);
813 : :
814 : 0 : PG_RETURN_POINTER(res);
815 : : }
|