@@ -148,12 +148,17 @@ eval0({'not_in', Expr, ValueList}, Headers) ->
148148
149149eval0 ({'like' , Expr , Pattern , Escape }, Headers ) ->
150150 Value = eval0 (Expr , Headers ),
151- PatternVal = eval0 (Pattern , Headers ),
152- EscapeVal = case Escape of
153- no_escape -> no_escape ;
154- _ -> eval0 (Escape , Headers )
155- end ,
156- is_like (Value , PatternVal , EscapeVal );
151+ case is_binary (Value ) of
152+ true ->
153+ case unicode :characters_to_list (Value ) of
154+ L when is_list (L ) ->
155+ like_match (L , Pattern , Escape );
156+ _ ->
157+ undefined
158+ end ;
159+ false ->
160+ undefined
161+ end ;
157162
158163eval0 ({'not_like' , Expr , Pattern , Escape }, Headers ) ->
159164 case eval0 ({'like' , Expr , Pattern , Escape }, Headers ) of
@@ -180,14 +185,29 @@ eval0(_, _) ->
180185% % Helper functions
181186
182187% % "Comparison or arithmetic with an unknown value always yields an unknown value."
183- compare (_ , undefined , _ ) -> undefined ;
184- compare (_ , _ , undefined ) -> undefined ;
185- compare ('=' , Left , Right ) -> Left == Right ;
186- compare ('<>' , Left , Right ) -> Left /= Right ;
187- compare ('>' , Left , Right ) -> Left > Right ;
188- compare ('<' , Left , Right ) -> Left < Right ;
189- compare ('>=' , Left , Right ) -> Left >= Right ;
190- compare ('<=' , Left , Right ) -> Left =< Right .
188+ compare (_ , undefined , _ ) ->
189+ undefined ;
190+ compare (_ , _ , undefined ) ->
191+ undefined ;
192+ % % "Only like type values can be compared.
193+ % % One exception is that it is valid to compare exact numeric values and approximate numeric values.
194+ % % String and Boolean comparison is restricted to = and <>."
195+ compare ('=' , Left , Right ) ->
196+ Left == Right ;
197+ compare ('<>' , Left , Right ) ->
198+ Left /= Right ;
199+ compare ('>' , Left , Right ) when is_number (Left ) andalso is_number (Right ) ->
200+ Left > Right ;
201+ compare ('<' , Left , Right ) when is_number (Left ) andalso is_number (Right ) ->
202+ Left < Right ;
203+ compare ('>=' , Left , Right ) when is_number (Left ) andalso is_number (Right ) ->
204+ Left >= Right ;
205+ compare ('<=' , Left , Right ) when is_number (Left ) andalso is_number (Right ) ->
206+ Left =< Right ;
207+ compare (_ , _ , _ ) ->
208+ % % "If the comparison of non-like type values is attempted,
209+ % % the value of the operation is false."
210+ false .
191211
192212arithmetic (_ , undefined , _ ) ->
193213 undefined ;
@@ -199,15 +219,7 @@ arithmetic('-', Left, Right) when is_number(Left) andalso is_number(Right) ->
199219 Left - Right ;
200220arithmetic ('*' , Left , Right ) when is_number (Left ) andalso is_number (Right ) ->
201221 Left * Right ;
202- arithmetic ('/' , Left , Right ) when Right =:= 0 andalso Left > 0 ->
203- infinity ;
204- arithmetic ('/' , Left , Right ) when Right =:= 0 andalso Left < 0 ->
205- '-infinity' ;
206- arithmetic ('/' , Left , Right ) when Right =:= 0 andalso Left =:= 0 ->
207- 'NaN' ;
208- arithmetic ('/' , Left , Right ) when is_integer (Left ) andalso is_integer (Right ) ->
209- Left div Right ;
210- arithmetic ('/' , Left , Right ) when is_number (Left ) andalso is_number (Right ) ->
222+ arithmetic ('/' , Left , Right ) when is_number (Left ) andalso is_number (Right ) andalso Right /= 0 ->
211223 Left / Right ;
212224arithmetic (_ , _ , _ ) ->
213225 undefined .
@@ -217,59 +229,53 @@ between(Value, From, To)
217229 From =:= undefined orelse
218230 To =:= undefined ->
219231 undefined ;
220- between (Value , From , To ) ->
221- From =< Value andalso Value =< To .
232+ between (Value , From , To )
233+ when is_number (Value ) andalso
234+ is_number (From ) andalso
235+ is_number (To ) ->
236+ From =< Value andalso Value =< To ;
237+ between (_ , _ , _ ) ->
238+ % % BETWEEN requires arithmetic expressions
239+ % % "a string cannot be used in an arithmetic expression"
240+ false .
222241
223242is_in (undefined , _ ) ->
224243 undefined ;
225244is_in (Value , List ) ->
226245 lists :member (Value , List ).
227246
228- is_like (Value , Pattern , Escape )
229- when is_binary (Value ) andalso
230- is_binary (Pattern ) ->
231- RegexPattern = like_to_regex (Pattern , Escape ),
232- case re :run (Value , RegexPattern , [{capture , none }]) of
233- match -> true ;
234- nomatch -> false
247+ like_match ([], [], _ ) ->
248+ true ;
249+ like_match (_ , [], _ ) ->
250+ false ;
251+ like_match ([], [Char | _ ], _ ) when Char =/= $% ->
252+ false ;
253+ like_match ([], [$% | Rest ], Escape ) ->
254+ % % String is empty, pattern starts with % - % can match empty string, continue with rest
255+ like_match ([], Rest , Escape );
256+ like_match (S , [Escape , Char | PatRest ], Escape ) when Escape =/= no_escape ->
257+ % % Found escape character, match the next character literally
258+ case S of
259+ [Char | StrRest ] ->
260+ like_match (StrRest , PatRest , Escape );
261+ _ ->
262+ false
235263 end ;
236- is_like (_ , _ , _ ) ->
237- undefined .
238-
239- % % Convert LIKE pattern to regex
240- % %
241- % % TODO compilation should happen when the consumer attaches.
242- % % Should this happen within rabbit_jms_selector_parser.yrl?
243- like_to_regex (Pattern , Escape ) ->
244- {ok , Regex } = convert_like_to_regex (binary_to_list (Pattern ), Escape ),
245- {ok , MP } = re :compile (<<" ^" , Regex /binary , " $" >>),
246- MP .
247-
248- convert_like_to_regex (Pattern , Escape ) ->
249- convert_like_to_regex (Pattern , [], Escape ).
250-
251- convert_like_to_regex ([], Acc , _ ) ->
252- {ok , iolist_to_binary (lists :reverse (Acc ))};
253- convert_like_to_regex ([Esc , Char | Rest ], Acc , Esc ) when Esc =/= no_escape ->
254- % Escaped character - take literally
255- convert_like_to_regex (Rest , [escape_regex_char (Char ) | Acc ], Esc );
256- convert_like_to_regex ([$% | Rest ], Acc , Esc ) ->
257- % % means any sequence of characters (including none)
258- convert_like_to_regex (Rest , [" .*" | Acc ], Esc );
259- convert_like_to_regex ([$_ | Rest ], Acc , Esc ) ->
260- % _ means any single character
261- convert_like_to_regex (Rest , [$. | Acc ], Esc );
262- convert_like_to_regex ([Char | Rest ], Acc , Esc ) ->
263- % Regular character - escape for regex
264- convert_like_to_regex (Rest , [escape_regex_char (Char ) | Acc ], Esc ).
265-
266- % % Escape special regex characters
267- escape_regex_char (Char )
268- when Char =:= $. orelse Char =:= $* orelse Char =:= $+ orelse
269- Char =:= $? orelse Char =:= $^ orelse Char =:= $$ orelse
270- Char =:= $[ orelse Char =:= $] orelse Char =:= $( orelse
271- Char =:= $) orelse Char =:= ${ orelse Char =:= $} orelse
272- Char =:= $| orelse Char =:= $\\ ->
273- [$\\ , Char ];
274- escape_regex_char (Char ) ->
275- Char .
264+ like_match ([_ | StrRest ], [$_ | PatRest ], Escape ) ->
265+ % % _ matches exactly 1 character
266+ like_match (StrRest , PatRest , Escape );
267+ like_match (S , [$% | PatRest ] = Pattern , Escape ) ->
268+ % % % matches 0 or more characters - try two paths:
269+ % % 1. Skip % (match 0 characters)
270+ like_match (S , PatRest , Escape ) orelse
271+ case S of
272+ [] ->
273+ false ;
274+ [_ | StrRest ] ->
275+ % % 2. Match current character and keep % in pattern for next iteration
276+ like_match (StrRest , Pattern , Escape )
277+ end ;
278+ like_match ([Char | StrRest ], [Char | PatRest ], Escape ) ->
279+ like_match (StrRest , PatRest , Escape );
280+ like_match (_ , Pattern , _ ) when is_list (Pattern ) ->
281+ false .
0 commit comments