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 ,
6774int * lenval ,char * * strval ,
@@ -93,6 +100,9 @@ struct TSQueryParserStateData
93100
94101/* state for value's parser */
95102TSVectorParseState 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)
194204if (ptr == endptr )
195205return false;
196206else if (errno == ERANGE || l < 0 || l > MAXENTRYPOS )
197- ereport ( ERROR ,
207+ ereturn ( pstate -> escontext , false ,
198208(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
199209errmsg ("distance in phrase operator must be an integer value between zero and %d inclusive" ,
200210MAXENTRYPOS )));
@@ -301,10 +311,8 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
301311}
302312else 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}
309317else if (!t_isspace (state -> buf ))
310318{
@@ -320,12 +328,17 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
320328state -> state = WAITOPERATOR ;
321329return PT_VAL ;
322330}
331+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
332+ {
333+ /* gettoken_tsvector reported a soft error */
334+ return PT_ERR ;
335+ }
323336else if (state -> state == WAITFIRSTOPERAND )
324337{
325338return PT_END ;
326339}
327340else
328- ereport ( ERROR ,
341+ ereturn ( state -> escontext , PT_ERR ,
329342(errcode (ERRCODE_SYNTAX_ERROR ),
330343errmsg ("no operand in tsquery: \"%s\"" ,
331344state -> buffer )));
@@ -354,6 +367,11 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
354367* operator = OP_PHRASE ;
355368return 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+ }
357375else if (t_iseq (state -> buf ,')' ))
358376{
359377state -> buf ++ ;
@@ -438,6 +456,11 @@ gettoken_query_websearch(TSQueryParserState state, int8 *operator,
438456state -> state = WAITOPERATOR ;
439457return PT_VAL ;
440458}
459+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
460+ {
461+ /* gettoken_tsvector reported a soft error */
462+ return PT_ERR ;
463+ }
441464else if (state -> state == WAITFIRSTOPERAND )
442465{
443466return PT_END ;
@@ -529,12 +552,12 @@ pushValue_internal(TSQueryParserState state, pg_crc32 valcrc, int distance, int
529552QueryOperand * tmp ;
530553
531554if (distance >=MAXSTRPOS )
532- ereport ( ERROR ,
555+ ereturn ( state -> escontext , ,
533556(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
534557errmsg ("value is too big in tsquery: \"%s\"" ,
535558state -> buffer )));
536559if (lenval >=MAXSTRLEN )
537- ereport ( ERROR ,
560+ ereturn ( state -> escontext , ,
538561(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
539562errmsg ("operand is too long in tsquery: \"%s\"" ,
540563state -> buffer )));
@@ -562,7 +585,7 @@ pushValue(TSQueryParserState state, char *strval, int lenval, int16 weight, bool
562585pg_crc32 valcrc ;
563586
564587if (lenval >=MAXSTRLEN )
565- ereport ( ERROR ,
588+ ereturn ( state -> escontext , ,
566589(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
567590errmsg ("word is too long in tsquery: \"%s\"" ,
568591state -> buffer )));
@@ -686,11 +709,17 @@ makepol(TSQueryParserState state,
686709return ;
687710case PT_ERR :
688711default :
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
696725cleanOpStack (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 ,
784821PushFunction pushval ,
785822Datum opaque ,
786- int flags )
823+ int flags ,
824+ Node * escontext )
787825{
788826struct TSQueryParserStateData state ;
789827int i ;
790828TSQuery query ;
791829int commonlen ;
792830QueryItem * ptr ;
793831ListCell * cell ;
832+ bool noisy ;
794833bool needcleanup ;
795834int tsv_flags = P_TSV_OPR_IS_DELIM |P_TSV_IS_TSQUERY ;
796835
@@ -808,15 +847,19 @@ parse_tsquery(char *buf,
808847else
809848state .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 */
812854state .buffer = buf ;
813855state .buf = buf ;
814856state .count = 0 ;
815857state .state = WAITFIRSTOPERAND ;
816858state .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 */
822865state .sumlen = 0 ;
@@ -829,19 +872,23 @@ parse_tsquery(char *buf,
829872
830873close_tsvector_parser (state .valstate );
831874
875+ if (SOFT_ERROR_OCCURRED (escontext ))
876+ return NULL ;
877+
832878if (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 )));
837884query = (TSQuery )palloc (HDRSIZETQ );
838885SET_VARSIZE (query ,HDRSIZETQ );
839886query -> size = 0 ;
840887return query ;
841888}
842889
843890if (TSQUERY_TOO_BIG (list_length (state .polstr ),state .sumlen ))
844- ereport ( ERROR ,
891+ ereturn ( escontext , NULL ,
845892(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
846893errmsg ("tsquery is too large" )));
847894commonlen = 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 */
891938if (needcleanup )
892- query = cleanup_tsquery_stopwords (query );
939+ query = cleanup_tsquery_stopwords (query , noisy );
893940
894941return query ;
895942}
@@ -908,8 +955,13 @@ Datum
908955tsqueryin (PG_FUNCTION_ARGS )
909956{
910957char * 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/*