Merge pull request #12381 from emqx/port-sql-funcs-from-emqx4
feat: port emqx/emqx-enterprise#1892, add some SQL functions
This commit is contained in:
commit
4eb0260eaf
|
@ -116,7 +116,9 @@
|
||||||
%% Data Type Validation Funcs
|
%% Data Type Validation Funcs
|
||||||
-export([
|
-export([
|
||||||
is_null/1,
|
is_null/1,
|
||||||
|
is_null_var/1,
|
||||||
is_not_null/1,
|
is_not_null/1,
|
||||||
|
is_not_null_var/1,
|
||||||
is_str/1,
|
is_str/1,
|
||||||
is_bool/1,
|
is_bool/1,
|
||||||
is_int/1,
|
is_int/1,
|
||||||
|
@ -153,6 +155,9 @@
|
||||||
ascii/1,
|
ascii/1,
|
||||||
find/2,
|
find/2,
|
||||||
find/3,
|
find/3,
|
||||||
|
join_to_string/1,
|
||||||
|
join_to_string/2,
|
||||||
|
join_to_sql_values_string/1,
|
||||||
jq/2,
|
jq/2,
|
||||||
jq/3
|
jq/3
|
||||||
]).
|
]).
|
||||||
|
@ -163,7 +168,10 @@
|
||||||
-export([
|
-export([
|
||||||
map_get/2,
|
map_get/2,
|
||||||
map_get/3,
|
map_get/3,
|
||||||
map_put/3
|
map_put/3,
|
||||||
|
map_keys/1,
|
||||||
|
map_values/1,
|
||||||
|
map_to_entries/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% For backward compatibility
|
%% For backward compatibility
|
||||||
|
@ -699,9 +707,16 @@ hexstr2bin(Str) when is_binary(Str) ->
|
||||||
is_null(undefined) -> true;
|
is_null(undefined) -> true;
|
||||||
is_null(_Data) -> false.
|
is_null(_Data) -> false.
|
||||||
|
|
||||||
|
%% Similar to is_null/1, but also works for the JSON value 'null'
|
||||||
|
is_null_var(null) -> true;
|
||||||
|
is_null_var(Data) -> is_null(Data).
|
||||||
|
|
||||||
is_not_null(Data) ->
|
is_not_null(Data) ->
|
||||||
not is_null(Data).
|
not is_null(Data).
|
||||||
|
|
||||||
|
is_not_null_var(Data) ->
|
||||||
|
not is_null_var(Data).
|
||||||
|
|
||||||
is_str(T) when is_binary(T) -> true;
|
is_str(T) when is_binary(T) -> true;
|
||||||
is_str(_) -> false.
|
is_str(_) -> false.
|
||||||
|
|
||||||
|
@ -847,6 +862,23 @@ find_s(S, P, Dir) ->
|
||||||
SubStr -> SubStr
|
SubStr -> SubStr
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
join_to_string(List) when is_list(List) ->
|
||||||
|
join_to_string(<<", ">>, List).
|
||||||
|
join_to_string(Sep, List) when is_list(List), is_binary(Sep) ->
|
||||||
|
iolist_to_binary(lists:join(Sep, [str(Item) || Item <- List])).
|
||||||
|
join_to_sql_values_string(List) ->
|
||||||
|
QuotedList =
|
||||||
|
[
|
||||||
|
case is_list(Item) of
|
||||||
|
true ->
|
||||||
|
emqx_placeholder:quote_sql(emqx_utils_json:encode(Item));
|
||||||
|
false ->
|
||||||
|
emqx_placeholder:quote_sql(Item)
|
||||||
|
end
|
||||||
|
|| Item <- List
|
||||||
|
],
|
||||||
|
iolist_to_binary(lists:join(<<", ">>, QuotedList)).
|
||||||
|
|
||||||
-spec jq(FilterProgram, JSON, TimeoutMS) -> Result when
|
-spec jq(FilterProgram, JSON, TimeoutMS) -> Result when
|
||||||
FilterProgram :: binary(),
|
FilterProgram :: binary(),
|
||||||
JSON :: binary() | term(),
|
JSON :: binary() | term(),
|
||||||
|
@ -920,7 +952,8 @@ map_put(Key, Val, Map) ->
|
||||||
mget(Key, Map) ->
|
mget(Key, Map) ->
|
||||||
mget(Key, Map, undefined).
|
mget(Key, Map, undefined).
|
||||||
|
|
||||||
mget(Key, Map, Default) ->
|
mget(Key, Map0, Default) ->
|
||||||
|
Map = map(Map0),
|
||||||
case maps:find(Key, Map) of
|
case maps:find(Key, Map) of
|
||||||
{ok, Val} ->
|
{ok, Val} ->
|
||||||
Val;
|
Val;
|
||||||
|
@ -947,7 +980,8 @@ mget(Key, Map, Default) ->
|
||||||
Default
|
Default
|
||||||
end.
|
end.
|
||||||
|
|
||||||
mput(Key, Val, Map) ->
|
mput(Key, Val, Map0) ->
|
||||||
|
Map = map(Map0),
|
||||||
case maps:find(Key, Map) of
|
case maps:find(Key, Map) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
maps:put(Key, Val, Map);
|
maps:put(Key, Val, Map);
|
||||||
|
@ -974,6 +1008,13 @@ mput(Key, Val, Map) ->
|
||||||
maps:put(Key, Val, Map)
|
maps:put(Key, Val, Map)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
map_keys(Map) ->
|
||||||
|
maps:keys(map(Map)).
|
||||||
|
map_values(Map) ->
|
||||||
|
maps:values(map(Map)).
|
||||||
|
map_to_entries(Map) ->
|
||||||
|
[#{key => K, value => V} || {K, V} <- maps:to_list(map(Map))].
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Hash Funcs
|
%% Hash Funcs
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -1168,16 +1209,18 @@ map_path(Key) ->
|
||||||
{path, [{key, P} || P <- string:split(Key, ".", all)]}.
|
{path, [{key, P} || P <- string:split(Key, ".", all)]}.
|
||||||
|
|
||||||
function_literal(Fun, []) when is_atom(Fun) ->
|
function_literal(Fun, []) when is_atom(Fun) ->
|
||||||
atom_to_list(Fun) ++ "()";
|
iolist_to_binary(atom_to_list(Fun) ++ "()");
|
||||||
function_literal(Fun, [FArg | Args]) when is_atom(Fun), is_list(Args) ->
|
function_literal(Fun, [FArg | Args]) when is_atom(Fun), is_list(Args) ->
|
||||||
WithFirstArg = io_lib:format("~ts(~0p", [atom_to_list(Fun), FArg]),
|
WithFirstArg = io_lib:format("~ts(~0p", [atom_to_list(Fun), FArg]),
|
||||||
|
FuncLiteral =
|
||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(Arg, Literal) ->
|
fun(Arg, Literal) ->
|
||||||
io_lib:format("~ts, ~0p", [Literal, Arg])
|
io_lib:format("~ts, ~0p", [Literal, Arg])
|
||||||
end,
|
end,
|
||||||
WithFirstArg,
|
WithFirstArg,
|
||||||
Args
|
Args
|
||||||
) ++ ")";
|
) ++ ")",
|
||||||
|
iolist_to_binary(FuncLiteral);
|
||||||
function_literal(Fun, Args) ->
|
function_literal(Fun, Args) ->
|
||||||
{invalid_func, {Fun, Args}}.
|
{invalid_func, {Fun, Args}}.
|
||||||
|
|
||||||
|
|
|
@ -215,15 +215,32 @@ hex_convert() ->
|
||||||
).
|
).
|
||||||
|
|
||||||
t_is_null(_) ->
|
t_is_null(_) ->
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null(null)),
|
||||||
?assertEqual(true, emqx_rule_funcs:is_null(undefined)),
|
?assertEqual(true, emqx_rule_funcs:is_null(undefined)),
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null(<<"undefined">>)),
|
||||||
?assertEqual(false, emqx_rule_funcs:is_null(a)),
|
?assertEqual(false, emqx_rule_funcs:is_null(a)),
|
||||||
?assertEqual(false, emqx_rule_funcs:is_null(<<>>)),
|
?assertEqual(false, emqx_rule_funcs:is_null(<<>>)),
|
||||||
?assertEqual(false, emqx_rule_funcs:is_null(<<"a">>)).
|
?assertEqual(false, emqx_rule_funcs:is_null(<<"a">>)).
|
||||||
|
|
||||||
|
t_is_null_var(_) ->
|
||||||
|
?assertEqual(true, emqx_rule_funcs:is_null_var(null)),
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null_var(<<"null">>)),
|
||||||
|
?assertEqual(true, emqx_rule_funcs:is_null_var(undefined)),
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null_var(<<"undefined">>)),
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null_var(a)),
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null_var(<<>>)),
|
||||||
|
?assertEqual(false, emqx_rule_funcs:is_null_var(<<"a">>)).
|
||||||
|
|
||||||
t_is_not_null(_) ->
|
t_is_not_null(_) ->
|
||||||
[
|
[
|
||||||
?assertEqual(emqx_rule_funcs:is_not_null(T), not emqx_rule_funcs:is_null(T))
|
?assertEqual(emqx_rule_funcs:is_not_null(T), not emqx_rule_funcs:is_null(T))
|
||||||
|| T <- [undefined, a, <<"a">>, <<>>]
|
|| T <- [undefined, <<"undefined">>, null, <<"null">>, a, <<"a">>, <<>>]
|
||||||
|
].
|
||||||
|
|
||||||
|
t_is_not_null_var(_) ->
|
||||||
|
[
|
||||||
|
?assertEqual(emqx_rule_funcs:is_not_null_var(T), not emqx_rule_funcs:is_null_var(T))
|
||||||
|
|| T <- [undefined, <<"undefined">>, null, <<"null">>, a, <<"a">>, <<>>]
|
||||||
].
|
].
|
||||||
|
|
||||||
t_is_str(_) ->
|
t_is_str(_) ->
|
||||||
|
@ -622,6 +639,63 @@ t_ascii(_) ->
|
||||||
?assertEqual(97, apply_func(ascii, [<<"a">>])),
|
?assertEqual(97, apply_func(ascii, [<<"a">>])),
|
||||||
?assertEqual(97, apply_func(ascii, [<<"ab">>])).
|
?assertEqual(97, apply_func(ascii, [<<"ab">>])).
|
||||||
|
|
||||||
|
t_join_to_string(_) ->
|
||||||
|
A = 1,
|
||||||
|
B = a,
|
||||||
|
C = <<"c">>,
|
||||||
|
D = #{a => 1},
|
||||||
|
E = [1, 2, 3],
|
||||||
|
F = [#{<<"key">> => 1, <<"value">> => 2}],
|
||||||
|
M = #{<<"a">> => a, <<"b">> => 1, <<"c">> => <<"c">>},
|
||||||
|
J = <<"{\"a\":\"a\",\"b\":1,\"c\":\"c\"}">>,
|
||||||
|
?assertEqual(<<"a,b,c">>, apply_func(join_to_string, [<<",">>, [<<"a">>, <<"b">>, <<"c">>]])),
|
||||||
|
?assertEqual(<<"a b c">>, apply_func(join_to_string, [<<" ">>, [<<"a">>, <<"b">>, <<"c">>]])),
|
||||||
|
?assertEqual(
|
||||||
|
<<"a, b, c">>, apply_func(join_to_string, [<<", ">>, [<<"a">>, <<"b">>, <<"c">>]])
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
<<"1, a, c, {\"a\":1}, [1,2,3], [{\"value\":2,\"key\":1}]">>,
|
||||||
|
apply_func(join_to_string, [<<", ">>, [A, B, C, D, E, F]])
|
||||||
|
),
|
||||||
|
?assertEqual(<<"a">>, apply_func(join_to_string, [<<",">>, [<<"a">>]])),
|
||||||
|
?assertEqual(<<"">>, apply_func(join_to_string, [<<",">>, []])),
|
||||||
|
?assertEqual(<<"a, b, c">>, apply_func(join_to_string, [emqx_rule_funcs:map_keys(M)])),
|
||||||
|
?assertEqual(<<"a, b, c">>, apply_func(join_to_string, [emqx_rule_funcs:map_keys(J)])),
|
||||||
|
?assertEqual(<<"a, 1, c">>, apply_func(join_to_string, [emqx_rule_funcs:map_values(M)])),
|
||||||
|
?assertEqual(<<"a, 1, c">>, apply_func(join_to_string, [emqx_rule_funcs:map_values(J)])).
|
||||||
|
|
||||||
|
t_join_to_sql_values_string(_) ->
|
||||||
|
A = 1,
|
||||||
|
B = a,
|
||||||
|
C = <<"c">>,
|
||||||
|
D = #{a => 1},
|
||||||
|
E = [1, 2, 3],
|
||||||
|
E1 = [97, 98],
|
||||||
|
F = [#{<<"key">> => 1, <<"value">> => 2}],
|
||||||
|
M = #{<<"a">> => a, <<"b">> => 1, <<"c">> => <<"c">>},
|
||||||
|
J = <<"{\"a\":\"a\",\"b\":1,\"c\":\"c\"}">>,
|
||||||
|
?assertEqual(
|
||||||
|
<<"'a', 'b', 'c'">>, apply_func(join_to_sql_values_string, [[<<"a">>, <<"b">>, <<"c">>]])
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
<<"1, 'a', 'c', '{\"a\":1}', '[1,2,3]', '[97,98]', '[{\"value\":2,\"key\":1}]'">>,
|
||||||
|
apply_func(join_to_sql_values_string, [[A, B, C, D, E, E1, F]])
|
||||||
|
),
|
||||||
|
?assertEqual(<<"'a'">>, apply_func(join_to_sql_values_string, [[<<"a">>]])),
|
||||||
|
?assertEqual(<<"">>, apply_func(join_to_sql_values_string, [[]])),
|
||||||
|
?assertEqual(
|
||||||
|
<<"'a', 'b', 'c'">>, apply_func(join_to_sql_values_string, [emqx_rule_funcs:map_keys(M)])
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
<<"'a', 'b', 'c'">>, apply_func(join_to_sql_values_string, [emqx_rule_funcs:map_keys(J)])
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
<<"'a', 1, 'c'">>, apply_func(join_to_sql_values_string, [emqx_rule_funcs:map_values(M)])
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
<<"'a', 1, 'c'">>, apply_func(join_to_sql_values_string, [emqx_rule_funcs:map_values(J)])
|
||||||
|
).
|
||||||
|
|
||||||
t_find(_) ->
|
t_find(_) ->
|
||||||
?assertEqual(<<"cbcd">>, apply_func(find, [<<"acbcd">>, <<"c">>])),
|
?assertEqual(<<"cbcd">>, apply_func(find, [<<"acbcd">>, <<"c">>])),
|
||||||
?assertEqual(<<"cbcd">>, apply_func(find, [<<"acbcd">>, <<"c">>, <<"leading">>])),
|
?assertEqual(<<"cbcd">>, apply_func(find, [<<"acbcd">>, <<"c">>, <<"leading">>])),
|
||||||
|
@ -746,14 +820,37 @@ t_map_put(_) ->
|
||||||
?assertEqual(#{a => 2}, apply_func(map_put, [<<"a">>, 2, #{a => 1}])).
|
?assertEqual(#{a => 2}, apply_func(map_put, [<<"a">>, 2, #{a => 1}])).
|
||||||
|
|
||||||
t_mget(_) ->
|
t_mget(_) ->
|
||||||
?assertEqual(1, apply_func(map_get, [<<"a">>, #{a => 1}])),
|
?assertEqual(1, apply_func(mget, [<<"a">>, #{a => 1}])),
|
||||||
?assertEqual(1, apply_func(map_get, [<<"a">>, #{<<"a">> => 1}])),
|
?assertEqual(1, apply_func(mget, [<<"a">>, <<"{\"a\" : 1}">>])),
|
||||||
?assertEqual(undefined, apply_func(map_get, [<<"a">>, #{}])).
|
?assertEqual(1, apply_func(mget, [<<"a">>, #{<<"a">> => 1}])),
|
||||||
|
?assertEqual(1, apply_func(mget, [<<"a.b">>, #{<<"a.b">> => 1}])),
|
||||||
|
?assertEqual(undefined, apply_func(mget, [<<"a">>, #{}])).
|
||||||
|
|
||||||
t_mput(_) ->
|
t_mput(_) ->
|
||||||
?assertEqual(#{<<"a">> => 1}, apply_func(map_put, [<<"a">>, 1, #{}])),
|
?assertEqual(#{<<"a">> => 1}, apply_func(mput, [<<"a">>, 1, #{}])),
|
||||||
?assertEqual(#{<<"a">> => 2}, apply_func(map_put, [<<"a">>, 2, #{<<"a">> => 1}])),
|
?assertEqual(#{<<"a">> => 2}, apply_func(mput, [<<"a">>, 2, #{<<"a">> => 1}])),
|
||||||
?assertEqual(#{a => 2}, apply_func(map_put, [<<"a">>, 2, #{a => 1}])).
|
?assertEqual(#{<<"a">> => 2}, apply_func(mput, [<<"a">>, 2, <<"{\"a\" : 1}">>])),
|
||||||
|
?assertEqual(#{<<"a.b">> => 2}, apply_func(mput, [<<"a.b">>, 2, #{<<"a.b">> => 1}])),
|
||||||
|
?assertEqual(#{a => 2}, apply_func(mput, [<<"a">>, 2, #{a => 1}])).
|
||||||
|
|
||||||
|
t_map_to_entries(_) ->
|
||||||
|
?assertEqual([], apply_func(map_to_entries, [#{}])),
|
||||||
|
M = #{a => 1, b => <<"b">>},
|
||||||
|
J = <<"{\"a\":1,\"b\":\"b\"}">>,
|
||||||
|
?assertEqual(
|
||||||
|
[
|
||||||
|
#{key => a, value => 1},
|
||||||
|
#{key => b, value => <<"b">>}
|
||||||
|
],
|
||||||
|
apply_func(map_to_entries, [M])
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
[
|
||||||
|
#{key => <<"a">>, value => 1},
|
||||||
|
#{key => <<"b">>, value => <<"b">>}
|
||||||
|
],
|
||||||
|
apply_func(map_to_entries, [J])
|
||||||
|
).
|
||||||
|
|
||||||
t_bitsize(_) ->
|
t_bitsize(_) ->
|
||||||
?assertEqual(8, apply_func(bitsize, [<<"a">>])),
|
?assertEqual(8, apply_func(bitsize, [<<"a">>])),
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Added new SQL functions: map_keys(), map_values(), map_to_entries(), join_to_string(), join_to_string(), join_to_sql_values_string(), is_null_var(), is_not_null_var().
|
||||||
|
|
||||||
|
For more information on the functions and their usage, refer to the documentation.
|
Loading…
Reference in New Issue