24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-14 22:00:16 +02:00

* src/odbc/ejabberd_odbc.erl: Support for mnesia-like transaction

interface
* src/mod_roster_odbc.erl: Updated to use
ejabberd_odbc:sql_transaction/2

SVN Revision: 434
This commit is contained in:
Alexey Shchepin 2005-11-16 02:59:05 +00:00
parent bbfd58a822
commit 57a6d0e1d3
3 changed files with 224 additions and 155 deletions

View File

@ -1,3 +1,10 @@
2005-11-16 Alexey Shchepin <alexey@sevcom.net>
* src/odbc/ejabberd_odbc.erl: Support for mnesia-like transaction
interface
* src/mod_roster_odbc.erl: Updated to use
ejabberd_odbc:sql_transaction/2
2005-11-12 Alexey Shchepin <alexey@sevcom.net> 2005-11-12 Alexey Shchepin <alexey@sevcom.net>
* src/ejabberd_s2s_out.erl: Fixed invalid behaviour upon * src/ejabberd_s2s_out.erl: Fixed invalid behaviour upon

View File

@ -229,15 +229,16 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
LJID = jlib:jid_tolower(JID1), LJID = jlib:jid_tolower(JID1),
Username = ejabberd_odbc:escape(LUser), Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
case catch ejabberd_odbc:sql_query( F = fun() ->
LServer, {selected,
["select username, jid, nick, subscription, ask, " ["username", "jid", "nick", "subscription",
"server, subscribe, type from rosterusers " "ask", "server", "subscribe", "type"],
Res} =
ejabberd_odbc:sql_query_t(
["select username, jid, nick, subscription, "
"ask, server, subscribe, type from rosterusers "
"where username='", Username, "' " "where username='", Username, "' "
"and jid='", SJID, "'"]) of "and jid='", SJID, "'"]),
{selected, ["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"],
Res} ->
Item = case Res of Item = case Res of
[] -> [] ->
#roster{user = LUser, #roster{user = LUser,
@ -251,23 +252,18 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
Item2 = process_item_els(Item1, Els), Item2 = process_item_els(Item1, Els),
case Item2#roster.subscription of case Item2#roster.subscription of
remove -> remove ->
catch ejabberd_odbc:sql_query( ejabberd_odbc:sql_query_t(
LServer, ["delete from rosterusers "
["begin;"
"delete from rosterusers "
" where username='", Username, "' " " where username='", Username, "' "
" and jid='", SJID, "';" " and jid='", SJID, "';"
"delete from rostergroups " "delete from rostergroups "
" where username='", Username, "' " " where username='", Username, "' "
" and jid='", SJID, "';" " and jid='", SJID, "'"]);
"commit"]);
_ -> _ ->
ItemVals = record_to_string(Item2), ItemVals = record_to_string(Item2),
ItemGroups = groups_to_string(Item2), ItemGroups = groups_to_string(Item2),
catch ejabberd_odbc:sql_query( ejabberd_odbc:sql_query_t(
LServer, ["delete from rosterusers "
["begin;"
"delete from rosterusers "
" where username='", Username, "' " " where username='", Username, "' "
" and jid='", SJID, "';" " and jid='", SJID, "';"
"insert into rosterusers(" "insert into rosterusers("
@ -281,18 +277,21 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
[["insert into rostergroups(" [["insert into rostergroups("
" username, jid, grp) " " username, jid, grp) "
" values ", ItemGroup, ";"] || " values ", ItemGroup, ";"] ||
ItemGroup <- ItemGroups], ItemGroup <- ItemGroups]])
"commit"])
end, end,
push_item(User, LServer, To, Item2), {Item, Item2}
case Item2#roster.subscription of end,
case ejabberd_odbc:sql_transaction(LServer, F) of
{atomic, {OldItem, Item}} ->
push_item(User, LServer, To, Item),
case Item#roster.subscription of
remove -> remove ->
IsTo = case Item#roster.subscription of IsTo = case OldItem#roster.subscription of
both -> true; both -> true;
to -> true; to -> true;
_ -> false _ -> false
end, end,
IsFrom = case Item#roster.subscription of IsFrom = case OldItem#roster.subscription of
both -> true; both -> true;
from -> true; from -> true;
_ -> false _ -> false
@ -300,7 +299,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
if IsTo -> if IsTo ->
ejabberd_router:route( ejabberd_router:route(
jlib:jid_remove_resource(From), jlib:jid_remove_resource(From),
jlib:make_jid(Item#roster.jid), jlib:make_jid(OldItem#roster.jid),
{xmlelement, "presence", {xmlelement, "presence",
[{"type", "unsubscribe"}], [{"type", "unsubscribe"}],
[]}); []});
@ -309,7 +308,7 @@ process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->
if IsFrom -> if IsFrom ->
ejabberd_router:route( ejabberd_router:route(
jlib:jid_remove_resource(From), jlib:jid_remove_resource(From),
jlib:make_jid(Item#roster.jid), jlib:make_jid(OldItem#roster.jid),
{xmlelement, "presence", {xmlelement, "presence",
[{"type", "unsubscribed"}], [{"type", "unsubscribed"}],
[]}); []});
@ -459,18 +458,20 @@ process_subscription(Direction, User, Server, JID1, Type) ->
LJID = jlib:jid_tolower(JID1), LJID = jlib:jid_tolower(JID1),
Username = ejabberd_odbc:escape(LUser), Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)), SJID = ejabberd_odbc:escape(jlib:jid_to_string(LJID)),
Item = case catch ejabberd_odbc:sql_query( F = fun() ->
LServer, Item =
case ejabberd_odbc:sql_query_t(
["select username, jid, nick, subscription, ask, " ["select username, jid, nick, subscription, ask, "
"server, subscribe, type from rosterusers " "server, subscribe, type from rosterusers "
"where username='", Username, "' " "where username='", Username, "' "
"and jid='", SJID, "'"]) of "and jid='", SJID, "'"]) of
{selected, ["username", "jid", "nick", "subscription", "ask", {selected,
["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"], "server", "subscribe", "type"],
[I]} -> [I]} ->
R = raw_to_record(I), R = raw_to_record(I),
Groups = case catch ejabberd_odbc:sql_query( Groups =
LServer, case ejabberd_odbc:sql_query_t(
["select grp from rostergroups " ["select grp from rostergroups "
"where username='", Username, "' " "where username='", Username, "' "
"and jid='", SJID, "'"]) of "and jid='", SJID, "'"]) of
@ -480,7 +481,10 @@ process_subscription(Direction, User, Server, JID1, Type) ->
[] []
end, end,
R#roster{groups = Groups}; R#roster{groups = Groups};
_ -> {selected,
["username", "jid", "nick", "subscription", "ask",
"server", "subscribe", "type"],
[]} ->
#roster{user = LUser, #roster{user = LUser,
jid = LJID} jid = LJID}
end, end,
@ -502,27 +506,27 @@ process_subscription(Direction, User, Server, JID1, Type) ->
Item#roster.ask, Item#roster.ask,
Type) Type)
end, end,
Push = case NewState of case NewState of
none -> none ->
none; {none, AutoReply};
{Subscription, Pending} -> {Subscription, Pending} ->
NewItem = Item#roster{subscription = Subscription, NewItem = Item#roster{subscription = Subscription,
ask = Pending}, ask = Pending},
ItemVals = record_to_string(NewItem), ItemVals = record_to_string(NewItem),
catch ejabberd_odbc:sql_query( ejabberd_odbc:sql_query_t(
LServer, ["delete from rosterusers "
["begin;"
"delete from rosterusers "
" where username='", Username, "' " " where username='", Username, "' "
" and jid='", SJID, "';" " and jid='", SJID, "';"
"insert into rosterusers(" "insert into rosterusers("
" username, jid, nick, " " username, jid, nick, "
" subscription, ask, " " subscription, ask, "
" server, subscribe, type) " " server, subscribe, type) "
" values ", ItemVals, ";" " values ", ItemVals]),
"commit"]), {{push, NewItem}, AutoReply}
{push, NewItem} end
end, end,
case ejabberd_odbc:sql_transaction(LServer, F) of
{atomic, {Push, AutoReply}} ->
case AutoReply of case AutoReply of
none -> none ->
ok; ok;
@ -536,11 +540,15 @@ process_subscription(Direction, User, Server, JID1, Type) ->
{xmlelement, "presence", [{"type", T}], []}) {xmlelement, "presence", [{"type", T}], []})
end, end,
case Push of case Push of
{push, PushItem} -> {push, Item} ->
push_item(User, Server, jlib:make_jid("", Server, ""), PushItem), push_item(User, Server,
jlib:make_jid("", Server, ""), Item),
true; true;
none -> none ->
false false
end;
_ ->
false
end. end.
@ -639,14 +647,15 @@ remove_user(User, Server) ->
LUser = jlib:nodeprep(User), LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server), LServer = jlib:nameprep(Server),
Username = ejabberd_odbc:escape(LUser), Username = ejabberd_odbc:escape(LUser),
catch ejabberd_odbc:sql_query( ejabberd_odbc:sql_transaction(
LServer, LServer,
["begin;" fun() ->
"delete from rosterusers " ejabberd_odbc:sql_query_t(
["delete from rosterusers "
" where username='", Username, "';" " where username='", Username, "';"
"delete from rostergroups " "delete from rostergroups "
" where username='", Username, "';" " where username='", Username, "'"])
"commit"]), end),
ok. ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View File

