25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-20 16:15:59 +01:00

Merge pull request #486 from saa/sqlite_support

Add SQLite support
This commit is contained in:
Evgeny Khramtsov 2015-04-08 14:34:56 +03:00
commit 5a820aca40
18 changed files with 538 additions and 11 deletions

3
.gitignore vendored
View File

@ -4,6 +4,8 @@
*~ *~
\#*# \#*#
.#* .#*
.edts
*.dump
/Makefile /Makefile
/config.log /config.log
/config.status /config.status
@ -38,3 +40,4 @@ XmppAddr.hrl
/dialyzer/ /dialyzer/
/test/*.beam /test/*.beam
/logs/ /logs/
/priv/sql

View File

@ -11,7 +11,7 @@ before_install:
- sudo apt-get -qq update - sudo apt-get -qq update
install: install:
- sudo apt-get -qq install libexpat1-dev libyaml-dev libpam0g-dev - sudo apt-get -qq install libexpat1-dev libyaml-dev libpam0g-dev libsqlite3-dev
before_script: before_script:
- mysql -u root -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';" - mysql -u root -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';"

View File

@ -43,6 +43,9 @@ SODIR = $(PRIVDIR)/lib
# /usr/lib/ejabberd/priv/msgs # /usr/lib/ejabberd/priv/msgs
MSGSDIR = $(PRIVDIR)/msgs MSGSDIR = $(PRIVDIR)/msgs
# /usr/lib/ejabberd/priv/sql
SQLDIR = $(PRIVDIR)/sql
# /var/lib/ejabberd/ # /var/lib/ejabberd/
SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
@ -176,11 +179,16 @@ install: all
$(INSTALL) -d $(SODIR) $(INSTALL) -d $(SODIR)
$(INSTALL) -m 644 $(DLLs) $(SODIR) $(INSTALL) -m 644 $(DLLs) $(SODIR)
-[ -f $(SODIR)/jiffy.so ] && (cd $(PRIVDIR); ln -s lib/jiffy.so; true) -[ -f $(SODIR)/jiffy.so ] && (cd $(PRIVDIR); ln -s lib/jiffy.so; true)
-[ -f $(SODIR)/sqlite3_drv.so ] && (cd $(PRIVDIR); ln -s lib/sqlite3_drv.so; true)
# #
# Translated strings # Translated strings
$(INSTALL) -d $(MSGSDIR) $(INSTALL) -d $(MSGSDIR)
$(INSTALL) -m 644 priv/msgs/*.msg $(MSGSDIR) $(INSTALL) -m 644 priv/msgs/*.msg $(MSGSDIR)
# #
# Copy lite.sql
-[ -d deps/sqlite3 ] && $(INSTALL) -d $(SQLDIR)
-[ -d deps/sqlite3 ] && $(INSTALL) -m 644 sql/lite.sql $(SQLDIR)
#
# Spool directory # Spool directory
$(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR) $(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR)
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT) $(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
@ -229,6 +237,8 @@ uninstall-binary:
rm -fr $(SODIR) rm -fr $(SODIR)
rm -f $(MSGSDIR)/*.msgs rm -f $(MSGSDIR)/*.msgs
rm -fr $(MSGSDIR) rm -fr $(MSGSDIR)
rm -f $(SQLDIR)/*.sql
rm -fr $(SQLDIR)
rm -fr $(PRIVDIR) rm -fr $(PRIVDIR)
rm -fr $(EJABBERDDIR) rm -fr $(EJABBERDDIR)
@ -310,6 +320,7 @@ test:
@echo "************************** NOTICE ***************************************" @echo "************************** NOTICE ***************************************"
@cat test/README @cat test/README
@echo "*************************************************************************" @echo "*************************************************************************"
@cd priv && ln -sf ../sql
$(REBAR) skip_deps=true ct $(REBAR) skip_deps=true ct
quicktest: quicktest:

View File

@ -106,10 +106,10 @@ AC_ARG_ENABLE(mssql,
esac],[db_type=generic]) esac],[db_type=generic])
AC_ARG_ENABLE(all, AC_ARG_ENABLE(all,
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-riak --enable-redis --enable-json --enable-elixir --enable-iconv --enable-debug --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])], [AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-sqlite --enable-pam --enable-zlib --enable-riak --enable-redis --enable-json --enable-elixir --enable-iconv --enable-debug --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])],
[case "${enableval}" in [case "${enableval}" in
yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true riak=true redis=true json=true elixir=true iconv=true debug=true lager=true tools=true ;; yes) nif=true odbc=true mysql=true pgsql=true sqlite=true pam=true zlib=true riak=true redis=true json=true elixir=true iconv=true debug=true lager=true tools=true ;;
no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false riak=false redis=false json=false elixir=false iconv=false debug=false lager=false tools=false ;; no) nif=false odbc=false mysql=false pgsql=false sqlite=false pam=false zlib=false riak=false redis=false json=false elixir=false iconv=false debug=false lager=false tools=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
esac],[]) esac],[])
@ -153,6 +153,14 @@ AC_ARG_ENABLE(pgsql,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;;
esac],[if test "x$pgsql" = "x"; then pgsql=false; fi]) esac],[if test "x$pgsql" = "x"; then pgsql=false; fi])
AC_ARG_ENABLE(sqlite,
[AC_HELP_STRING([--enable-sqlite], [enable SQLite support (default: no)])],
[case "${enableval}" in
yes) sqlite=true ;;
no) sqlite=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-sqlite) ;;
esac],[if test "x$sqlite" = "x"; then sqlite=false; fi])
AC_ARG_ENABLE(pam, AC_ARG_ENABLE(pam,
[AC_HELP_STRING([--enable-pam], [enable PAM support (default: no)])], [AC_HELP_STRING([--enable-pam], [enable PAM support (default: no)])],
[case "${enableval}" in [case "${enableval}" in
@ -254,6 +262,7 @@ AC_SUBST(db_type)
AC_SUBST(odbc) AC_SUBST(odbc)
AC_SUBST(mysql) AC_SUBST(mysql)
AC_SUBST(pgsql) AC_SUBST(pgsql)
AC_SUBST(sqlite)
AC_SUBST(pam) AC_SUBST(pam)
AC_SUBST(zlib) AC_SUBST(zlib)
AC_SUBST(riak) AC_SUBST(riak)

View File

@ -343,6 +343,12 @@ auth_method: internal
## ##
## pgsql_users_number_estimate: true ## pgsql_users_number_estimate: true
##
## SQLite:
##
## odbc_type: sqlite
## odbc_database: "/path/to/database.db"
## ##
## ODBC compatible or MSSQL server: ## ODBC compatible or MSSQL server:
## ##

View File

@ -31,6 +31,12 @@
-define(MSGS_DIR, filename:join(["priv", "msgs"])). -define(MSGS_DIR, filename:join(["priv", "msgs"])).
-define(SQL_DIR, filename:join(["priv", "sql"])).
-define(SQLITE_DB, ejabberd_sqlite).
-define(DEFAULT_SQLITE_DB_PATH, <<"/tmp/ejabberd.db">>).
-define(CONFIG_PATH, <<"ejabberd.cfg">>). -define(CONFIG_PATH, <<"ejabberd.cfg">>).
-define(LOG_PATH, <<"ejabberd.log">>). -define(LOG_PATH, <<"ejabberd.log">>).

View File

@ -90,6 +90,8 @@ CfgDeps = lists:flatmap(
[{p1_mysql, ".*", {git, "git://github.com/processone/mysql"}}]; [{p1_mysql, ".*", {git, "git://github.com/processone/mysql"}}];
({pgsql, true}) -> ({pgsql, true}) ->
[{p1_pgsql, ".*", {git, "git://github.com/processone/pgsql"}}]; [{p1_pgsql, ".*", {git, "git://github.com/processone/pgsql"}}];
({sqlite, true}) ->
[{sqlite3, ".*", {git, "git://github.com/alexeyr/erlang-sqlite3"}}];
({pam, true}) -> ({pam, true}) ->
[{p1_pam, ".*", {git, "git://github.com/processone/epam"}}]; [{p1_pam, ".*", {git, "git://github.com/processone/epam"}}];
({zlib, true}) -> ({zlib, true}) ->

272
sql/lite.sql Normal file
View File

@ -0,0 +1,272 @@
--
-- ejabberd, Copyright (C) 2002-2015 ProcessOne
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation; either version 2 of the
-- License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--
CREATE TABLE users (
username text PRIMARY KEY,
password text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE last (
username text PRIMARY KEY,
seconds text NOT NULL,
state text NOT NULL
);
CREATE TABLE rosterusers (
username text NOT NULL,
jid text NOT NULL,
nick text NOT NULL,
subscription character(1) NOT NULL,
ask character(1) NOT NULL,
askmessage text NOT NULL,
server character(1) NOT NULL,
subscribe text,
type text,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers (username, jid);
CREATE INDEX i_rosteru_username ON rosterusers (username);
CREATE INDEX i_rosteru_jid ON rosterusers (jid);
CREATE TABLE rostergroups (
username text NOT NULL,
jid text NOT NULL,
grp text NOT NULL
);
CREATE INDEX pk_rosterg_user_jid ON rostergroups (username, jid);
CREATE TABLE sr_group (
name text NOT NULL,
opts text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE sr_user (
jid text NOT NULL,
grp text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user (jid, grp);
CREATE INDEX i_sr_user_jid ON sr_user (jid);
CREATE INDEX i_sr_user_grp ON sr_user (grp);
CREATE TABLE spool (
username text NOT NULL,
xml text NOT NULL,
seq INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX i_despool ON spool (username);
CREATE TABLE vcard (
username text PRIMARY KEY,
vcard text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE vcard_xupdate (
username text PRIMARY KEY,
hash text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE vcard_search (
username text NOT NULL,
lusername text PRIMARY KEY,
fn text NOT NULL,
lfn text NOT NULL,
family text NOT NULL,
lfamily text NOT NULL,
given text NOT NULL,
lgiven text NOT NULL,
middle text NOT NULL,
lmiddle text NOT NULL,
nickname text NOT NULL,
lnickname text NOT NULL,
bday text NOT NULL,
lbday text NOT NULL,
ctry text NOT NULL,
lctry text NOT NULL,
locality text NOT NULL,
llocality text NOT NULL,
email text NOT NULL,
lemail text NOT NULL,
orgname text NOT NULL,
lorgname text NOT NULL,
orgunit text NOT NULL,
lorgunit text NOT NULL
);
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn);
CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily);
CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven);
CREATE INDEX i_vcard_search_lmiddle ON vcard_search(lmiddle);
CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname);
CREATE INDEX i_vcard_search_lbday ON vcard_search(lbday);
CREATE INDEX i_vcard_search_lctry ON vcard_search(lctry);
CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality);
CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail);
CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
CREATE TABLE privacy_default_list (
username text PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE privacy_list (
username text NOT NULL,
name text NOT NULL,
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX i_privacy_list_username ON privacy_list (username);
CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list (username, name);
CREATE TABLE privacy_list_data (
id bigint REFERENCES privacy_list(id) ON DELETE CASCADE,
t character(1) NOT NULL,
value text NOT NULL,
action character(1) NOT NULL,
ord NUMERIC NOT NULL,
match_all boolean NOT NULL,
match_iq boolean NOT NULL,
match_message boolean NOT NULL,
match_presence_in boolean NOT NULL,
match_presence_out boolean NOT NULL
);
CREATE TABLE private_storage (
username text NOT NULL,
namespace text NOT NULL,
data text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX i_private_storage_username ON private_storage (username);
CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage (username, namespace);
CREATE TABLE roster_version (
username text PRIMARY KEY,
version text NOT NULL
);
CREATE TABLE pubsub_node (
host text,
node text,
parent text,
type text,
nodeid INTEGER PRIMARY KEY AUTOINCREMENT
);
CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent);
CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node);
CREATE TABLE pubsub_node_option (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
name text,
val text
);
CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid);
CREATE TABLE pubsub_node_owner (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
owner text
);
CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid);
CREATE TABLE pubsub_state (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
jid text,
affiliation character(1),
subscriptions text,
stateid INTEGER PRIMARY KEY AUTOINCREMENT
);
CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid);
CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid);
CREATE TABLE pubsub_item (
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
itemid text,
publisher text,
creation text,
modification text,
payload text
);
CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid);
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid);
CREATE TABLE pubsub_subscription_opt (
subid text,
opt_name varchar(32),
opt_value text
);
CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt (subid, opt_name);
CREATE TABLE muc_room (
name text NOT NULL,
host text NOT NULL,
opts text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host);
CREATE TABLE muc_registered (
jid text NOT NULL,
host text NOT NULL,
nick text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX i_muc_registered_nick ON muc_registered (nick);
CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host);
CREATE TABLE irc_custom (
jid text NOT NULL,
host text NOT NULL,
data text NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host);
CREATE TABLE motd (
username text PRIMARY KEY,
xml text,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE caps_features (
node text NOT NULL,
subnode text NOT NULL,
feature text,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode);

View File

@ -58,7 +58,7 @@
-record(state, -record(state,
{db_ref = self() :: pid(), {db_ref = self() :: pid(),
db_type = odbc :: pgsql | mysql | odbc, db_type = odbc :: pgsql | mysql | sqlite | odbc,
start_interval = 0 :: non_neg_integer(), start_interval = 0 :: non_neg_integer(),
host = <<"">> :: binary(), host = <<"">> :: binary(),
max_pending_requests_len :: non_neg_integer(), max_pending_requests_len :: non_neg_integer(),
@ -224,7 +224,8 @@ init([Host, StartInterval]) ->
connecting(connect, #state{host = Host} = State) -> connecting(connect, #state{host = Host} = State) ->
ConnectRes = case db_opts(Host) of ConnectRes = case db_opts(Host) of
[mysql | Args] -> apply(fun mysql_connect/5, Args); [mysql | Args] -> apply(fun mysql_connect/5, Args);
[pgsql | Args] -> apply(fun pgsql_connect/5, Args); [pgsql | Args] -> apply(fun pgsql_connect/5, Args);
[sqlite | Args] -> apply(fun sqlite_connect/1, Args);
[odbc | Args] -> apply(fun odbc_connect/1, Args) [odbc | Args] -> apply(fun odbc_connect/1, Args)
end, end,
{_, PendingRequests} = State#state.pending_requests, {_, PendingRequests} = State#state.pending_requests,
@ -327,8 +328,9 @@ handle_info(Info, StateName, State) ->
terminate(_Reason, _StateName, State) -> terminate(_Reason, _StateName, State) ->
ejabberd_odbc_sup:remove_pid(State#state.host, self()), ejabberd_odbc_sup:remove_pid(State#state.host, self()),
case State#state.db_type of case State#state.db_type of
mysql -> catch p1_mysql_conn:stop(State#state.db_ref); mysql -> catch p1_mysql_conn:stop(State#state.db_ref);
_ -> ok sqlite -> catch sqlite3:close(?SQLITE_DB);
_ -> ok
end, end,
ok. ok.
@ -456,7 +458,9 @@ sql_query_internal(Query) ->
[{timeout, (?TRANSACTION_TIMEOUT) - 1000}, [{timeout, (?TRANSACTION_TIMEOUT) - 1000},
{result_type, binary}])), {result_type, binary}])),
%% ?INFO_MSG("MySQL, Received result~n~p~n", [R]), %% ?INFO_MSG("MySQL, Received result~n~p~n", [R]),
R R;
sqlite ->
sqlite_to_odbc(sqlite3:sql_exec(?SQLITE_DB, Query))
end, end,
case Res of case Res of
{error, <<"No SQL-driver information available.">>} -> {error, <<"No SQL-driver information available.">>} ->
@ -488,6 +492,34 @@ odbc_connect(SQLServer) ->
ejabberd:start_app(odbc), ejabberd:start_app(odbc),
odbc:connect(binary_to_list(SQLServer), [{scrollable_cursors, off}]). odbc:connect(binary_to_list(SQLServer), [{scrollable_cursors, off}]).
%% == Native SQLite code
%% part of init/1
%% Open a database connection to SQLite
sqlite_connect(DB) ->
process_flag(trap_exit, true),
case sqlite3:open(?SQLITE_DB, [{file, binary_to_list(DB)}]) of
{ok, Ref} ->
{ok, Ref};
{error, {already_started, Ref}} ->
{ok, Ref};
{error, Reason} ->
{error, Reason}
end.
%% Convert SQLite query result to Erlang ODBC result formalism
sqlite_to_odbc(ok) ->
{updated, sqlite3:changes(?SQLITE_DB)};
sqlite_to_odbc({rowid, _}) ->
{updated, sqlite3:changes(?SQLITE_DB)};
sqlite_to_odbc([{columns, Columns}, {rows, Rows}]) ->
{selected, [list_to_binary(C) || C <- Columns], [tuple_to_list(Row) || Row <- Rows]};
sqlite_to_odbc({error, _Code, Reason}) ->
{error, Reason};
sqlite_to_odbc(_) ->
{updated, undefined}.
%% == Native PostgreSQL code %% == Native PostgreSQL code
%% part of init/1 %% part of init/1
@ -585,6 +617,7 @@ db_opts(Host) ->
Type = ejabberd_config:get_option({odbc_type, Host}, Type = ejabberd_config:get_option({odbc_type, Host},
fun(mysql) -> mysql; fun(mysql) -> mysql;
(pgsql) -> pgsql; (pgsql) -> pgsql;
(sqlite) -> sqlite;
(odbc) -> odbc (odbc) -> odbc
end, odbc), end, odbc),
Server = ejabberd_config:get_option({odbc_server, Host}, Server = ejabberd_config:get_option({odbc_server, Host},
@ -593,6 +626,11 @@ db_opts(Host) ->
case Type of case Type of
odbc -> odbc ->
[odbc, Server]; [odbc, Server];
sqlite ->
DB = ejabberd_config:get_option({odbc_database, Host},
fun iolist_to_binary/1,
?DEFAULT_SQLITE_DB_PATH),
[sqlite, DB];
_ -> _ ->
Port = ejabberd_config:get_option( Port = ejabberd_config:get_option(
{odbc_port, Host}, {odbc_port, Host},

View File

@ -67,6 +67,22 @@ init([Host]) ->
{odbc_start_interval, Host}, {odbc_start_interval, Host},
fun(I) when is_integer(I), I>0 -> I end, fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_ODBC_START_INTERVAL), ?DEFAULT_ODBC_START_INTERVAL),
Type = ejabberd_config:get_option({odbc_type, Host},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
(sqlite) -> sqlite;
(odbc) -> odbc
end, odbc),
case Type of
sqlite ->
DB = ejabberd_config:get_option({odbc_database, Host},
fun iolist_to_binary/1,
?DEFAULT_SQLITE_DB_PATH),
check_sqlite_db(DB);
_ ->
ok
end,
{ok, {ok,
{{one_for_one, PoolSize * 10, 1}, {{one_for_one, PoolSize * 10, 1},
lists:map(fun (I) -> lists:map(fun (I) ->
@ -113,5 +129,75 @@ transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts); transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) -> transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts); transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
transform_options({odbc_server, {sqlite, DB}}, Opts) ->
transform_options({odbc_server, {sqlite, DB}}, Opts);
transform_options(Opt, Opts) -> transform_options(Opt, Opts) ->
[Opt|Opts]. [Opt|Opts].
check_sqlite_db(DB) ->
process_flag(trap_exit, true),
Ret = case sqlite3:open(?SQLITE_DB, [{file, binary_to_list(DB)}]) of
{ok, _Ref} -> ok;
{error, {already_started, _Ref}} -> ok;
{error, R} -> {error, R}
end,
case Ret of
ok ->
case sqlite3:list_tables(?SQLITE_DB) of
[] ->
create_sqlite_tables(),
sqlite3:close(?SQLITE_DB),
ok;
[_H | _] ->
ok
end;
{error, Reason} ->
?INFO_MSG("Failed open sqlite database, reason ~p", [Reason])
end.
create_sqlite_tables() ->
SqlDir = case code:priv_dir(ejabberd) of
{error, _} ->
?SQL_DIR;
PrivDir ->
filename:join(PrivDir, "sql")
end,
File = filename:join(SqlDir, "lite.sql"),
case file:open(File, [read, binary]) of
{ok, Fd} ->
Qs = read_lines(Fd, File, []),
ok = sqlite3:sql_exec(?SQLITE_DB, "begin"),
[ok = sqlite3:sql_exec(?SQLITE_DB, Q) || Q <- Qs],
ok = sqlite3:sql_exec(?SQLITE_DB, "commit");
{error, Reason} ->
?INFO_MSG("Not found sqlite database schema, reason: ~p", [Reason]),
ok
end.
read_lines(Fd, File, Acc) ->
case file:read_line(Fd) of
{ok, Line} ->
NewAcc = case str:strip(str:strip(Line, both, $\r), both, $\n) of
<<"--", _/binary>> ->
Acc;
<<>> ->
Acc;
_ ->
[Line|Acc]
end,
read_lines(Fd, File, NewAcc);
eof ->
QueryList = str:tokens(list_to_binary(lists:reverse(Acc)), <<";">>),
lists:flatmap(
fun(Query) ->
case str:strip(str:strip(Query, both, $\r), both, $\n) of
<<>> ->
[];
Q ->
[<<Q/binary, $;>>]
end
end, QueryList);
{error, _} = Err ->
?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
[]
end.

View File

@ -74,10 +74,12 @@ needs_odbc(Host) ->
case ejabberd_config:get_option({odbc_type, LHost}, case ejabberd_config:get_option({odbc_type, LHost},
fun(mysql) -> mysql; fun(mysql) -> mysql;
(pgsql) -> pgsql; (pgsql) -> pgsql;
(sqlite) -> sqlite;
(odbc) -> odbc (odbc) -> odbc
end, undefined) of end, undefined) of
mysql -> {true, p1_mysql}; mysql -> {true, p1_mysql};
pgsql -> {true, p1_pgsql}; pgsql -> {true, p1_pgsql};
sqlite -> {true, sqlite3};
odbc -> {true, odbc}; odbc -> {true, odbc};
undefined -> false undefined -> false
end. end.

View File

@ -940,8 +940,10 @@ ip_to_list(IP) ->
binary_to_atom(Bin) -> binary_to_atom(Bin) ->
erlang:binary_to_atom(Bin, utf8). erlang:binary_to_atom(Bin, utf8).
binary_to_integer(Bin) -> binary_to_integer(Bin) when is_binary(Bin) ->
list_to_integer(binary_to_list(Bin)). list_to_integer(binary_to_list(Bin));
binary_to_integer(Bin) when is_integer(Bin) ->
Bin.
binary_to_integer(Bin, Base) -> binary_to_integer(Bin, Base) ->
list_to_integer(binary_to_list(Bin), Base). list_to_integer(binary_to_list(Bin), Base).

View File

@ -264,6 +264,7 @@ users_number(LServer) ->
Type = ejabberd_config:get_option({odbc_type, LServer}, Type = ejabberd_config:get_option({odbc_type, LServer},
fun(pgsql) -> pgsql; fun(pgsql) -> pgsql;
(mysql) -> mysql; (mysql) -> mysql;
(sqlite) -> sqlite;
(odbc) -> odbc (odbc) -> odbc
end, odbc), end, odbc),
case Type of case Type of

View File

@ -35,6 +35,7 @@ init_per_suite(Config) ->
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]), LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
{ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])), {ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])),
{ok, _} = ldap_srv:start(LDIFFile), {ok, _} = ldap_srv:start(LDIFFile),
file:delete("/tmp/ejabberd_test.db"),
ok = application:start(ejabberd), ok = application:start(ejabberd),
NewConfig. NewConfig.
@ -65,6 +66,14 @@ init_per_group(pgsql, Config) ->
Err -> Err ->
{skip, {pgsql_not_available, Err}} {skip, {pgsql_not_available, Err}}
end; end;
init_per_group(sqlite, Config) ->
case catch ejabberd_odbc:sql_query(?SQLITE_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?SQLITE_VHOST),
set_opt(server, ?SQLITE_VHOST, Config);
Err ->
{skip, {sqlite_not_available, Err}}
end;
init_per_group(ldap, Config) -> init_per_group(ldap, Config) ->
set_opt(server, ?LDAP_VHOST, Config); set_opt(server, ?LDAP_VHOST, Config);
init_per_group(extauth, Config) -> init_per_group(extauth, Config) ->
@ -88,6 +97,8 @@ end_per_group(mysql, _Config) ->
ok; ok;
end_per_group(pgsql, _Config) -> end_per_group(pgsql, _Config) ->
ok; ok;
end_per_group(sqlite, _Config) ->
ok;
end_per_group(no_db, _Config) -> end_per_group(no_db, _Config) ->
ok; ok;
end_per_group(ldap, _Config) -> end_per_group(ldap, _Config) ->
@ -300,6 +311,7 @@ groups() ->
{mnesia, [sequence], db_tests(mnesia)}, {mnesia, [sequence], db_tests(mnesia)},
{mysql, [sequence], db_tests(mysql)}, {mysql, [sequence], db_tests(mysql)},
{pgsql, [sequence], db_tests(pgsql)}, {pgsql, [sequence], db_tests(pgsql)},
{sqlite, [sequence], db_tests(sqlite)},
{riak, [sequence], db_tests(riak)}]. {riak, [sequence], db_tests(riak)}].
all() -> all() ->
@ -308,6 +320,7 @@ all() ->
{group, mnesia}, {group, mnesia},
{group, mysql}, {group, mysql},
{group, pgsql}, {group, pgsql},
{group, sqlite},
{group, extauth}, {group, extauth},
{group, riak}, {group, riak},
stop_ejabberd]. stop_ejabberd].
@ -1631,6 +1644,8 @@ socks5_recv(Sock, Data) ->
%%%=================================================================== %%%===================================================================
%%% SQL stuff %%% SQL stuff
%%%=================================================================== %%%===================================================================
create_sql_tables(sqlite, _BaseDir) ->
ok;
create_sql_tables(Type, BaseDir) -> create_sql_tables(Type, BaseDir) ->
{VHost, File} = case Type of {VHost, File} = case Type of
mysql -> mysql ->

View File

@ -3,6 +3,7 @@
"mnesia.localhost", "mnesia.localhost",
"mysql.localhost", "mysql.localhost",
"pgsql.localhost", "pgsql.localhost",
"sqlite.localhost",
"extauth.localhost", "extauth.localhost",
"ldap.localhost"]}. "ldap.localhost"]}.
{define_macro, 'CERTFILE', "cert.pem"}. {define_macro, 'CERTFILE', "cert.pem"}.
@ -120,6 +121,25 @@
{mod_roster, [{db_type, odbc}]}, {mod_roster, [{db_type, odbc}]},
{mod_vcard, [{db_type, odbc}]}]} {mod_vcard, [{db_type, odbc}]}]}
]}. ]}.
{host_config, "sqlite.localhost",
[{auth_method, odbc},
{odbc_pool_size, 1},
{odbc_server, {sqlite, "/tmp/ejabberd_test.db"}},
{{add, modules}, [{mod_announce, [{db_type, odbc}]},
{mod_blocking, [{db_type, odbc}]},
{mod_caps, [{db_type, odbc}]},
{mod_last, [{db_type, odbc}]},
{mod_muc, [{db_type, odbc}]},
{mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true},
{last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]},
{mod_roster, [{db_type, odbc}]},
{mod_vcard, [{db_type, odbc}]}]}
]}.
{host_config, "ldap.localhost", {host_config, "ldap.localhost",
[{auth_method, ldap}, [{auth_method, ldap},
{ldap_servers, ["localhost"]}, {ldap_servers, ["localhost"]},

View File

@ -51,6 +51,57 @@ host_config:
welcome_message: welcome_message:
subject: "Welcome!" subject: "Welcome!"
body: "Hi. body: "Hi.
Welcome to this XMPP server."
mod_stats: []
mod_time: []
mod_version: []
"sqlite.localhost":
odbc_type: sqlite
odbc_database: "/tmp/ejabberd_test.db"
auth_method: odbc
modules:
mod_announce:
db_type: odbc
access: local
mod_blocking:
db_type: odbc
mod_caps:
db_type: odbc
mod_last:
db_type: odbc
mod_muc:
db_type: odbc
mod_offline:
db_type: odbc
mod_privacy:
db_type: odbc
mod_private:
db_type: odbc
mod_pubsub_odbc:
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false
plugins:
- "flat"
- "hometree"
- "pep"
mod_roster:
versioning: true
store_current_id: true
db_type: odbc
mod_vcard:
db_type: odbc
mod_vcard_xupdate:
db_type: odbc
mod_adhoc: []
mod_configure: []
mod_disco: []
mod_ping: []
mod_proxy65: []
mod_register:
welcome_message:
subject: "Welcome!"
body: "Hi.
Welcome to this XMPP server." Welcome to this XMPP server."
mod_stats: [] mod_stats: []
mod_time: [] mod_time: []
@ -242,6 +293,7 @@ hosts:
- "extauth.localhost" - "extauth.localhost"
- "ldap.localhost" - "ldap.localhost"
- "riak.localhost" - "riak.localhost"
- "sqlite.localhost"
access: access:
announce: announce:
admin: allow admin: allow

View File

@ -59,6 +59,7 @@
-define(MNESIA_VHOST, <<"mnesia.localhost">>). -define(MNESIA_VHOST, <<"mnesia.localhost">>).
-define(MYSQL_VHOST, <<"mysql.localhost">>). -define(MYSQL_VHOST, <<"mysql.localhost">>).
-define(PGSQL_VHOST, <<"pgsql.localhost">>). -define(PGSQL_VHOST, <<"pgsql.localhost">>).
-define(SQLITE_VHOST, <<"sqlite.localhost">>).
-define(LDAP_VHOST, <<"ldap.localhost">>). -define(LDAP_VHOST, <<"ldap.localhost">>).
-define(EXTAUTH_VHOST, <<"extauth.localhost">>). -define(EXTAUTH_VHOST, <<"extauth.localhost">>).
-define(RIAK_VHOST, <<"riak.localhost">>). -define(RIAK_VHOST, <<"riak.localhost">>).

View File

@ -23,6 +23,7 @@
{odbc, @odbc@}. {odbc, @odbc@}.
{mysql, @mysql@}. {mysql, @mysql@}.
{pgsql, @pgsql@}. {pgsql, @pgsql@}.
{sqlite, @sqlite@}.
{pam, @pam@}. {pam, @pam@}.
{zlib, @zlib@}. {zlib, @zlib@}.
{riak, @riak@}. {riak, @riak@}.