2 * in/out function for ltree and lquery
3 * Teodor Sigaev <teodor@stack.net>
4 * contrib/ltree/ltree_io.c
11 #include "libpq/pqformat.h"
19 int len
; /* length in bytes */
21 int wlen
; /* length in characters */
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
36 parse_ltree(const char *buf
, struct Node
*escontext
)
43 int state
= LTPRS_WAITNAME
;
45 ltree_level
*curlevel
;
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", \
57 charlen
= pg_mblen(ptr
);
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));
72 charlen
= pg_mblen(ptr
);
81 state
= LTPRS_WAITDELIM
;
89 if (!finish_nodeitem(lptr
, ptr
, false, pos
, escontext
))
91 totallen
+= MAXALIGN(lptr
->len
+ LEVEL_HDRSIZE
);
93 state
= LTPRS_WAITNAME
;
95 else if (!ISLABEL(ptr
))
99 elog(ERROR
, "internal error in ltree parser");
107 if (state
== LTPRS_WAITDELIM
)
109 if (!finish_nodeitem(lptr
, ptr
, false, pos
, escontext
))
111 totallen
+= MAXALIGN(lptr
->len
+ LEVEL_HDRSIZE
);
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
);
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
);
141 * returns a null terminated string
144 deparse_ltree(const ltree
*in
)
149 ltree_level
*curlevel
;
151 ptr
= buf
= (char *) palloc(VARSIZE(in
));
152 curlevel
= LTREE_FIRST(in
);
153 for (i
= 0; i
< in
->numlevel
; i
++)
160 memcpy(ptr
, curlevel
->name
, curlevel
->len
);
161 ptr
+= curlevel
->len
;
162 curlevel
= LEVEL_NEXT(curlevel
);
170 * Basic ltree I/O functions
172 PG_FUNCTION_INFO_V1(ltree_in
);
174 ltree_in(PG_FUNCTION_ARGS
)
176 char *buf
= (char *) PG_GETARG_POINTER(0);
179 if ((res
= parse_ltree(buf
, fcinfo
->context
)) == NULL
)
182 PG_RETURN_POINTER(res
);
185 PG_FUNCTION_INFO_V1(ltree_out
);
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
);
204 ltree_send(PG_FUNCTION_ARGS
)
206 ltree
*in
= PG_GETARG_LTREE_P(0);
209 char *res
= deparse_ltree(in
);
211 pq_begintypsend(&buf
);
212 pq_sendint8(&buf
, version
);
213 pq_sendtext(&buf
, res
, strlen(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
);
229 ltree_recv(PG_FUNCTION_ARGS
)
231 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
232 int version
= pq_getmsgint(buf
, 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
);
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
268 parse_lquery(const char *buf
, struct Node
*escontext
)
274 int state
= LQPRS_WAITLEVEL
;
276 nodeitem
*lptr
= NULL
;
280 lquery_variant
*lrptr
= NULL
;
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", \
294 charlen
= pg_mblen(ptr
);
296 if (t_iseq(ptr
, '.'))
298 else if (t_iseq(ptr
, '|'))
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
);
314 charlen
= pg_mblen(ptr
);
318 case LQPRS_WAITLEVEL
:
321 GETVAR(curqlevel
) = lptr
= (nodeitem
*) palloc0(sizeof(nodeitem
) * (numOR
+ 1));
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
;
336 else if (t_iseq(ptr
, '*'))
337 state
= LQPRS_WAITOPEN
;
346 state
= LQPRS_WAITDELIM
;
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
))
372 state
= LQPRS_WAITVAR
;
374 else if (t_iseq(ptr
, '{'))
376 if (!finish_nodeitem(lptr
, ptr
, true, pos
, escontext
))
378 curqlevel
->flag
|= LQL_COUNT
;
379 state
= LQPRS_WAITFNUM
;
381 else if (t_iseq(ptr
, '.'))
383 if (!finish_nodeitem(lptr
, ptr
, true, pos
, escontext
))
385 state
= LQPRS_WAITLEVEL
;
386 curqlevel
= NEXTLEV(curqlevel
);
388 else if (ISLABEL(ptr
))
390 /* disallow more chars after a flag */
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 */
404 curqlevel
->high
= LTREE_MAX_LEVELS
;
405 curqlevel
= NEXTLEV(curqlevel
);
406 state
= LQPRS_WAITLEVEL
;
412 if (t_iseq(ptr
, ','))
413 state
= LQPRS_WAITSNUM
;
414 else if (isdigit((unsigned char) *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
;
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
;
460 case LQPRS_WAITCLOSE
:
461 if (t_iseq(ptr
, '}'))
462 state
= LQPRS_WAITEND
;
463 else if (!isdigit((unsigned char) *ptr
))
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
))
478 if (t_iseq(ptr
, '.'))
480 state
= LQPRS_WAITLEVEL
;
481 curqlevel
= NEXTLEV(curqlevel
);
487 elog(ERROR
, "internal error in lquery parser");
491 if (state
== LQPRS_WAITDELIM
)
496 if (state
== LQPRS_WAITDELIM
)
498 if (!finish_nodeitem(lptr
, ptr
, true, pos
, escontext
))
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.")));
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
);
523 curqlevel
= NEXTLEV(curqlevel
);
526 result
= (lquery
*) palloc0(totallen
);
527 SET_VARSIZE(result
, totallen
);
528 result
->numlevel
= num
;
529 result
->firstgood
= 0;
532 result
->flag
|= LQUERY_HASNOT
;
533 cur
= LQUERY_FIRST(result
);
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
);
551 lrptr
= LVAR_NEXT(lrptr
);
553 pfree(GETVAR(curqlevel
));
554 if (cur
->numvar
> 1 || cur
->flag
!= 0)
556 /* Not a simple match */
559 else if (wasbad
== false)
561 /* count leading simple matches */
562 (result
->firstgood
)++;
567 /* '*', so this isn't a simple match */
570 curqlevel
= NEXTLEV(curqlevel
);
581 * Close out parsing an ltree or lquery nodeitem:
582 * compute the correct length, and complain if it's not OK
585 finish_nodeitem(nodeitem
*lptr
, const char *ptr
, bool is_lquery
, int pos
,
586 struct Node
*escontext
)
591 * Back up over any flag characters, and discount them from length and
594 while (ptr
> lptr
->start
&& strchr("@*%", ptr
[-1]) != NULL
)
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 */
607 ereturn(escontext
, false,
608 (errcode(ERRCODE_SYNTAX_ERROR
),
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
)));
624 * returns a null terminated string
627 deparse_lquery(const lquery
*in
)
634 lquery_level
*curqlevel
;
635 lquery_variant
*curtlevel
;
637 curqlevel
= LQUERY_FIRST(in
);
638 for (i
= 0; i
< in
->numlevel
; i
++)
641 if (curqlevel
->numvar
)
643 totallen
+= 1 + (curqlevel
->numvar
* 4) + curqlevel
->totallen
;
644 if (curqlevel
->flag
& LQL_COUNT
)
645 totallen
+= 2 * 11 + 3;
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
++)
661 if (curqlevel
->numvar
)
663 if (curqlevel
->flag
& LQL_NOT
)
668 curtlevel
= LQL_FIRST(curqlevel
);
669 for (j
= 0; j
< curqlevel
->numvar
; j
++)
676 memcpy(ptr
, curtlevel
->name
, curtlevel
->len
);
677 ptr
+= curtlevel
->len
;
678 if ((curtlevel
->flag
& LVAR_SUBLEXEME
))
683 if ((curtlevel
->flag
& LVAR_INCASE
))
688 if ((curtlevel
->flag
& LVAR_ANYEND
))
693 curtlevel
= LVAR_NEXT(curtlevel
);
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 */
721 sprintf(ptr
, "{,%d}", curqlevel
->high
);
723 else if (curqlevel
->high
== LTREE_MAX_LEVELS
)
725 sprintf(ptr
, "{%d,}", curqlevel
->low
);
728 sprintf(ptr
, "{%d,%d}", curqlevel
->low
, curqlevel
->high
);
729 ptr
= strchr(ptr
, '\0');
732 curqlevel
= LQL_NEXT(curqlevel
);
740 * Basic lquery I/O functions
742 PG_FUNCTION_INFO_V1(lquery_in
);
744 lquery_in(PG_FUNCTION_ARGS
)
746 char *buf
= (char *) PG_GETARG_POINTER(0);
749 if ((res
= parse_lquery(buf
, fcinfo
->context
)) == NULL
)
752 PG_RETURN_POINTER(res
);
755 PG_FUNCTION_INFO_V1(lquery_out
);
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
);
774 lquery_send(PG_FUNCTION_ARGS
)
776 lquery
*in
= PG_GETARG_LQUERY_P(0);
779 char *res
= deparse_lquery(in
);
781 pq_begintypsend(&buf
);
782 pq_sendint8(&buf
, version
);
783 pq_sendtext(&buf
, res
, strlen(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
);
799 lquery_recv(PG_FUNCTION_ARGS
)
801 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
802 int version
= pq_getmsgint(buf
, 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
);
814 PG_RETURN_POINTER(res
);