emqx_rule_sqlparser.erl 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_rule_sqlparser).
  17. -include("rule_engine.hrl").
  18. -export([parse/1, parse/2]).
  19. -export([
  20. select_fields/1,
  21. select_is_foreach/1,
  22. select_doeach/1,
  23. select_incase/1,
  24. select_from/1,
  25. select_where/1
  26. ]).
  27. -import(proplists, [
  28. get_value/2,
  29. get_value/3
  30. ]).
  31. -record(select, {fields, from, where, is_foreach, doeach, incase}).
  32. -opaque select() :: #select{}.
  33. -type const() :: {const, number() | binary()}.
  34. -type variable() :: binary() | list(binary()).
  35. -type alias() :: binary() | list(binary()).
  36. %% TODO: So far the SQL function module names and function names are as binary(),
  37. %% binary_to_atom is called to convert to module and function name.
  38. %% For better performance, the function references
  39. %% can be converted to a fun Module:Function/N When compiling the SQL.
  40. -type ext_module_name() :: atom() | binary().
  41. -type func_name() :: atom() | binary().
  42. -type func_args() :: [field()].
  43. %% Functions defiend in emqx_rule_funcs
  44. -type builtin_func_ref() :: {var, func_name()}.
  45. %% Functions defined in other modules, reference syntax: Module.Function(Arg1, Arg2, ...)
  46. %% NOTE: it's '.' (Elixir style), but not ':' (Erlang style).
  47. %% Parsed as a two element path-list: [{key, Module}, {key, Func}].
  48. -type external_func_ref() :: {path, [{key, ext_module_name() | func_name()}]}.
  49. -type func_ref() :: builtin_func_ref() | external_func_ref().
  50. -type sql_func() :: {'fun', func_ref(), func_args()}.
  51. -type field() :: const() | variable() | {as, field(), alias()} | sql_func().
  52. -type parse_opts() :: #{
  53. %% Whether `from' clause should be mandatory.
  54. %% Default: `true'.
  55. with_from => boolean()
  56. }.
  57. -export_type([select/0]).
  58. %% Parse one select statement.
  59. -spec parse(string() | binary()) -> {ok, select()} | {error, term()}.
  60. parse(Sql) ->
  61. parse(Sql, _Opts = #{}).
  62. -spec parse(string() | binary(), parse_opts()) -> {ok, select()} | {error, term()}.
  63. parse(Sql, Opts) ->
  64. WithFrom = maps:get(with_from, Opts, true),
  65. case do_parse(Sql) of
  66. {ok, Parsed} when WithFrom ->
  67. ensure_non_empty_from(Parsed);
  68. {ok, Parsed} ->
  69. ensure_empty_from(Parsed);
  70. Error = {error, _} ->
  71. Error
  72. end.
  73. -spec select_fields(select()) -> list(field()).
  74. select_fields(#select{fields = Fields}) ->
  75. Fields.
  76. -spec select_is_foreach(select()) -> boolean().
  77. select_is_foreach(#select{is_foreach = IsForeach}) ->
  78. IsForeach.
  79. -spec select_doeach(select()) -> list(field()).
  80. select_doeach(#select{doeach = DoEach}) ->
  81. DoEach.
  82. -spec select_incase(select()) -> list(field()).
  83. select_incase(#select{incase = InCase}) ->
  84. InCase.
  85. -spec select_from(select()) -> list(binary()).
  86. select_from(#select{from = From}) ->
  87. From.
  88. -spec select_where(select()) -> tuple().
  89. select_where(#select{where = Where}) ->
  90. Where.
  91. -spec do_parse(string() | binary()) -> {ok, select()} | {error, term()}.
  92. do_parse(Sql) ->
  93. try
  94. case rulesql:parsetree(Sql) of
  95. {ok, {select, Clauses}} ->
  96. Parsed = #select{
  97. is_foreach = false,
  98. fields = get_value(fields, Clauses),
  99. doeach = [],
  100. incase = {},
  101. from = get_value(from, Clauses),
  102. where = get_value(where, Clauses)
  103. },
  104. {ok, Parsed};
  105. {ok, {foreach, Clauses}} ->
  106. Parsed = #select{
  107. is_foreach = true,
  108. fields = get_value(fields, Clauses),
  109. doeach = get_value(do, Clauses, []),
  110. incase = get_value(incase, Clauses, {}),
  111. from = get_value(from, Clauses),
  112. where = get_value(where, Clauses)
  113. },
  114. {ok, Parsed};
  115. Error ->
  116. {error, Error}
  117. end
  118. catch
  119. _Error:Reason:StackTrace ->
  120. {error, {Reason, StackTrace}}
  121. end.
  122. ensure_non_empty_from(#select{from = []}) ->
  123. {error, empty_from_clause};
  124. ensure_non_empty_from(Parsed) ->
  125. {ok, Parsed}.
  126. ensure_empty_from(#select{from = [_ | _]}) ->
  127. {error, non_empty_from_clause};
  128. ensure_empty_from(Parsed) ->
  129. {ok, Parsed}.