|
@@ -59,6 +59,9 @@
|
|
|
default_port => ?PGSQL_DEFAULT_PORT
|
|
default_port => ?PGSQL_DEFAULT_PORT
|
|
|
}).
|
|
}).
|
|
|
|
|
|
|
|
|
|
+-type connector_resource_id() :: binary().
|
|
|
|
|
+-type action_resource_id() :: binary().
|
|
|
|
|
+
|
|
|
-type template() :: {unicode:chardata(), emqx_template_sql:row_template()}.
|
|
-type template() :: {unicode:chardata(), emqx_template_sql:row_template()}.
|
|
|
-type state() ::
|
|
-type state() ::
|
|
|
#{
|
|
#{
|
|
@@ -319,38 +322,40 @@ do_check_channel_sql(
|
|
|
on_get_channels(ResId) ->
|
|
on_get_channels(ResId) ->
|
|
|
emqx_bridge_v2:get_channels_for_connector(ResId).
|
|
emqx_bridge_v2:get_channels_for_connector(ResId).
|
|
|
|
|
|
|
|
-on_query(InstId, {TypeOrKey, NameOrSQL}, State) ->
|
|
|
|
|
- on_query(InstId, {TypeOrKey, NameOrSQL, []}, State);
|
|
|
|
|
|
|
+-spec on_query
|
|
|
|
|
+ %% Called from authn and authz modules
|
|
|
|
|
+ (connector_resource_id(), {prepared_query, binary(), [term()]}, state()) ->
|
|
|
|
|
+ {ok, _} | {error, term()};
|
|
|
|
|
+ %% Called from bridges
|
|
|
|
|
+ (connector_resource_id(), {action_resource_id(), map()}, state()) ->
|
|
|
|
|
+ {ok, _} | {error, term()}.
|
|
|
|
|
+on_query(InstId, {TypeOrKey, NameOrMap}, State) ->
|
|
|
|
|
+ on_query(InstId, {TypeOrKey, NameOrMap, []}, State);
|
|
|
on_query(
|
|
on_query(
|
|
|
InstId,
|
|
InstId,
|
|
|
- {TypeOrKey, NameOrSQL, Params},
|
|
|
|
|
|
|
+ {TypeOrKey, NameOrMap, Params},
|
|
|
#{pool_name := PoolName} = State
|
|
#{pool_name := PoolName} = State
|
|
|
) ->
|
|
) ->
|
|
|
?SLOG(debug, #{
|
|
?SLOG(debug, #{
|
|
|
msg => "postgresql_connector_received_sql_query",
|
|
msg => "postgresql_connector_received_sql_query",
|
|
|
connector => InstId,
|
|
connector => InstId,
|
|
|
type => TypeOrKey,
|
|
type => TypeOrKey,
|
|
|
- sql => NameOrSQL,
|
|
|
|
|
|
|
+ sql => NameOrMap,
|
|
|
state => State
|
|
state => State
|
|
|
}),
|
|
}),
|
|
|
- Type = pgsql_query_type(TypeOrKey, State),
|
|
|
|
|
- {NameOrSQL2, Data} = proc_sql_params(TypeOrKey, NameOrSQL, Params, State),
|
|
|
|
|
- Res = on_sql_query(TypeOrKey, InstId, PoolName, Type, NameOrSQL2, Data),
|
|
|
|
|
|
|
+ {QueryType, NameOrSQL2, Data} = proc_sql_params(TypeOrKey, NameOrMap, Params, State),
|
|
|
|
|
+ emqx_trace:rendered_action_template(
|
|
|
|
|
+ TypeOrKey,
|
|
|
|
|
+ #{
|
|
|
|
|
+ statement_type => QueryType,
|
|
|
|
|
+ statement_or_name => NameOrSQL2,
|
|
|
|
|
+ data => Data
|
|
|
|
|
+ }
|
|
|
|
|
+ ),
|
|
|
|
|
+ Res = on_sql_query(InstId, PoolName, QueryType, NameOrSQL2, Data),
|
|
|
?tp(postgres_bridge_connector_on_query_return, #{instance_id => InstId, result => Res}),
|
|
?tp(postgres_bridge_connector_on_query_return, #{instance_id => InstId, result => Res}),
|
|
|
handle_result(Res).
|
|
handle_result(Res).
|
|
|
|
|
|
|
|
-pgsql_query_type(_TypeOrTag, #{prepares := disabled}) ->
|
|
|
|
|
- query;
|
|
|
|
|
-pgsql_query_type(sql, _ConnectorState) ->
|
|
|
|
|
- query;
|
|
|
|
|
-pgsql_query_type(query, _ConnectorState) ->
|
|
|
|
|
- query;
|
|
|
|
|
-pgsql_query_type(prepared_query, _ConnectorState) ->
|
|
|
|
|
- prepared_query;
|
|
|
|
|
-%% for bridge
|
|
|
|
|
-pgsql_query_type(_, ConnectorState) ->
|
|
|
|
|
- pgsql_query_type(prepared_query, ConnectorState).
|
|
|
|
|
-
|
|
|
|
|
on_batch_query(
|
|
on_batch_query(
|
|
|
InstId,
|
|
InstId,
|
|
|
[{Key, _} = Request | _] = BatchReq,
|
|
[{Key, _} = Request | _] = BatchReq,
|
|
@@ -370,7 +375,15 @@ on_batch_query(
|
|
|
{_Statement, RowTemplate} ->
|
|
{_Statement, RowTemplate} ->
|
|
|
StatementTemplate = get_templated_statement(BinKey, State),
|
|
StatementTemplate = get_templated_statement(BinKey, State),
|
|
|
Rows = [render_prepare_sql_row(RowTemplate, Data) || {_Key, Data} <- BatchReq],
|
|
Rows = [render_prepare_sql_row(RowTemplate, Data) || {_Key, Data} <- BatchReq],
|
|
|
- case on_sql_query(Key, InstId, PoolName, execute_batch, StatementTemplate, Rows) of
|
|
|
|
|
|
|
+ emqx_trace:rendered_action_template(
|
|
|
|
|
+ Key,
|
|
|
|
|
+ #{
|
|
|
|
|
+ statement_type => execute_batch,
|
|
|
|
|
+ statement_or_name => StatementTemplate,
|
|
|
|
|
+ data => Rows
|
|
|
|
|
+ }
|
|
|
|
|
+ ),
|
|
|
|
|
+ case on_sql_query(InstId, PoolName, execute_batch, StatementTemplate, Rows) of
|
|
|
{error, _Error} = Result ->
|
|
{error, _Error} = Result ->
|
|
|
handle_result(Result);
|
|
handle_result(Result);
|
|
|
{_Column, Results} ->
|
|
{_Column, Results} ->
|
|
@@ -386,25 +399,38 @@ on_batch_query(InstId, BatchReq, State) ->
|
|
|
}),
|
|
}),
|
|
|
{error, {unrecoverable_error, invalid_request}}.
|
|
{error, {unrecoverable_error, invalid_request}}.
|
|
|
|
|
|
|
|
-proc_sql_params(query, SQLOrKey, Params, _State) ->
|
|
|
|
|
- {SQLOrKey, Params};
|
|
|
|
|
-proc_sql_params(prepared_query, SQLOrKey, Params, _State) ->
|
|
|
|
|
- {SQLOrKey, Params};
|
|
|
|
|
-proc_sql_params(TypeOrKey, SQLOrData, Params, State) ->
|
|
|
|
|
- DisablePreparedStatements = maps:get(prepares, State, #{}) =:= disabled,
|
|
|
|
|
- BinKey = to_bin(TypeOrKey),
|
|
|
|
|
- case get_template(BinKey, State) of
|
|
|
|
|
- undefined ->
|
|
|
|
|
- {SQLOrData, Params};
|
|
|
|
|
- {Statement, RowTemplate} ->
|
|
|
|
|
- Rendered = render_prepare_sql_row(RowTemplate, SQLOrData),
|
|
|
|
|
- case DisablePreparedStatements of
|
|
|
|
|
- true ->
|
|
|
|
|
- {Statement, Rendered};
|
|
|
|
|
- false ->
|
|
|
|
|
- {BinKey, Rendered}
|
|
|
|
|
- end
|
|
|
|
|
- end.
|
|
|
|
|
|
|
+proc_sql_params(ActionResId, #{} = Map, [], State) when is_binary(ActionResId) ->
|
|
|
|
|
+ %% When this connector is called from actions/bridges.
|
|
|
|
|
+ DisablePreparedStatements = prepared_statements_disabled(State),
|
|
|
|
|
+ {ExprTemplate, RowTemplate} = get_template(ActionResId, State),
|
|
|
|
|
+ Rendered = render_prepare_sql_row(RowTemplate, Map),
|
|
|
|
|
+ case DisablePreparedStatements of
|
|
|
|
|
+ true ->
|
|
|
|
|
+ {query, ExprTemplate, Rendered};
|
|
|
|
|
+ false ->
|
|
|
|
|
+ {prepared_query, ActionResId, Rendered}
|
|
|
|
|
+ end;
|
|
|
|
|
+proc_sql_params(prepared_query, ConnResId, Params, State) ->
|
|
|
|
|
+ %% When this connector is called from authn/authz modules
|
|
|
|
|
+ DisablePreparedStatements = prepared_statements_disabled(State),
|
|
|
|
|
+ case DisablePreparedStatements of
|
|
|
|
|
+ true ->
|
|
|
|
|
+ #{query_templates := #{ConnResId := {ExprTemplate, _VarsTemplate}}} = State,
|
|
|
|
|
+ {query, ExprTemplate, Params};
|
|
|
|
|
+ false ->
|
|
|
|
|
+ %% Connector resource id itself is the prepared statement name
|
|
|
|
|
+ {prepared_query, ConnResId, Params}
|
|
|
|
|
+ end;
|
|
|
|
|
+proc_sql_params(QueryType, SQL, Params, _State) when
|
|
|
|
|
+ is_atom(QueryType) andalso
|
|
|
|
|
+ (is_binary(SQL) orelse is_list(SQL)) andalso
|
|
|
|
|
+ is_list(Params)
|
|
|
|
|
+->
|
|
|
|
|
+ %% When called to do ad-hoc commands/queries.
|
|
|
|
|
+ {QueryType, SQL, Params}.
|
|
|
|
|
+
|
|
|
|
|
+prepared_statements_disabled(State) ->
|
|
|
|
|
+ maps:get(prepares, State, #{}) =:= disabled.
|
|
|
|
|
|
|
|
get_template(Key, #{installed_channels := Channels} = _State) when is_map_key(Key, Channels) ->
|
|
get_template(Key, #{installed_channels := Channels} = _State) when is_map_key(Key, Channels) ->
|
|
|
BinKey = to_bin(Key),
|
|
BinKey = to_bin(Key),
|
|
@@ -420,21 +446,17 @@ get_templated_statement(Key, #{installed_channels := Channels} = _State) when
|
|
|
->
|
|
->
|
|
|
BinKey = to_bin(Key),
|
|
BinKey = to_bin(Key),
|
|
|
ChannelState = maps:get(BinKey, Channels),
|
|
ChannelState = maps:get(BinKey, Channels),
|
|
|
- ChannelPreparedStatements = maps:get(prepares, ChannelState),
|
|
|
|
|
- maps:get(BinKey, ChannelPreparedStatements);
|
|
|
|
|
|
|
+ case ChannelState of
|
|
|
|
|
+ #{prepares := disabled, query_templates := #{BinKey := {ExprTemplate, _}}} ->
|
|
|
|
|
+ ExprTemplate;
|
|
|
|
|
+ #{prepares := #{BinKey := ExprTemplate}} ->
|
|
|
|
|
+ ExprTemplate
|
|
|
|
|
+ end;
|
|
|
get_templated_statement(Key, #{prepares := PrepStatements}) ->
|
|
get_templated_statement(Key, #{prepares := PrepStatements}) ->
|
|
|
BinKey = to_bin(Key),
|
|
BinKey = to_bin(Key),
|
|
|
maps:get(BinKey, PrepStatements).
|
|
maps:get(BinKey, PrepStatements).
|
|
|
|
|
|
|
|
-on_sql_query(Key, InstId, PoolName, Type, NameOrSQL, Data) ->
|
|
|
|
|
- emqx_trace:rendered_action_template(
|
|
|
|
|
- Key,
|
|
|
|
|
- #{
|
|
|
|
|
- statement_type => Type,
|
|
|
|
|
- statement_or_name => NameOrSQL,
|
|
|
|
|
- data => Data
|
|
|
|
|
- }
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+on_sql_query(InstId, PoolName, Type, NameOrSQL, Data) ->
|
|
|
try ecpool:pick_and_do(PoolName, {?MODULE, Type, [NameOrSQL, Data]}, no_handover) of
|
|
try ecpool:pick_and_do(PoolName, {?MODULE, Type, [NameOrSQL, Data]}, no_handover) of
|
|
|
{error, Reason} ->
|
|
{error, Reason} ->
|
|
|
?tp(
|
|
?tp(
|
|
@@ -785,6 +807,7 @@ handle_batch_result([{error, Error} | _Rest], _Acc) ->
|
|
|
TranslatedError = translate_to_log_context(Error),
|
|
TranslatedError = translate_to_log_context(Error),
|
|
|
{error, {unrecoverable_error, export_error(TranslatedError)}};
|
|
{error, {unrecoverable_error, export_error(TranslatedError)}};
|
|
|
handle_batch_result([], Acc) ->
|
|
handle_batch_result([], Acc) ->
|
|
|
|
|
+ ?tp("postgres_success_batch_result", #{row_count => Acc}),
|
|
|
{ok, Acc}.
|
|
{ok, Acc}.
|
|
|
|
|
|
|
|
translate_to_log_context({error, Reason}) ->
|
|
translate_to_log_context({error, Reason}) ->
|