diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 773775743..a2855b1bf 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -138,6 +138,7 @@ -export([sql_password/0, sql_password/1]). -export([sql_pool_size/0, sql_pool_size/1]). -export([sql_port/0, sql_port/1]). +-export([sql_prepared_statements/0, sql_prepared_statements/1]). -export([sql_query_timeout/0, sql_query_timeout/1]). -export([sql_queue_type/0, sql_queue_type/1]). -export([sql_server/0, sql_server/1]). @@ -933,6 +934,13 @@ sql_port() -> sql_port(Host) -> ejabberd_config:get_option({sql_port, Host}). +-spec sql_prepared_statements() -> boolean(). +sql_prepared_statements() -> + sql_prepared_statements(global). +-spec sql_prepared_statements(global | binary()) -> boolean(). +sql_prepared_statements(Host) -> + ejabberd_config:get_option({sql_prepared_statements, Host}). + -spec sql_query_timeout() -> pos_integer(). sql_query_timeout() -> sql_query_timeout(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index f0987986a..d1019dd62 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -380,6 +380,8 @@ opt_type(sql_type) -> econf:enum([mysql, pgsql, sqlite, mssql, odbc]); opt_type(sql_username) -> econf:binary(); +opt_type(sql_prepared_statements) -> + econf:bool(); opt_type(trusted_proxies) -> econf:either(all, econf:list(econf:ip_mask())); opt_type(use_cache) -> @@ -652,6 +654,7 @@ options() -> {sql_ssl_verify, false}, {sql_start_interval, timer:seconds(30)}, {sql_username, <<"ejabberd">>}, + {sql_prepared_statements, true}, {trusted_proxies, []}, {validate_stream, false}, {websocket_origin, []}, diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 1f3d27421..c1d9383a0 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -583,14 +583,23 @@ sql_query_internal(#sql_query{} = Query) -> Key = {?PREPARE_KEY, Query#sql_query.hash}, case get(Key) of undefined -> - case pgsql_prepare(Query, State) of - {ok, _, _, _} -> - put(Key, prepared); - {error, Error} -> - ?ERROR_MSG("PREPARE failed for SQL query " + Host = State#state.host, + PreparedStatements = + ejabberd_option:sql_prepared_statements(Host), + case PreparedStatements of + false -> + put(Key, ignore); + true -> + case pgsql_prepare(Query, State) of + {ok, _, _, _} -> + put(Key, prepared); + {error, Error} -> + ?ERROR_MSG( + "PREPARE failed for SQL query " "at ~p: ~p", [Query#sql_query.loc, Error]), - put(Key, ignore) + put(Key, ignore) + end end; _ -> ok @@ -599,7 +608,7 @@ sql_query_internal(#sql_query{} = Query) -> prepared -> pgsql_execute_sql_query(Query, State); _ -> - generic_sql_query(Query) + pgsql_sql_query(Query) end; mysql -> generic_sql_query(Query); @@ -695,6 +704,24 @@ generic_escape() -> in_array_string = fun(X) -> <<"'", (escape(X))/binary, "'">> end }. +pgsql_sql_query(SQLQuery) -> + sql_query_format_res( + sql_query_internal(pgsql_sql_query_format(SQLQuery)), + SQLQuery). + +pgsql_sql_query_format(SQLQuery) -> + Args = (SQLQuery#sql_query.args)(pgsql_escape()), + (SQLQuery#sql_query.format_query)(Args). + +pgsql_escape() -> + #sql_escape{string = fun(X) -> <<"E'", (escape(X))/binary, "'">> end, + integer = fun(X) -> misc:i2l(X) end, + boolean = fun(true) -> <<"1">>; + (false) -> <<"0">> + end, + in_array_string = fun(X) -> <<"E'", (escape(X))/binary, "'">> end + }. + sqlite_sql_query(SQLQuery) -> sql_query_format_res( sql_query_internal(sqlite_sql_query_format(SQLQuery)),