1616
1717#include "libpq/pqformat.h"
1818#include "miscadmin.h"
19+ #include "nodes/miscnodes.h"
1920#include "tsearch/ts_locale.h"
2021#include "tsearch/ts_type.h"
2122#include "tsearch/ts_utils.h"
@@ -58,10 +59,16 @@ typedef enum
5859/*
5960 * get token from query string
6061 *
61- * *operator is filled in with OP_* when return values is PT_OPR,
62- * but *weight could contain a distance value in case of phrase operator.
63- * *strval, *lenval and *weight are filled in when return value is PT_VAL
62+ * All arguments except "state" are output arguments.
6463 *
64+ * If return value is PT_OPR, then *operator is filled with an OP_* code
65+ * and *weight will contain a distance value in case of phrase operator.
66+ *
67+ * If return value is PT_VAL, then *lenval, *strval, *weight, and *prefix
68+ * are filled.
69+ *
70+ * If PT_ERR is returned then a soft error has occurred. If state->escontext
71+ * isn't already filled then this should be reported as a generic parse error.
6572 */
6673typedef ts_tokentype (* ts_tokenizer ) (TSQueryParserState state , int8 * operator ,
6774 int * lenval , char * * strval ,
@@ -93,6 +100,9 @@ struct TSQueryParserStateData
93100
94101 /* state for value's parser */
95102 TSVectorParseState valstate ;
103+
104+ /* context object for soft errors - must match valstate's escontext */
105+ Node * escontext ;
96106};
97107
98108/*
@@ -194,7 +204,7 @@ parse_phrase_operator(TSQueryParserState pstate, int16 *distance)
194204 if (ptr == endptr )
195205 return false;
196206 else if (errno == ERANGE || l < 0 || l > MAXENTRYPOS )
197- ereport ( ERROR ,
207+ ereturn ( pstate -> escontext , false ,
198208 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
199209 errmsg ("distance in phrase operator must be an integer value between zero and %d inclusive" ,
200210 MAXENTRYPOS )));
@@ -301,10 +311,8 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
301311 }
302312 else if (t_iseq (state -> buf , ':' ))
303313 {
304- ereport (ERROR ,
305- (errcode (ERRCODE_SYNTAX_ERROR ),
306- errmsg ("syntax error in tsquery: \"%s\"" ,
307- state -> buffer )));
314+ /* generic syntax error message is fine */
315+ return PT_ERR ;
308316 }
309317 else if (!t_isspace (state -> buf ))
310318 {
@@ -320,12 +328,17 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
320328 state -> state = WAITOPERATOR ;
321329 return PT_VAL ;
322330 }
331+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
332+ {
333+ /* gettoken_tsvector reported a soft error */
334+ return PT_ERR ;
335+ }
323336 else if (state -> state == WAITFIRSTOPERAND )
324337 {
325338 return PT_END ;
326339 }
327340 else
328- ereport ( ERROR ,
341+ ereturn ( state -> escontext , PT_ERR ,
329342 (errcode (ERRCODE_SYNTAX_ERROR ),
330343 errmsg ("no operand in tsquery: \"%s\"" ,
331344 state -> buffer )));
@@ -354,6 +367,11 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
354367 * operator = OP_PHRASE ;
355368 return PT_OPR ;
356369 }
370+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
371+ {
372+ /* parse_phrase_operator reported a soft error */
373+ return PT_ERR ;
374+ }
357375 else if (t_iseq (state -> buf , ')' ))
358376 {
359377 state -> buf ++ ;
@@ -438,6 +456,11 @@ gettoken_query_websearch(TSQueryParserState state, int8 *operator,
438456 state -> state = WAITOPERATOR ;
439457 return PT_VAL ;
440458 }
459+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
460+ {
461+ /* gettoken_tsvector reported a soft error */
462+ return PT_ERR ;
463+ }
441464 else if (state -> state == WAITFIRSTOPERAND )
442465 {
443466 return PT_END ;
@@ -529,12 +552,12 @@ pushValue_internal(TSQueryParserState state, pg_crc32 valcrc, int distance, int
529552 QueryOperand * tmp ;
530553
531554 if (distance >= MAXSTRPOS )
532- ereport ( ERROR ,
555+ ereturn ( state -> escontext , ,
533556 (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
534557 errmsg ("value is too big in tsquery: \"%s\"" ,
535558 state -> buffer )));
536559 if (lenval >= MAXSTRLEN )
537- ereport ( ERROR ,
560+ ereturn ( state -> escontext , ,
538561 (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
539562 errmsg ("operand is too long in tsquery: \"%s\"" ,
540563 state -> buffer )));
@@ -562,7 +585,7 @@ pushValue(TSQueryParserState state, char *strval, int lenval, int16 weight, bool
562585 pg_crc32 valcrc ;
563586
564587 if (lenval >= MAXSTRLEN )
565- ereport ( ERROR ,
588+ ereturn ( state -> escontext , ,
566589 (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
567590 errmsg ("word is too long in tsquery: \"%s\"" ,
568591 state -> buffer )));
@@ -686,11 +709,17 @@ makepol(TSQueryParserState state,
686709 return ;
687710 case PT_ERR :
688711 default :
689- ereport (ERROR ,
690- (errcode (ERRCODE_SYNTAX_ERROR ),
691- errmsg ("syntax error in tsquery: \"%s\"" ,
692- state -> buffer )));
712+ /* don't overwrite a soft error saved by gettoken function */
713+ if (!SOFT_ERROR_OCCURRED (state -> escontext ))
714+ errsave (state -> escontext ,
715+ (errcode (ERRCODE_SYNTAX_ERROR ),
716+ errmsg ("syntax error in tsquery: \"%s\"" ,
717+ state -> buffer )));
718+ return ;
693719 }
720+ /* detect soft error in pushval or recursion */
721+ if (SOFT_ERROR_OCCURRED (state -> escontext ))
722+ return ;
694723 }
695724
696725 cleanOpStack (state , opstack , & lenstack , OP_OR /* lowest */ );
@@ -769,6 +798,8 @@ findoprnd(QueryItem *ptr, int size, bool *needcleanup)
769798
770799
771800/*
801+ * Parse the tsquery stored in "buf".
802+ *
772803 * Each value (operand) in the query is passed to pushval. pushval can
773804 * transform the simple value to an arbitrarily complex expression using
774805 * pushValue and pushOperator. It must push a single value with pushValue,
@@ -778,19 +809,27 @@ findoprnd(QueryItem *ptr, int size, bool *needcleanup)
778809 *
779810 * opaque is passed on to pushval as is, pushval can use it to store its
780811 * private state.
812+ *
813+ * The pushval function can record soft errors via escontext.
814+ * Callers must check SOFT_ERROR_OCCURRED to detect that.
815+ *
816+ * A bitmask of flags (see ts_utils.h) and an error context object
817+ * can be provided as well. If a soft error occurs, NULL is returned.
781818 */
782819TSQuery
783820parse_tsquery (char * buf ,
784821 PushFunction pushval ,
785822 Datum opaque ,
786- int flags )
823+ int flags ,
824+ Node * escontext )
787825{
788826 struct TSQueryParserStateData state ;
789827 int i ;
790828 TSQuery query ;
791829 int commonlen ;
792830 QueryItem * ptr ;
793831 ListCell * cell ;
832+ bool noisy ;
794833 bool needcleanup ;
795834 int tsv_flags = P_TSV_OPR_IS_DELIM | P_TSV_IS_TSQUERY ;
796835
@@ -808,15 +847,19 @@ parse_tsquery(char *buf,
808847 else
809848 state .gettoken = gettoken_query_standard ;
810849
850+ /* emit nuisance NOTICEs only if not doing soft errors */
851+ noisy = !(escontext && IsA (escontext , ErrorSaveContext ));
852+
811853 /* init state */
812854 state .buffer = buf ;
813855 state .buf = buf ;
814856 state .count = 0 ;
815857 state .state = WAITFIRSTOPERAND ;
816858 state .polstr = NIL ;
859+ state .escontext = escontext ;
817860
818861 /* init value parser's state */
819- state .valstate = init_tsvector_parser (state .buffer , tsv_flags );
862+ state .valstate = init_tsvector_parser (state .buffer , tsv_flags , escontext );
820863
821864 /* init list of operand */
822865 state .sumlen = 0 ;
@@ -829,19 +872,23 @@ parse_tsquery(char *buf,
829872
830873 close_tsvector_parser (state .valstate );
831874
875+ if (SOFT_ERROR_OCCURRED (escontext ))
876+ return NULL ;
877+
832878 if (state .polstr == NIL )
833879 {
834- ereport (NOTICE ,
835- (errmsg ("text-search query doesn't contain lexemes: \"%s\"" ,
836- state .buffer )));
880+ if (noisy )
881+ ereport (NOTICE ,
882+ (errmsg ("text-search query doesn't contain lexemes: \"%s\"" ,
883+ state .buffer )));
837884 query = (TSQuery ) palloc (HDRSIZETQ );
838885 SET_VARSIZE (query , HDRSIZETQ );
839886 query -> size = 0 ;
840887 return query ;
841888 }
842889
843890 if (TSQUERY_TOO_BIG (list_length (state .polstr ), state .sumlen ))
844- ereport ( ERROR ,
891+ ereturn ( escontext , NULL ,
845892 (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
846893 errmsg ("tsquery is too large" )));
847894 commonlen = COMPUTESIZE (list_length (state .polstr ), state .sumlen );
@@ -889,7 +936,7 @@ parse_tsquery(char *buf,
889936 * If there are QI_VALSTOP nodes, delete them and simplify the tree.
890937 */
891938 if (needcleanup )
892- query = cleanup_tsquery_stopwords (query );
939+ query = cleanup_tsquery_stopwords (query , noisy );
893940
894941 return query ;
895942}
@@ -908,8 +955,13 @@ Datum
908955tsqueryin (PG_FUNCTION_ARGS )
909956{
910957 char * in = PG_GETARG_CSTRING (0 );
958+ Node * escontext = fcinfo -> context ;
911959
912- PG_RETURN_TSQUERY (parse_tsquery (in , pushval_asis , PointerGetDatum (NULL ), 0 ));
960+ PG_RETURN_TSQUERY (parse_tsquery (in ,
961+ pushval_asis ,
962+ PointerGetDatum (NULL ),
963+ 0 ,
964+ escontext ));
913965}
914966
915967/*
0 commit comments