121 lines
3.9 KiB
Erlang
121 lines
3.9 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
%%
|
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
%% you may not use this file except in compliance with the License.
|
|
%% You may obtain a copy of the License at
|
|
%%
|
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
%%
|
|
%% Unless required by applicable law or agreed to in writing, software
|
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
%% See the License for the specific language governing permissions and
|
|
%% limitations under the License.
|
|
%%--------------------------------------------------------------------
|
|
|
|
-module(emqx_rule_sqlparser).
|
|
|
|
-include("rule_engine.hrl").
|
|
|
|
-export([parse/1]).
|
|
|
|
-export([
|
|
select_fields/1,
|
|
select_is_foreach/1,
|
|
select_doeach/1,
|
|
select_incase/1,
|
|
select_from/1,
|
|
select_where/1
|
|
]).
|
|
|
|
-import(proplists, [
|
|
get_value/2,
|
|
get_value/3
|
|
]).
|
|
|
|
-record(select, {fields, from, where, is_foreach, doeach, incase}).
|
|
|
|
-opaque select() :: #select{}.
|
|
|
|
-type const() :: {const, number() | binary()}.
|
|
|
|
-type variable() :: binary() | list(binary()).
|
|
|
|
-type alias() :: binary() | list(binary()).
|
|
|
|
%% TODO: So far the SQL function module names and function names are as binary(),
|
|
%% binary_to_atom is called to convert to module and function name.
|
|
%% For better performance, the function references
|
|
%% can be converted to a fun Module:Function/N When compiling the SQL.
|
|
-type ext_module_name() :: atom() | binary().
|
|
-type func_name() :: atom() | binary().
|
|
-type func_args() :: [field()].
|
|
%% Functions defiend in emqx_rule_funcs
|
|
-type builtin_func_ref() :: {var, func_name()}.
|
|
%% Functions defined in other modules, reference syntax: Module.Function(Arg1, Arg2, ...)
|
|
%% NOTE: it's '.' (Elixir style), but not ':' (Erlang style).
|
|
%% Parsed as a two element path-list: [{key, Module}, {key, Func}].
|
|
-type external_func_ref() :: {path, [{key, ext_module_name() | func_name()}]}.
|
|
-type func_ref() :: builtin_func_ref() | external_func_ref().
|
|
-type sql_func() :: {'fun', func_ref(), func_args()}.
|
|
|
|
-type field() :: const() | variable() | {as, field(), alias()} | sql_func().
|
|
|
|
-export_type([select/0]).
|
|
|
|
%% Parse one select statement.
|
|
-spec parse(string() | binary()) -> {ok, select()} | {error, term()}.
|
|
parse(Sql) ->
|
|
try
|
|
case rulesql:parsetree(Sql) of
|
|
{ok, {select, Clauses}} ->
|
|
{ok, #select{
|
|
is_foreach = false,
|
|
fields = get_value(fields, Clauses),
|
|
doeach = [],
|
|
incase = {},
|
|
from = get_value(from, Clauses),
|
|
where = get_value(where, Clauses)
|
|
}};
|
|
{ok, {foreach, Clauses}} ->
|
|
{ok, #select{
|
|
is_foreach = true,
|
|
fields = get_value(fields, Clauses),
|
|
doeach = get_value(do, Clauses, []),
|
|
incase = get_value(incase, Clauses, {}),
|
|
from = get_value(from, Clauses),
|
|
where = get_value(where, Clauses)
|
|
}};
|
|
Error ->
|
|
{error, Error}
|
|
end
|
|
catch
|
|
_Error:Reason:StackTrace ->
|
|
{error, {Reason, StackTrace}}
|
|
end.
|
|
|
|
-spec select_fields(select()) -> list(field()).
|
|
select_fields(#select{fields = Fields}) ->
|
|
Fields.
|
|
|
|
-spec select_is_foreach(select()) -> boolean().
|
|
select_is_foreach(#select{is_foreach = IsForeach}) ->
|
|
IsForeach.
|
|
|
|
-spec select_doeach(select()) -> list(field()).
|
|
select_doeach(#select{doeach = DoEach}) ->
|
|
DoEach.
|
|
|
|
-spec select_incase(select()) -> list(field()).
|
|
select_incase(#select{incase = InCase}) ->
|
|
InCase.
|
|
|
|
-spec select_from(select()) -> list(binary()).
|
|
select_from(#select{from = From}) ->
|
|
From.
|
|
|
|
-spec select_where(select()) -> tuple().
|
|
select_where(#select{where = Where}) ->
|
|
Where.
|