16
16
17
17
#include "libpq/pqformat.h"
18
18
#include "miscadmin.h"
19
+ #include "nodes/miscnodes.h"
19
20
#include "tsearch/ts_locale.h"
20
21
#include "tsearch/ts_type.h"
21
22
#include "tsearch/ts_utils.h"
@@ -58,10 +59,16 @@ typedef enum
58
59
/*
59
60
* get token from query string
60
61
*
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.
64
63
*
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.
65
72
*/
66
73
typedef ts_tokentype (* ts_tokenizer ) (TSQueryParserState state , int8 * operator ,
67
74
int * lenval , char * * strval ,
@@ -93,6 +100,9 @@ struct TSQueryParserStateData
93
100
94
101
/* state for value's parser */
95
102
TSVectorParseState valstate ;
103
+
104
+ /* context object for soft errors - must match valstate's escontext */
105
+ Node * escontext ;
96
106
};
97
107
98
108
/*
@@ -194,7 +204,7 @@ parse_phrase_operator(TSQueryParserState pstate, int16 *distance)
194
204
if (ptr == endptr )
195
205
return false;
196
206
else if (errno == ERANGE || l < 0 || l > MAXENTRYPOS )
197
- ereport ( ERROR ,
207
+ ereturn ( pstate -> escontext , false ,
198
208
(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
199
209
errmsg ("distance in phrase operator must be an integer value between zero and %d inclusive" ,
200
210
MAXENTRYPOS )));
@@ -301,10 +311,8 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
301
311
}
302
312
else if (t_iseq (state -> buf , ':' ))
303
313
{
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 ;
308
316
}
309
317
else if (!t_isspace (state -> buf ))
310
318
{
@@ -320,12 +328,17 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
320
328
state -> state = WAITOPERATOR ;
321
329
return PT_VAL ;
322
330
}
331
+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
332
+ {
333
+ /* gettoken_tsvector reported a soft error */
334
+ return PT_ERR ;
335
+ }
323
336
else if (state -> state == WAITFIRSTOPERAND )
324
337
{
325
338
return PT_END ;
326
339
}
327
340
else
328
- ereport ( ERROR ,
341
+ ereturn ( state -> escontext , PT_ERR ,
329
342
(errcode (ERRCODE_SYNTAX_ERROR ),
330
343
errmsg ("no operand in tsquery: \"%s\"" ,
331
344
state -> buffer )));
@@ -354,6 +367,11 @@ gettoken_query_standard(TSQueryParserState state, int8 *operator,
354
367
* operator = OP_PHRASE ;
355
368
return PT_OPR ;
356
369
}
370
+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
371
+ {
372
+ /* parse_phrase_operator reported a soft error */
373
+ return PT_ERR ;
374
+ }
357
375
else if (t_iseq (state -> buf , ')' ))
358
376
{
359
377
state -> buf ++ ;
@@ -438,6 +456,11 @@ gettoken_query_websearch(TSQueryParserState state, int8 *operator,
438
456
state -> state = WAITOPERATOR ;
439
457
return PT_VAL ;
440
458
}
459
+ else if (SOFT_ERROR_OCCURRED (state -> escontext ))
460
+ {
461
+ /* gettoken_tsvector reported a soft error */
462
+ return PT_ERR ;
463
+ }
441
464
else if (state -> state == WAITFIRSTOPERAND )
442
465
{
443
466
return PT_END ;
@@ -529,12 +552,12 @@ pushValue_internal(TSQueryParserState state, pg_crc32 valcrc, int distance, int
529
552
QueryOperand * tmp ;
530
553
531
554
if (distance >= MAXSTRPOS )
532
- ereport ( ERROR ,
555
+ ereturn ( state -> escontext , ,
533
556
(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
534
557
errmsg ("value is too big in tsquery: \"%s\"" ,
535
558
state -> buffer )));
536
559
if (lenval >= MAXSTRLEN )
537
- ereport ( ERROR ,
560
+ ereturn ( state -> escontext , ,
538
561
(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
539
562
errmsg ("operand is too long in tsquery: \"%s\"" ,
540
563
state -> buffer )));
@@ -562,7 +585,7 @@ pushValue(TSQueryParserState state, char *strval, int lenval, int16 weight, bool
562
585
pg_crc32 valcrc ;
563
586
564
587
if (lenval >= MAXSTRLEN )
565
- ereport ( ERROR ,
588
+ ereturn ( state -> escontext , ,
566
589
(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
567
590
errmsg ("word is too long in tsquery: \"%s\"" ,
568
591
state -> buffer )));
@@ -686,11 +709,17 @@ makepol(TSQueryParserState state,
686
709
return ;
687
710
case PT_ERR :
688
711
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 ;
693
719
}
720
+ /* detect soft error in pushval or recursion */
721
+ if (SOFT_ERROR_OCCURRED (state -> escontext ))
722
+ return ;
694
723
}
695
724
696
725
cleanOpStack (state , opstack , & lenstack , OP_OR /* lowest */ );
@@ -769,6 +798,8 @@ findoprnd(QueryItem *ptr, int size, bool *needcleanup)
769
798
770
799
771
800
/*
801
+ * Parse the tsquery stored in "buf".
802
+ *
772
803
* Each value (operand) in the query is passed to pushval. pushval can
773
804
* transform the simple value to an arbitrarily complex expression using
774
805
* pushValue and pushOperator. It must push a single value with pushValue,
@@ -778,19 +809,27 @@ findoprnd(QueryItem *ptr, int size, bool *needcleanup)
778
809
*
779
810
* opaque is passed on to pushval as is, pushval can use it to store its
780
811
* 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.
781
818
*/
782
819
TSQuery
783
820
parse_tsquery (char * buf ,
784
821
PushFunction pushval ,
785
822
Datum opaque ,
786
- int flags )
823
+ int flags ,
824
+ Node * escontext )
787
825
{
788
826
struct TSQueryParserStateData state ;
789
827
int i ;
790
828
TSQuery query ;
791
829
int commonlen ;
792
830
QueryItem * ptr ;
793
831
ListCell * cell ;
832
+ bool noisy ;
794
833
bool needcleanup ;
795
834
int tsv_flags = P_TSV_OPR_IS_DELIM | P_TSV_IS_TSQUERY ;
796
835
@@ -808,15 +847,19 @@ parse_tsquery(char *buf,
808
847
else
809
848
state .gettoken = gettoken_query_standard ;
810
849
850
+ /* emit nuisance NOTICEs only if not doing soft errors */
851
+ noisy = !(escontext && IsA (escontext , ErrorSaveContext ));
852
+
811
853
/* init state */
812
854
state .buffer = buf ;
813
855
state .buf = buf ;
814
856
state .count = 0 ;
815
857
state .state = WAITFIRSTOPERAND ;
816
858
state .polstr = NIL ;
859
+ state .escontext = escontext ;
817
860
818
861
/* 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 );
820
863
821
864
/* init list of operand */
822
865
state .sumlen = 0 ;
@@ -829,19 +872,23 @@ parse_tsquery(char *buf,
829
872
830
873
close_tsvector_parser (state .valstate );
831
874
875
+ if (SOFT_ERROR_OCCURRED (escontext ))
876
+ return NULL ;
877
+
832
878
if (state .polstr == NIL )
833
879
{
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 )));
837
884
query = (TSQuery ) palloc (HDRSIZETQ );
838
885
SET_VARSIZE (query , HDRSIZETQ );
839
886
query -> size = 0 ;
840
887
return query ;
841
888
}
842
889
843
890
if (TSQUERY_TOO_BIG (list_length (state .polstr ), state .sumlen ))
844
- ereport ( ERROR ,
891
+ ereturn ( escontext , NULL ,
845
892
(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
846
893
errmsg ("tsquery is too large" )));
847
894
commonlen = COMPUTESIZE (list_length (state .polstr ), state .sumlen );
@@ -889,7 +936,7 @@ parse_tsquery(char *buf,
889
936
* If there are QI_VALSTOP nodes, delete them and simplify the tree.
890
937
*/
891
938
if (needcleanup )
892
- query = cleanup_tsquery_stopwords (query );
939
+ query = cleanup_tsquery_stopwords (query , noisy );
893
940
894
941
return query ;
895
942
}
@@ -908,8 +955,13 @@ Datum
908
955
tsqueryin (PG_FUNCTION_ARGS )
909
956
{
910
957
char * in = PG_GETARG_CSTRING (0 );
958
+ Node * escontext = fcinfo -> context ;
911
959
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 ));
913
965
}
914
966
915
967
/*
0 commit comments