1 /*-------------------------------------------------------------------------
4 * I/O functions for tsquery
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
10 * src/backend/utils/adt/tsquery.c
12 *-------------------------------------------------------------------------
17 #include "libpq/pqformat.h"
18 #include "miscadmin.h"
19 #include "nodes/miscnodes.h"
20 #include "tsearch/ts_locale.h"
21 #include "tsearch/ts_type.h"
22 #include "tsearch/ts_utils.h"
23 #include "utils/builtins.h"
24 #include "utils/memutils.h"
25 #include "utils/pg_crc.h"
28 /* FTS operator priorities, see ts_type.h */
29 const int tsearch_op_priority
[OP_COUNT
] =
48 * token types for parsing
61 * get token from query string
63 * All arguments except "state" are output arguments.
65 * If return value is PT_OPR, then *operator is filled with an OP_* code
66 * and *weight will contain a distance value in case of phrase operator.
68 * If return value is PT_VAL, then *lenval, *strval, *weight, and *prefix
71 * If PT_ERR is returned then a soft error has occurred. If state->escontext
72 * isn't already filled then this should be reported as a generic parse error.
74 typedef ts_tokentype (*ts_tokenizer
) (TSQueryParserState state
, int8
*operator,
75 int *lenval
, char **strval
,
76 int16
*weight
, bool *prefix
);
78 struct TSQueryParserStateData
80 /* Tokenizer used for parsing tsquery */
81 ts_tokenizer gettoken
;
83 /* State of tokenizer function */
84 char *buffer
; /* entire string we are scanning */
85 char *buf
; /* current scan point */
86 int count
; /* nesting count, incremented by (,
90 /* polish (prefix) notation in list, filled in by push* functions */
94 * Strings from operands are collected in op. curop is a pointer to the
95 * end of used space of op.
99 int lenop
; /* allocated size of op */
100 int sumlen
; /* used size of op */
102 /* state for value's parser */
103 TSVectorParseState valstate
;
105 /* context object for soft errors - must match valstate's escontext */
110 * subroutine to parse the modifiers (weight and prefix flag currently)
111 * part, like ':AB*' of a query.
114 get_modifiers(char *buf
, int16
*weight
, bool *prefix
)
119 if (!t_iseq(buf
, ':'))
123 while (*buf
&& pg_mblen(buf
) == 1)
156 * Parse phrase operator. The operator
157 * may take the following forms:
159 * a <N> b (distance is exactly N lexemes)
160 * a <-> b (default distance = 1)
162 * The buffer should begin with '<' char
165 parse_phrase_operator(TSQueryParserState pstate
, int16
*distance
)
173 } state
= PHRASE_OPEN
;
174 char *ptr
= pstate
->buf
;
176 long l
= 1; /* default distance */
183 if (t_iseq(ptr
, '<'))
193 if (t_iseq(ptr
, '-'))
195 state
= PHRASE_CLOSE
;
200 if (!isdigit((unsigned char) *ptr
))
204 l
= strtol(ptr
, &endptr
, 10);
207 else if (errno
== ERANGE
|| l
< 0 || l
> MAXENTRYPOS
)
208 ereturn(pstate
->escontext
, false,
209 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
210 errmsg("distance in phrase operator must be an integer value between zero and %d inclusive",
214 state
= PHRASE_CLOSE
;
220 if (t_iseq(ptr
, '>'))
222 state
= PHRASE_FINISH
;
230 *distance
= (int16
) l
;
240 * Parse OR operator used in websearch_to_tsquery(), returns true if we
241 * believe that "OR" literal could be an operator OR
244 parse_or_operator(TSQueryParserState pstate
)
246 char *ptr
= pstate
->buf
;
248 /* it should begin with "OR" literal */
249 if (pg_strncasecmp(ptr
, "or", 2) != 0)
255 * it shouldn't be a part of any word but somewhere later it should be
258 if (*ptr
== '\0') /* no operand */
261 /* it shouldn't be a part of any word */
262 if (t_iseq(ptr
, '-') || t_iseq(ptr
, '_') || t_isalnum(ptr
))
267 ptr
+= pg_mblen(ptr
);
269 if (*ptr
== '\0') /* got end of string without operand */
273 * Suppose, we found an operand, but could be a not correct operand.
274 * So we still treat OR literal as operation with possibly incorrect
275 * operand and will not search it as lexeme
277 if (!isspace((unsigned char) *ptr
))
286 gettoken_query_standard(TSQueryParserState state
, int8
*operator,
287 int *lenval
, char **strval
,
288 int16
*weight
, bool *prefix
)
295 switch (state
->state
)
297 case WAITFIRSTOPERAND
:
299 if (t_iseq(state
->buf
, '!'))
302 state
->state
= WAITOPERAND
;
306 else if (t_iseq(state
->buf
, '('))
309 state
->state
= WAITOPERAND
;
313 else if (t_iseq(state
->buf
, ':'))
315 /* generic syntax error message is fine */
318 else if (!isspace((unsigned char) *state
->buf
))
321 * We rely on the tsvector parser to parse the value for
324 reset_tsvector_parser(state
->valstate
, state
->buf
);
325 if (gettoken_tsvector(state
->valstate
, strval
, lenval
,
326 NULL
, NULL
, &state
->buf
))
328 state
->buf
= get_modifiers(state
->buf
, weight
, prefix
);
329 state
->state
= WAITOPERATOR
;
332 else if (SOFT_ERROR_OCCURRED(state
->escontext
))
334 /* gettoken_tsvector reported a soft error */
337 else if (state
->state
== WAITFIRSTOPERAND
)
342 ereturn(state
->escontext
, PT_ERR
,
343 (errcode(ERRCODE_SYNTAX_ERROR
),
344 errmsg("no operand in tsquery: \"%s\"",
350 if (t_iseq(state
->buf
, '&'))
353 state
->state
= WAITOPERAND
;
357 else if (t_iseq(state
->buf
, '|'))
360 state
->state
= WAITOPERAND
;
364 else if (parse_phrase_operator(state
, weight
))
366 /* weight var is used as storage for distance */
367 state
->state
= WAITOPERAND
;
368 *operator = OP_PHRASE
;
371 else if (SOFT_ERROR_OCCURRED(state
->escontext
))
373 /* parse_phrase_operator reported a soft error */
376 else if (t_iseq(state
->buf
, ')'))
380 return (state
->count
< 0) ? PT_ERR
: PT_CLOSE
;
382 else if (*state
->buf
== '\0')
384 return (state
->count
) ? PT_ERR
: PT_END
;
386 else if (!isspace((unsigned char) *state
->buf
))
393 state
->buf
+= pg_mblen(state
->buf
);
398 gettoken_query_websearch(TSQueryParserState state
, int8
*operator,
399 int *lenval
, char **strval
,
400 int16
*weight
, bool *prefix
)
407 switch (state
->state
)
409 case WAITFIRSTOPERAND
:
411 if (t_iseq(state
->buf
, '-'))
414 state
->state
= WAITOPERAND
;
419 else if (t_iseq(state
->buf
, '"'))
421 /* Everything in quotes is processed as a single token */
423 /* skip opening quote */
425 *strval
= state
->buf
;
427 /* iterate to the closing quote or end of the string */
428 while (*state
->buf
!= '\0' && !t_iseq(state
->buf
, '"'))
430 *lenval
= state
->buf
- *strval
;
432 /* skip closing quote if not end of the string */
433 if (*state
->buf
!= '\0')
436 state
->state
= WAITOPERATOR
;
440 else if (ISOPERATOR(state
->buf
))
442 /* ignore, else gettoken_tsvector() will raise an error */
444 state
->state
= WAITOPERAND
;
447 else if (!isspace((unsigned char) *state
->buf
))
450 * We rely on the tsvector parser to parse the value for
453 reset_tsvector_parser(state
->valstate
, state
->buf
);
454 if (gettoken_tsvector(state
->valstate
, strval
, lenval
,
455 NULL
, NULL
, &state
->buf
))
457 state
->state
= WAITOPERATOR
;
460 else if (SOFT_ERROR_OCCURRED(state
->escontext
))
462 /* gettoken_tsvector reported a soft error */
465 else if (state
->state
== WAITFIRSTOPERAND
)
471 /* finally, we have to provide an operand */
479 if (*state
->buf
== '\0')
483 else if (parse_or_operator(state
))
485 state
->state
= WAITOPERAND
;
489 else if (ISOPERATOR(state
->buf
))
491 /* ignore other operators in this state too */
495 else if (!isspace((unsigned char) *state
->buf
))
497 /* insert implicit AND between operands */
498 state
->state
= WAITOPERAND
;
505 state
->buf
+= pg_mblen(state
->buf
);
510 gettoken_query_plain(TSQueryParserState state
, int8
*operator,
511 int *lenval
, char **strval
,
512 int16
*weight
, bool *prefix
)
517 if (*state
->buf
== '\0')
520 *strval
= state
->buf
;
521 *lenval
= strlen(state
->buf
);
522 state
->buf
+= *lenval
;
528 * Push an operator to state->polstr
531 pushOperator(TSQueryParserState state
, int8 oper
, int16 distance
)
535 Assert(oper
== OP_NOT
|| oper
== OP_AND
|| oper
== OP_OR
|| oper
== OP_PHRASE
);
537 tmp
= (QueryOperator
*) palloc0(sizeof(QueryOperator
));
540 tmp
->distance
= (oper
== OP_PHRASE
) ? distance
: 0;
541 /* left is filled in later with findoprnd */
543 state
->polstr
= lcons(tmp
, state
->polstr
);
547 pushValue_internal(TSQueryParserState state
, pg_crc32 valcrc
, int distance
, int lenval
, int weight
, bool prefix
)
551 if (distance
>= MAXSTRPOS
)
552 ereturn(state
->escontext
,,
553 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
554 errmsg("value is too big in tsquery: \"%s\"",
556 if (lenval
>= MAXSTRLEN
)
557 ereturn(state
->escontext
,,
558 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
559 errmsg("operand is too long in tsquery: \"%s\"",
562 tmp
= (QueryOperand
*) palloc0(sizeof(QueryOperand
));
564 tmp
->weight
= weight
;
565 tmp
->prefix
= prefix
;
566 tmp
->valcrc
= (int32
) valcrc
;
567 tmp
->length
= lenval
;
568 tmp
->distance
= distance
;
570 state
->polstr
= lcons(tmp
, state
->polstr
);
574 * Push an operand to state->polstr.
576 * strval must point to a string equal to state->curop. lenval is the length
580 pushValue(TSQueryParserState state
, char *strval
, int lenval
, int16 weight
, bool prefix
)
584 if (lenval
>= MAXSTRLEN
)
585 ereturn(state
->escontext
,,
586 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
587 errmsg("word is too long in tsquery: \"%s\"",
590 INIT_LEGACY_CRC32(valcrc
);
591 COMP_LEGACY_CRC32(valcrc
, strval
, lenval
);
592 FIN_LEGACY_CRC32(valcrc
);
593 pushValue_internal(state
, valcrc
, state
->curop
- state
->op
, lenval
, weight
, prefix
);
595 /* append the value string to state.op, enlarging buffer if needed first */
596 while (state
->curop
- state
->op
+ lenval
+ 1 >= state
->lenop
)
598 int used
= state
->curop
- state
->op
;
601 state
->op
= (char *) repalloc(state
->op
, state
->lenop
);
602 state
->curop
= state
->op
+ used
;
604 memcpy(state
->curop
, strval
, lenval
);
605 state
->curop
+= lenval
;
606 *(state
->curop
) = '\0';
608 state
->sumlen
+= lenval
+ 1 /* \0 */ ;
613 * Push a stopword placeholder to state->polstr
616 pushStop(TSQueryParserState state
)
620 tmp
= (QueryOperand
*) palloc0(sizeof(QueryOperand
));
621 tmp
->type
= QI_VALSTOP
;
623 state
->polstr
= lcons(tmp
, state
->polstr
);
627 #define STACKDEPTH 32
629 typedef struct OperatorElement
636 pushOpStack(OperatorElement
*stack
, int *lenstack
, int8 op
, int16 distance
)
638 if (*lenstack
== STACKDEPTH
) /* internal error */
639 elog(ERROR
, "tsquery stack too small");
641 stack
[*lenstack
].op
= op
;
642 stack
[*lenstack
].distance
= distance
;
648 cleanOpStack(TSQueryParserState state
,
649 OperatorElement
*stack
, int *lenstack
, int8 op
)
651 int opPriority
= OP_PRIORITY(op
);
655 /* NOT is right associative unlike to others */
656 if ((op
!= OP_NOT
&& opPriority
> OP_PRIORITY(stack
[*lenstack
- 1].op
)) ||
657 (op
== OP_NOT
&& opPriority
>= OP_PRIORITY(stack
[*lenstack
- 1].op
)))
661 pushOperator(state
, stack
[*lenstack
].op
,
662 stack
[*lenstack
].distance
);
667 * Make polish (prefix) notation of query.
669 * See parse_tsquery for explanation of pushval.
672 makepol(TSQueryParserState state
,
673 PushFunction pushval
,
680 OperatorElement opstack
[STACKDEPTH
];
685 /* since this function recurses, it could be driven to stack overflow */
688 while ((type
= state
->gettoken(state
, &operator,
690 &weight
, &prefix
)) != PT_END
)
695 pushval(opaque
, state
, strval
, lenval
, weight
, prefix
);
698 cleanOpStack(state
, opstack
, &lenstack
, operator);
699 pushOpStack(opstack
, &lenstack
, operator, weight
);
702 makepol(state
, pushval
, opaque
);
705 cleanOpStack(state
, opstack
, &lenstack
, OP_OR
/* lowest */ );
709 /* don't overwrite a soft error saved by gettoken function */
710 if (!SOFT_ERROR_OCCURRED(state
->escontext
))
711 errsave(state
->escontext
,
712 (errcode(ERRCODE_SYNTAX_ERROR
),
713 errmsg("syntax error in tsquery: \"%s\"",
717 /* detect soft error in pushval or recursion */
718 if (SOFT_ERROR_OCCURRED(state
->escontext
))
722 cleanOpStack(state
, opstack
, &lenstack
, OP_OR
/* lowest */ );
726 findoprnd_recurse(QueryItem
*ptr
, uint32
*pos
, int nnodes
, bool *needcleanup
)
728 /* since this function recurses, it could be driven to stack overflow. */
732 elog(ERROR
, "malformed tsquery: operand not found");
734 if (ptr
[*pos
].type
== QI_VAL
)
738 else if (ptr
[*pos
].type
== QI_VALSTOP
)
740 *needcleanup
= true; /* we'll have to remove stop words */
745 Assert(ptr
[*pos
].type
== QI_OPR
);
747 if (ptr
[*pos
].qoperator
.oper
== OP_NOT
)
749 ptr
[*pos
].qoperator
.left
= 1; /* fixed offset */
752 /* process the only argument */
753 findoprnd_recurse(ptr
, pos
, nnodes
, needcleanup
);
757 QueryOperator
*curitem
= &ptr
[*pos
].qoperator
;
758 int tmp
= *pos
; /* save current position */
760 Assert(curitem
->oper
== OP_AND
||
761 curitem
->oper
== OP_OR
||
762 curitem
->oper
== OP_PHRASE
);
766 /* process RIGHT argument */
767 findoprnd_recurse(ptr
, pos
, nnodes
, needcleanup
);
769 curitem
->left
= *pos
- tmp
; /* set LEFT arg's offset */
771 /* process LEFT argument */
772 findoprnd_recurse(ptr
, pos
, nnodes
, needcleanup
);
779 * Fill in the left-fields previously left unfilled.
780 * The input QueryItems must be in polish (prefix) notation.
781 * Also, set *needcleanup to true if there are any QI_VALSTOP nodes.
784 findoprnd(QueryItem
*ptr
, int size
, bool *needcleanup
)
788 *needcleanup
= false;
790 findoprnd_recurse(ptr
, &pos
, size
, needcleanup
);
793 elog(ERROR
, "malformed tsquery: extra nodes");
798 * Parse the tsquery stored in "buf".
800 * Each value (operand) in the query is passed to pushval. pushval can
801 * transform the simple value to an arbitrarily complex expression using
802 * pushValue and pushOperator. It must push a single value with pushValue,
803 * a complete expression with all operands, or a stopword placeholder
804 * with pushStop, otherwise the prefix notation representation will be broken,
805 * having an operator with no operand.
807 * opaque is passed on to pushval as is, pushval can use it to store its
810 * The pushval function can record soft errors via escontext.
811 * Callers must check SOFT_ERROR_OCCURRED to detect that.
813 * A bitmask of flags (see ts_utils.h) and an error context object
814 * can be provided as well. If a soft error occurs, NULL is returned.
817 parse_tsquery(char *buf
,
818 PushFunction pushval
,
823 struct TSQueryParserStateData state
;
831 int tsv_flags
= P_TSV_OPR_IS_DELIM
| P_TSV_IS_TSQUERY
;
833 /* plain should not be used with web */
834 Assert((flags
& (P_TSQ_PLAIN
| P_TSQ_WEB
)) != (P_TSQ_PLAIN
| P_TSQ_WEB
));
836 /* select suitable tokenizer */
837 if (flags
& P_TSQ_PLAIN
)
838 state
.gettoken
= gettoken_query_plain
;
839 else if (flags
& P_TSQ_WEB
)
841 state
.gettoken
= gettoken_query_websearch
;
842 tsv_flags
|= P_TSV_IS_WEB
;
845 state
.gettoken
= gettoken_query_standard
;
847 /* emit nuisance NOTICEs only if not doing soft errors */
848 noisy
= !(escontext
&& IsA(escontext
, ErrorSaveContext
));
854 state
.state
= WAITFIRSTOPERAND
;
856 state
.escontext
= escontext
;
858 /* init value parser's state */
859 state
.valstate
= init_tsvector_parser(state
.buffer
, tsv_flags
, escontext
);
861 /* init list of operand */
864 state
.curop
= state
.op
= (char *) palloc(state
.lenop
);
865 *(state
.curop
) = '\0';
867 /* parse query & make polish notation (postfix, but in reverse order) */
868 makepol(&state
, pushval
, opaque
);
870 close_tsvector_parser(state
.valstate
);
872 if (SOFT_ERROR_OCCURRED(escontext
))
875 if (state
.polstr
== NIL
)
879 (errmsg("text-search query doesn't contain lexemes: \"%s\"",
881 query
= (TSQuery
) palloc(HDRSIZETQ
);
882 SET_VARSIZE(query
, HDRSIZETQ
);
887 if (TSQUERY_TOO_BIG(list_length(state
.polstr
), state
.sumlen
))
888 ereturn(escontext
, NULL
,
889 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED
),
890 errmsg("tsquery is too large")));
891 commonlen
= COMPUTESIZE(list_length(state
.polstr
), state
.sumlen
);
893 /* Pack the QueryItems in the final TSQuery struct to return to caller */
894 query
= (TSQuery
) palloc0(commonlen
);
895 SET_VARSIZE(query
, commonlen
);
896 query
->size
= list_length(state
.polstr
);
897 ptr
= GETQUERY(query
);
899 /* Copy QueryItems to TSQuery */
901 foreach(cell
, state
.polstr
)
903 QueryItem
*item
= (QueryItem
*) lfirst(cell
);
908 memcpy(&ptr
[i
], item
, sizeof(QueryOperand
));
911 ptr
[i
].type
= QI_VALSTOP
;
914 memcpy(&ptr
[i
], item
, sizeof(QueryOperator
));
917 elog(ERROR
, "unrecognized QueryItem type: %d", item
->type
);
922 /* Copy all the operand strings to TSQuery */
923 memcpy(GETOPERAND(query
), state
.op
, state
.sumlen
);
927 * Set left operand pointers for every operator. While we're at it,
928 * detect whether there are any QI_VALSTOP nodes.
930 findoprnd(ptr
, query
->size
, &needcleanup
);
933 * If there are QI_VALSTOP nodes, delete them and simplify the tree.
936 query
= cleanup_tsquery_stopwords(query
, noisy
);
942 pushval_asis(Datum opaque
, TSQueryParserState state
, char *strval
, int lenval
,
943 int16 weight
, bool prefix
)
945 pushValue(state
, strval
, lenval
, weight
, prefix
);
949 * in without morphology
952 tsqueryin(PG_FUNCTION_ARGS
)
954 char *in
= PG_GETARG_CSTRING(0);
955 Node
*escontext
= fcinfo
->context
;
957 PG_RETURN_TSQUERY(parse_tsquery(in
,
959 PointerGetDatum(NULL
),
976 /* Makes sure inf->buf is large enough for adding 'addsize' bytes */
977 #define RESIZEBUF(inf, addsize) \
978 while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
980 int len = (inf)->cur - (inf)->buf; \
981 (inf)->buflen *= 2; \
982 (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
983 (inf)->cur = (inf)->buf + len; \
987 * recursively traverse the tree and
988 * print it in infix (human-readable) form
991 infix(INFIX
*in
, int parentPriority
, bool rightPhraseOp
)
993 /* since this function recurses, it could be driven to stack overflow. */
996 if (in
->curpol
->type
== QI_VAL
)
998 QueryOperand
*curpol
= &in
->curpol
->qoperand
;
999 char *op
= in
->op
+ curpol
->distance
;
1002 RESIZEBUF(in
, curpol
->length
* (pg_database_encoding_max_length() + 1) + 2 + 6);
1007 if (t_iseq(op
, '\''))
1012 else if (t_iseq(op
, '\\'))
1017 COPYCHAR(in
->cur
, op
);
1019 clen
= pg_mblen(op
);
1025 if (curpol
->weight
|| curpol
->prefix
)
1034 if (curpol
->weight
& (1 << 3))
1039 if (curpol
->weight
& (1 << 2))
1044 if (curpol
->weight
& (1 << 1))
1049 if (curpol
->weight
& 1)
1058 else if (in
->curpol
->qoperator
.oper
== OP_NOT
)
1060 int priority
= QO_PRIORITY(in
->curpol
);
1062 if (priority
< parentPriority
)
1065 sprintf(in
->cur
, "( ");
1066 in
->cur
= strchr(in
->cur
, '\0');
1074 infix(in
, priority
, false);
1075 if (priority
< parentPriority
)
1078 sprintf(in
->cur
, " )");
1079 in
->cur
= strchr(in
->cur
, '\0');
1084 int8 op
= in
->curpol
->qoperator
.oper
;
1085 int priority
= QO_PRIORITY(in
->curpol
);
1086 int16 distance
= in
->curpol
->qoperator
.distance
;
1088 bool needParenthesis
= false;
1091 if (priority
< parentPriority
||
1092 /* phrase operator depends on order */
1093 (op
== OP_PHRASE
&& rightPhraseOp
))
1095 needParenthesis
= true;
1097 sprintf(in
->cur
, "( ");
1098 in
->cur
= strchr(in
->cur
, '\0');
1101 nrm
.curpol
= in
->curpol
;
1104 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
1106 /* get right operand */
1107 infix(&nrm
, priority
, (op
== OP_PHRASE
));
1109 /* get & print left operand */
1110 in
->curpol
= nrm
.curpol
;
1111 infix(in
, priority
, false);
1113 /* print operator & right operand */
1114 RESIZEBUF(in
, 3 + (2 + 10 /* distance */ ) + (nrm
.cur
- nrm
.buf
));
1118 sprintf(in
->cur
, " | %s", nrm
.buf
);
1121 sprintf(in
->cur
, " & %s", nrm
.buf
);
1125 sprintf(in
->cur
, " <%d> %s", distance
, nrm
.buf
);
1127 sprintf(in
->cur
, " <-> %s", nrm
.buf
);
1130 /* OP_NOT is handled in above if-branch */
1131 elog(ERROR
, "unrecognized operator type: %d", op
);
1133 in
->cur
= strchr(in
->cur
, '\0');
1136 if (needParenthesis
)
1139 sprintf(in
->cur
, " )");
1140 in
->cur
= strchr(in
->cur
, '\0');
1146 tsqueryout(PG_FUNCTION_ARGS
)
1148 TSQuery query
= PG_GETARG_TSQUERY(0);
1151 if (query
->size
== 0)
1153 char *b
= palloc(1);
1156 PG_RETURN_POINTER(b
);
1158 nrm
.curpol
= GETQUERY(query
);
1160 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
1162 nrm
.op
= GETOPERAND(query
);
1163 infix(&nrm
, -1 /* lowest priority */ , false);
1165 PG_FREE_IF_COPY(query
, 0);
1166 PG_RETURN_CSTRING(nrm
.buf
);
1170 * Binary Input / Output functions. The binary format is as follows:
1172 * uint32 number of operators/operands in the query
1174 * Followed by the operators and operands, in prefix notation. For each
1177 * uint8 type, QI_VAL
1179 * operand text in client encoding, null-terminated
1182 * For each operator:
1183 * uint8 type, QI_OPR
1184 * uint8 operator, one of OP_AND, OP_PHRASE OP_OR, OP_NOT.
1185 * uint16 distance (only for OP_PHRASE)
1188 tsquerysend(PG_FUNCTION_ARGS
)
1190 TSQuery query
= PG_GETARG_TSQUERY(0);
1193 QueryItem
*item
= GETQUERY(query
);
1195 pq_begintypsend(&buf
);
1197 pq_sendint32(&buf
, query
->size
);
1198 for (i
= 0; i
< query
->size
; i
++)
1200 pq_sendint8(&buf
, item
->type
);
1205 pq_sendint8(&buf
, item
->qoperand
.weight
);
1206 pq_sendint8(&buf
, item
->qoperand
.prefix
);
1207 pq_sendstring(&buf
, GETOPERAND(query
) + item
->qoperand
.distance
);
1210 pq_sendint8(&buf
, item
->qoperator
.oper
);
1211 if (item
->qoperator
.oper
== OP_PHRASE
)
1212 pq_sendint16(&buf
, item
->qoperator
.distance
);
1215 elog(ERROR
, "unrecognized tsquery node type: %d", item
->type
);
1220 PG_FREE_IF_COPY(query
, 0);
1222 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
1226 tsqueryrecv(PG_FUNCTION_ARGS
)
1228 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
1236 const char **operands
;
1239 size
= pq_getmsgint(buf
, sizeof(uint32
));
1240 if (size
> (MaxAllocSize
/ sizeof(QueryItem
)))
1241 elog(ERROR
, "invalid size of tsquery");
1243 /* Allocate space to temporarily hold operand strings */
1244 operands
= palloc(size
* sizeof(char *));
1246 /* Allocate space for all the QueryItems. */
1247 len
= HDRSIZETQ
+ sizeof(QueryItem
) * size
;
1248 query
= (TSQuery
) palloc0(len
);
1250 item
= GETQUERY(query
);
1253 for (i
= 0; i
< size
; i
++)
1255 item
->type
= (int8
) pq_getmsgint(buf
, sizeof(int8
));
1257 if (item
->type
== QI_VAL
)
1259 size_t val_len
; /* length after recoding to server
1266 weight
= (uint8
) pq_getmsgint(buf
, sizeof(uint8
));
1267 prefix
= (uint8
) pq_getmsgint(buf
, sizeof(uint8
));
1268 val
= pq_getmsgstring(buf
);
1269 val_len
= strlen(val
);
1274 elog(ERROR
, "invalid tsquery: invalid weight bitmap");
1276 if (val_len
> MAXSTRLEN
)
1277 elog(ERROR
, "invalid tsquery: operand too long");
1279 if (datalen
> MAXSTRPOS
)
1280 elog(ERROR
, "invalid tsquery: total operand length exceeded");
1284 INIT_LEGACY_CRC32(valcrc
);
1285 COMP_LEGACY_CRC32(valcrc
, val
, val_len
);
1286 FIN_LEGACY_CRC32(valcrc
);
1288 item
->qoperand
.weight
= weight
;
1289 item
->qoperand
.prefix
= (prefix
) ? true : false;
1290 item
->qoperand
.valcrc
= (int32
) valcrc
;
1291 item
->qoperand
.length
= val_len
;
1292 item
->qoperand
.distance
= datalen
;
1295 * Operand strings are copied to the final struct after this loop;
1296 * here we just collect them to an array
1300 datalen
+= val_len
+ 1; /* + 1 for the '\0' terminator */
1302 else if (item
->type
== QI_OPR
)
1306 oper
= (int8
) pq_getmsgint(buf
, sizeof(int8
));
1307 if (oper
!= OP_NOT
&& oper
!= OP_OR
&& oper
!= OP_AND
&& oper
!= OP_PHRASE
)
1308 elog(ERROR
, "invalid tsquery: unrecognized operator type %d",
1311 elog(ERROR
, "invalid pointer to right operand");
1313 item
->qoperator
.oper
= oper
;
1314 if (oper
== OP_PHRASE
)
1315 item
->qoperator
.distance
= (int16
) pq_getmsgint(buf
, sizeof(int16
));
1318 elog(ERROR
, "unrecognized tsquery node type: %d", item
->type
);
1323 /* Enlarge buffer to make room for the operand values. */
1324 query
= (TSQuery
) repalloc(query
, len
+ datalen
);
1325 item
= GETQUERY(query
);
1326 ptr
= GETOPERAND(query
);
1329 * Fill in the left-pointers. Checks that the tree is well-formed as a
1332 findoprnd(item
, size
, &needcleanup
);
1334 /* Can't have found any QI_VALSTOP nodes */
1335 Assert(!needcleanup
);
1337 /* Copy operands to output struct */
1338 for (i
= 0; i
< size
; i
++)
1340 if (item
->type
== QI_VAL
)
1342 memcpy(ptr
, operands
[i
], item
->qoperand
.length
+ 1);
1343 ptr
+= item
->qoperand
.length
+ 1;
1350 Assert(ptr
- GETOPERAND(query
) == datalen
);
1352 SET_VARSIZE(query
, len
+ datalen
);
1354 PG_RETURN_TSQUERY(query
);
1358 * debug function, used only for view query
1359 * which will be executed in non-leaf pages in index
1362 tsquerytree(PG_FUNCTION_ARGS
)
1364 TSQuery query
= PG_GETARG_TSQUERY(0);
1370 if (query
->size
== 0)
1372 res
= (text
*) palloc(VARHDRSZ
);
1373 SET_VARSIZE(res
, VARHDRSZ
);
1374 PG_RETURN_POINTER(res
);
1377 q
= clean_NOT(GETQUERY(query
), &len
);
1381 res
= cstring_to_text("T");
1387 nrm
.cur
= nrm
.buf
= (char *) palloc(sizeof(char) * nrm
.buflen
);
1389 nrm
.op
= GETOPERAND(query
);
1390 infix(&nrm
, -1, false);
1391 res
= cstring_to_text_with_len(nrm
.buf
, nrm
.cur
- nrm
.buf
);
1395 PG_FREE_IF_COPY(query
, 0);
1397 PG_RETURN_TEXT_P(res
);