-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prepare SQL from file on application start. New function for using pr… #4
base: master
Are you sure you want to change the base?
Changes from all commits
91baf6a
ba40771
74b0ff4
dc1fdaf
9a05571
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
{"simple", "select 1+1"}. | ||
{"increment", "select $1+1"}. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
|
||
-export([squery/1, squery/2, squery/3, | ||
equery/2, equery/3, equery/4, | ||
prepared_query/2, prepared_query/3, prepared_query/4, | ||
with_transaction/2, with_transaction/3]). | ||
|
||
-export([start_link/1]). | ||
|
@@ -20,7 +21,8 @@ | |
-record(state, {conn::pid(), | ||
delay::pos_integer(), | ||
timer::timer:tref(), | ||
start_args::proplists:proplist()}). | ||
start_args::proplists:proplist(), | ||
sql_text::proplists:proplist()}). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Id on't think |
||
|
||
-define(INITIAL_DELAY, 500). % Half a second | ||
-define(MAXIMUM_DELAY, 5 * 60 * 1000). % Five minutes | ||
|
@@ -67,6 +69,26 @@ equery(PoolName, Sql, Params, Timeout) -> | |
{equery, Sql, Params}, Timeout) | ||
end, Timeout). | ||
|
||
prepared_query(Name, Params) -> | ||
case get(?STATE_VAR) of | ||
undefined -> | ||
prepared_query(epgsql_pool, Name, Params); | ||
Conn -> | ||
epgsql:prepared_query(Conn, Name, Params) | ||
end. | ||
|
||
prepared_query(PoolName, Name, Params) when is_atom(PoolName) -> | ||
prepared_query(PoolName, Name, Params, ?TIMEOUT); | ||
prepared_query(Name, Params, Timeout) -> | ||
prepared_query(epgsql_pool, Name, Params, Timeout). | ||
|
||
prepared_query(PoolName, Name, Params, Timeout) -> | ||
poolboy:transaction(PoolName, | ||
fun (Worker) -> | ||
gen_server:call(Worker, | ||
{prepared_query, Name, Params}, Timeout) | ||
end, Timeout). | ||
|
||
with_transaction(PoolName, Fun) -> | ||
with_transaction(PoolName, Fun, ?TIMEOUT). | ||
|
||
|
@@ -77,16 +99,29 @@ with_transaction(PoolName, Fun, Timeout) -> | |
{transaction, Fun}, Timeout) | ||
end, Timeout). | ||
|
||
|
||
-include_lib("epgsql/include/epgsql.hrl"). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't find any place, where data from this .hrl is used. |
||
-include("worker_args.hrl"). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Includes should be somewhere in module's header (near defines). |
||
|
||
start_link(Args) -> | ||
gen_server:start_link(?MODULE, Args, []). | ||
|
||
init(Args) -> | ||
init(#worker_args{connection_args = Args, prepared_sql = PreparedSQL}) -> | ||
process_flag(trap_exit, true), | ||
{ok, connect(#state{start_args = Args, delay = ?INITIAL_DELAY})}. | ||
{ok, connect(#state{ | ||
start_args = Args, | ||
sql_text = PreparedSQL, | ||
delay = ?INITIAL_DELAY | ||
})}. | ||
|
||
handle_call({squery, Sql}, _From, | ||
#state{conn=Conn} = State) when Conn /= undefined -> | ||
{reply, epgsql:squery(Conn, Sql), State}; | ||
|
||
handle_call({prepared_query, Name, Params}, _From, | ||
#state{conn = Conn} = State) when Conn /= undefined -> | ||
{reply, epgsql:prepared_query(Conn, Name, Params), State}; | ||
|
||
handle_call({equery, Sql, Params}, _From, | ||
#state{conn = Conn} = State) when Conn /= undefined -> | ||
{reply, epgsql:equery(Conn, Sql, Params), State}; | ||
|
@@ -129,6 +164,21 @@ terminate(_Reason, #state{conn=Conn}) -> | |
code_change(_OldVsn, State, _Extra) -> | ||
{ok, State}. | ||
|
||
|
||
prepare_statements(Con, PreparedSQL) -> | ||
lists:all( | ||
fun({Name, Query}) -> | ||
case epgsql:parse(Con, Name, Query, []) of | ||
{ok, _Statement} -> | ||
true; | ||
{error, Reason} -> | ||
error_logger:error_msg("Error ~p parsing SQL query ~p", [Reason, Query]), | ||
false | ||
end | ||
end, | ||
PreparedSQL | ||
). | ||
|
||
connect(State) -> | ||
Args = State#state.start_args, | ||
Hostname = proplists:get_value(host, Args), | ||
|
@@ -141,19 +191,34 @@ connect(State) -> | |
"~p Connected to ~s at ~s with user ~s: ~p~n", | ||
[self(), Database, Hostname, Username, Conn]), | ||
timer:cancel(State#state.timer), | ||
State#state{conn=Conn, delay=?INITIAL_DELAY, timer = undefined}; | ||
case prepare_statements(Conn, State#state.sql_text) of | ||
true -> | ||
State#state{conn=Conn, delay=?INITIAL_DELAY, timer = undefined}; | ||
false -> | ||
ok = epgsql:close(Conn), | ||
NewState = handle_connection_error(State), | ||
error_logger:error_msg( | ||
"~p Unable to prepare statements on ~s at ~s with user ~s " | ||
"- attempting reconnect in ~p ms~n", | ||
[self(), Database, Hostname, Username, NewState#state.delay]), | ||
NewState | ||
end; | ||
Error -> | ||
NewDelay = calculate_delay(State#state.delay), | ||
NewState = handle_connection_error(State), | ||
error_logger:warning_msg( | ||
"~p Unable to connect to ~s at ~s with user ~s (~p) " | ||
"- attempting reconnect in ~p ms~n", | ||
[self(), Database, Hostname, Username, Error, NewDelay]), | ||
{ok, Tref} = | ||
timer:apply_after( | ||
State#state.delay, gen_server, cast, [self(), reconnect]), | ||
State#state{conn=undefined, delay = NewDelay, timer = Tref} | ||
[self(), Database, Hostname, Username, Error, NewState#state.delay]), | ||
NewState | ||
end. | ||
|
||
handle_connection_error(#state{delay = Delay} = State) -> | ||
NewDelay = calculate_delay(Delay), | ||
{ok, Tref} = | ||
timer:apply_after( | ||
Delay, gen_server, cast, [self(), reconnect]), | ||
State#state{conn=undefined, delay = NewDelay, timer = Tref}. | ||
|
||
calculate_delay(Delay) when (Delay * 2) >= ?MAXIMUM_DELAY -> | ||
?MAXIMUM_DELAY; | ||
calculate_delay(Delay) -> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
-record( | ||
worker_args, | ||
{ | ||
prepared_sql, | ||
connection_args | ||
}). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why record if we can just use tuple?