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