From 3710dc1e3be25b7061b72867b5f06336873896a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 25 Jul 2023 16:52:49 +0200 Subject: [PATCH] Use prepared statement with mysql --- mix.exs | 2 +- rebar.config | 2 +- src/ejabberd_sql.erl | 27 ++++++++++++++++++++++++++- src/ejabberd_sql_pt.erl | 17 +++++++++++++---- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 490ec4a49..8a1615778 100644 --- a/mix.exs +++ b/mix.exs @@ -138,7 +138,7 @@ defmodule Ejabberd.MixProject do {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_below('22', true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.0"}}, - {config(:mysql), {:p1_mysql, "~> 1.0.20"}}, + {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql.git", ref: "150c15b96d2fb84cb00e07cc53cd97ec72b77efc"}}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: diff --git a/rebar.config b/rebar.config index 0f954ae85..386187d4e 100644 --- a/rebar.config +++ b/rebar.config @@ -63,7 +63,7 @@ {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.21"}}}}, + {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", "150c15b96d2fb84cb00e07cc53cd97ec72b77efc"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.11"}}}, {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.22"}}}}, diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 35b77d3d9..99f74953e 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -683,7 +683,14 @@ sql_query_internal(#sql_query{} = Query) -> pgsql_sql_query(Query) end; mysql -> - generic_sql_query(Query); + case {Query#sql_query.flags, ejabberd_option:sql_prepared_statements(State#state.host)} of + {1, _} -> + generic_sql_query(Query); + {_, false} -> + generic_sql_query(Query); + _ -> + mysql_prepared_execute(Query, State) + end; sqlite -> sqlite_sql_query(Query) end @@ -862,6 +869,24 @@ pgsql_execute_sql_query(SQLQuery, State) -> Res = pgsql_execute_to_odbc(ExecuteRes), sql_query_format_res(Res, SQLQuery). +mysql_prepared_execute(#sql_query{hash = Hash} = Query, State) -> + ValEsc = #sql_escape{like_escape = fun() -> ignore end, _ = fun(X) -> X end}, + TypesEsc = #sql_escape{string = fun(_) -> string end, + integer = fun(_) -> integer end, + boolean = fun(_) -> bool end, + in_array_string = fun(_) -> string end, + like_escape = fun() -> ignore end}, + Val = [X || X <- (Query#sql_query.args)(ValEsc), X /= ignore], + Types = [X || X <- (Query#sql_query.args)(TypesEsc), X /= ignore], + QueryFn = fun() -> + PrepEsc = #sql_escape{like_escape = fun() -> <<>> end, _ = fun(_) -> <<"?">> end}, + (Query#sql_query.format_query)((Query#sql_query.args)(PrepEsc)) + end, + QueryTimeout = query_timeout(State#state.host), + Res = p1_mysql_conn:prepared_query(State#state.db_ref, QueryFn, Hash, Val, Types, + self(), [{timeout, QueryTimeout - 1000}]), + Res2 = mysql_to_odbc(Res), + sql_query_format_res(Res2, Query). sql_query_format_res({selected, _, Rows}, SQLQuery) -> Res = diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 5d72fde24..61f9e421c 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -42,7 +42,8 @@ used_vars = [], use_new_schema, need_timestamp_pass = false, - need_array_pass = false}). + need_array_pass = false, + has_list = false}). -define(QUERY_RECORD, "sql_query"). @@ -268,9 +269,12 @@ parse1([$@, $( | S], Acc, State) -> Convert = case Type of integer -> - erl_syntax:application( - erl_syntax:atom(binary_to_integer), - [EVar]); + erl_syntax:if_expr([ + erl_syntax:clause( + [erl_syntax:application(erl_syntax:atom(is_binary), [EVar])], + [erl_syntax:application(erl_syntax:atom(binary_to_integer), [EVar])]), + erl_syntax:clause([erl_syntax:atom(true)], [EVar]) + ]); string -> EVar; timestamp -> @@ -339,6 +343,7 @@ parse1([$%, $( | S], Acc, State) -> erl_syntax:variable(Name)]), State2#state{'query' = [[{var, Var, Type}] | State2#state.'query'], need_array_pass = true, + has_list = true, args = [[Convert, ConvertArr] | State2#state.args], params = [Var | State2#state.params], param_pos = State2#state.param_pos + 1, @@ -467,6 +472,7 @@ make_sql_query(State, Type) -> Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}), SHash = <<"Q", (integer_to_binary(Hash))/binary>>, Query = pack_query(State#state.'query'), + Flags = case State#state.has_list of true -> 1; _ -> 0 end, EQuery = lists:flatmap( fun({str, S}) -> @@ -515,6 +521,9 @@ make_sql_query(State, Type) -> none, [erl_syntax:tuple(State#state.res)] )])), + erl_syntax:record_field( + erl_syntax:atom(flags), + erl_syntax:abstract(Flags)), erl_syntax:record_field( erl_syntax:atom(loc), erl_syntax:abstract({get(?MOD), State#state.loc}))