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