TLA Line data Source code
1 : /*
2 : * txtquery io
3 : * Teodor Sigaev <teodor@stack.net>
4 : * contrib/ltree/ltxtquery_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 "miscadmin.h"
14 : #include "nodes/miscnodes.h"
15 : #include "varatt.h"
16 :
17 :
18 : /* parser's states */
19 : #define WAITOPERAND 1
20 : #define INOPERAND 2
21 : #define WAITOPERATOR 3
22 :
23 : /*
24 : * node of query tree, also used
25 : * for storing polish notation in parser
26 : */
27 : typedef struct NODE
28 : {
29 : int32 type;
30 : int32 val;
31 : int16 distance;
32 : int16 length;
33 : uint16 flag;
34 : struct NODE *next;
35 : } NODE;
36 :
37 : typedef struct
38 : {
39 : char *buf;
40 : int32 state;
41 : int32 count;
42 : struct Node *escontext;
43 : /* reverse polish notation in list (for temporary usage) */
44 : NODE *str;
45 : /* number in str */
46 : int32 num;
47 :
48 : /* user-friendly operand */
49 : int32 lenop;
50 : int32 sumlen;
51 : char *op;
52 : char *curop;
53 : } QPRS_STATE;
54 :
55 : /*
56 : * get token from query string
57 : *
58 : * caller needs to check if a soft-error was set if the result is ERR.
59 : */
60 : static int32
61 GIC 108 : gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
62 : {
63 : int charlen;
64 :
65 : for (;;)
66 ECB : {
67 GIC 365 : charlen = pg_mblen(state->buf);
68 :
69 365 : switch (state->state)
70 : {
71 83 : case WAITOPERAND:
72 GNC 83 : if (t_iseq(state->buf, '!'))
73 : {
74 CBC 6 : (state->buf)++;
75 GIC 6 : *val = (int32) '!';
76 CBC 6 : return OPR;
77 ECB : }
78 GNC 77 : else if (t_iseq(state->buf, '('))
79 ECB : {
80 LBC 0 : state->count++;
81 0 : (state->buf)++;
82 UIC 0 : return OPEN;
83 ECB : }
84 GNC 77 : else if (ISLABEL(state->buf))
85 EUB : {
86 GBC 50 : state->state = INOPERAND;
87 50 : *strval = state->buf;
88 GIC 50 : *lenval = charlen;
89 CBC 50 : *flag = 0;
90 : }
91 27 : else if (!t_isspace(state->buf))
92 GNC 2 : ereturn(state->escontext, ERR,
93 ECB : (errcode(ERRCODE_SYNTAX_ERROR),
94 : errmsg("operand syntax error")));
95 GIC 75 : break;
96 CBC 207 : case INOPERAND:
97 GNC 207 : if (ISLABEL(state->buf))
98 : {
99 GIC 127 : if (*flag)
100 UNC 0 : ereturn(state->escontext, ERR,
101 ECB : (errcode(ERRCODE_SYNTAX_ERROR),
102 : errmsg("modifiers syntax error")));
103 GIC 127 : *lenval += charlen;
104 ECB : }
105 GNC 80 : else if (t_iseq(state->buf, '%'))
106 GIC 4 : *flag |= LVAR_SUBLEXEME;
107 GNC 76 : else if (t_iseq(state->buf, '@'))
108 CBC 12 : *flag |= LVAR_INCASE;
109 GNC 64 : else if (t_iseq(state->buf, '*'))
110 CBC 14 : *flag |= LVAR_ANYEND;
111 ECB : else
112 : {
113 CBC 50 : state->state = WAITOPERATOR;
114 50 : return VAL;
115 ECB : }
116 GIC 157 : break;
117 75 : case WAITOPERATOR:
118 GNC 75 : if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|'))
119 ECB : {
120 GIC 25 : state->state = WAITOPERAND;
121 CBC 25 : *val = (int32) *(state->buf);
122 25 : (state->buf)++;
123 25 : return OPR;
124 : }
125 GNC 50 : else if (t_iseq(state->buf, ')'))
126 ECB : {
127 LBC 0 : (state->buf)++;
128 0 : state->count--;
129 UIC 0 : return (state->count < 0) ? ERR : CLOSE;
130 ECB : }
131 GIC 50 : else if (*(state->buf) == '\0')
132 : {
133 GBC 25 : return (state->count) ? ERR : END;
134 : }
135 GNC 25 : else if (!t_iseq(state->buf, ' '))
136 : {
137 UBC 0 : return ERR;
138 : }
139 GIC 25 : break;
140 LBC 0 : default:
141 UIC 0 : return ERR;
142 ECB : break;
143 : }
144 :
145 GIC 257 : state->buf += charlen;
146 EUB : }
147 :
148 : /* should not get here */
149 : }
150 ECB :
151 EUB : /*
152 : * push new one in polish notation reverse view
153 : */
154 : static bool
155 GIC 81 : pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
156 ECB : {
157 GIC 81 : NODE *tmp = (NODE *) palloc(sizeof(NODE));
158 :
159 81 : tmp->type = type;
160 81 : tmp->val = val;
161 81 : tmp->flag = flag;
162 81 : if (distance > 0xffff)
163 UNC 0 : ereturn(state->escontext, false,
164 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
165 : errmsg("value is too big")));
166 CBC 81 : if (lenval > 0xff)
167 UNC 0 : ereturn(state->escontext, false,
168 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
169 : errmsg("operand is too long")));
170 CBC 81 : tmp->distance = distance;
171 81 : tmp->length = lenval;
172 81 : tmp->next = state->str;
173 81 : state->str = tmp;
174 GBC 81 : state->num++;
175 GNC 81 : return true;
176 : }
177 :
178 ECB : /*
179 EUB : * This function is used for query text parsing
180 : */
181 : static bool
182 CBC 50 : pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
183 ECB : {
184 CBC 50 : if (lenval > 0xffff)
185 UNC 0 : ereturn(state->escontext, false,
186 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
187 : errmsg("word is too long")));
188 :
189 GNC 50 : if (! pushquery(state, type, ltree_crc32_sz(strval, lenval),
190 50 : state->curop - state->op, lenval, flag))
191 UNC 0 : return false;
192 :
193 GIC 50 : while (state->curop - state->op + lenval + 1 >= state->lenop)
194 : {
195 LBC 0 : int32 tmp = state->curop - state->op;
196 :
197 0 : state->lenop *= 2;
198 UNC 0 : state->op = (char *) repalloc(state->op, state->lenop);
199 UIC 0 : state->curop = state->op + tmp;
200 : }
201 GNC 50 : memcpy(state->curop, strval, lenval);
202 CBC 50 : state->curop += lenval;
203 50 : *(state->curop) = '\0';
204 GBC 50 : state->curop++;
205 GIC 50 : state->sumlen += lenval + 1;
206 GNC 50 : return true;
207 ECB : }
208 :
209 EUB : #define STACKDEPTH 32
210 : /*
211 : * make polish notation of query
212 : */
213 : static int32
214 GIC 27 : makepol(QPRS_STATE *state)
215 ECB : {
216 CBC 27 : int32 val = 0,
217 ECB : type;
218 CBC 27 : int32 lenval = 0;
219 27 : char *strval = NULL;
220 ECB : int32 stack[STACKDEPTH];
221 GIC 27 : int32 lenstack = 0;
222 27 : uint16 flag = 0;
223 :
224 : /* since this function recurses, it could be driven to stack overflow */
225 27 : check_stack_depth();
226 :
227 108 : while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
228 ECB : {
229 GIC 83 : switch (type)
230 ECB : {
231 GIC 50 : case VAL:
232 GNC 50 : if (!pushval_asis(state, VAL, strval, lenval, flag))
233 UNC 0 : return ERR;
234 CBC 79 : while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
235 GIC 8 : stack[lenstack - 1] == (int32) '!'))
236 ECB : {
237 CBC 29 : lenstack--;
238 GNC 29 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
239 UNC 0 : return ERR;
240 : }
241 CBC 50 : break;
242 GIC 31 : case OPR:
243 CBC 31 : if (lenstack && val == (int32) '|')
244 : {
245 UNC 0 : if (!pushquery(state, OPR, val, 0, 0, 0))
246 0 : return ERR;
247 : }
248 ECB : else
249 : {
250 CBC 31 : if (lenstack == STACKDEPTH)
251 ECB : /* internal error */
252 UBC 0 : elog(ERROR, "stack too short");
253 CBC 31 : stack[lenstack] = val;
254 31 : lenstack++;
255 : }
256 31 : break;
257 LBC 0 : case OPEN:
258 UBC 0 : if (makepol(state) == ERR)
259 UIC 0 : return ERR;
260 LBC 0 : while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
261 0 : stack[lenstack - 1] == (int32) '!'))
262 ECB : {
263 UIC 0 : lenstack--;
264 UNC 0 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
265 0 : return ERR;
266 EUB : }
267 UIC 0 : break;
268 0 : case CLOSE:
269 0 : while (lenstack)
270 ECB : {
271 UIC 0 : lenstack--;
272 UNC 0 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
273 0 : return ERR;
274 ECB : };
275 LBC 0 : return END;
276 : break;
277 CBC 2 : case ERR:
278 GNC 2 : if (SOFT_ERROR_OCCURRED(state->escontext))
279 2 : return ERR;
280 : /* fall through */
281 EUB : default:
282 UNC 0 : ereturn(state->escontext, ERR,
283 EUB : (errcode(ERRCODE_SYNTAX_ERROR),
284 : errmsg("syntax error")));
285 :
286 : }
287 : }
288 GBC 27 : while (lenstack)
289 : {
290 2 : lenstack--;
291 GNC 2 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
292 UNC 0 : return ERR;
293 EUB : };
294 GIC 25 : return END;
295 EUB : }
296 :
297 : static void
298 GIC 81 : findoprnd(ITEM *ptr, int32 *pos)
299 EUB : {
300 : /* since this function recurses, it could be driven to stack overflow. */
301 CBC 81 : check_stack_depth();
302 ECB :
303 CBC 81 : if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
304 : {
305 GIC 50 : ptr[*pos].left = 0;
306 GBC 50 : (*pos)++;
307 : }
308 GIC 31 : else if (ptr[*pos].val == (int32) '!')
309 : {
310 6 : ptr[*pos].left = 1;
311 6 : (*pos)++;
312 CBC 6 : findoprnd(ptr, pos);
313 : }
314 ECB : else
315 : {
316 GBC 25 : ITEM *curitem = &ptr[*pos];
317 GIC 25 : int32 tmp = *pos;
318 ECB :
319 GIC 25 : (*pos)++;
320 25 : findoprnd(ptr, pos);
321 25 : curitem->left = *pos - tmp;
322 CBC 25 : findoprnd(ptr, pos);
323 : }
324 GIC 81 : }
325 ECB :
326 :
327 : /*
328 : * input
329 : */
330 : static ltxtquery *
331 GNC 27 : queryin(char *buf, struct Node *escontext)
332 ECB : {
333 : QPRS_STATE state;
334 : int32 i;
335 : ltxtquery *query;
336 : int32 commonlen;
337 : ITEM *ptr;
338 : NODE *tmp;
339 GIC 27 : int32 pos = 0;
340 ECB :
341 : #ifdef BS_DEBUG
342 : char pbuf[16384],
343 : *cur;
344 : #endif
345 :
346 : /* init state */
347 GIC 27 : state.buf = buf;
348 CBC 27 : state.state = WAITOPERAND;
349 GIC 27 : state.count = 0;
350 27 : state.num = 0;
351 27 : state.str = NULL;
352 GNC 27 : state.escontext = escontext;
353 :
354 : /* init list of operand */
355 GIC 27 : state.sumlen = 0;
356 CBC 27 : state.lenop = 64;
357 GIC 27 : state.curop = state.op = (char *) palloc(state.lenop);
358 27 : *(state.curop) = '\0';
359 :
360 : /* parse query & make polish notation (postfix, but in reverse order) */
361 GNC 27 : if (makepol(&state) == ERR)
362 2 : return NULL;
363 GIC 25 : if (!state.num)
364 UNC 0 : ereturn(escontext, NULL,
365 ECB : (errcode(ERRCODE_SYNTAX_ERROR),
366 : errmsg("syntax error"),
367 : errdetail("Empty query.")));
368 :
369 GIC 25 : if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
370 UNC 0 : ereturn(escontext, NULL,
371 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
372 : errmsg("ltxtquery is too large")));
373 CBC 25 : commonlen = COMPUTESIZE(state.num, state.sumlen);
374 ECB :
375 CBC 25 : query = (ltxtquery *) palloc0(commonlen);
376 25 : SET_VARSIZE(query, commonlen);
377 25 : query->size = state.num;
378 25 : ptr = GETQUERY(query);
379 :
380 : /* set item in polish notation */
381 106 : for (i = 0; i < state.num; i++)
382 ECB : {
383 CBC 81 : ptr[i].type = state.str->type;
384 81 : ptr[i].val = state.str->val;
385 GIC 81 : ptr[i].distance = state.str->distance;
386 81 : ptr[i].length = state.str->length;
387 CBC 81 : ptr[i].flag = state.str->flag;
388 81 : tmp = state.str->next;
389 81 : pfree(state.str);
390 GBC 81 : state.str = tmp;
391 : }
392 :
393 : /* set user-friendly operand view */
394 GNC 25 : memcpy(GETOPERAND(query), state.op, state.sumlen);
395 CBC 25 : pfree(state.op);
396 EUB :
397 : /* set left operand's position for every operator */
398 GIC 25 : pos = 0;
399 CBC 25 : findoprnd(ptr, &pos);
400 :
401 25 : return query;
402 ECB : }
403 :
404 : /*
405 : * in without morphology
406 : */
407 CBC 3 : PG_FUNCTION_INFO_V1(ltxtq_in);
408 : Datum
409 27 : ltxtq_in(PG_FUNCTION_ARGS)
410 ECB : {
411 : ltxtquery *res;
412 :
413 GNC 27 : if ((res = queryin((char *) PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
414 2 : PG_RETURN_NULL();
415 25 : PG_RETURN_POINTER(res);
416 ECB : }
417 :
418 : /*
419 : * ltxtquery type recv function
420 : *
421 : * The type is sent as text in binary mode, so this is almost the same
422 : * as the input function, but it's prefixed with a version number so we
423 : * can change the binary format sent in future if necessary. For now,
424 : * only version 1 is supported.
425 : */
426 GIC 2 : PG_FUNCTION_INFO_V1(ltxtq_recv);
427 : Datum
428 LBC 0 : ltxtq_recv(PG_FUNCTION_ARGS)
429 ECB : {
430 UIC 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
431 LBC 0 : int version = pq_getmsgint(buf, 1);
432 : char *str;
433 : int nbytes;
434 : ltxtquery *res;
435 :
436 UIC 0 : if (version != 1)
437 LBC 0 : elog(ERROR, "unsupported ltxtquery version number %d", version);
438 :
439 0 : str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
440 UNC 0 : res = queryin(str, NULL);
441 UIC 0 : pfree(str);
442 :
443 LBC 0 : PG_RETURN_POINTER(res);
444 ECB : }
445 :
446 : /*
447 : * out function
448 : */
449 : typedef struct
450 : {
451 : ITEM *curpol;
452 : char *buf;
453 : char *cur;
454 : char *op;
455 : int32 buflen;
456 : } INFIX;
457 :
458 EUB : #define RESIZEBUF(inf,addsize) \
459 : while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
460 : { \
461 : int32 len = (inf)->cur - (inf)->buf; \
462 : (inf)->buflen *= 2; \
463 : (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
464 : (inf)->cur = (inf)->buf + len; \
465 : }
466 :
467 : /*
468 : * recursive walk on tree and print it in
469 : * infix (human-readable) view
470 : */
471 : static void
472 GIC 10 : infix(INFIX *in, bool first)
473 EUB : {
474 : /* since this function recurses, it could be driven to stack overflow. */
475 GIC 10 : check_stack_depth();
476 :
477 10 : if (in->curpol->type == VAL)
478 : {
479 6 : char *op = in->op + in->curpol->distance;
480 :
481 8 : RESIZEBUF(in, in->curpol->length * 2 + 5);
482 32 : while (*op)
483 : {
484 26 : *(in->cur) = *op;
485 26 : op++;
486 26 : in->cur++;
487 : }
488 6 : if (in->curpol->flag & LVAR_SUBLEXEME)
489 : {
490 2 : *(in->cur) = '%';
491 2 : in->cur++;
492 : }
493 6 : if (in->curpol->flag & LVAR_INCASE)
494 : {
495 1 : *(in->cur) = '@';
496 1 : in->cur++;
497 : }
498 6 : if (in->curpol->flag & LVAR_ANYEND)
499 : {
500 3 : *(in->cur) = '*';
501 3 : in->cur++;
502 ECB : }
503 GIC 6 : *(in->cur) = '\0';
504 6 : in->curpol++;
505 ECB : }
506 GIC 4 : else if (in->curpol->val == (int32) '!')
507 ECB : {
508 GIC 1 : bool isopr = false;
509 ECB :
510 GIC 1 : RESIZEBUF(in, 1);
511 CBC 1 : *(in->cur) = '!';
512 1 : in->cur++;
513 GIC 1 : *(in->cur) = '\0';
514 CBC 1 : in->curpol++;
515 1 : if (in->curpol->type == OPR)
516 ECB : {
517 UIC 0 : isopr = true;
518 LBC 0 : RESIZEBUF(in, 2);
519 UIC 0 : sprintf(in->cur, "( ");
520 LBC 0 : in->cur = strchr(in->cur, '\0');
521 ECB : }
522 GIC 1 : infix(in, isopr);
523 CBC 1 : if (isopr)
524 : {
525 LBC 0 : RESIZEBUF(in, 2);
526 0 : sprintf(in->cur, " )");
527 UIC 0 : in->cur = strchr(in->cur, '\0');
528 ECB : }
529 : }
530 : else
531 : {
532 GIC 3 : int32 op = in->curpol->val;
533 ECB : INFIX nrm;
534 :
535 GIC 3 : in->curpol++;
536 CBC 3 : if (op == (int32) '|' && !first)
537 : {
538 LBC 0 : RESIZEBUF(in, 2);
539 UIC 0 : sprintf(in->cur, "( ");
540 LBC 0 : in->cur = strchr(in->cur, '\0');
541 ECB : }
542 :
543 CBC 3 : nrm.curpol = in->curpol;
544 3 : nrm.op = in->op;
545 3 : nrm.buflen = 16;
546 GIC 3 : nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
547 EUB :
548 : /* get right operand */
549 GBC 3 : infix(&nrm, false);
550 EUB :
551 : /* get & print left operand */
552 CBC 3 : in->curpol = nrm.curpol;
553 3 : infix(in, false);
554 :
555 EUB : /* print operator & right operand */
556 GBC 3 : RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
557 3 : sprintf(in->cur, " %c %s", op, nrm.buf);
558 GIC 3 : in->cur = strchr(in->cur, '\0');
559 3 : pfree(nrm.buf);
560 :
561 3 : if (op == (int32) '|' && !first)
562 ECB : {
563 UIC 0 : RESIZEBUF(in, 2);
564 0 : sprintf(in->cur, " )");
565 LBC 0 : in->cur = strchr(in->cur, '\0');
566 ECB : }
567 : }
568 GBC 10 : }
569 EUB :
570 GBC 3 : PG_FUNCTION_INFO_V1(ltxtq_out);
571 : Datum
572 GIC 3 : ltxtq_out(PG_FUNCTION_ARGS)
573 ECB : {
574 CBC 3 : ltxtquery *query = PG_GETARG_LTXTQUERY_P(0);
575 ECB : INFIX nrm;
576 :
577 GIC 3 : if (query->size == 0)
578 UIC 0 : ereport(ERROR,
579 ECB : (errcode(ERRCODE_SYNTAX_ERROR),
580 : errmsg("syntax error"),
581 : errdetail("Empty query.")));
582 :
583 CBC 3 : nrm.curpol = GETQUERY(query);
584 GIC 3 : nrm.buflen = 32;
585 3 : nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
586 CBC 3 : *(nrm.cur) = '\0';
587 3 : nrm.op = GETOPERAND(query);
588 3 : infix(&nrm, true);
589 ECB :
590 GIC 3 : PG_RETURN_POINTER(nrm.buf);
591 ECB : }
592 :
593 EUB : /*
594 : * ltxtquery type send function
595 : *
596 : * The type is sent as text in binary mode, so this is almost the same
597 : * as the output function, but it's prefixed with a version number so we
598 ECB : * can change the binary format sent in future if necessary. For now,
599 : * only version 1 is supported.
600 : */
601 GIC 2 : PG_FUNCTION_INFO_V1(ltxtq_send);
602 ECB : Datum
603 UIC 0 : ltxtq_send(PG_FUNCTION_ARGS)
604 ECB : {
605 UIC 0 : ltxtquery *query = PG_GETARG_LTXTQUERY_P(0);
606 : StringInfoData buf;
607 LBC 0 : int version = 1;
608 EUB : INFIX nrm;
609 :
610 UIC 0 : if (query->size == 0)
611 0 : ereport(ERROR,
612 : (errcode(ERRCODE_SYNTAX_ERROR),
613 ECB : errmsg("syntax error"),
614 : errdetail("Empty query.")));
615 :
616 LBC 0 : nrm.curpol = GETQUERY(query);
617 0 : nrm.buflen = 32;
618 0 : nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
619 UIC 0 : *(nrm.cur) = '\0';
620 LBC 0 : nrm.op = GETOPERAND(query);
621 UIC 0 : infix(&nrm, true);
622 :
623 0 : pq_begintypsend(&buf);
624 0 : pq_sendint8(&buf, version);
625 0 : pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
626 0 : pfree(nrm.buf);
627 :
628 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
629 : }
|