Add data for WAL in pg_stat_io and backend statistics
[pgsql.git] / contrib / ltree / ltree_io.c
blobb54a15d6c685e6aacd11fe6670d60e8e8b1f17d3
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"
8 #include <ctype.h>
10 #include "crc32.h"
11 #include "libpq/pqformat.h"
12 #include "ltree.h"
13 #include "varatt.h"
16 typedef struct
18 const char *start;
19 int len; /* length in bytes */
20 int flag;
21 int wlen; /* length in characters */
22 } nodeitem;
24 #define LTPRS_WAITNAME 0
25 #define LTPRS_WAITDELIM 1
27 static bool finish_nodeitem(nodeitem *lptr, const char *ptr,
28 bool is_lquery, int pos, struct Node *escontext);
32 * expects a null terminated string
33 * returns an ltree
35 static ltree *
36 parse_ltree(const char *buf, struct Node *escontext)
38 const char *ptr;
39 nodeitem *list,
40 *lptr;
41 int num = 0,
42 totallen = 0;
43 int state = LTPRS_WAITNAME;
44 ltree *result;
45 ltree_level *curlevel;
46 int charlen;
47 int pos = 1; /* character position for error messages */
49 #define UNCHAR ereturn(escontext, NULL,\
50 errcode(ERRCODE_SYNTAX_ERROR), \
51 errmsg("ltree syntax error at character %d", \
52 pos))
54 ptr = buf;
55 while (*ptr)
57 charlen = pg_mblen(ptr);
58 if (t_iseq(ptr, '.'))
59 num++;
60 ptr += charlen;
63 if (num + 1 > LTREE_MAX_LEVELS)
64 ereturn(escontext, NULL,
65 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
66 errmsg("number of ltree labels (%d) exceeds the maximum allowed (%d)",
67 num + 1, LTREE_MAX_LEVELS)));
68 list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
69 ptr = buf;
70 while (*ptr)
72 charlen = pg_mblen(ptr);
74 switch (state)
76 case LTPRS_WAITNAME:
77 if (ISLABEL(ptr))
79 lptr->start = ptr;
80 lptr->wlen = 0;
81 state = LTPRS_WAITDELIM;
83 else
84 UNCHAR;
85 break;
86 case LTPRS_WAITDELIM:
87 if (t_iseq(ptr, '.'))
89 if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
90 return NULL;
91 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
92 lptr++;
93 state = LTPRS_WAITNAME;
95 else if (!ISLABEL(ptr))
96 UNCHAR;
97 break;
98 default:
99 elog(ERROR, "internal error in ltree parser");
102 ptr += charlen;
103 lptr->wlen++;
104 pos++;
107 if (state == LTPRS_WAITDELIM)
109 if (!finish_nodeitem(lptr, ptr, false, pos, escontext))
110 return NULL;
111 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
112 lptr++;
114 else if (!(state == LTPRS_WAITNAME && lptr == list))
115 ereturn(escontext, NULL,
116 (errcode(ERRCODE_SYNTAX_ERROR),
117 errmsg("ltree syntax error"),
118 errdetail("Unexpected end of input.")));
120 result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
121 SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
122 result->numlevel = lptr - list;
123 curlevel = LTREE_FIRST(result);
124 lptr = list;
125 while (lptr - list < result->numlevel)
127 curlevel->len = (uint16) lptr->len;
128 memcpy(curlevel->name, lptr->start, lptr->len);
129 curlevel = LEVEL_NEXT(curlevel);
130 lptr++;
133 pfree(list);
134 return result;
136 #undef UNCHAR
140 * expects an ltree
141 * returns a null terminated string
143 static char *
144 deparse_ltree(const ltree *in)
146 char *buf,
147 *ptr;
148 int i;
149 ltree_level *curlevel;
151 ptr = buf = (char *) palloc(VARSIZE(in));
152 curlevel = LTREE_FIRST(in);
153 for (i = 0; i < in->numlevel; i++)
155 if (i != 0)
157 *ptr = '.';
158 ptr++;
160 memcpy(ptr, curlevel->name, curlevel->len);
161 ptr += curlevel->len;
162 curlevel = LEVEL_NEXT(curlevel);
165 *ptr = '\0';
166 return buf;
170 * Basic ltree I/O functions
172 PG_FUNCTION_INFO_V1(ltree_in);
173 Datum
174 ltree_in(PG_FUNCTION_ARGS)
176 char *buf = (char *) PG_GETARG_POINTER(0);
177 ltree *res;
179 if ((res = parse_ltree(buf, fcinfo->context)) == NULL)
180 PG_RETURN_NULL();
182 PG_RETURN_POINTER(res);
185 PG_FUNCTION_INFO_V1(ltree_out);
186 Datum
187 ltree_out(PG_FUNCTION_ARGS)
189 ltree *in = PG_GETARG_LTREE_P(0);
191 PG_RETURN_POINTER(deparse_ltree(in));
195 * ltree type send function
197 * The type is sent as text in binary mode, so this is almost the same
198 * as the output function, but it's prefixed with a version number so we
199 * can change the binary format sent in future if necessary. For now,
200 * only version 1 is supported.
202 PG_FUNCTION_INFO_V1(ltree_send);
203 Datum
204 ltree_send(PG_FUNCTION_ARGS)
206 ltree *in = PG_GETARG_LTREE_P(0);
207 StringInfoData buf;
208 int version = 1;
209 char *res = deparse_ltree(in);
211 pq_begintypsend(&buf);
212 pq_sendint8(&buf, version);
213 pq_sendtext(&buf, res, strlen(res));
214 pfree(res);
216 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
220 * ltree type recv function
222 * The type is sent as text in binary mode, so this is almost the same
223 * as the input function, but it's prefixed with a version number so we
224 * can change the binary format sent in future if necessary. For now,
225 * only version 1 is supported.
227 PG_FUNCTION_INFO_V1(ltree_recv);
228 Datum
229 ltree_recv(PG_FUNCTION_ARGS)
231 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
232 int version = pq_getmsgint(buf, 1);
233 char *str;
234 int nbytes;
235 ltree *res;
237 if (version != 1)
238 elog(ERROR, "unsupported ltree version number %d", version);
240 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
241 res = parse_ltree(str, NULL);
242 pfree(str);
244 PG_RETURN_POINTER(res);
248 #define LQPRS_WAITLEVEL 0
249 #define LQPRS_WAITDELIM 1
250 #define LQPRS_WAITOPEN 2
251 #define LQPRS_WAITFNUM 3
252 #define LQPRS_WAITSNUM 4
253 #define LQPRS_WAITND 5
254 #define LQPRS_WAITCLOSE 6
255 #define LQPRS_WAITEND 7
256 #define LQPRS_WAITVAR 8
259 #define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
260 #define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
261 #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
264 * expects a null terminated string
265 * returns an lquery
267 static lquery *
268 parse_lquery(const char *buf, struct Node *escontext)
270 const char *ptr;
271 int num = 0,
272 totallen = 0,
273 numOR = 0;
274 int state = LQPRS_WAITLEVEL;
275 lquery *result;
276 nodeitem *lptr = NULL;
277 lquery_level *cur,
278 *curqlevel,
279 *tmpql;
280 lquery_variant *lrptr = NULL;
281 bool hasnot = false;
282 bool wasbad = false;
283 int charlen;
284 int pos = 1; /* character position for error messages */
286 #define UNCHAR ereturn(escontext, NULL,\
287 errcode(ERRCODE_SYNTAX_ERROR), \
288 errmsg("lquery syntax error at character %d", \
289 pos))
291 ptr = buf;
292 while (*ptr)
294 charlen = pg_mblen(ptr);
296 if (t_iseq(ptr, '.'))
297 num++;
298 else if (t_iseq(ptr, '|'))
299 numOR++;
301 ptr += charlen;
304 num++;
305 if (num > LQUERY_MAX_LEVELS)
306 ereturn(escontext, NULL,
307 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
308 errmsg("number of lquery items (%d) exceeds the maximum allowed (%d)",
309 num, LQUERY_MAX_LEVELS)));
310 curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
311 ptr = buf;
312 while (*ptr)
314 charlen = pg_mblen(ptr);
316 switch (state)
318 case LQPRS_WAITLEVEL:
319 if (ISLABEL(ptr))
321 GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
322 lptr->start = ptr;
323 state = LQPRS_WAITDELIM;
324 curqlevel->numvar = 1;
326 else if (t_iseq(ptr, '!'))
328 GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
329 lptr->start = ptr + 1;
330 lptr->wlen = -1; /* compensate for counting ! below */
331 state = LQPRS_WAITDELIM;
332 curqlevel->numvar = 1;
333 curqlevel->flag |= LQL_NOT;
334 hasnot = true;
336 else if (t_iseq(ptr, '*'))
337 state = LQPRS_WAITOPEN;
338 else
339 UNCHAR;
340 break;
341 case LQPRS_WAITVAR:
342 if (ISLABEL(ptr))
344 lptr++;
345 lptr->start = ptr;
346 state = LQPRS_WAITDELIM;
347 curqlevel->numvar++;
349 else
350 UNCHAR;
351 break;
352 case LQPRS_WAITDELIM:
353 if (t_iseq(ptr, '@'))
355 lptr->flag |= LVAR_INCASE;
356 curqlevel->flag |= LVAR_INCASE;
358 else if (t_iseq(ptr, '*'))
360 lptr->flag |= LVAR_ANYEND;
361 curqlevel->flag |= LVAR_ANYEND;
363 else if (t_iseq(ptr, '%'))
365 lptr->flag |= LVAR_SUBLEXEME;
366 curqlevel->flag |= LVAR_SUBLEXEME;
368 else if (t_iseq(ptr, '|'))
370 if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
371 return NULL;
372 state = LQPRS_WAITVAR;
374 else if (t_iseq(ptr, '{'))
376 if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
377 return NULL;
378 curqlevel->flag |= LQL_COUNT;
379 state = LQPRS_WAITFNUM;
381 else if (t_iseq(ptr, '.'))
383 if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
384 return NULL;
385 state = LQPRS_WAITLEVEL;
386 curqlevel = NEXTLEV(curqlevel);
388 else if (ISLABEL(ptr))
390 /* disallow more chars after a flag */
391 if (lptr->flag)
392 UNCHAR;
394 else
395 UNCHAR;
396 break;
397 case LQPRS_WAITOPEN:
398 if (t_iseq(ptr, '{'))
399 state = LQPRS_WAITFNUM;
400 else if (t_iseq(ptr, '.'))
402 /* We only get here for '*', so these are correct defaults */
403 curqlevel->low = 0;
404 curqlevel->high = LTREE_MAX_LEVELS;
405 curqlevel = NEXTLEV(curqlevel);
406 state = LQPRS_WAITLEVEL;
408 else
409 UNCHAR;
410 break;
411 case LQPRS_WAITFNUM:
412 if (t_iseq(ptr, ','))
413 state = LQPRS_WAITSNUM;
414 else if (isdigit((unsigned char) *ptr))
416 int low = atoi(ptr);
418 if (low < 0 || low > LTREE_MAX_LEVELS)
419 ereturn(escontext, NULL,
420 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
421 errmsg("lquery syntax error"),
422 errdetail("Low limit (%d) exceeds the maximum allowed (%d), at character %d.",
423 low, LTREE_MAX_LEVELS, pos)));
425 curqlevel->low = (uint16) low;
426 state = LQPRS_WAITND;
428 else
429 UNCHAR;
430 break;
431 case LQPRS_WAITSNUM:
432 if (isdigit((unsigned char) *ptr))
434 int high = atoi(ptr);
436 if (high < 0 || high > LTREE_MAX_LEVELS)
437 ereturn(escontext, NULL,
438 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
439 errmsg("lquery syntax error"),
440 errdetail("High limit (%d) exceeds the maximum allowed (%d), at character %d.",
441 high, LTREE_MAX_LEVELS, pos)));
442 else if (curqlevel->low > high)
443 ereturn(escontext, NULL,
444 (errcode(ERRCODE_SYNTAX_ERROR),
445 errmsg("lquery syntax error"),
446 errdetail("Low limit (%d) is greater than high limit (%d), at character %d.",
447 curqlevel->low, high, pos)));
449 curqlevel->high = (uint16) high;
450 state = LQPRS_WAITCLOSE;
452 else if (t_iseq(ptr, '}'))
454 curqlevel->high = LTREE_MAX_LEVELS;
455 state = LQPRS_WAITEND;
457 else
458 UNCHAR;
459 break;
460 case LQPRS_WAITCLOSE:
461 if (t_iseq(ptr, '}'))
462 state = LQPRS_WAITEND;
463 else if (!isdigit((unsigned char) *ptr))
464 UNCHAR;
465 break;
466 case LQPRS_WAITND:
467 if (t_iseq(ptr, '}'))
469 curqlevel->high = curqlevel->low;
470 state = LQPRS_WAITEND;
472 else if (t_iseq(ptr, ','))
473 state = LQPRS_WAITSNUM;
474 else if (!isdigit((unsigned char) *ptr))
475 UNCHAR;
476 break;
477 case LQPRS_WAITEND:
478 if (t_iseq(ptr, '.'))
480 state = LQPRS_WAITLEVEL;
481 curqlevel = NEXTLEV(curqlevel);
483 else
484 UNCHAR;
485 break;
486 default:
487 elog(ERROR, "internal error in lquery parser");
490 ptr += charlen;
491 if (state == LQPRS_WAITDELIM)
492 lptr->wlen++;
493 pos++;
496 if (state == LQPRS_WAITDELIM)
498 if (!finish_nodeitem(lptr, ptr, true, pos, escontext))
499 return NULL;
501 else if (state == LQPRS_WAITOPEN)
502 curqlevel->high = LTREE_MAX_LEVELS;
503 else if (state != LQPRS_WAITEND)
504 ereturn(escontext, NULL,
505 (errcode(ERRCODE_SYNTAX_ERROR),
506 errmsg("lquery syntax error"),
507 errdetail("Unexpected end of input.")));
509 curqlevel = tmpql;
510 totallen = LQUERY_HDRSIZE;
511 while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
513 totallen += LQL_HDRSIZE;
514 if (curqlevel->numvar)
516 lptr = GETVAR(curqlevel);
517 while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
519 totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
520 lptr++;
523 curqlevel = NEXTLEV(curqlevel);
526 result = (lquery *) palloc0(totallen);
527 SET_VARSIZE(result, totallen);
528 result->numlevel = num;
529 result->firstgood = 0;
530 result->flag = 0;
531 if (hasnot)
532 result->flag |= LQUERY_HASNOT;
533 cur = LQUERY_FIRST(result);
534 curqlevel = tmpql;
535 while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
537 memcpy(cur, curqlevel, LQL_HDRSIZE);
538 cur->totallen = LQL_HDRSIZE;
539 if (curqlevel->numvar)
541 lrptr = LQL_FIRST(cur);
542 lptr = GETVAR(curqlevel);
543 while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
545 cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
546 lrptr->len = lptr->len;
547 lrptr->flag = lptr->flag;
548 lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
549 memcpy(lrptr->name, lptr->start, lptr->len);
550 lptr++;
551 lrptr = LVAR_NEXT(lrptr);
553 pfree(GETVAR(curqlevel));
554 if (cur->numvar > 1 || cur->flag != 0)
556 /* Not a simple match */
557 wasbad = true;
559 else if (wasbad == false)
561 /* count leading simple matches */
562 (result->firstgood)++;
565 else
567 /* '*', so this isn't a simple match */
568 wasbad = true;
570 curqlevel = NEXTLEV(curqlevel);
571 cur = LQL_NEXT(cur);
574 pfree(tmpql);
575 return result;
577 #undef UNCHAR
581 * Close out parsing an ltree or lquery nodeitem:
582 * compute the correct length, and complain if it's not OK
584 static bool
585 finish_nodeitem(nodeitem *lptr, const char *ptr, bool is_lquery, int pos,
586 struct Node *escontext)
588 if (is_lquery)
591 * Back up over any flag characters, and discount them from length and
592 * position.
594 while (ptr > lptr->start && strchr("@*%", ptr[-1]) != NULL)
596 ptr--;
597 lptr->wlen--;
598 pos--;
602 /* Now compute the byte length, which we weren't tracking before. */
603 lptr->len = ptr - lptr->start;
605 /* Complain if it's empty or too long */
606 if (lptr->len == 0)
607 ereturn(escontext, false,
608 (errcode(ERRCODE_SYNTAX_ERROR),
609 is_lquery ?
610 errmsg("lquery syntax error at character %d", pos) :
611 errmsg("ltree syntax error at character %d", pos),
612 errdetail("Empty labels are not allowed.")));
613 if (lptr->wlen > LTREE_LABEL_MAX_CHARS)
614 ereturn(escontext, false,
615 (errcode(ERRCODE_NAME_TOO_LONG),
616 errmsg("label string is too long"),
617 errdetail("Label length is %d, must be at most %d, at character %d.",
618 lptr->wlen, LTREE_LABEL_MAX_CHARS, pos)));
619 return true;
623 * expects an lquery
624 * returns a null terminated string
626 static char *
627 deparse_lquery(const lquery *in)
629 char *buf,
630 *ptr;
631 int i,
633 totallen = 1;
634 lquery_level *curqlevel;
635 lquery_variant *curtlevel;
637 curqlevel = LQUERY_FIRST(in);
638 for (i = 0; i < in->numlevel; i++)
640 totallen++;
641 if (curqlevel->numvar)
643 totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
644 if (curqlevel->flag & LQL_COUNT)
645 totallen += 2 * 11 + 3;
647 else
648 totallen += 2 * 11 + 4;
649 curqlevel = LQL_NEXT(curqlevel);
652 ptr = buf = (char *) palloc(totallen);
653 curqlevel = LQUERY_FIRST(in);
654 for (i = 0; i < in->numlevel; i++)
656 if (i != 0)
658 *ptr = '.';
659 ptr++;
661 if (curqlevel->numvar)
663 if (curqlevel->flag & LQL_NOT)
665 *ptr = '!';
666 ptr++;
668 curtlevel = LQL_FIRST(curqlevel);
669 for (j = 0; j < curqlevel->numvar; j++)
671 if (j != 0)
673 *ptr = '|';
674 ptr++;
676 memcpy(ptr, curtlevel->name, curtlevel->len);
677 ptr += curtlevel->len;
678 if ((curtlevel->flag & LVAR_SUBLEXEME))
680 *ptr = '%';
681 ptr++;
683 if ((curtlevel->flag & LVAR_INCASE))
685 *ptr = '@';
686 ptr++;
688 if ((curtlevel->flag & LVAR_ANYEND))
690 *ptr = '*';
691 ptr++;
693 curtlevel = LVAR_NEXT(curtlevel);
696 else
698 *ptr = '*';
699 ptr++;
702 if ((curqlevel->flag & LQL_COUNT) || curqlevel->numvar == 0)
704 if (curqlevel->low == curqlevel->high)
706 sprintf(ptr, "{%d}", curqlevel->low);
708 else if (curqlevel->low == 0)
710 if (curqlevel->high == LTREE_MAX_LEVELS)
712 if (curqlevel->numvar == 0)
714 /* This is default for '*', so print nothing */
715 *ptr = '\0';
717 else
718 sprintf(ptr, "{,}");
720 else
721 sprintf(ptr, "{,%d}", curqlevel->high);
723 else if (curqlevel->high == LTREE_MAX_LEVELS)
725 sprintf(ptr, "{%d,}", curqlevel->low);
727 else
728 sprintf(ptr, "{%d,%d}", curqlevel->low, curqlevel->high);
729 ptr = strchr(ptr, '\0');
732 curqlevel = LQL_NEXT(curqlevel);
735 *ptr = '\0';
736 return buf;
740 * Basic lquery I/O functions
742 PG_FUNCTION_INFO_V1(lquery_in);
743 Datum
744 lquery_in(PG_FUNCTION_ARGS)
746 char *buf = (char *) PG_GETARG_POINTER(0);
747 lquery *res;
749 if ((res = parse_lquery(buf, fcinfo->context)) == NULL)
750 PG_RETURN_NULL();
752 PG_RETURN_POINTER(res);
755 PG_FUNCTION_INFO_V1(lquery_out);
756 Datum
757 lquery_out(PG_FUNCTION_ARGS)
759 lquery *in = PG_GETARG_LQUERY_P(0);
761 PG_RETURN_POINTER(deparse_lquery(in));
765 * lquery type send function
767 * The type is sent as text in binary mode, so this is almost the same
768 * as the output function, but it's prefixed with a version number so we
769 * can change the binary format sent in future if necessary. For now,
770 * only version 1 is supported.
772 PG_FUNCTION_INFO_V1(lquery_send);
773 Datum
774 lquery_send(PG_FUNCTION_ARGS)
776 lquery *in = PG_GETARG_LQUERY_P(0);
777 StringInfoData buf;
778 int version = 1;
779 char *res = deparse_lquery(in);
781 pq_begintypsend(&buf);
782 pq_sendint8(&buf, version);
783 pq_sendtext(&buf, res, strlen(res));
784 pfree(res);
786 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
790 * lquery type recv function
792 * The type is sent as text in binary mode, so this is almost the same
793 * as the input function, but it's prefixed with a version number so we
794 * can change the binary format sent in future if necessary. For now,
795 * only version 1 is supported.
797 PG_FUNCTION_INFO_V1(lquery_recv);
798 Datum
799 lquery_recv(PG_FUNCTION_ARGS)
801 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
802 int version = pq_getmsgint(buf, 1);
803 char *str;
804 int nbytes;
805 lquery *res;
807 if (version != 1)
808 elog(ERROR, "unsupported lquery version number %d", version);
810 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
811 res = parse_lquery(str, NULL);
812 pfree(str);
814 PG_RETURN_POINTER(res);