3 * Teodor Sigaev <teodor@stack.net>
13 PG_FUNCTION_INFO_V1(ltxtq_in
);
14 Datum
ltxtq_in(PG_FUNCTION_ARGS
);
16 PG_FUNCTION_INFO_V1(ltxtq_out
);
17 Datum
ltxtq_out(PG_FUNCTION_ARGS
);
23 #define WAITOPERATOR 3
26 * node of query tree, also used
27 * for storing polish notation in parser
44 /* reverse polish notation in list (for temporary usage) */
49 /* user-friendly operand */
57 * get token from query string
60 gettoken_query(QPRS_STATE
*state
, int4
*val
, int4
*lenval
, char **strval
, uint16
*flag
)
66 charlen
= pg_mblen(state
->buf
);
71 if (charlen
== 1 && t_iseq(state
->buf
, '!'))
77 else if (charlen
== 1 && t_iseq(state
->buf
, '('))
83 else if (ISALNUM(state
->buf
))
85 state
->state
= INOPERAND
;
90 else if (!t_isspace(state
->buf
))
92 (errcode(ERRCODE_SYNTAX_ERROR
),
93 errmsg("operand syntax error")));
96 if (ISALNUM(state
->buf
))
100 (errcode(ERRCODE_SYNTAX_ERROR
),
101 errmsg("modificators syntax error")));
104 else if (charlen
== 1 && t_iseq(state
->buf
, '%'))
105 *flag
|= LVAR_SUBLEXEME
;
106 else if (charlen
== 1 && t_iseq(state
->buf
, '@'))
107 *flag
|= LVAR_INCASE
;
108 else if (charlen
== 1 && t_iseq(state
->buf
, '*'))
109 *flag
|= LVAR_ANYEND
;
112 state
->state
= WAITOPERATOR
;
117 if (charlen
== 1 && (t_iseq(state
->buf
, '&') || t_iseq(state
->buf
, '|')))
119 state
->state
= WAITOPERAND
;
120 *val
= (int4
) *(state
->buf
);
124 else if (charlen
== 1 && t_iseq(state
->buf
, ')'))
128 return (state
->count
< 0) ? ERR
: CLOSE
;
130 else if (*(state
->buf
) == '\0')
131 return (state
->count
) ? ERR
: END
;
132 else if (charlen
== 1 && !t_iseq(state
->buf
, ' '))
140 state
->buf
+= charlen
;
146 * push new one in polish notation reverse view
149 pushquery(QPRS_STATE
*state
, int4 type
, int4 val
, int4 distance
, int4 lenval
, uint16 flag
)
151 NODE
*tmp
= (NODE
*) palloc(sizeof(NODE
));
156 if (distance
> 0xffff)
158 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
159 errmsg("value is too big")));
162 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
163 errmsg("operand is too long")));
164 tmp
->distance
= distance
;
165 tmp
->length
= lenval
;
166 tmp
->next
= state
->str
;
172 * This function is used for query_txt parsing
175 pushval_asis(QPRS_STATE
*state
, int type
, char *strval
, int lenval
, uint16 flag
)
179 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
180 errmsg("word is too long")));
182 pushquery(state
, type
, ltree_crc32_sz(strval
, lenval
),
183 state
->curop
- state
->op
, lenval
, flag
);
185 while (state
->curop
- state
->op
+ lenval
+ 1 >= state
->lenop
)
187 int4 tmp
= state
->curop
- state
->op
;
190 state
->op
= (char *) repalloc((void *) state
->op
, state
->lenop
);
191 state
->curop
= state
->op
+ tmp
;
193 memcpy((void *) state
->curop
, (void *) strval
, lenval
);
194 state
->curop
+= lenval
;
195 *(state
->curop
) = '\0';
197 state
->sumlen
+= lenval
+ 1;
201 #define STACKDEPTH 32
203 * make polish notaion of query
206 makepol(QPRS_STATE
*state
)
212 int4 stack
[STACKDEPTH
];
216 while ((type
= gettoken_query(state
, &val
, &lenval
, &strval
, &flag
)) != END
)
221 pushval_asis(state
, VAL
, strval
, lenval
, flag
);
222 while (lenstack
&& (stack
[lenstack
- 1] == (int4
) '&' ||
223 stack
[lenstack
- 1] == (int4
) '!'))
226 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
230 if (lenstack
&& val
== (int4
) '|')
231 pushquery(state
, OPR
, val
, 0, 0, 0);
234 if (lenstack
== STACKDEPTH
)
236 elog(ERROR
, "stack too short");
237 stack
[lenstack
] = val
;
242 if (makepol(state
) == ERR
)
244 if (lenstack
&& (stack
[lenstack
- 1] == (int4
) '&' ||
245 stack
[lenstack
- 1] == (int4
) '!'))
248 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
255 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
262 (errcode(ERRCODE_SYNTAX_ERROR
),
263 errmsg("syntax error")));
272 pushquery(state
, OPR
, stack
[lenstack
], 0, 0, 0);
278 findoprnd(ITEM
*ptr
, int4
*pos
)
280 if (ptr
[*pos
].type
== VAL
|| ptr
[*pos
].type
== VALTRUE
)
285 else if (ptr
[*pos
].val
== (int4
) '!')
293 ITEM
*curitem
= &ptr
[*pos
];
298 curitem
->left
= *pos
- tmp
;
325 state
.state
= WAITOPERAND
;
330 /* init list of operand */
333 state
.curop
= state
.op
= (char *) palloc(state
.lenop
);
334 *(state
.curop
) = '\0';
336 /* parse query & make polish notation (postfix, but in reverse order) */
340 (errcode(ERRCODE_SYNTAX_ERROR
),
341 errmsg("syntax error"),
342 errdetail("Empty query.")));
344 /* make finish struct */
345 commonlen
= COMPUTESIZE(state
.num
, state
.sumlen
);
346 query
= (ltxtquery
*) palloc(commonlen
);
347 SET_VARSIZE(query
, commonlen
);
348 query
->size
= state
.num
;
349 ptr
= GETQUERY(query
);
351 /* set item in polish notation */
352 for (i
= 0; i
< state
.num
; i
++)
354 ptr
[i
].type
= state
.str
->type
;
355 ptr
[i
].val
= state
.str
->val
;
356 ptr
[i
].distance
= state
.str
->distance
;
357 ptr
[i
].length
= state
.str
->length
;
358 ptr
[i
].flag
= state
.str
->flag
;
359 tmp
= state
.str
->next
;
364 /* set user friendly-operand view */
365 memcpy((void *) GETOPERAND(query
), (void *) state
.op
, state
.sumlen
);
368 /* set left operand's position for every operator */
370 findoprnd(ptr
, &pos
);
376 * in without morphology
379 ltxtq_in(PG_FUNCTION_ARGS
)
381 PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
396 #define RESIZEBUF(inf,addsize) \
397 while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
399 int4 len = (inf)->cur - (inf)->buf; \
400 (inf)->buflen *= 2; \
401 (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
402 (inf)->cur = (inf)->buf + len; \
406 * recursive walk on tree and print it in
407 * infix (human-readable) view
410 infix(INFIX
*in
, bool first
)
412 if (in
->curpol
->type
== VAL
)
414 char *op
= in
->op
+ in
->curpol
->distance
;
416 RESIZEBUF(in
, in
->curpol
->length
* 2 + 5);
423 if (in
->curpol
->flag
& LVAR_SUBLEXEME
)
428 if (in
->curpol
->flag
& LVAR_INCASE
)
433 if (in
->curpol
->flag
& LVAR_ANYEND
)
441 else if (in
->curpol
->val
== (int4
) '!')
450 if (in
->curpol
->type
== OPR
)
454 sprintf(in
->cur
, "( ");
455 in
->cur
= strchr(in
->cur
, '\0');
461 sprintf(in
->cur
, " )");
462 in
->cur
= strchr(in
->cur
, '\0');
467 int4 op
= in
->curpol
->val
;
471 if (op
== (int4
) '|' && !first
)
474 sprintf(in
->cur
, "( ");
475 in
->cur
= strchr(in
->cur
, '\0');
478 nrm
.curpol
= in
->curpol
;
481 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
483 /* get right operand */
486 /* get & print left operand */
487 in
->curpol
= nrm
.curpol
;
490 /* print operator & right operand */
491 RESIZEBUF(in
, 3 + (nrm
.cur
- nrm
.buf
));
492 sprintf(in
->cur
, " %c %s", op
, nrm
.buf
);
493 in
->cur
= strchr(in
->cur
, '\0');
496 if (op
== (int4
) '|' && !first
)
499 sprintf(in
->cur
, " )");
500 in
->cur
= strchr(in
->cur
, '\0');
506 ltxtq_out(PG_FUNCTION_ARGS
)
508 ltxtquery
*query
= PG_GETARG_LTXTQUERY(0);
511 if (query
->size
== 0)
513 (errcode(ERRCODE_SYNTAX_ERROR
),
514 errmsg("syntax error"),
515 errdetail("Empty query.")));
517 nrm
.curpol
= GETQUERY(query
);
519 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
521 nrm
.op
= GETOPERAND(query
);
524 PG_FREE_IF_COPY(query
, 0);
525 PG_RETURN_POINTER(nrm
.buf
);