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 "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 *
1249 andrew@dunslane.net 37 :CBC 4860 : parse_ltree(const char *buf, struct Node *escontext)
38 : : {
39 : : const char *ptr;
40 : : nodeitem *list,
41 : : *lptr;
8669 bruce@momjian.us 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;
2251 tgl@sss.pgh.pa.us 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 : :
8669 bruce@momjian.us 55 : 4860 : ptr = buf;
56 [ + + ]: 479128 : while (*ptr)
57 : : {
143 tmunro@postgresql.or 58 : 474268 : charlen = pg_mblen_cstr(ptr);
2250 tgl@sss.pgh.pa.us 59 [ + + ]: 474268 : if (t_iseq(ptr, '.'))
8705 bruce@momjian.us 60 : 222773 : num++;
6197 61 : 474268 : ptr += charlen;
62 : : }
63 : :
2254 tgl@sss.pgh.pa.us 64 [ + + ]: 4860 : if (num + 1 > LTREE_MAX_LEVELS)
1249 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)));
176 michael@paquier.xyz 69 :GNC 4859 : list = lptr = palloc_array(nodeitem, num + 1);
8669 bruce@momjian.us 70 :CBC 4859 : ptr = buf;
71 [ + + ]: 348021 : while (*ptr)
72 : : {
143 tmunro@postgresql.or 73 : 343167 : charlen = pg_mblen_cstr(ptr);
74 : :
2250 tgl@sss.pgh.pa.us 75 [ + + - ]: 343167 : switch (state)
76 : : {
77 : 162080 : case LTPRS_WAITNAME:
1240 andrew@dunslane.net 78 [ + + + + : 162080 : if (ISLABEL(ptr))
+ + ]
79 : : {
2250 tgl@sss.pgh.pa.us 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 : : {
1249 andrew@dunslane.net 90 [ - + ]: 157231 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
1249 andrew@dunslane.net 91 :UBC 0 : return NULL;
2250 tgl@sss.pgh.pa.us 92 :CBC 157231 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
93 : 157231 : lptr++;
94 : 157231 : state = LTPRS_WAITNAME;
95 : : }
1240 andrew@dunslane.net 96 [ + + - + : 23856 : else if (!ISLABEL(ptr))
- - ]
2250 tgl@sss.pgh.pa.us 97 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 98 :CBC 181087 : break;
2250 tgl@sss.pgh.pa.us 99 :UBC 0 : default:
100 [ # # ]: 0 : elog(ERROR, "internal error in ltree parser");
101 : : }
102 : :
6197 bruce@momjian.us 103 :CBC 343162 : ptr += charlen;
6543 teodor@sigaev.ru 104 : 343162 : lptr->wlen++;
105 : 343162 : pos++;
106 : : }
107 : :
8669 bruce@momjian.us 108 [ + + ]: 4854 : if (state == LTPRS_WAITDELIM)
109 : : {
1249 andrew@dunslane.net 110 [ - + ]: 4844 : if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
1249 andrew@dunslane.net 111 :UBC 0 : return NULL;
8700 bruce@momjian.us 112 :CBC 4843 : totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
8705 113 : 4843 : lptr++;
114 : : }
8669 115 [ + - + + ]: 10 : else if (!(state == LTPRS_WAITNAME && lptr == list))
1249 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 : :
6623 tgl@sss.pgh.pa.us 121 : 4850 : result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
7031 122 : 4850 : SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
8669 bruce@momjian.us 123 : 4850 : result->numlevel = lptr - list;
8705 124 : 4850 : curlevel = LTREE_FIRST(result);
8669 125 : 4850 : lptr = list;
126 [ + + ]: 166917 : while (lptr - list < result->numlevel)
127 : : {
6543 teodor@sigaev.ru 128 : 162067 : curlevel->len = (uint16) lptr->len;
8669 bruce@momjian.us 129 : 162067 : memcpy(curlevel->name, lptr->start, lptr->len);
130 : 162067 : curlevel = LEVEL_NEXT(curlevel);
8705 131 : 162067 : lptr++;
132 : : }
133 : :
134 : 4850 : pfree(list);
2250 tgl@sss.pgh.pa.us 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 : :
7031 152 : 6263 : ptr = buf = (char *) palloc(VARSIZE(in));
8705 bruce@momjian.us 153 : 6263 : curlevel = LTREE_FIRST(in);
8669 154 [ + + ]: 47260 : for (i = 0; i < in->numlevel; i++)
155 : : {
156 [ + + ]: 40997 : if (i != 0)
157 : : {
8705 158 : 34748 : *ptr = '.';
159 : 34748 : ptr++;
160 : : }
8669 161 : 40997 : memcpy(ptr, curlevel->name, curlevel->len);
162 : 40997 : ptr += curlevel->len;
8705 163 : 40997 : curlevel = LEVEL_NEXT(curlevel);
164 : : }
165 : :
8669 166 : 6263 : *ptr = '\0';
2250 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 : 4860 : ltree_in(PG_FUNCTION_ARGS)
176 : : {
177 : 4860 : char *buf = (char *) PG_GETARG_POINTER(0);
178 : : ltree *res;
179 : :
1249 andrew@dunslane.net 180 [ + + ]: 4860 : if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
181 : 4 : PG_RETURN_NULL();
182 : :
183 : 4850 : PG_RETURN_POINTER(res);
184 : : }
185 : :
2250 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
2250 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 : : */
2250 tgl@sss.pgh.pa.us 228 :CBC 2 : PG_FUNCTION_INFO_V1(ltree_recv);
229 : : Datum
2250 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);
1249 andrew@dunslane.net 242 : 0 : res = parse_ltree(str, NULL);
2250 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 *
1249 andrew@dunslane.net 269 :CBC 193 : parse_lquery(const char *buf, struct Node *escontext)
270 : : {
271 : : const char *ptr;
8669 bruce@momjian.us 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;
2251 tgl@sss.pgh.pa.us 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 : :
8669 bruce@momjian.us 292 : 193 : ptr = buf;
293 [ + + ]: 464698 : while (*ptr)
294 : : {
143 tmunro@postgresql.or 295 : 464505 : charlen = pg_mblen_cstr(ptr);
296 : :
2250 tgl@sss.pgh.pa.us 297 [ + + ]: 464505 : if (t_iseq(ptr, '.'))
298 : 131474 : num++;
299 [ + + ]: 333031 : else if (t_iseq(ptr, '|'))
300 : 65825 : numOR++;
301 : :
6197 bruce@momjian.us 302 : 464505 : ptr += charlen;
303 : : }
304 : :
8705 305 : 193 : num++;
2254 tgl@sss.pgh.pa.us 306 [ + + ]: 193 : if (num > LQUERY_MAX_LEVELS)
1249 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)));
6623 tgl@sss.pgh.pa.us 311 : 192 : curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
8669 bruce@momjian.us 312 : 192 : ptr = buf;
313 [ + + ]: 333569 : while (*ptr)
314 : : {
143 tmunro@postgresql.or 315 : 333393 : charlen = pg_mblen_cstr(ptr);
316 : :
2250 tgl@sss.pgh.pa.us 317 [ + + + + : 333393 : switch (state)
+ + + + +
- ]
318 : : {
319 : 66114 : case LQPRS_WAITLEVEL:
1240 andrew@dunslane.net 320 [ + + + - : 66114 : if (ISLABEL(ptr))
- + ]
321 : : {
176 michael@paquier.xyz 322 :GNC 65841 : GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
2250 tgl@sss.pgh.pa.us 323 :CBC 65841 : lptr->start = ptr;
324 : 65841 : state = LQPRS_WAITDELIM;
325 : 65841 : curqlevel->numvar = 1;
326 : : }
327 [ + + ]: 273 : else if (t_iseq(ptr, '!'))
328 : : {
176 michael@paquier.xyz 329 :GNC 78 : GETVAR(curqlevel) = lptr = palloc0_array(nodeitem, numOR + 1);
2250 tgl@sss.pgh.pa.us 330 :CBC 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
8705 bruce@momjian.us 340 [ + + ]: 9 : UNCHAR;
2250 tgl@sss.pgh.pa.us 341 : 66105 : break;
342 : 65825 : case LQPRS_WAITVAR:
1240 andrew@dunslane.net 343 [ + + + - : 65825 : if (ISLABEL(ptr))
- + ]
344 : : {
2250 tgl@sss.pgh.pa.us 345 : 65824 : lptr++;
346 : 65824 : lptr->start = ptr;
347 : 65824 : state = LQPRS_WAITDELIM;
19 michael@paquier.xyz 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
8705 bruce@momjian.us 356 [ + - ]: 1 : UNCHAR;
2250 tgl@sss.pgh.pa.us 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 : : {
1249 andrew@dunslane.net 376 [ - + ]: 65825 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
1249 andrew@dunslane.net 377 :UBC 0 : return NULL;
2250 tgl@sss.pgh.pa.us 378 :CBC 65825 : state = LQPRS_WAITVAR;
379 : : }
380 [ + + ]: 135207 : else if (t_iseq(ptr, '{'))
381 : : {
1249 andrew@dunslane.net 382 [ - + ]: 20 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
1249 andrew@dunslane.net 383 :UBC 0 : return NULL;
2250 tgl@sss.pgh.pa.us 384 :CBC 20 : curqlevel->flag |= LQL_COUNT;
385 : 20 : state = LQPRS_WAITFNUM;
386 : : }
387 [ + + ]: 135187 : else if (t_iseq(ptr, '.'))
388 : : {
1249 andrew@dunslane.net 389 [ - + ]: 65789 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
1249 andrew@dunslane.net 390 :UBC 0 : return NULL;
2250 tgl@sss.pgh.pa.us 391 :CBC 65787 : state = LQPRS_WAITLEVEL;
392 : 65787 : curqlevel = NEXTLEV(curqlevel);
393 : : }
1240 andrew@dunslane.net 394 [ + + - + : 69398 : else if (ISLABEL(ptr))
- - ]
395 : : {
396 : : /* disallow more chars after a flag */
2250 tgl@sss.pgh.pa.us 397 [ - + ]: 69398 : if (lptr->flag)
2250 tgl@sss.pgh.pa.us 398 [ # # ]:UBC 0 : UNCHAR;
399 : : }
400 : : else
8705 bruce@momjian.us 401 [ # # ]: 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 402 :CBC 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
8705 bruce@momjian.us 415 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 416 :CBC 140 : break;
417 : 70 : case LQPRS_WAITFNUM:
418 [ + + ]: 70 : if (t_iseq(ptr, ','))
419 : 16 : state = LQPRS_WAITSNUM;
529 peter@eisentraut.org 420 [ + - ]: 54 : else if (isdigit((unsigned char) *ptr))
421 : : {
2250 tgl@sss.pgh.pa.us 422 : 54 : int low = atoi(ptr);
423 : :
424 [ + - + + ]: 54 : if (low < 0 || low > LTREE_MAX_LEVELS)
1249 andrew@dunslane.net 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 : :
2250 tgl@sss.pgh.pa.us 431 : 53 : curqlevel->low = (uint16) low;
432 : 53 : state = LQPRS_WAITND;
433 : : }
434 : : else
2250 tgl@sss.pgh.pa.us 435 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 436 :CBC 69 : break;
437 : 37 : case LQPRS_WAITSNUM:
529 peter@eisentraut.org 438 [ + + ]: 37 : if (isdigit((unsigned char) *ptr))
439 : : {
2250 tgl@sss.pgh.pa.us 440 : 21 : int high = atoi(ptr);
441 : :
442 [ + - + + ]: 21 : if (high < 0 || high > LTREE_MAX_LEVELS)
1249 andrew@dunslane.net 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)));
2250 tgl@sss.pgh.pa.us 448 [ + + ]: 20 : else if (curqlevel->low > high)
1249 andrew@dunslane.net 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 : :
2250 tgl@sss.pgh.pa.us 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
2250 tgl@sss.pgh.pa.us 464 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 465 :CBC 35 : break;
466 : 28 : case LQPRS_WAITCLOSE:
467 [ + + ]: 28 : if (t_iseq(ptr, '}'))
468 : 19 : state = LQPRS_WAITEND;
529 peter@eisentraut.org 469 [ - + ]: 9 : else if (!isdigit((unsigned char) *ptr))
2250 tgl@sss.pgh.pa.us 470 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 471 :CBC 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;
529 peter@eisentraut.org 480 [ - + ]: 4 : else if (!isdigit((unsigned char) *ptr))
2250 tgl@sss.pgh.pa.us 481 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 482 :CBC 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
2250 tgl@sss.pgh.pa.us 490 [ # # ]:UBC 0 : UNCHAR;
2250 tgl@sss.pgh.pa.us 491 :CBC 46 : break;
2250 tgl@sss.pgh.pa.us 492 :UBC 0 : default:
493 [ # # ]: 0 : elog(ERROR, "internal error in lquery parser");
494 : : }
495 : :
6197 bruce@momjian.us 496 :CBC 333377 : ptr += charlen;
497 [ + + ]: 333377 : if (state == LQPRS_WAITDELIM)
6543 teodor@sigaev.ru 498 : 201184 : lptr->wlen++;
499 : 333377 : pos++;
500 : : }
501 : :
8669 bruce@momjian.us 502 [ + + ]: 176 : if (state == LQPRS_WAITDELIM)
503 : : {
1249 andrew@dunslane.net 504 [ - + ]: 108 : if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
1248 andrew@dunslane.net 505 :UBC 0 : return NULL;
506 : : }
8669 bruce@momjian.us 507 [ + + ]:CBC 68 : else if (state == LQPRS_WAITOPEN)
2254 tgl@sss.pgh.pa.us 508 : 46 : curqlevel->high = LTREE_MAX_LEVELS;
8669 bruce@momjian.us 509 [ + + ]: 22 : else if (state != LQPRS_WAITEND)
1249 andrew@dunslane.net 510 [ + - ]: 1 : ereturn(escontext, NULL,
511 : : (errcode(ERRCODE_SYNTAX_ERROR),
512 : : errmsg("lquery syntax error"),
513 : : errdetail("Unexpected end of input.")));
514 : :
8705 bruce@momjian.us 515 : 172 : curqlevel = tmpql;
8669 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 : : {
8705 522 : 65901 : lptr = GETVAR(curqlevel);
8669 523 [ + + ]: 132091 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
524 : : {
8700 525 : 66190 : totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
8705 526 : 66190 : lptr++;
527 : : }
528 : : }
8700 529 : 66084 : curqlevel = NEXTLEV(curqlevel);
530 : : }
531 : :
6623 tgl@sss.pgh.pa.us 532 : 172 : result = (lquery *) palloc0(totallen);
7031 533 : 172 : SET_VARSIZE(result, totallen);
8705 bruce@momjian.us 534 : 172 : result->numlevel = num;
535 : 172 : result->firstgood = 0;
8669 536 : 172 : result->flag = 0;
537 [ + + ]: 172 : if (hasnot)
8705 538 : 52 : result->flag |= LQUERY_HASNOT;
539 : 172 : cur = LQUERY_FIRST(result);
540 : 172 : curqlevel = tmpql;
8669 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 : : {
8705 547 : 65901 : lrptr = LQL_FIRST(cur);
548 : 65901 : lptr = GETVAR(curqlevel);
8669 549 [ + + ]: 132082 : while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
550 : : {
19 michael@paquier.xyz 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 : :
8669 bruce@momjian.us 561 : 66181 : lrptr->len = lptr->len;
8705 562 : 66181 : lrptr->flag = lptr->flag;
7553 tgl@sss.pgh.pa.us 563 : 66181 : lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
8669 bruce@momjian.us 564 : 66181 : memcpy(lrptr->name, lptr->start, lptr->len);
8705 565 : 66181 : lptr++;
8669 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 : : }
8700 585 : 66083 : curqlevel = NEXTLEV(curqlevel);
8705 586 : 66083 : cur = LQL_NEXT(cur);
587 : : }
588 : :
589 : 171 : pfree(tmpql);
2250 tgl@sss.pgh.pa.us 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
1249 andrew@dunslane.net 600 : 293817 : finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
601 : : struct Node *escontext)
602 : : {
2250 tgl@sss.pgh.pa.us 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)
1249 andrew@dunslane.net 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.")));
2250 tgl@sss.pgh.pa.us 628 [ + + ]: 293814 : if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
1249 andrew@dunslane.net 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 *
2250 tgl@sss.pgh.pa.us 642 : 30 : deparse_lquery(const lquery *in)
643 : : {
644 : : char *buf,
645 : : *ptr;
646 : : int i,
647 : : j,
8200 648 : 30 : totallen = 1;
649 : : lquery_level *curqlevel;
650 : : lquery_variant *curtlevel;
651 : :
8705 bruce@momjian.us 652 : 30 : curqlevel = LQUERY_FIRST(in);
8669 653 [ + + ]: 106 : for (i = 0; i < in->numlevel; i++)
654 : : {
8200 tgl@sss.pgh.pa.us 655 : 76 : totallen++;
8669 bruce@momjian.us 656 [ + + ]: 76 : if (curqlevel->numvar)
657 : : {
8200 tgl@sss.pgh.pa.us 658 : 51 : totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
2251 659 [ + + ]: 51 : if (curqlevel->flag & LQL_COUNT)
660 : 4 : totallen += 2 * 11 + 3;
661 : : }
662 : : else
8200 663 : 25 : totallen += 2 * 11 + 4;
8705 bruce@momjian.us 664 : 76 : curqlevel = LQL_NEXT(curqlevel);
665 : : }
666 : :
8669 667 : 30 : ptr = buf = (char *) palloc(totallen);
8705 668 : 30 : curqlevel = LQUERY_FIRST(in);
8669 669 [ + + ]: 106 : for (i = 0; i < in->numlevel; i++)
670 : : {
671 [ + + ]: 76 : if (i != 0)
672 : : {
8705 673 : 46 : *ptr = '.';
674 : 46 : ptr++;
675 : : }
8669 676 [ + + ]: 76 : if (curqlevel->numvar)
677 : : {
678 [ + + ]: 51 : if (curqlevel->flag & LQL_NOT)
679 : : {
8705 680 : 2 : *ptr = '!';
681 : 2 : ptr++;
682 : : }
683 : 51 : curtlevel = LQL_FIRST(curqlevel);
8669 684 [ + + ]: 131 : for (j = 0; j < curqlevel->numvar; j++)
685 : : {
686 [ + + ]: 80 : if (j != 0)
687 : : {
8705 688 : 29 : *ptr = '|';
689 : 29 : ptr++;
690 : : }
8669 691 : 80 : memcpy(ptr, curtlevel->name, curtlevel->len);
692 : 80 : ptr += curtlevel->len;
7395 neilc@samurai.com 693 [ + + ]: 80 : if ((curtlevel->flag & LVAR_SUBLEXEME))
694 : : {
8705 bruce@momjian.us 695 : 1 : *ptr = '%';
696 : 1 : ptr++;
697 : : }
8669 698 [ + + ]: 80 : if ((curtlevel->flag & LVAR_INCASE))
699 : : {
8705 700 : 3 : *ptr = '@';
701 : 3 : ptr++;
702 : : }
8669 703 [ + + ]: 80 : if ((curtlevel->flag & LVAR_ANYEND))
704 : : {
8705 705 : 4 : *ptr = '*';
706 : 4 : ptr++;
707 : : }
708 : 80 : curtlevel = LVAR_NEXT(curtlevel);
709 : : }
710 : : }
711 : : else
712 : : {
2251 tgl@sss.pgh.pa.us 713 : 25 : *ptr = '*';
714 : 25 : ptr++;
715 : : }
716 : :
717 [ + + + + ]: 76 : if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
718 : : {
8669 bruce@momjian.us 719 [ + + ]: 29 : if (curqlevel->low == curqlevel->high)
720 : : {
2251 tgl@sss.pgh.pa.us 721 : 2 : sprintf(ptr, "{%d}", curqlevel->low);
722 : : }
8669 bruce@momjian.us 723 [ + + ]: 27 : else if (curqlevel->low == 0)
724 : : {
2254 tgl@sss.pgh.pa.us 725 [ + + ]: 23 : if (curqlevel->high == LTREE_MAX_LEVELS)
726 : : {
2251 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 : : }
2254 738 [ + + ]: 4 : else if (curqlevel->high == LTREE_MAX_LEVELS)
739 : : {
2251 740 : 2 : sprintf(ptr, "{%d,}", curqlevel->low);
741 : : }
742 : : else
743 : 2 : sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
8669 bruce@momjian.us 744 : 29 : ptr = strchr(ptr, '\0');
745 : : }
746 : :
8705 747 : 76 : curqlevel = LQL_NEXT(curqlevel);
748 : : }
749 : :
8669 750 : 30 : *ptr = '\0';
2250 tgl@sss.pgh.pa.us 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 : :
1249 andrew@dunslane.net 764 [ + + ]: 193 : if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
765 : 4 : PG_RETURN_NULL();
766 : :
767 : 171 : PG_RETURN_POINTER(res);
768 : : }
769 : :
2250 tgl@sss.pgh.pa.us 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
2250 tgl@sss.pgh.pa.us 789 :UBC 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 : : */
2250 tgl@sss.pgh.pa.us 812 :CBC 2 : PG_FUNCTION_INFO_V1(lquery_recv);
813 : : Datum
2250 tgl@sss.pgh.pa.us 814 :UBC 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);
1249 andrew@dunslane.net 826 : 0 : res = parse_lquery(str, NULL);
2250 tgl@sss.pgh.pa.us 827 : 0 : pfree(str);
828 : :
829 : 0 : PG_RETURN_POINTER(res);
830 : : }
|