aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Abramyan <sa.abramyan@gmail.com>2015-03-16 21:53:19 +0300
committerSergey Abramyan <sa.abramyan@gmail.com>2015-03-20 01:10:47 +0300
commit5ae01e8bb40915371052b7f46eeb2f92b95c2e27 (patch)
tree8cb5aeca69c1e1adc91db47bdc3c1a4f754d1c80
parentAdd missing list_to_binary call (diff)
Add SQLite support
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml2
-rw-r--r--Makefile.in11
-rw-r--r--configure.ac15
-rw-r--r--ejabberd.yml.example6
-rw-r--r--include/ejabberd.hrl6
-rw-r--r--rebar.config.script2
-rw-r--r--sql/lite.sql272
-rw-r--r--src/ejabberd_odbc.erl48
-rw-r--r--src/ejabberd_odbc_sup.erl86
-rw-r--r--src/ejabberd_rdbms.erl2
-rw-r--r--src/odbc_queries.erl1
-rw-r--r--test/ejabberd_SUITE.erl15
-rw-r--r--test/ejabberd_SUITE_data/ejabberd.cfg20
-rw-r--r--test/ejabberd_SUITE_data/ejabberd.yml52
-rw-r--r--test/suite.hrl1
-rw-r--r--vars.config.in1
17 files changed, 534 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
index c72fcdc36..8964ea72d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@
*~
\#*#
.#*
+.edts
+*.dump
/Makefile
/config.log
/config.status
@@ -38,3 +40,4 @@
/dialyzer/
/test/*.beam
/logs/
+/priv/sql
diff --git a/.travis.yml b/.travis.yml
index 1dd745a77..f68795459 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@ before_install:
- sudo apt-get -qq update
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:
- mysql -u root -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';"
diff --git a/Makefile.in b/Makefile.in
index 90a116ac9..87ace381a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -43,6 +43,9 @@ SODIR = $(PRIVDIR)/lib
# /usr/lib/ejabberd/priv/msgs
MSGSDIR = $(PRIVDIR)/msgs
+# /usr/lib/ejabberd/priv/sql
+SQLDIR = $(PRIVDIR)/sql
+
# /var/lib/ejabberd/
SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
@@ -176,11 +179,16 @@ install: all
$(INSTALL) -d $(SODIR)
$(INSTALL) -m 644 $(DLLs) $(SODIR)
-[ -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
$(INSTALL) -d $(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
$(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR)
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
@@ -229,6 +237,8 @@ uninstall-binary:
rm -fr $(SODIR)
rm -f $(MSGSDIR)/*.msgs
rm -fr $(MSGSDIR)
+ rm -f $(SQLDIR)/*.sql
+ rm -fr $(SQLDIR)
rm -fr $(PRIVDIR)
rm -fr $(EJABBERDDIR)
@@ -310,6 +320,7 @@ test:
@echo "************************** NOTICE ***************************************"
@cat test/README
@echo "*************************************************************************"
+ @cd priv && ln -sf ../sql
$(REBAR) skip_deps=true ct
quicktest:
diff --git a/configure.ac b/configure.ac
index 6bef15864..a70530990 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,10 +106,10 @@ AC_ARG_ENABLE(mssql,
esac],[db_type=generic])
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
- 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 ;;
- 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 ;;
+ 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 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) ;;
esac],[])
@@ -153,6 +153,14 @@ AC_ARG_ENABLE(pgsql,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-pgsql) ;;
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_HELP_STRING([--enable-pam], [enable PAM support (default: no)])],
[case "${enableval}" in
@@ -254,6 +262,7 @@ AC_SUBST(db_type)
AC_SUBST(odbc)
AC_SUBST(mysql)
AC_SUBST(pgsql)
+AC_SUBST(sqlite)
AC_SUBST(pam)
AC_SUBST(zlib)
AC_SUBST(riak)
diff --git a/ejabberd.yml.example b/ejabberd.yml.example
index 3d69df236..a35bb1a51 100644
--- a/ejabberd.yml.example
+++ b/ejabberd.yml.example
@@ -344,6 +344,12 @@ auth_method: internal
## pgsql_users_number_estimate: true
##
+## SQLite:
+##
+## odbc_type: sqlite
+## odbc_database: "/path/to/database.db"
+
+##
## ODBC compatible or MSSQL server:
##
## odbc_type: odbc
diff --git a/include/ejabberd.hrl b/include/ejabberd.hrl
index 0e5ba1b20..c5f8dffe3 100644
--- a/include/ejabberd.hrl
+++ b/include/ejabberd.hrl
@@ -31,6 +31,12 @@
-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(LOG_PATH, <<"ejabberd.log">>).
diff --git a/rebar.config.script b/rebar.config.script
index 69964852b..a9754b061 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -87,6 +87,8 @@ CfgDeps = lists:flatmap(
[{p1_mysql, ".*", {git, "git://github.com/processone/mysql"}}];
({pgsql, true}) ->
[{p1_pgsql, ".*", {git, "git://github.com/processone/pgsql"}}];
+ ({sqlite, true}) ->
+ [{sqlite3, ".*", {git, "git://github.com/alexeyr/erlang-sqlite3"}}];
({pam, true}) ->
[{p1_pam, ".*", {git, "git://github.com/processone/epam"}}];
({zlib, true}) ->
diff --git a/sql/lite.sql b/sql/lite.sql
new file mode 100644
index 000000000..c11f7524b
--- /dev/null
+++ b/sql/lite.sql
@@ -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 SERIAL,
+ 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 SERIAL UNIQUE,
+ 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 SERIAL UNIQUE
+);
+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 SERIAL UNIQUE
+);
+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);
diff --git a/src/ejabberd_odbc.erl b/src/ejabberd_odbc.erl
index 5828912d5..278bb760f 100644
--- a/src/ejabberd_odbc.erl
+++ b/src/ejabberd_odbc.erl
@@ -58,7 +58,7 @@
-record(state,
{db_ref = self() :: pid(),
- db_type = odbc :: pgsql | mysql | odbc,
+ db_type = odbc :: pgsql | mysql | sqlite | odbc,
start_interval = 0 :: non_neg_integer(),
host = <<"">> :: binary(),
max_pending_requests_len :: non_neg_integer(),
@@ -224,7 +224,8 @@ init([Host, StartInterval]) ->
connecting(connect, #state{host = Host} = State) ->
ConnectRes = case db_opts(Host) of
[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)
end,
{_, PendingRequests} = State#state.pending_requests,
@@ -327,8 +328,9 @@ handle_info(Info, StateName, State) ->
terminate(_Reason, _StateName, State) ->
ejabberd_odbc_sup:remove_pid(State#state.host, self()),
case State#state.db_type of
- mysql -> catch p1_mysql_conn:stop(State#state.db_ref);
- _ -> ok
+ mysql -> catch p1_mysql_conn:stop(State#state.db_ref);
+ sqlite -> catch sqlite3:close(?SQLITE_DB);
+ _ -> ok
end,
ok.
@@ -456,7 +458,9 @@ sql_query_internal(Query) ->
[{timeout, (?TRANSACTION_TIMEOUT) - 1000},
{result_type, binary}])),
%% ?INFO_MSG("MySQL, Received result~n~p~n", [R]),
- R
+ R;
+ sqlite ->
+ sqlite_to_odbc(sqlite3:sql_exec(?SQLITE_DB, Query))
end,
case Res of
{error, <<"No SQL-driver information available.">>} ->
@@ -488,6 +492,34 @@ odbc_connect(SQLServer) ->
ejabberd:start_app(odbc),
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
%% part of init/1
@@ -584,6 +616,7 @@ db_opts(Host) ->
Type = ejabberd_config:get_option({odbc_type, Host},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
+ (sqlite) -> sqlite;
(odbc) -> odbc
end, odbc),
Server = ejabberd_config:get_option({odbc_server, Host},
@@ -592,6 +625,11 @@ db_opts(Host) ->
case Type of
odbc ->
[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(
{odbc_port, Host},
diff --git a/src/ejabberd_odbc_sup.erl b/src/ejabberd_odbc_sup.erl
index 602e7e03b..95e8fa300 100644
--- a/src/ejabberd_odbc_sup.erl
+++ b/src/ejabberd_odbc_sup.erl
@@ -67,6 +67,22 @@ init([Host]) ->
{odbc_start_interval, Host},
fun(I) when is_integer(I), I>0 -> I end,
?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,
{{one_for_one, PoolSize * 10, 1},
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, {pgsql, Server, 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) ->
[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.
diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl
index e71728da5..93bd9fc49 100644
--- a/src/ejabberd_rdbms.erl
+++ b/src/ejabberd_rdbms.erl
@@ -74,10 +74,12 @@ needs_odbc(Host) ->
case ejabberd_config:get_option({odbc_type, LHost},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
+ (sqlite) -> sqlite;
(odbc) -> odbc
end, undefined) of
mysql -> {true, p1_mysql};
pgsql -> {true, p1_pgsql};
+ sqlite -> {true, sqlite3};
odbc -> {true, odbc};
undefined -> false
end.
diff --git a/src/odbc_queries.erl b/src/odbc_queries.erl
index f2771e52f..86c3af6d0 100644
--- a/src/odbc_queries.erl
+++ b/src/odbc_queries.erl
@@ -229,6 +229,7 @@ users_number(LServer) ->
Type = ejabberd_config:get_option({odbc_type, LServer},
fun(pgsql) -> pgsql;
(mysql) -> mysql;
+ (sqlite) -> sqlite;
(odbc) -> odbc
end, odbc),
case Type of
diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl
index 9ab9c247e..ec60b728d 100644
--- a/test/ejabberd_SUITE.erl
+++ b/test/ejabberd_SUITE.erl
@@ -35,6 +35,7 @@ init_per_suite(Config) ->
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
{ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])),
{ok, _} = ldap_srv:start(LDIFFile),
+ file:delete("/tmp/ejabberd_test.db"),
ok = application:start(ejabberd),
NewConfig.
@@ -65,6 +66,14 @@ init_per_group(pgsql, Config) ->
Err ->
{skip, {pgsql_not_available, Err}}
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) ->
set_opt(server, ?LDAP_VHOST, Config);
init_per_group(extauth, Config) ->
@@ -88,6 +97,8 @@ end_per_group(mysql, _Config) ->
ok;
end_per_group(pgsql, _Config) ->
ok;
+end_per_group(sqlite, _Config) ->
+ ok;
end_per_group(no_db, _Config) ->
ok;
end_per_group(ldap, _Config) ->
@@ -300,6 +311,7 @@ groups() ->
{mnesia, [sequence], db_tests(mnesia)},
{mysql, [sequence], db_tests(mysql)},
{pgsql, [sequence], db_tests(pgsql)},
+ {sqlite, [sequence], db_tests(sqlite)},
{riak, [sequence], db_tests(riak)}].
all() ->
@@ -308,6 +320,7 @@ all() ->
{group, mnesia},
{group, mysql},
{group, pgsql},
+ {group, sqlite},
{group, extauth},
{group, riak},
stop_ejabberd].
@@ -1631,6 +1644,8 @@ socks5_recv(Sock, Data) ->
%%%===================================================================
%%% SQL stuff
%%%===================================================================
+create_sql_tables(sqlite, _BaseDir) ->
+ ok;
create_sql_tables(Type, BaseDir) ->
{VHost, File} = case Type of
mysql ->
diff --git a/test/ejabberd_SUITE_data/ejabberd.cfg b/test/ejabberd_SUITE_data/ejabberd.cfg
index 458de2c7d..cb321661e 100644
--- a/test/ejabberd_SUITE_data/ejabberd.cfg
+++ b/test/ejabberd_SUITE_data/ejabberd.cfg
@@ -3,6 +3,7 @@
"mnesia.localhost",
"mysql.localhost",
"pgsql.localhost",
+ "sqlite.localhost",
"extauth.localhost",
"ldap.localhost"]}.
{define_macro, 'CERTFILE', "cert.pem"}.
@@ -120,6 +121,25 @@
{mod_roster, [{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",
[{auth_method, ldap},
{ldap_servers, ["localhost"]},
diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml
index 9bd8a8b0a..1714464d5 100644
--- a/test/ejabberd_SUITE_data/ejabberd.yml
+++ b/test/ejabberd_SUITE_data/ejabberd.yml
@@ -55,6 +55,57 @@ 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."
+ mod_stats: []
+ mod_time: []
+ mod_version: []
"mysql.localhost":
odbc_username: "ejabberd_test"
odbc_type: mysql
@@ -242,6 +293,7 @@ hosts:
- "extauth.localhost"
- "ldap.localhost"
- "riak.localhost"
+ - "sqlite.localhost"
access:
announce:
admin: allow
diff --git a/test/suite.hrl b/test/suite.hrl
index 49bfff2d9..269620179 100644
--- a/test/suite.hrl
+++ b/test/suite.hrl
@@ -59,6 +59,7 @@
-define(MNESIA_VHOST, <<"mnesia.localhost">>).
-define(MYSQL_VHOST, <<"mysql.localhost">>).
-define(PGSQL_VHOST, <<"pgsql.localhost">>).
+-define(SQLITE_VHOST, <<"sqlite.localhost">>).
-define(LDAP_VHOST, <<"ldap.localhost">>).
-define(EXTAUTH_VHOST, <<"extauth.localhost">>).
-define(RIAK_VHOST, <<"riak.localhost">>).
diff --git a/vars.config.in b/vars.config.in
index cb82775aa..2ccd5c76f 100644
--- a/vars.config.in
+++ b/vars.config.in
@@ -23,6 +23,7 @@
{odbc, @odbc@}.
{mysql, @mysql@}.
{pgsql, @pgsql@}.
+{sqlite, @sqlite@}.
{pam, @pam@}.
{zlib, @zlib@}.
{riak, @riak@}.