3 * Teodor Sigaev <teodor@stack.net>
4 * contrib/ltree/ltxtquery_io.c
11 #include "libpq/pqformat.h"
13 #include "miscadmin.h"
14 #include "nodes/miscnodes.h"
21 #define WAITOPERATOR 3
24 * node of query tree, also used
25 * for storing polish notation in parser
42 struct Node
*escontext
;
43 /* reverse polish notation in list (for temporary usage) */
48 /* user-friendly operand */
56 * get token from query string
58 * caller needs to check if a soft-error was set if the result is ERR.
61 gettoken_query(QPRS_STATE
*state
, int32
*val
, int32
*lenval
, char **strval
, uint16
*flag
)
67 charlen
= pg_mblen(state
->buf
);
72 if (t_iseq(state
->buf
, '!'))
78 else if (t_iseq(state
->buf
, '('))
84 else if (ISLABEL(state
->buf
))
86 state
->state
= INOPERAND
;
91 else if (!isspace((unsigned char) *state
->buf
))
92 ereturn(state
->escontext
, ERR
,
93 (errcode(ERRCODE_SYNTAX_ERROR
),
94 errmsg("operand syntax error")));
97 if (ISLABEL(state
->buf
))
100 ereturn(state
->escontext
, ERR
,
101 (errcode(ERRCODE_SYNTAX_ERROR
),
102 errmsg("modifiers syntax error")));
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
;
113 state
->state
= WAITOPERATOR
;
118 if (t_iseq(state
->buf
, '&') || t_iseq(state
->buf
, '|'))
120 state
->state
= WAITOPERAND
;
121 *val
= (int32
) *(state
->buf
);
125 else if (t_iseq(state
->buf
, ')'))
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
, ' '))
145 state
->buf
+= charlen
;
148 /* should not get here */
152 * push new one in polish notation reverse view
155 pushquery(QPRS_STATE
*state
, int32 type
, int32 val
, int32 distance
, int32 lenval
, uint16 flag
)
157 NODE
*tmp
= (NODE
*) palloc(sizeof(NODE
));
162 if (distance
> 0xffff)
163 ereturn(state
->escontext
, false,
164 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
165 errmsg("value is too big")));
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
;
179 * This function is used for query text parsing
182 pushval_asis(QPRS_STATE
*state
, int type
, char *strval
, int lenval
, uint16 flag
)
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
))
193 while (state
->curop
- state
->op
+ lenval
+ 1 >= state
->lenop
)
195 int32 tmp
= state
->curop
- state
->op
;
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';
205 state
->sumlen
+= lenval
+ 1;
209 #define STACKDEPTH 32
211 * make polish notation of query
214 makepol(QPRS_STATE
*state
)
220 int32 stack
[STACKDEPTH
];
224 /* since this function recurses, it could be driven to stack overflow */
227 while ((type
= gettoken_query(state
, &val
, &lenval
, &strval
, &flag
)) != END
)
232 if (!pushval_asis(state
, VAL
, strval
, lenval
, flag
))
234 while (lenstack
&& (stack
[lenstack
- 1] == (int32
) '&' ||
235 stack
[lenstack
- 1] == (int32
) '!'))
238 if (!pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0))
243 if (lenstack
&& val
== (int32
) '|')
245 if (!pushquery(state
, OPR
, val
, 0, 0, 0))
250 if (lenstack
== STACKDEPTH
)
252 elog(ERROR
, "stack too short");
253 stack
[lenstack
] = val
;
258 if (makepol(state
) == ERR
)
260 while (lenstack
&& (stack
[lenstack
- 1] == (int32
) '&' ||
261 stack
[lenstack
- 1] == (int32
) '!'))
264 if (!pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0))
272 if (!pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0))
278 if (SOFT_ERROR_OCCURRED(state
->escontext
))
282 ereturn(state
->escontext
, ERR
,
283 (errcode(ERRCODE_SYNTAX_ERROR
),
284 errmsg("syntax error")));
291 if (!pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0))
298 findoprnd(ITEM
*ptr
, int32
*pos
)
300 /* since this function recurses, it could be driven to stack overflow. */
303 if (ptr
[*pos
].type
== VAL
|| ptr
[*pos
].type
== VALTRUE
)
308 else if (ptr
[*pos
].val
== (int32
) '!')
316 ITEM
*curitem
= &ptr
[*pos
];
321 curitem
->left
= *pos
- tmp
;
331 queryin(char *buf
, struct Node
*escontext
)
348 state
.state
= WAITOPERAND
;
352 state
.escontext
= escontext
;
354 /* init list of operand */
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
)
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
;
393 /* set user-friendly operand view */
394 memcpy(GETOPERAND(query
), state
.op
, state
.sumlen
);
397 /* set left operand's position for every operator */
399 findoprnd(ptr
, &pos
);
405 * in without morphology
407 PG_FUNCTION_INFO_V1(ltxtq_in
);
409 ltxtq_in(PG_FUNCTION_ARGS
)
413 if ((res
= queryin((char *) PG_GETARG_POINTER(0), fcinfo
->context
)) == 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
);
428 ltxtq_recv(PG_FUNCTION_ARGS
)
430 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
431 int version
= pq_getmsgint(buf
, 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
);
443 PG_RETURN_POINTER(res
);
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
472 infix(INFIX
*in
, bool first
)
474 /* since this function recurses, it could be driven to stack overflow. */
477 if (in
->curpol
->type
== VAL
)
479 char *op
= in
->op
+ in
->curpol
->distance
;
481 RESIZEBUF(in
, in
->curpol
->length
* 2 + 5);
488 if (in
->curpol
->flag
& LVAR_SUBLEXEME
)
493 if (in
->curpol
->flag
& LVAR_INCASE
)
498 if (in
->curpol
->flag
& LVAR_ANYEND
)
506 else if (in
->curpol
->val
== (int32
) '!')
515 if (in
->curpol
->type
== OPR
)
519 sprintf(in
->cur
, "( ");
520 in
->cur
= strchr(in
->cur
, '\0');
526 sprintf(in
->cur
, " )");
527 in
->cur
= strchr(in
->cur
, '\0');
532 int32 op
= in
->curpol
->val
;
536 if (op
== (int32
) '|' && !first
)
539 sprintf(in
->cur
, "( ");
540 in
->cur
= strchr(in
->cur
, '\0');
543 nrm
.curpol
= in
->curpol
;
546 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
548 /* get right operand */
551 /* get & print left operand */
552 in
->curpol
= nrm
.curpol
;
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');
561 if (op
== (int32
) '|' && !first
)
564 sprintf(in
->cur
, " )");
565 in
->cur
= strchr(in
->cur
, '\0');
570 PG_FUNCTION_INFO_V1(ltxtq_out
);
572 ltxtq_out(PG_FUNCTION_ARGS
)
574 ltxtquery
*query
= PG_GETARG_LTXTQUERY_P(0);
577 if (query
->size
== 0)
579 (errcode(ERRCODE_SYNTAX_ERROR
),
580 errmsg("syntax error"),
581 errdetail("Empty query.")));
583 nrm
.curpol
= GETQUERY(query
);
585 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
587 nrm
.op
= GETOPERAND(query
);
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
);
603 ltxtq_send(PG_FUNCTION_ARGS
)
605 ltxtquery
*query
= PG_GETARG_LTXTQUERY_P(0);
610 if (query
->size
== 0)
612 (errcode(ERRCODE_SYNTAX_ERROR
),
613 errmsg("syntax error"),
614 errdetail("Empty query.")));
616 nrm
.curpol
= GETQUERY(query
);
618 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
620 nrm
.op
= GETOPERAND(query
);
623 pq_begintypsend(&buf
);
624 pq_sendint8(&buf
, version
);
625 pq_sendtext(&buf
, nrm
.buf
, strlen(nrm
.buf
));
628 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));