Просмотр исходного кода

Merge pull request #13640 from zmstone/0817-add-coalesce-function-to-rule-sql

0817 add coalesce function to rule sql
zmstone 1 год назад
Родитель
Сommit
ef26610ffd

+ 1 - 1
apps/emqx/src/emqx_shared_sub.erl

@@ -392,7 +392,7 @@ subscribers(Group, Topic, FailedSubs) ->
 
 %% Select ETS table to get all subscriber pids.
 subscribers(Group, Topic) ->
-    ets:select(?SHARED_SUBSCRIPTION, [{{emqx_shared_subscription, Group, Topic, '$1'}, [], ['$1']}]).
+    ets:select(?SHARED_SUBSCRIPTION, [{{?SHARED_SUBSCRIPTION, Group, Topic, '$1'}, [], ['$1']}]).
 
 %%--------------------------------------------------------------------
 %% gen_server callbacks

+ 26 - 1
apps/emqx_rule_engine/src/emqx_rule_funcs.erl

@@ -39,7 +39,11 @@
     contains_topic/3,
     contains_topic_match/2,
     contains_topic_match/3,
-    null/0
+    null/0,
+    coalesce/1,
+    coalesce/2,
+    coalesce_ne/1,
+    coalesce_ne/2
 ]).
 
 %% Arithmetic Funcs
@@ -431,6 +435,27 @@ null() ->
 bytesize(IoList) ->
     erlang:iolist_size(IoList).
 
+%% @doc coalesce returns the first non-null value
+coalesce([]) -> null();
+coalesce([undefined | T]) -> coalesce(T);
+coalesce([H | _T]) -> H.
+
+%% @doc This is a short-cut of SQL `CASE WHEN is_null(A) THEN A ELSE B END'
+coalesce(A, B) ->
+    coalesce([A, B]).
+
+%% @doc coalesce_ne returns the first non-empty value.
+%% `undefined', `""', and `<<>>' are considered 'empty'.
+coalesce_ne([]) -> null();
+coalesce_ne([undefined | T]) -> coalesce_ne(T);
+coalesce_ne(["" | T]) -> coalesce_ne(T);
+coalesce_ne([<<>> | T]) -> coalesce_ne(T);
+coalesce_ne([H | _T]) -> H.
+
+%% @doc Same as coalesce/2, but considers a value null when it's empty string.
+coalesce_ne(A, B) ->
+    coalesce_ne([A, B]).
+
 %%------------------------------------------------------------------------------
 %% Arithmetic Funcs
 %%------------------------------------------------------------------------------

+ 20 - 0
apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl

@@ -333,6 +333,26 @@ t_is_array(_) ->
      || T <- [<<>>, a]
     ].
 
+t_coalesce(_) ->
+    ?assertEqual(undefined, emqx_rule_funcs:coalesce([])),
+    ?assertEqual(undefined, emqx_rule_funcs:coalesce([undefined, undefined, undefined])),
+    ?assertEqual(42, emqx_rule_funcs:coalesce([undefined, 42, undefined])),
+    ?assertEqual(hello, emqx_rule_funcs:coalesce([hello, undefined])),
+    ?assertEqual(world, emqx_rule_funcs:coalesce([world])),
+    ?assertEqual(hello, emqx_rule_funcs:coalesce(hello, world)),
+    ?assertEqual(world, emqx_rule_funcs:coalesce(undefined, world)),
+    ok.
+
+t_coalesce_ne(_) ->
+    ?assertEqual(undefined, emqx_rule_funcs:coalesce_ne([])),
+    ?assertEqual(undefined, emqx_rule_funcs:coalesce_ne([<<>>, undefined, ""])),
+    ?assertEqual(42, emqx_rule_funcs:coalesce_ne(["", 42, undefined])),
+    ?assertEqual(hello, emqx_rule_funcs:coalesce_ne([hello, <<>>])),
+    ?assertEqual(world, emqx_rule_funcs:coalesce_ne([world])),
+    ?assertEqual(hello, emqx_rule_funcs:coalesce_ne(hello, world)),
+    ?assertEqual(world, emqx_rule_funcs:coalesce_ne("", world)),
+    ok.
+
 %%------------------------------------------------------------------------------
 %% Test cases for arith op
 %%------------------------------------------------------------------------------

+ 17 - 0
changes/ce/feat-13640.en.md

@@ -0,0 +1,17 @@
+Add two new rule SQL functions `coalesce/2` and `coalesce_ne/2`.
+
+The two functions can help to simplify null-checks in rule SQL.
+For example
+
+```
+SELECT
+    CASE
+        WHEN is_null(payload.path.to.value) THEN
+            0
+        ELSE
+            payload.path.to.value
+    END AS my_value
+```
+
+can be as simple as: `SELECT coalesce(payload.path.to.value, 0) AS my_value`.
+