PostgreSQL Source Code git master
Loading...
Searching...
No Matches
ltxtquery_io.c
Go to the documentation of this file.
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 */
27typedef struct NODE
28{
29 int32 type;
30 int32 val;
34 struct NODE *next;
36
37typedef struct
38{
39 char *buf;
42 struct Node *escontext;
43 /* reverse polish notation in list (for temporary usage) */
45 /* number in str */
47
48 /* user-friendly operand */
51 char *op;
52 char *curop;
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 */
60static int32
62{
63 int charlen;
64
65 for (;;)
66 {
67 charlen = pg_mblen_cstr(state->buf);
68
69 switch (state->state)
70 {
71 case WAITOPERAND:
72 if (t_iseq(state->buf, '!'))
73 {
74 (state->buf)++;
75 *val = (int32) '!';
76 return OPR;
77 }
78 else if (t_iseq(state->buf, '('))
79 {
80 state->count++;
81 (state->buf)++;
82 return OPEN;
83 }
84 else if (ISLABEL(state->buf))
85 {
86 state->state = INOPERAND;
87 *strval = state->buf;
88 *lenval = charlen;
89 *flag = 0;
90 }
91 else if (!isspace((unsigned char) *state->buf))
92 ereturn(state->escontext, ERR,
94 errmsg("operand syntax error")));
95 break;
96 case INOPERAND:
97 if (ISLABEL(state->buf))
98 {
99 if (*flag)
100 ereturn(state->escontext, ERR,
102 errmsg("modifiers syntax error")));
103 *lenval += charlen;
104 }
105 else if (t_iseq(state->buf, '%'))
107 else if (t_iseq(state->buf, '@'))
108 *flag |= LVAR_INCASE;
109 else if (t_iseq(state->buf, '*'))
110 *flag |= LVAR_ANYEND;
111 else
112 {
113 state->state = WAITOPERATOR;
114 return VAL;
115 }
116 break;
117 case WAITOPERATOR:
118 if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|'))
119 {
120 state->state = WAITOPERAND;
121 *val = (int32) *(state->buf);
122 (state->buf)++;
123 return OPR;
124 }
125 else if (t_iseq(state->buf, ')'))
126 {
127 (state->buf)++;
128 state->count--;
129 return (state->count < 0) ? ERR : CLOSE;
130 }
131 else if (*(state->buf) == '\0')
132 {
133 return (state->count) ? ERR : END;
134 }
135 else if (!t_iseq(state->buf, ' '))
136 {
137 return ERR;
138 }
139 break;
140 default:
141 return ERR;
142 break;
143 }
144
145 state->buf += charlen;
146 }
147
148 /* should not get here */
149}
150
151/*
152 * push new one in polish notation reverse view
153 */
154static bool
156{
157 NODE *tmp = palloc_object(NODE);
158
159 tmp->type = type;
160 tmp->val = val;
161 tmp->flag = flag;
162 if (distance > 0xffff)
163 ereturn(state->escontext, false,
165 errmsg("value is too big")));
166 if (lenval > 0xff)
167 ereturn(state->escontext, false,
169 errmsg("operand is too long")));
170 tmp->distance = distance;
171 tmp->length = lenval;
172 tmp->next = state->str;
173 state->str = tmp;
174 state->num++;
175 return true;
176}
177
178/*
179 * This function is used for query text parsing
180 */
181static bool
183{
184 if (lenval > 0xffff)
185 ereturn(state->escontext, false,
187 errmsg("word is too long")));
188
189 if (!pushquery(state, type, ltree_crc32_sz(strval, lenval),
190 state->curop - state->op, lenval, flag))
191 return false;
192
193 while (state->curop - state->op + lenval + 1 >= state->lenop)
194 {
195 int32 tmp = state->curop - state->op;
196
197 state->lenop *= 2;
198 state->op = (char *) repalloc(state->op, state->lenop);
199 state->curop = state->op + tmp;
200 }
201 memcpy(state->curop, strval, lenval);
202 state->curop += lenval;
203 *(state->curop) = '\0';
204 state->curop++;
205 state->sumlen += lenval + 1;
206 return true;
207}
208
209#define STACKDEPTH 32
210/*
211 * make polish notation of query
212 */
213static int32
215{
216 int32 val = 0,
217 type;
218 int32 lenval = 0;
219 char *strval = NULL;
220 int32 stack[STACKDEPTH];
221 int32 lenstack = 0;
222 uint16 flag = 0;
223
224 /* since this function recurses, it could be driven to stack overflow */
226
227 while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
228 {
229 switch (type)
230 {
231 case VAL:
232 if (!pushval_asis(state, VAL, strval, lenval, flag))
233 return ERR;
234 while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
235 stack[lenstack - 1] == (int32) '!'))
236 {
237 lenstack--;
238 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
239 return ERR;
240 }
241 break;
242 case OPR:
243 if (lenstack && val == (int32) '|')
244 {
245 if (!pushquery(state, OPR, val, 0, 0, 0))
246 return ERR;
247 }
248 else
249 {
250 if (lenstack == STACKDEPTH)
251 /* internal error */
252 elog(ERROR, "stack too short");
253 stack[lenstack] = val;
254 lenstack++;
255 }
256 break;
257 case OPEN:
258 if (makepol(state) == ERR)
259 return ERR;
260 while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
261 stack[lenstack - 1] == (int32) '!'))
262 {
263 lenstack--;
264 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
265 return ERR;
266 }
267 break;
268 case CLOSE:
269 while (lenstack)
270 {
271 lenstack--;
272 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
273 return ERR;
274 };
275 return END;
276 break;
277 case ERR:
278 if (SOFT_ERROR_OCCURRED(state->escontext))
279 return ERR;
281 default:
282 ereturn(state->escontext, ERR,
284 errmsg("syntax error")));
285
286 }
287 }
288 while (lenstack)
289 {
290 lenstack--;
291 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
292 return ERR;
293 };
294 return END;
295}
296
297/*
298 * Recursively fill the "left" fields of an ITEM array that represents
299 * a valid postfix tree.
300 *
301 * state: only needed for error reporting
302 * ptr: starting element of array
303 * pos: in/out argument, the array index this call is responsible to fill
304 *
305 * At exit, *pos has been incremented to point after the sub-tree whose
306 * top is the entry-time value of *pos.
307 *
308 * Returns true if okay, false if error (the only possible error is
309 * overflow of a "left" field).
310 */
311static bool
313{
314 int32 mypos;
315
316 /* since this function recurses, it could be driven to stack overflow. */
318
319 /* get the position this call is supposed to update */
320 mypos = *pos;
321
322 /* in all cases, we should increment *pos to advance over this item */
323 (*pos)++;
324
325 if (ptr[mypos].type == VAL || ptr[mypos].type == VALTRUE)
326 {
327 /* base case: a VAL has no operand, so just set its left to zero */
328 ptr[mypos].left = 0;
329 }
330 else if (ptr[mypos].val == (int32) '!')
331 {
332 /* unary operator, likewise easy: operand is just after it */
333 ptr[mypos].left = 1;
334 /* recurse to scan operand */
335 if (!findoprnd(state, ptr, pos))
336 return false;
337 }
338 else
339 {
340 /* binary operator */
341 int32 delta;
342
343 /* recurse to scan right operand */
344 if (!findoprnd(state, ptr, pos))
345 return false;
346 /* we must fill left with offset to left operand's top */
347 /* delta can't overflow, see LTXTQUERY_TOO_BIG ... */
348 delta = *pos - mypos;
349 /* ... but it might be too large to fit in the 16-bit left field */
350 Assert(delta > 0);
351 if (unlikely(delta > PG_INT16_MAX))
352 ereturn(state->escontext, false,
354 errmsg("ltxtquery is too large")));
355 ptr[mypos].left = (int16) delta;
356 /* recurse to scan left operand */
357 if (!findoprnd(state, ptr, pos))
358 return false;
359 }
360
361 return true;
362}
363
364
365/*
366 * input
367 */
368static ltxtquery *
369queryin(char *buf, struct Node *escontext)
370{
372 int32 i;
373 ltxtquery *query;
375 ITEM *ptr;
376 NODE *tmp;
377 int32 pos = 0;
378
379 /* init state */
380 state.buf = buf;
381 state.state = WAITOPERAND;
382 state.count = 0;
383 state.num = 0;
384 state.str = NULL;
385 state.escontext = escontext;
386
387 /* init list of operand */
388 state.sumlen = 0;
389 state.lenop = 64;
390 state.curop = state.op = (char *) palloc(state.lenop);
391 *(state.curop) = '\0';
392
393 /* parse query & make polish notation (postfix, but in reverse order) */
394 if (makepol(&state) == ERR)
395 return NULL;
396 if (!state.num)
397 ereturn(escontext, NULL,
399 errmsg("syntax error"),
400 errdetail("Empty query.")));
401
402 if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
403 ereturn(escontext, NULL,
405 errmsg("ltxtquery is too large")));
406 commonlen = COMPUTESIZE(state.num, state.sumlen);
407
408 query = (ltxtquery *) palloc0(commonlen);
409 SET_VARSIZE(query, commonlen);
410 query->size = state.num;
411 ptr = GETQUERY(query);
412
413 /* set item in polish notation */
414 for (i = 0; i < state.num; i++)
415 {
416 ptr[i].type = state.str->type;
417 ptr[i].val = state.str->val;
418 ptr[i].distance = state.str->distance;
419 ptr[i].length = state.str->length;
420 ptr[i].flag = state.str->flag;
421 tmp = state.str->next;
422 pfree(state.str);
423 state.str = tmp;
424 }
425
426 /* set user-friendly operand view */
427 memcpy(GETOPERAND(query), state.op, state.sumlen);
428 pfree(state.op);
429
430 /* set left operand's position for every operator */
431 pos = 0;
432 if (!findoprnd(&state, ptr, &pos))
433 return NULL;
434 /* if successful, findoprnd should have scanned the whole array */
435 Assert(pos == state.num);
436
437 return query;
438}
439
440/*
441 * in without morphology
442 */
444Datum
446{
447 ltxtquery *res;
448
449 if ((res = queryin(PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
452}
453
454/*
455 * ltxtquery type recv function
456 *
457 * The type is sent as text in binary mode, so this is almost the same
458 * as the input function, but it's prefixed with a version number so we
459 * can change the binary format sent in future if necessary. For now,
460 * only version 1 is supported.
461 */
463Datum
465{
467 int version = pq_getmsgint(buf, 1);
468 char *str;
469 int nbytes;
470 ltxtquery *res;
471
472 if (version != 1)
473 elog(ERROR, "unsupported ltxtquery version number %d", version);
474
475 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
476 res = queryin(str, NULL);
477 pfree(str);
478
480}
481
482/*
483 * out function
484 */
485typedef struct
486{
487 ITEM *curpol;
488 char *buf;
489 char *cur;
490 char *op;
491 int32 buflen;
492} INFIX;
493
494#define RESIZEBUF(inf,addsize) \
495while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
496{ \
497 int32 len = (inf)->cur - (inf)->buf; \
498 (inf)->buflen *= 2; \
499 (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
500 (inf)->cur = (inf)->buf + len; \
501}
502
503/*
504 * recursive walk on tree and print it in
505 * infix (human-readable) view
506 */
507static void
508infix(INFIX *in, bool first)
509{
510 /* since this function recurses, it could be driven to stack overflow. */
512
513 if (in->curpol->type == VAL)
514 {
515 char *op = in->op + in->curpol->distance;
516
517 RESIZEBUF(in, in->curpol->length * 2 + 5);
518 while (*op)
519 {
520 *(in->cur) = *op;
521 op++;
522 in->cur++;
523 }
524 if (in->curpol->flag & LVAR_SUBLEXEME)
525 {
526 *(in->cur) = '%';
527 in->cur++;
528 }
529 if (in->curpol->flag & LVAR_INCASE)
530 {
531 *(in->cur) = '@';
532 in->cur++;
533 }
534 if (in->curpol->flag & LVAR_ANYEND)
535 {
536 *(in->cur) = '*';
537 in->cur++;
538 }
539 *(in->cur) = '\0';
540 in->curpol++;
541 }
542 else if (in->curpol->val == (int32) '!')
543 {
544 bool isopr = false;
545
546 RESIZEBUF(in, 1);
547 *(in->cur) = '!';
548 in->cur++;
549 *(in->cur) = '\0';
550 in->curpol++;
551 if (in->curpol->type == OPR)
552 {
553 isopr = true;
554 RESIZEBUF(in, 2);
555 sprintf(in->cur, "( ");
556 in->cur = strchr(in->cur, '\0');
557 }
558 infix(in, isopr);
559 if (isopr)
560 {
561 RESIZEBUF(in, 2);
562 sprintf(in->cur, " )");
563 in->cur = strchr(in->cur, '\0');
564 }
565 }
566 else
567 {
568 int32 op = in->curpol->val;
569 INFIX nrm;
570
571 in->curpol++;
572 if (op == (int32) '|' && !first)
573 {
574 RESIZEBUF(in, 2);
575 sprintf(in->cur, "( ");
576 in->cur = strchr(in->cur, '\0');
577 }
578
579 nrm.curpol = in->curpol;
580 nrm.op = in->op;
581 nrm.buflen = 16;
582 nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
583
584 /* get right operand */
585 infix(&nrm, false);
586
587 /* get & print left operand */
588 in->curpol = nrm.curpol;
589 infix(in, false);
590
591 /* print operator & right operand */
592 RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
593 sprintf(in->cur, " %c %s", op, nrm.buf);
594 in->cur = strchr(in->cur, '\0');
595 pfree(nrm.buf);
596
597 if (op == (int32) '|' && !first)
598 {
599 RESIZEBUF(in, 2);
600 sprintf(in->cur, " )");
601 in->cur = strchr(in->cur, '\0');
602 }
603 }
604}
605
607Datum
609{
611 INFIX nrm;
612
613 if (query->size == 0)
616 errmsg("syntax error"),
617 errdetail("Empty query.")));
618
619 nrm.curpol = GETQUERY(query);
620 nrm.buflen = 32;
621 nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
622 *(nrm.cur) = '\0';
623 nrm.op = GETOPERAND(query);
624 infix(&nrm, true);
625
627}
628
629/*
630 * ltxtquery type send function
631 *
632 * The type is sent as text in binary mode, so this is almost the same
633 * as the output function, but it's prefixed with a version number so we
634 * can change the binary format sent in future if necessary. For now,
635 * only version 1 is supported.
636 */
638Datum
640{
643 int version = 1;
644 INFIX nrm;
645
646 if (query->size == 0)
649 errmsg("syntax error"),
650 errdetail("Empty query.")));
651
652 nrm.curpol = GETQUERY(query);
653 nrm.buflen = 32;
654 nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
655 *(nrm.cur) = '\0';
656 nrm.op = GETOPERAND(query);
657 infix(&nrm, true);
658
660 pq_sendint8(&buf, version);
661 pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
662 pfree(nrm.buf);
663
665}
#define CLOSE
Definition _int.h:165
#define OPEN
Definition _int.h:164
#define END
Definition _int.h:160
#define COMPUTESIZE(size)
Definition _int.h:155
#define OPR
Definition _int.h:163
#define VAL
Definition _int.h:162
#define ERR
Definition _int.h:161
#define GETQUERY(x)
Definition _int.h:157
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
int32_t int32
Definition c.h:620
uint16_t uint16
Definition c.h:623
#define unlikely(x)
Definition c.h:438
#define pg_fallthrough
Definition c.h:161
#define PG_INT16_MAX
Definition c.h:670
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
unsigned int ltree_crc32_sz(const char *buf, int size)
Definition crc32.c:22
struct cursor * cur
Definition ecpg.c:29
int errcode(int sqlerrcode)
Definition elog.c:875
#define ereturn(context, dummy_value,...)
Definition elog.h:280
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define palloc_object(type)
Definition fe_memutils.h:89
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define PG_RETURN_BYTEA_P(x)
Definition fmgr.h:373
#define PG_GETARG_POINTER(n)
Definition fmgr.h:277
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
const char * str
long val
Definition informix.c:689
int i
Definition isn.c:77
#define VALTRUE
Definition ltree.h:175
#define PG_GETARG_LTXTQUERY_P(n)
Definition ltree.h:229
#define LVAR_INCASE
Definition ltree.h:75
#define LVAR_ANYEND
Definition ltree.h:74
#define ISLABEL(x)
Definition ltree.h:130
#define LTXTQUERY_TOO_BIG(size, lenofoperand)
Definition ltree.h:162
#define GETOPERAND(x)
Definition ltree.h:165
#define LVAR_SUBLEXEME
Definition ltree.h:76
static bool findoprnd(QPRS_STATE *state, ITEM *ptr, int32 *pos)
static ltxtquery * queryin(char *buf, struct Node *escontext)
static int32 gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
Datum ltxtq_recv(PG_FUNCTION_ARGS)
Datum ltxtq_send(PG_FUNCTION_ARGS)
static bool pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
static int32 makepol(QPRS_STATE *state)
static bool pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
#define RESIZEBUF(inf, addsize)
#define WAITOPERAND
Datum ltxtq_out(PG_FUNCTION_ARGS)
static void infix(INFIX *in, bool first)
#define STACKDEPTH
#define WAITOPERATOR
#define INOPERAND
Datum ltxtq_in(PG_FUNCTION_ARGS)
struct NODE NODE
int pg_mblen_cstr(const char *mbstr)
Definition mbutils.c:1045
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc0(Size size)
Definition mcxt.c:1420
void * palloc(Size size)
Definition mcxt.c:1390
#define SOFT_ERROR_OCCURRED(escontext)
Definition miscnodes.h:53
static char * errmsg
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define sprintf
Definition port.h:263
uint64_t Datum
Definition postgres.h:70
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition pqformat.c:414
void pq_sendtext(StringInfo buf, const char *str, int slen)
Definition pqformat.c:172
char * pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes)
Definition pqformat.c:545
void pq_begintypsend(StringInfo buf)
Definition pqformat.c:325
bytea * pq_endtypsend(StringInfo buf)
Definition pqformat.c:345
static void pq_sendint8(StringInfo buf, uint8 i)
Definition pqformat.h:128
static int fb(int x)
void check_stack_depth(void)
Definition stack_depth.c:95
struct StringInfoData * StringInfo
Definition string.h:15
char * cur
Definition _int_bool.c:599
ITEM * curpol
Definition _int_bool.c:597
char * op
Definition _int.h:141
uint16 distance
Definition ltree.h:146
int16 left
Definition _int.h:143
uint8 flag
Definition ltree.h:143
int32 val
Definition _int.h:144
int16 type
Definition _int.h:142
uint8 length
Definition ltree.h:145
struct NODE * next
Definition _int_bool.c:29
int32 val
Definition _int_bool.c:28
int16 distance
uint16 flag
int16 length
int32 type
Definition _int_bool.c:27
Definition nodes.h:135
char * curop
struct Node * escontext
int32 size
Definition ltree.h:156
char flag
Definition regguts.h:334
struct state * next
Definition regguts.h:340
char * flag(int b)
Definition test-ctype.c:33
#define t_iseq(x, c)
Definition ts_locale.h:38
static void SET_VARSIZE(void *PTR, Size len)
Definition varatt.h:432
const char * type