Add data for WAL in pg_stat_io and backend statistics
[pgsql.git] / contrib / ltree / ltxtquery_io.c
blob7b8fba17ff2ab466cc28199a11dcce2da57cb30d
1 /*
2 * txtquery io
3 * Teodor Sigaev <teodor@stack.net>
4 * contrib/ltree/ltxtquery_io.c
5 */
6 #include "postgres.h"
8 #include <ctype.h>
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"
18 /* parser's states */
19 #define WAITOPERAND 1
20 #define INOPERAND 2
21 #define WAITOPERATOR 3
24 * node of query tree, also used
25 * for storing polish notation in parser
27 typedef struct NODE
29 int32 type;
30 int32 val;
31 int16 distance;
32 int16 length;
33 uint16 flag;
34 struct NODE *next;
35 } NODE;
37 typedef struct
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;
48 /* user-friendly operand */
49 int32 lenop;
50 int32 sumlen;
51 char *op;
52 char *curop;
53 } QPRS_STATE;
56 * get token from query string
58 * caller needs to check if a soft-error was set if the result is ERR.
60 static int32
61 gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
63 int charlen;
65 for (;;)
67 charlen = pg_mblen(state->buf);
69 switch (state->state)
71 case WAITOPERAND:
72 if (t_iseq(state->buf, '!'))
74 (state->buf)++;
75 *val = (int32) '!';
76 return OPR;
78 else if (t_iseq(state->buf, '('))
80 state->count++;
81 (state->buf)++;
82 return OPEN;
84 else if (ISLABEL(state->buf))
86 state->state = INOPERAND;
87 *strval = state->buf;
88 *lenval = charlen;
89 *flag = 0;
91 else if (!isspace((unsigned char) *state->buf))
92 ereturn(state->escontext, ERR,
93 (errcode(ERRCODE_SYNTAX_ERROR),
94 errmsg("operand syntax error")));
95 break;
96 case INOPERAND:
97 if (ISLABEL(state->buf))
99 if (*flag)
100 ereturn(state->escontext, ERR,
101 (errcode(ERRCODE_SYNTAX_ERROR),
102 errmsg("modifiers syntax error")));
103 *lenval += charlen;
105 else if (t_iseq(state->buf, '%'))
106 *flag |= LVAR_SUBLEXEME;
107 else if (t_iseq(state->buf, '@'))
108 *flag |= LVAR_INCASE;
109 else if (t_iseq(state->buf, '*'))
110 *flag |= LVAR_ANYEND;
111 else
113 state->state = WAITOPERATOR;
114 return VAL;
116 break;
117 case WAITOPERATOR:
118 if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|'))
120 state->state = WAITOPERAND;
121 *val = (int32) *(state->buf);
122 (state->buf)++;
123 return OPR;
125 else if (t_iseq(state->buf, ')'))
127 (state->buf)++;
128 state->count--;
129 return (state->count < 0) ? ERR : CLOSE;
131 else if (*(state->buf) == '\0')
133 return (state->count) ? ERR : END;
135 else if (!t_iseq(state->buf, ' '))
137 return ERR;
139 break;
140 default:
141 return ERR;
142 break;
145 state->buf += charlen;
148 /* should not get here */
152 * push new one in polish notation reverse view
154 static bool
155 pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
157 NODE *tmp = (NODE *) palloc(sizeof(NODE));
159 tmp->type = type;
160 tmp->val = val;
161 tmp->flag = flag;
162 if (distance > 0xffff)
163 ereturn(state->escontext, false,
164 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
165 errmsg("value is too big")));
166 if (lenval > 0xff)
167 ereturn(state->escontext, false,
168 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
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;
179 * This function is used for query text parsing
181 static bool
182 pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
184 if (lenval > 0xffff)
185 ereturn(state->escontext, false,
186 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
187 errmsg("word is too long")));
189 if (!pushquery(state, type, ltree_crc32_sz(strval, lenval),
190 state->curop - state->op, lenval, flag))
191 return false;
193 while (state->curop - state->op + lenval + 1 >= state->lenop)
195 int32 tmp = state->curop - state->op;
197 state->lenop *= 2;
198 state->op = (char *) repalloc(state->op, state->lenop);
199 state->curop = state->op + tmp;
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;
209 #define STACKDEPTH 32
211 * make polish notation of query
213 static int32
214 makepol(QPRS_STATE *state)
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;
224 /* since this function recurses, it could be driven to stack overflow */
225 check_stack_depth();
227 while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
229 switch (type)
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) '!'))
237 lenstack--;
238 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
239 return ERR;
241 break;
242 case OPR:
243 if (lenstack && val == (int32) '|')
245 if (!pushquery(state, OPR, val, 0, 0, 0))
246 return ERR;
248 else
250 if (lenstack == STACKDEPTH)
251 /* internal error */
252 elog(ERROR, "stack too short");
253 stack[lenstack] = val;
254 lenstack++;
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) '!'))
263 lenstack--;
264 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
265 return ERR;
267 break;
268 case CLOSE:
269 while (lenstack)
271 lenstack--;
272 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
273 return ERR;
275 return END;
276 break;
277 case ERR:
278 if (SOFT_ERROR_OCCURRED(state->escontext))
279 return ERR;
280 /* fall through */
281 default:
282 ereturn(state->escontext, ERR,
283 (errcode(ERRCODE_SYNTAX_ERROR),
284 errmsg("syntax error")));
288 while (lenstack)
290 lenstack--;
291 if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
292 return ERR;
294 return END;
297 static void
298 findoprnd(ITEM *ptr, int32 *pos)
300 /* since this function recurses, it could be driven to stack overflow. */
301 check_stack_depth();
303 if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
305 ptr[*pos].left = 0;
306 (*pos)++;
308 else if (ptr[*pos].val == (int32) '!')
310 ptr[*pos].left = 1;
311 (*pos)++;
312 findoprnd(ptr, pos);
314 else
316 ITEM *curitem = &ptr[*pos];
317 int32 tmp = *pos;
319 (*pos)++;
320 findoprnd(ptr, pos);
321 curitem->left = *pos - tmp;
322 findoprnd(ptr, pos);
328 * input
330 static ltxtquery *
331 queryin(char *buf, struct Node *escontext)
333 QPRS_STATE state;
334 int32 i;
335 ltxtquery *query;
336 int32 commonlen;
337 ITEM *ptr;
338 NODE *tmp;
339 int32 pos = 0;
341 #ifdef BS_DEBUG
342 char pbuf[16384],
343 *cur;
344 #endif
346 /* init state */
347 state.buf = buf;
348 state.state = WAITOPERAND;
349 state.count = 0;
350 state.num = 0;
351 state.str = NULL;
352 state.escontext = escontext;
354 /* init list of operand */
355 state.sumlen = 0;
356 state.lenop = 64;
357 state.curop = state.op = (char *) palloc(state.lenop);
358 *(state.curop) = '\0';
360 /* parse query & make polish notation (postfix, but in reverse order) */
361 if (makepol(&state) == ERR)
362 return NULL;
363 if (!state.num)
364 ereturn(escontext, NULL,
365 (errcode(ERRCODE_SYNTAX_ERROR),
366 errmsg("syntax error"),
367 errdetail("Empty query.")));
369 if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
370 ereturn(escontext, NULL,
371 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
372 errmsg("ltxtquery is too large")));
373 commonlen = COMPUTESIZE(state.num, state.sumlen);
375 query = (ltxtquery *) palloc0(commonlen);
376 SET_VARSIZE(query, commonlen);
377 query->size = state.num;
378 ptr = GETQUERY(query);
380 /* set item in polish notation */
381 for (i = 0; i < state.num; i++)
383 ptr[i].type = state.str->type;
384 ptr[i].val = state.str->val;
385 ptr[i].distance = state.str->distance;
386 ptr[i].length = state.str->length;
387 ptr[i].flag = state.str->flag;
388 tmp = state.str->next;
389 pfree(state.str);
390 state.str = tmp;
393 /* set user-friendly operand view */
394 memcpy(GETOPERAND(query), state.op, state.sumlen);
395 pfree(state.op);
397 /* set left operand's position for every operator */
398 pos = 0;
399 findoprnd(ptr, &pos);
401 return query;
405 * in without morphology
407 PG_FUNCTION_INFO_V1(ltxtq_in);
408 Datum
409 ltxtq_in(PG_FUNCTION_ARGS)
411 ltxtquery *res;
413 if ((res = queryin((char *) PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
414 PG_RETURN_NULL();
415 PG_RETURN_POINTER(res);
419 * ltxtquery type recv function
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.
426 PG_FUNCTION_INFO_V1(ltxtq_recv);
427 Datum
428 ltxtq_recv(PG_FUNCTION_ARGS)
430 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
431 int version = pq_getmsgint(buf, 1);
432 char *str;
433 int nbytes;
434 ltxtquery *res;
436 if (version != 1)
437 elog(ERROR, "unsupported ltxtquery version number %d", version);
439 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
440 res = queryin(str, NULL);
441 pfree(str);
443 PG_RETURN_POINTER(res);
447 * out function
449 typedef struct
451 ITEM *curpol;
452 char *buf;
453 char *cur;
454 char *op;
455 int32 buflen;
456 } INFIX;
458 #define RESIZEBUF(inf,addsize) \
459 while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
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; \
468 * recursive walk on tree and print it in
469 * infix (human-readable) view
471 static void
472 infix(INFIX *in, bool first)
474 /* since this function recurses, it could be driven to stack overflow. */
475 check_stack_depth();
477 if (in->curpol->type == VAL)
479 char *op = in->op + in->curpol->distance;
481 RESIZEBUF(in, in->curpol->length * 2 + 5);
482 while (*op)
484 *(in->cur) = *op;
485 op++;
486 in->cur++;
488 if (in->curpol->flag & LVAR_SUBLEXEME)
490 *(in->cur) = '%';
491 in->cur++;
493 if (in->curpol->flag & LVAR_INCASE)
495 *(in->cur) = '@';
496 in->cur++;
498 if (in->curpol->flag & LVAR_ANYEND)
500 *(in->cur) = '*';
501 in->cur++;
503 *(in->cur) = '\0';
504 in->curpol++;
506 else if (in->curpol->val == (int32) '!')
508 bool isopr = false;
510 RESIZEBUF(in, 1);
511 *(in->cur) = '!';
512 in->cur++;
513 *(in->cur) = '\0';
514 in->curpol++;
515 if (in->curpol->type == OPR)
517 isopr = true;
518 RESIZEBUF(in, 2);
519 sprintf(in->cur, "( ");
520 in->cur = strchr(in->cur, '\0');
522 infix(in, isopr);
523 if (isopr)
525 RESIZEBUF(in, 2);
526 sprintf(in->cur, " )");
527 in->cur = strchr(in->cur, '\0');
530 else
532 int32 op = in->curpol->val;
533 INFIX nrm;
535 in->curpol++;
536 if (op == (int32) '|' && !first)
538 RESIZEBUF(in, 2);
539 sprintf(in->cur, "( ");
540 in->cur = strchr(in->cur, '\0');
543 nrm.curpol = in->curpol;
544 nrm.op = in->op;
545 nrm.buflen = 16;
546 nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
548 /* get right operand */
549 infix(&nrm, false);
551 /* get & print left operand */
552 in->curpol = nrm.curpol;
553 infix(in, false);
555 /* print operator & right operand */
556 RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
557 sprintf(in->cur, " %c %s", op, nrm.buf);
558 in->cur = strchr(in->cur, '\0');
559 pfree(nrm.buf);
561 if (op == (int32) '|' && !first)
563 RESIZEBUF(in, 2);
564 sprintf(in->cur, " )");
565 in->cur = strchr(in->cur, '\0');
570 PG_FUNCTION_INFO_V1(ltxtq_out);
571 Datum
572 ltxtq_out(PG_FUNCTION_ARGS)
574 ltxtquery *query = PG_GETARG_LTXTQUERY_P(0);
575 INFIX nrm;
577 if (query->size == 0)
578 ereport(ERROR,
579 (errcode(ERRCODE_SYNTAX_ERROR),
580 errmsg("syntax error"),
581 errdetail("Empty query.")));
583 nrm.curpol = GETQUERY(query);
584 nrm.buflen = 32;
585 nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
586 *(nrm.cur) = '\0';
587 nrm.op = GETOPERAND(query);
588 infix(&nrm, true);
590 PG_RETURN_POINTER(nrm.buf);
594 * ltxtquery type send function
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.
601 PG_FUNCTION_INFO_V1(ltxtq_send);
602 Datum
603 ltxtq_send(PG_FUNCTION_ARGS)
605 ltxtquery *query = PG_GETARG_LTXTQUERY_P(0);
606 StringInfoData buf;
607 int version = 1;
608 INFIX nrm;
610 if (query->size == 0)
611 ereport(ERROR,
612 (errcode(ERRCODE_SYNTAX_ERROR),
613 errmsg("syntax error"),
614 errdetail("Empty query.")));
616 nrm.curpol = GETQUERY(query);
617 nrm.buflen = 32;
618 nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
619 *(nrm.cur) = '\0';
620 nrm.op = GETOPERAND(query);
621 infix(&nrm, true);
623 pq_begintypsend(&buf);
624 pq_sendint8(&buf, version);
625 pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
626 pfree(nrm.buf);
628 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));