@ -15,6 +15,8 @@
%% External exports %% External exports
-export([start/1, start_link/1, -export([start/1, start_link/1,
sql_query/2, sql_query/2,
sql_query_t/1,
sql_transaction/2,
escape/1]). escape/1]).
%% gen_server callbacks %% gen_server callbacks
@ -27,6 +29,9 @@
-record(state, {db_ref, db_type}). -record(state, {db_ref, db_type}).
-define(STATE_KEY, ejabberd_odbc_state).
-define(MAX_TRANSACTION_RESTARTS, 10).
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% API %%% API
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
@ -40,6 +45,30 @@ sql_query(Host, Query) ->
gen_server:call(ejabberd_odbc_sup:get_random_pid(Host), gen_server:call(ejabberd_odbc_sup:get_random_pid(Host),
{sql_query, Query}, 60000). {sql_query, Query}, 60000).
sql_transaction(Host, F) ->
gen_server:call(ejabberd_odbc_sup:get_random_pid(Host),
{sql_transaction, F}, 60000).
sql_query_t(Query) ->
State = get(?STATE_KEY),
QRes = sql_query_internal(State, Query),
case QRes of
{error, "No SQL-driver information available."} ->
% workaround for odbc bug
{updated, 0};
{error, _} ->
throw(aborted);
Rs when is_list(Rs) ->
case lists:keymember(error, 1, Rs) of
true ->
throw(aborted);
_ ->
QRes
end;
_ ->
QRes
end.
escape(S) -> escape(S) ->
[case C of [case C of
$\0 -> "\\0"; $\0 -> "\\0";
@ -91,13 +120,13 @@ init([Host]) ->
%% {stop, Reason, State} (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called)
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_call({sql_query, Query}, _From, State) -> handle_call({sql_query, Query}, _From, State) ->
Reply = case State#state.db_type of Reply = sql_query_internal(State, Query),
odbc ->
odbc:sql_query(State#state.db_ref, Query);
pgsql ->
pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query))
end,
{reply, Reply, State}; {reply, Reply, State};
handle_call({sql_transaction, F}, _From, State) ->
Reply = execute_transaction(State, F, ?MAX_TRANSACTION_RESTARTS),
{reply, Reply, State};
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State}.
@ -136,6 +165,30 @@ terminate(_Reason, _State) ->
%%% Internal functions %%% Internal functions
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
sql_query_internal(State, Query) ->
case State#state.db_type of
odbc ->
odbc:sql_query(State#state.db_ref, Query);
pgsql ->
pgsql_to_odbc(pgsql:squery(State#state.db_ref, Query))
end.
execute_transaction(_State, _F, 0) ->
{aborted, restarts_exceeded};
execute_transaction(State, F, NRestarts) ->
put(?STATE_KEY, State),
sql_query_internal(State, "begin"),
case catch F() of
aborted ->
execute_transaction(State, F, NRestarts - 1);
{'EXIT', Reason} ->
sql_query_internal(State, "rollback"),
{aborted, Reason};
Res ->
sql_query_internal(State, "commit"),
{atomic, Res}
end.
pgsql_to_odbc({ok, PGSQLResult}) -> pgsql_to_odbc({ok, PGSQLResult}) ->
case PGSQLResult of case PGSQLResult of
[Item] -> [Item] ->
@ -149,7 +202,7 @@ pgsql_item_to_odbc({"SELECT", Rows, Recs}) ->
[element(1, Row) || Row <- Rows], [element(1, Row) || Row <- Rows],
[list_to_tuple(Rec) || Rec <- Recs]}; [list_to_tuple(Rec) || Rec <- Recs]};
pgsql_item_to_odbc("INSERT " ++ OIDN) -> pgsql_item_to_odbc("INSERT " ++ OIDN) ->
[OID, N] = string:tokens(OIDN, " "), [_OID, N] = string:tokens(OIDN, " "),
{updated, list_to_integer(N)}; {updated, list_to_integer(N)};
pgsql_item_to_odbc("DELETE " ++ N) -> pgsql_item_to_odbc("DELETE " ++ N) ->
{updated, list_to_integer(N)}; {updated, list_to_integer(N)};