summaryrefslogtreecommitdiff
path: root/deps/rabbitmq_jms_topic_exchange/src/sjx_evaluator.erl
diff options
context:
space:
mode:
Diffstat (limited to 'deps/rabbitmq_jms_topic_exchange/src/sjx_evaluator.erl')
-rw-r--r--deps/rabbitmq_jms_topic_exchange/src/sjx_evaluator.erl169
1 files changed, 169 insertions, 0 deletions
diff --git a/deps/rabbitmq_jms_topic_exchange/src/sjx_evaluator.erl b/deps/rabbitmq_jms_topic_exchange/src/sjx_evaluator.erl
new file mode 100644
index 0000000000..ec5f574291
--- /dev/null
+++ b/deps/rabbitmq_jms_topic_exchange/src/sjx_evaluator.erl
@@ -0,0 +1,169 @@
+%% This Source Code Form is subject to the terms of the Mozilla Public
+%% License, v. 2.0. If a copy of the MPL was not distributed with this
+%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+%%
+%% Copyright (c) 2012-2020 VMware, Inc. or its affiliates. All rights reserved.
+%% -----------------------------------------------------------------------------
+%% Derived from works which were:
+%% Copyright (c) 2002, 2012 Tim Watson (watson.timothy@gmail.com)
+%% Copyright (c) 2012, 2013 Steve Powell (Zteve.Powell@gmail.com)
+%% -----------------------------------------------------------------------------
+
+%% Evaluate an SQL expression for filtering purposes
+
+%% -----------------------------------------------------------------------------
+
+-module(sjx_evaluator).
+
+-export([evaluate/2]).
+%% Evaluation function
+%%
+%% Given Headers (a list of keyed typed values), and a
+%% parsed SQL string, evaluate the truth or falsity of the expression.
+%%
+%% If an identifier is absent from Headers, or the types do not match the comparisons, the
+%% expression will evaluate to false.
+
+-type itemname() :: binary().
+-type itemtype() ::
+ 'longstr' | 'signedint' | 'byte' | 'double' | 'float' | 'long' | 'short' | 'bool'.
+-type itemvalue() :: any().
+
+-type tableitem() :: { itemname(), itemtype(), itemvalue() }.
+-type table() :: list(tableitem()).
+
+-type expression() :: any().
+
+-spec evaluate(expression(), table()) -> true | false | error.
+
+
+evaluate( true, _Headers ) -> true;
+evaluate( false, _Headers ) -> false;
+
+evaluate( {'not', Exp }, Headers ) -> not3(evaluate(Exp, Headers));
+evaluate( {'ident', Ident }, Headers ) -> lookup_value(Headers, Ident);
+evaluate( {'is_null', Exp }, Headers ) -> val_of(Exp, Headers) =:= undefined;
+evaluate( {'not_null', Exp }, Headers ) -> val_of(Exp, Headers) =/= undefined;
+evaluate( { Op, Exp }, Headers ) -> do_una_op(Op, evaluate(Exp, Headers));
+
+evaluate( {'and', Exp1, Exp2 }, Headers ) -> and3(evaluate(Exp1, Headers), evaluate(Exp2, Headers));
+evaluate( {'or', Exp1, Exp2 }, Headers ) -> or3(evaluate(Exp1, Headers), evaluate(Exp2, Headers));
+evaluate( {'like', LHS, Patt, Esc }, Headers ) -> isLike(val_of(LHS, Headers), {Patt, Esc});
+evaluate( {'not_like', LHS, Patt, Esc }, Headers ) -> not3(isLike(val_of(LHS, Headers), {Patt, Esc}));
+evaluate( { Op, Exp, {range, From, To} }, Headers ) -> evaluate({ Op, Exp, From, To }, Headers);
+evaluate( {'between', Exp, From, To}, Hs ) -> between(evaluate(Exp, Hs), evaluate(From, Hs), evaluate(To, Hs));
+evaluate( {'not_between', Exp, From, To}, Hs ) -> not3(between(evaluate(Exp, Hs), evaluate(From, Hs), evaluate(To, Hs)));
+evaluate( { Op, LHS, RHS }, Headers ) -> do_bin_op(Op, evaluate(LHS, Headers), evaluate(RHS, Headers));
+
+evaluate( Value, _Headers ) -> Value.
+
+not3(true ) -> false;
+not3(false) -> true;
+not3(_ ) -> undefined.
+
+and3(true, true ) -> true;
+and3(false, _ ) -> false;
+and3(_, false) -> false;
+and3(_, _ ) -> undefined.
+
+or3(false, false) -> false;
+or3(true, _ ) -> true;
+or3(_, true ) -> true;
+or3(_, _ ) -> undefined.
+
+do_una_op(_, undefined) -> undefined;
+do_una_op('-', E) -> -E;
+do_una_op('+', E) -> +E;
+do_una_op(_, _) -> error.
+
+do_bin_op(_, undefined, _) -> undefined;
+do_bin_op(_, _, undefined ) -> undefined;
+do_bin_op('=' , L, R) -> L == R;
+do_bin_op('<>', L, R) -> L /= R;
+do_bin_op('>' , L, R) -> L > R;
+do_bin_op('<' , L, R) -> L < R;
+do_bin_op('>=', L, R) -> L >= R;
+do_bin_op('<=', L, R) -> L =< R;
+do_bin_op('in', L, R) -> isIn(L, R);
+do_bin_op('not_in', L, R) -> not isIn(L, R);
+do_bin_op('+' , L, R) -> L + R;
+do_bin_op('-' , L, R) -> L - R;
+do_bin_op('*' , L, R) -> L * R;
+do_bin_op('/' , L, R) when R /= 0 -> L / R;
+do_bin_op('/' , L, R) when L > 0 andalso R == 0 -> plus_infinity;
+do_bin_op('/' , L, R) when L < 0 andalso R == 0 -> minus_infinity;
+do_bin_op('/' , L, R) when L == 0 andalso R == 0 -> nan;
+do_bin_op(_,_,_) -> error.
+
+isLike(undefined, _Patt) -> undefined;
+isLike(L, {regex, MP}) -> patt_match(L, MP);
+isLike(L, {Patt, Esc}) -> patt_match(L, pattern_of(Patt, Esc)).
+
+patt_match(L, MP) ->
+ BS = byte_size(L),
+ case re:run(L, MP, [{capture, first}]) of
+ {match, [{0, BS}]} -> true;
+ _ -> false
+ end.
+
+isIn(_L, [] ) -> false;
+isIn( L, [L|_]) -> true;
+isIn( L, [_|R]) -> isIn(L,R).
+
+val_of({'ident', Ident}, Hs) -> lookup_value(Hs, Ident);
+val_of(Value, _Hs) -> Value.
+
+between(E, F, T) when E =:= undefined orelse F =:= undefined orelse T =:= undefined -> undefined;
+between(Value, Lo, Hi) -> Lo =< Value andalso Value =< Hi.
+
+lookup_value(Table, Key) ->
+ case lists:keyfind(Key, 1, Table) of
+ {_, longstr, Value} -> Value;
+ {_, signedint, Value} -> Value;
+ {_, float, Value} -> Value;
+ {_, double, Value} -> Value;
+ {_, byte, Value} -> Value;
+ {_, short, Value} -> Value;
+ {_, long, Value} -> Value;
+ {_, bool, Value} -> Value;
+ false -> undefined
+ end.
+
+pattern_of(S, Esc) -> compile_re(gen_re(binary_to_list(S), Esc)).
+
+gen_re(S, <<Ch>> ) -> convert(S, [], Ch );
+gen_re(S, no_escape) -> convert(S, [], no_escape);
+gen_re(_,_) -> error.
+
+convert([], Acc, _Esc) -> lists:reverse(Acc);
+convert([Esc, Ch | Rest], Acc, Esc) -> convert(Rest, [escape(Ch) | Acc], Esc);
+convert([$_ | Rest], Acc, Esc) -> convert(Rest, [$. | Acc], Esc);
+convert([$% | Rest], Acc, Esc) -> convert(Rest, [".*" | Acc], Esc);
+convert([Ch | Rest], Acc, Esc) -> convert(Rest, [escape(Ch) | Acc], Esc).
+
+escape($.) -> "\\.";
+escape($*) -> "\\*";
+escape($+) -> "\\+";
+escape($?) -> "\\?";
+escape($^) -> "\\^";
+escape($=) -> "\\=";
+escape($!) -> "\\!";
+escape($:) -> "\\:";
+escape($$) -> "\\$";
+escape(${) -> "\\{";
+escape($}) -> "\\}";
+escape($() -> "\\(";
+escape($)) -> "\\)";
+escape($|) -> "\\|";
+escape($[) -> "\\[";
+escape($]) -> "\\]";
+escape($/) -> "\\/";
+escape($\\) -> "\\\\";
+escape(Ch) -> Ch.
+
+compile_re(error) -> error;
+compile_re(MatchMany) ->
+ case re:compile(MatchMany)
+ of {ok, Rx} -> Rx;
+ _ -> error
+ end.