Skip to content
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

emysql_query and emysql_saver #139

Open
wants to merge 66 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
a49df26
add emysql_pool to manager pools
Apr 10, 2014
2414e7c
add transaction
slepher Apr 10, 2014
acd2a2d
add ActiveRecord style query interface
Apr 24, 2014
72cb0df
format code
Apr 24, 2014
84e01fc
Merge branch 'master' into taodi
Apr 24, 2014
25c2004
add deps to gitignore
Apr 28, 2014
5bd0773
adjust to emysql:add_pool/2
Apr 28, 2014
28a08fc
add test database config
Apr 29, 2014
a14465f
complete find and find_first
Apr 29, 2014
8f3c2f5
fix typo
Apr 29, 2014
3cf0a8c
fix emysql:prepare error
Apr 29, 2014
5342929
refine find
Apr 29, 2014
dba1345
find and find_first works
Apr 29, 2014
ac8abbf
trace sql execution in some cases
Apr 29, 2014
3b107c9
assemble execute_trace process
Apr 29, 2014
bce4aa3
assemble execute_trace process
Apr 29, 2014
d2119b2
clean the code
Apr 29, 2014
6fd8918
add transaction trace
Apr 30, 2014
d6c4b50
reformat execute log
Apr 30, 2014
292d43b
complete find_each
Apr 30, 2014
a7863cf
add INPUT macro
May 3, 2014
e765c98
emysql_saver module wrappes some update/save functions
May 3, 2014
14d43af
add batch save/update
May 4, 2014
7c83806
add find_and_create_by
May 4, 2014
baa2362
Merge remote-tracking branch 'upstream/master'
May 4, 2014
6a7169b
Merge branch 'master' into taodi
May 4, 2014
36a62a0
add rebar deps notes
May 5, 2014
aede183
format it
May 5, 2014
76ee47a
add lager lib
May 6, 2014
87b0a4e
fix typo
May 6, 2014
170529e
support dict style 'where'
May 7, 2014
a5087ea
add AS_VAL support
May 7, 2014
0de8719
supports insert ignore
May 7, 2014
922c3e6
fix typos
May 8, 2014
65182a9
refine ?INPUT macro
May 8, 2014
259a8af
fix INPUT macro
May 8, 2014
26be1b3
clean up macros
May 8, 2014
04e6848
fix SqlFields bug in save
May 9, 2014
8462979
fix return value of find_first
May 9, 2014
aed6bba
support created_at and updated_at
May 9, 2014
056a5d2
add update_or_create_by
May 9, 2014
4cf46f7
fix merge_rec bug
May 9, 2014
146d803
fix bugs in update_or_create_by
May 9, 2014
93719ca
fix bug in update_or_create_by
May 11, 2014
21538da
fix created_at/updated_at bug
May 11, 2014
e461b15
ignore created_at in updating records
May 13, 2014
74e61ff
return updated rec when saving one rec
May 13, 2014
8dd8960
add AccIn to collect find_each result
Jun 10, 2014
3c04fa4
fix ?AS_VAL bug
Jun 13, 2014
1704d77
fix find_each bug
Jun 16, 2014
2a9bfb5
find_or_create_by returns one object
Jun 16, 2014
b70157f
fix generate_insert_sql bug
Dec 25, 2014
1402ce5
Merge pull request #1 from rabbitz/taodi
Dec 26, 2014
1db6cf5
add fold function to emysql_query module
Feb 2, 2015
e811eb2
Merge pull request #2 from rabbitz/taodi
Feb 2, 2015
bd89e7a
enhanced emysql_query:find_each
Feb 10, 2015
bd86894
insert on duplicate
Feb 12, 2015
fdce72b
compatible order in previous version
Feb 12, 2015
c4c71f3
remove AccIn when it is undefined
Feb 12, 2015
161d213
export as_record/5
Feb 13, 2015
8548ae3
fix bug in emysql_query:find_each
Feb 15, 2015
9bed3ea
fix bug in lists:append
Feb 15, 2015
f29c4eb
fix bug in lists:foldl
Feb 25, 2015
38f562d
Fix in statement in emysql_query
May 11, 2015
a930662
Remove deprecated api
Dec 11, 2015
f1ab3f8
Remove deprecated api
Dec 11, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ test/ct_log_cache
logs/*
.#*
include/crypto_compat.hrl
deps/*
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ or (after building emysql.app and the database, as explained above), start a_hel

General Notes on using Emysql, including the actual specs:

#### Resolve Emysql dependency via rebar.config if possible

```Erlang
{deps,
[ {'emysql', ".*", { git, "git://github.com/Eonblast/Emysql.git", "master" } } ]
```
or some other branch
```Erlang
{deps,
[ {'emysql', ".*", { git, "git://github.com/jacktang/Emysql.git", "taodi" } } ]
```

#### Starting an Application

The Emysql driver is an Erlang gen-server, and, application.
Expand Down
11 changes: 8 additions & 3 deletions include/emysql.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
port :: number(),
database :: string(),
encoding :: utf8 | latin1 | {utf8, utf8_unicode_ci} | {utf8, utf8_general_ci},
available=queue:new() :: queue(),
locked=gb_trees:empty() :: gb_tree(),
waiting=queue:new() :: queue(),
available=queue:new() :: queue:queue(),
locked=gb_trees:empty() :: gb_tree:tree(),
waiting=queue:new() :: queue:queue(),
start_cmds=[] :: string(),
conn_test_period=0 :: number(),
connect_timeout=infinity :: number() | infinity,
Expand Down Expand Up @@ -180,3 +180,8 @@
% number of result set columns.
-define(SERVER_STATUS_METADATA_CHANGED, 1024).

% Wrap the query result in erlang record
-define(AS_REC(RecAtom), [RecAtom, record_info(fields, RecAtom)]).
-define(AS_VAL, as_val).
%
-define(INPUT(RecAtom, Records), [Records, record_info(fields, RecAtom)]).
13 changes: 12 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
% -*- Erlang -*-
% vim: ts=4 sw=4 et ft=erlang
{erl_opts, [
nowarn_deprecated_type
nowarn_deprecated_type,
{parse_transform, lager_transform}
]}.

{pre_hooks,[
{"linux|bsd|darwin|solaris", compile, "escript ./support/crypto_compat.escript"},
{"win32", compile, "escript.exe support/crypto_compat.escript"}
]}.

{deps, [
{'espec', ".*", { git, "git://github.com/lucaspiller/espec.git", "master" } },
{'lager', ".*", { git, "git://github.com/basho/lager.git", "master" } },
{'erl_utils', ".*", { git, "git://github.com/jacktang/erl_utils.git", "master" } }
]}.


{xref_warnings, true}.
9 changes: 7 additions & 2 deletions src/emysql.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
{registered, [emysql_conn_mgr, emysql_sup]},
{applications, [kernel, stdlib, crypto]},
{env, [
{default_timeout, 5000},
{conn_test_period, 28000}]}
{default_timeout, 5000},
{conn_test_period, 28000},
{default_conn_opt, [ {host, "localhost"},
{user, "root"},
{database, test}
]}
]}
]}.
14 changes: 13 additions & 1 deletion src/emysql.erl
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
default_timeout/0
]).

-export([transaction/2, transaction/3, abort/1]).

%% Result Conversion API
-export([
as_dict/1,
Expand Down Expand Up @@ -631,6 +633,16 @@ execute(PoolId, StmtName, Args, Timeout, nonblocking) when is_atom(StmtName), is
unavailable
end.

transaction(PoolId, Fun) ->
transaction(PoolId, Fun, default_timeout()).

transaction(PoolId, Fun, Timeout) ->
Connection = emysql_conn_mgr:wait_for_connection(PoolId),
monitor_work(Connection, Timeout, [Connection, transaction, Fun]).

abort(Reason) ->
throw(Reason).

%% @doc Return the field names of a result packet
%% @end
-spec field_names(Result) -> [Name]
Expand Down Expand Up @@ -669,7 +681,7 @@ result_type(#eof_packet{}) -> eof.
-spec as_dict(Result) -> Dict
when
Result :: #result_packet{},
Dict :: dict().
Dict :: dict:dict().
as_dict(Res) -> emysql_conv:as_dict(Res).


Expand Down
39 changes: 39 additions & 0 deletions src/emysql_conn.erl
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,37 @@ set_encoding(Connection, Encoding) ->
canonicalize_query(Q) when is_binary(Q) -> Q;
canonicalize_query(QL) when is_list(QL) -> iolist_to_binary(QL).

execute(Connection, transaction, Fun) when is_function(Fun) ->
case begin_transaction(Connection) of
#ok_packet{} ->
try Fun(Connection) of
Val ->
case commit_transaction(Connection) of
#ok_packet{} ->
{atomic, Val};
#error_packet{} = ErrorPacket ->
{aborted, {commit_error, ErrorPacket}}
end
catch
throw:Reason ->
rollback_transaction(Connection),
{aborted, Reason};
Class:Exception ->
rollback_transaction(Connection),
erlang:raise(Class, Exception, erlang:get_stacktrace())
end;
#error_packet{} = ErrorPacket ->
{aborted, {begin_error, ErrorPacket}}
end;
execute(Connection, StmtName, []) when is_atom(StmtName) ->
prepare_statement(Connection, StmtName),
StmtNameBin = atom_to_binary(StmtName, utf8),
execute_trace:execute(StmtName),
Packet = <<?COM_QUERY, "EXECUTE ", StmtNameBin/binary>>,
send_recv(Connection, Packet);
execute(Connection, Query, []) ->
QB = canonicalize_query(Query),
execute_trace:execute(raw_query, QB),
Packet = <<?COM_QUERY, QB/binary>>,
send_recv(Connection, Packet);
execute(Connection, Query, Args) when (is_list(Query) orelse is_binary(Query)) andalso is_list(Args) ->
Expand All @@ -103,6 +127,7 @@ execute(Connection, Query, Args) when (is_list(Query) orelse is_binary(Query)) a
OK when is_record(OK, ok_packet) ->
ParamNamesBin = list_to_binary(string:join([[$@ | integer_to_list(I)] || I <- lists:seq(1, length(Args))], ", ")), % todo: utf8?
Packet = <<?COM_QUERY, "EXECUTE ", (list_to_binary(StmtName))/binary, " USING ", ParamNamesBin/binary>>, % todo: utf8?
execute_trace:execute(StmtName, Args),
send_recv(Connection, Packet);
Error ->
Error
Expand All @@ -126,6 +151,7 @@ prepare(Connection, Name, Statement) when is_atom(Name) ->
prepare(Connection, atom_to_list(Name), Statement);
prepare(Connection, Name, Statement) ->
StatementBin = encode(Statement, binary),
execute_trace:prepare(Name, Statement),
Packet = <<?COM_QUERY, "PREPARE ", (list_to_binary(Name))/binary, " FROM ", StatementBin/binary>>, % todo: utf8?
case send_recv(Connection, Packet) of
OK when is_record(OK, ok_packet) ->
Expand All @@ -137,9 +163,22 @@ prepare(Connection, Name, Statement) ->
unprepare(Connection, Name) when is_atom(Name)->
unprepare(Connection, atom_to_list(Name));
unprepare(Connection, Name) ->
execute_trace:unprepare(Name),
Packet = <<?COM_QUERY, "DEALLOCATE PREPARE ", (list_to_binary(Name))/binary>>, % todo: utf8?
send_recv(Connection, Packet).

begin_transaction(Connection) ->
execute_trace:begin_transaction(),
emysql_conn:execute(Connection, <<"BEGIN">>, []).

rollback_transaction(Connection) ->
execute_trace:rollback_transaction(),
emysql_conn:execute(Connection, <<"ROLLBACK">>, []).

commit_transaction(Connection) ->
execute_trace:commit_transaction(),
emysql_conn:execute(Connection, <<"COMMIT">>, []).

open_n_connections(PoolId, N) ->
case emysql_conn_mgr:find_pool(PoolId, emysql_conn_mgr:pools()) of
{Pool, _} ->
Expand Down
2 changes: 1 addition & 1 deletion src/emysql_conn_mgr.erl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

-include("emysql.hrl").

-record(state, {pools, lockers = dict:new() :: dict()}).
-record(state, {pools, lockers = dict:new() :: dict:dict()}).

%%====================================================================
%% API
Expand Down
32 changes: 24 additions & 8 deletions src/emysql_conv.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
as_json/1,
as_proplist/1,
as_record/3,
as_record/5,
as_record/4
]).

Expand All @@ -28,15 +29,15 @@ as_proplist(#result_packet{field_list=_Cols,rows=_Rows}) when is_list(_Cols),
as_proplist(#result_packet{field_list=_Cols,rows=_Rows}) when is_list(_Cols),
_Rows =:= [] ->
[];
as_proplist(Res = #result_packet{field_list=Cols,rows=Rows}) when is_list(Cols),
as_proplist(#result_packet{field_list=Cols,rows=Rows} = Res) when is_list(Cols),
is_list(Rows) ->
Fields = emysql:field_names(Res),
[begin
[{K, V} || {K, V} <- lists:zip(Fields, R)]
end || R <- Rows].

%% @see emysql:as_record/1
as_record(Result = #result_packet{}, RecordName, Fields, Fun) when is_atom(RecordName), is_list(Fields), is_function(Fun) ->
as_record(#result_packet{} = Result, RecordName, Fields, Fun, AccIn) when is_atom(RecordName), is_list(Fields), is_function(Fun) ->
Columns = Result#result_packet.field_list,

S = lists:seq(1, length(Columns)),
Expand All @@ -50,13 +51,28 @@ as_record(Result = #result_packet{}, RecordName, Fields, Fun) when is_atom(Recor
end
end,
Fs = [ F(FieldName) || FieldName <- Fields ],
F1 = fun(Row) ->
RecordData = [ Fx(Row) || Fx <- Fs ],
Fun(list_to_tuple([RecordName|RecordData]))
end,
[ F1(Row) || Row <- Result#result_packet.rows ].

as_record(Result = #result_packet{}, RecordName, Fields) when is_atom(RecordName), is_list(Fields) ->
case AccIn of
undefined ->
F1 = fun(Row) ->
RecordData = [ Fx(Row) || Fx <- Fs ],
Fun(list_to_tuple([RecordName|RecordData]))
end,
[ F1(Row) || Row <- Result#result_packet.rows ];
_ ->
lists:foldl(fun(Row, {RsAccIn, FunAccIn}) ->
RecordData = [ Fx(Row) || Fx <- Fs ],
Rec = list_to_tuple([RecordName|RecordData]),
NFunAccIn = Fun(Rec, FunAccIn),
{lists:append(RsAccIn, [Rec]), NFunAccIn}
end, {[], AccIn}, Result#result_packet.rows)
end.

as_record(#result_packet{} = Result, RecordName, Fields, Fun) when is_atom(RecordName), is_list(Fields), is_function(Fun) ->
as_record(Result, RecordName, Fields, Fun, undefined).


as_record(#result_packet{} = Result, RecordName, Fields) when is_atom(RecordName), is_list(Fields) ->
as_record(Result, RecordName, Fields, fun(A) -> A end).

%% @see emysql:as_json/1
Expand Down
Loading