3 * Teodor Sigaev <teodor@stack.net>
4 * contrib/ltree/ltxtquery_io.c
11 #include "libpq/pqformat.h"
13 #include "miscadmin.h"
19 #define WAITOPERATOR 3
22 * node of query tree, also used
23 * for storing polish notation in parser
40 /* reverse polish notation in list (for temporary usage) */
45 /* user-friendly operand */
53 * get token from query string
56 gettoken_query(QPRS_STATE
*state
, int32
*val
, int32
*lenval
, char **strval
, uint16
*flag
)
62 charlen
= pg_mblen(state
->buf
);
67 if (charlen
== 1 && t_iseq(state
->buf
, '!'))
73 else if (charlen
== 1 && t_iseq(state
->buf
, '('))
79 else if (ISALNUM(state
->buf
))
81 state
->state
= INOPERAND
;
86 else if (!t_isspace(state
->buf
))
88 (errcode(ERRCODE_SYNTAX_ERROR
),
89 errmsg("operand syntax error")));
92 if (ISALNUM(state
->buf
))
96 (errcode(ERRCODE_SYNTAX_ERROR
),
97 errmsg("modifiers syntax error")));
100 else if (charlen
== 1 && t_iseq(state
->buf
, '%'))
101 *flag
|= LVAR_SUBLEXEME
;
102 else if (charlen
== 1 && t_iseq(state
->buf
, '@'))
103 *flag
|= LVAR_INCASE
;
104 else if (charlen
== 1 && t_iseq(state
->buf
, '*'))
105 *flag
|= LVAR_ANYEND
;
108 state
->state
= WAITOPERATOR
;
113 if (charlen
== 1 && (t_iseq(state
->buf
, '&') || t_iseq(state
->buf
, '|')))
115 state
->state
= WAITOPERAND
;
116 *val
= (int32
) *(state
->buf
);
120 else if (charlen
== 1 && t_iseq(state
->buf
, ')'))
124 return (state
->count
< 0) ? ERR
: CLOSE
;
126 else if (*(state
->buf
) == '\0')
127 return (state
->count
) ? ERR
: END
;
128 else if (charlen
== 1 && !t_iseq(state
->buf
, ' '))
136 state
->buf
+= charlen
;
141 * push new one in polish notation reverse view
144 pushquery(QPRS_STATE
*state
, int32 type
, int32 val
, int32 distance
, int32 lenval
, uint16 flag
)
146 NODE
*tmp
= (NODE
*) palloc(sizeof(NODE
));
151 if (distance
> 0xffff)
153 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
154 errmsg("value is too big")));
157 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
158 errmsg("operand is too long")));
159 tmp
->distance
= distance
;
160 tmp
->length
= lenval
;
161 tmp
->next
= state
->str
;
167 * This function is used for query text parsing
170 pushval_asis(QPRS_STATE
*state
, int type
, char *strval
, int lenval
, uint16 flag
)
174 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
175 errmsg("word is too long")));
177 pushquery(state
, type
, ltree_crc32_sz(strval
, lenval
),
178 state
->curop
- state
->op
, lenval
, flag
);
180 while (state
->curop
- state
->op
+ lenval
+ 1 >= state
->lenop
)
182 int32 tmp
= state
->curop
- state
->op
;
185 state
->op
= (char *) repalloc((void *) state
->op
, state
->lenop
);
186 state
->curop
= state
->op
+ tmp
;
188 memcpy((void *) state
->curop
, (void *) strval
, lenval
);
189 state
->curop
+= lenval
;
190 *(state
->curop
) = '\0';
192 state
->sumlen
+= lenval
+ 1;
195 #define STACKDEPTH 32
197 * make polish notation of query
200 makepol(QPRS_STATE
*state
)
206 int32 stack
[STACKDEPTH
];
210 /* since this function recurses, it could be driven to stack overflow */
213 while ((type
= gettoken_query(state
, &val
, &lenval
, &strval
, &flag
)) != END
)
218 pushval_asis(state
, VAL
, strval
, lenval
, flag
);
219 while (lenstack
&& (stack
[lenstack
- 1] == (int32
) '&' ||
220 stack
[lenstack
- 1] == (int32
) '!'))
223 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
227 if (lenstack
&& val
== (int32
) '|')
228 pushquery(state
, OPR
, val
, 0, 0, 0);
231 if (lenstack
== STACKDEPTH
)
233 elog(ERROR
, "stack too short");
234 stack
[lenstack
] = val
;
239 if (makepol(state
) == ERR
)
241 while (lenstack
&& (stack
[lenstack
- 1] == (int32
) '&' ||
242 stack
[lenstack
- 1] == (int32
) '!'))
245 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
252 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
259 (errcode(ERRCODE_SYNTAX_ERROR
),
260 errmsg("syntax error")));
268 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
274 findoprnd(ITEM
*ptr
, int32
*pos
)
276 /* since this function recurses, it could be driven to stack overflow. */
279 if (ptr
[*pos
].type
== VAL
|| ptr
[*pos
].type
== VALTRUE
)
284 else if (ptr
[*pos
].val
== (int32
) '!')
292 ITEM
*curitem
= &ptr
[*pos
];
297 curitem
->left
= *pos
- tmp
;
324 state
.state
= WAITOPERAND
;
329 /* init list of operand */
332 state
.curop
= state
.op
= (char *) palloc(state
.lenop
);
333 *(state
.curop
) = '\0';
335 /* parse query & make polish notation (postfix, but in reverse order) */
339 (errcode(ERRCODE_SYNTAX_ERROR
),
340 errmsg("syntax error"),
341 errdetail("Empty query.")));
343 if (LTXTQUERY_TOO_BIG(state
.num
, state
.sumlen
))
345 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
346 errmsg("ltxtquery is too large")));
347 commonlen
= COMPUTESIZE(state
.num
, state
.sumlen
);
349 query
= (ltxtquery
*) palloc0(commonlen
);
350 SET_VARSIZE(query
, commonlen
);
351 query
->size
= state
.num
;
352 ptr
= GETQUERY(query
);
354 /* set item in polish notation */
355 for (i
= 0; i
< state
.num
; i
++)
357 ptr
[i
].type
= state
.str
->type
;
358 ptr
[i
].val
= state
.str
->val
;
359 ptr
[i
].distance
= state
.str
->distance
;
360 ptr
[i
].length
= state
.str
->length
;
361 ptr
[i
].flag
= state
.str
->flag
;
362 tmp
= state
.str
->next
;
367 /* set user-friendly operand view */
368 memcpy((void *) GETOPERAND(query
), (void *) state
.op
, state
.sumlen
);
371 /* set left operand's position for every operator */
373 findoprnd(ptr
, &pos
);
379 * in without morphology
381 PG_FUNCTION_INFO_V1(ltxtq_in
);
383 ltxtq_in(PG_FUNCTION_ARGS
)
385 PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
389 * ltxtquery type recv function
391 * The type is sent as text in binary mode, so this is almost the same
392 * as the input function, but it's prefixed with a version number so we
393 * can change the binary format sent in future if necessary. For now,
394 * only version 1 is supported.
396 PG_FUNCTION_INFO_V1(ltxtq_recv
);
398 ltxtq_recv(PG_FUNCTION_ARGS
)
400 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
401 int version
= pq_getmsgint(buf
, 1);
407 elog(ERROR
, "unsupported ltxtquery version number %d", version
);
409 str
= pq_getmsgtext(buf
, buf
->len
- buf
->cursor
, &nbytes
);
413 PG_RETURN_POINTER(res
);
428 #define RESIZEBUF(inf,addsize) \
429 while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
431 int32 len = (inf)->cur - (inf)->buf; \
432 (inf)->buflen *= 2; \
433 (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
434 (inf)->cur = (inf)->buf + len; \
438 * recursive walk on tree and print it in
439 * infix (human-readable) view
442 infix(INFIX
*in
, bool first
)
444 /* since this function recurses, it could be driven to stack overflow. */
447 if (in
->curpol
->type
== VAL
)
449 char *op
= in
->op
+ in
->curpol
->distance
;
451 RESIZEBUF(in
, in
->curpol
->length
* 2 + 5);
458 if (in
->curpol
->flag
& LVAR_SUBLEXEME
)
463 if (in
->curpol
->flag
& LVAR_INCASE
)
468 if (in
->curpol
->flag
& LVAR_ANYEND
)
476 else if (in
->curpol
->val
== (int32
) '!')
485 if (in
->curpol
->type
== OPR
)
489 sprintf(in
->cur
, "( ");
490 in
->cur
= strchr(in
->cur
, '\0');
496 sprintf(in
->cur
, " )");
497 in
->cur
= strchr(in
->cur
, '\0');
502 int32 op
= in
->curpol
->val
;
506 if (op
== (int32
) '|' && !first
)
509 sprintf(in
->cur
, "( ");
510 in
->cur
= strchr(in
->cur
, '\0');
513 nrm
.curpol
= in
->curpol
;
516 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
518 /* get right operand */
521 /* get & print left operand */
522 in
->curpol
= nrm
.curpol
;
525 /* print operator & right operand */
526 RESIZEBUF(in
, 3 + (nrm
.cur
- nrm
.buf
));
527 sprintf(in
->cur
, " %c %s", op
, nrm
.buf
);
528 in
->cur
= strchr(in
->cur
, '\0');
531 if (op
== (int32
) '|' && !first
)
534 sprintf(in
->cur
, " )");
535 in
->cur
= strchr(in
->cur
, '\0');
540 PG_FUNCTION_INFO_V1(ltxtq_out
);
542 ltxtq_out(PG_FUNCTION_ARGS
)
544 ltxtquery
*query
= PG_GETARG_LTXTQUERY_P(0);
547 if (query
->size
== 0)
549 (errcode(ERRCODE_SYNTAX_ERROR
),
550 errmsg("syntax error"),
551 errdetail("Empty query.")));
553 nrm
.curpol
= GETQUERY(query
);
555 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
557 nrm
.op
= GETOPERAND(query
);
560 PG_RETURN_POINTER(nrm
.buf
);
564 * ltxtquery type send function
566 * The type is sent as text in binary mode, so this is almost the same
567 * as the output function, but it's prefixed with a version number so we
568 * can change the binary format sent in future if necessary. For now,
569 * only version 1 is supported.
571 PG_FUNCTION_INFO_V1(ltxtq_send
);
573 ltxtq_send(PG_FUNCTION_ARGS
)
575 ltxtquery
*query
= PG_GETARG_LTXTQUERY_P(0);
580 if (query
->size
== 0)
582 (errcode(ERRCODE_SYNTAX_ERROR
),
583 errmsg("syntax error"),
584 errdetail("Empty query.")));
586 nrm
.curpol
= GETQUERY(query
);
588 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
590 nrm
.op
= GETOPERAND(query
);
593 pq_begintypsend(&buf
);
594 pq_sendint8(&buf
, version
);
595 pq_sendtext(&buf
, nrm
.buf
, strlen(nrm
.buf
));
598 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));