• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

processone / ejabberd / 1195

14 Nov 2025 07:30AM UTC coverage: 14.534% (-19.2%) from 33.775%
1195

Pull #4493

github

web-flow
Merge 53c116bc1 into 026bd24a5
Pull Request #4493: Great invitations

0 of 521 new or added lines in 8 files covered. (0.0%)

2195 existing lines in 61 files now uncovered.

6752 of 46457 relevant lines covered (14.53%)

47.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.71
/src/ejabberd_sql_sup.erl
1
%%%----------------------------------------------------------------------
2
%%% File    : ejabberd_sql_sup.erl
3
%%% Author  : Alexey Shchepin <alexey@process-one.net>
4
%%% Purpose : SQL connections supervisor
5
%%% Created : 22 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
6
%%%
7
%%%
8
%%% ejabberd, Copyright (C) 2002-2025   ProcessOne
9
%%%
10
%%% This program is free software; you can redistribute it and/or
11
%%% modify it under the terms of the GNU General Public License as
12
%%% published by the Free Software Foundation; either version 2 of the
13
%%% License, or (at your option) any later version.
14
%%%
15
%%% This program is distributed in the hope that it will be useful,
16
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
%%% General Public License for more details.
19
%%%
20
%%% You should have received a copy of the GNU General Public License along
21
%%% with this program; if not, write to the Free Software Foundation, Inc.,
22
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
%%%
24
%%%----------------------------------------------------------------------
25

26
-module(ejabberd_sql_sup).
27

28
-author('alexey@process-one.net').
29

30
-export([start/1, stop/1, stop/0]).
31
-export([start_link/0, start_link/1]).
32
-export([init/1, reload/1, config_reloaded/0, is_started/1]).
33

34
-include("logger.hrl").
35

36
start(Host) ->
37
    case is_started(Host) of
×
38
        true -> ok;
×
39
        false ->
40
            case lists:member(Host, ejabberd_option:hosts()) of
×
41
                false ->
42
                    ?WARNING_MSG("Rejecting start of sql worker for unknown host: ~ts", [Host]),
×
43
                    {error, invalid_host};
×
44
                true ->
45
                    App = case ejabberd_option:sql_type(Host) of
×
46
                              mysql -> p1_mysql;
×
47
                              pgsql -> p1_pgsql;
×
48
                              sqlite -> sqlite3;
×
49
                              _ -> odbc
×
50
                          end,
51
                    ejabberd:start_app(App),
×
52
                    Spec = #{id => gen_mod:get_module_proc(Host, ?MODULE),
×
53
                        start => {ejabberd_sql_sup, start_link, [Host]},
54
                        restart => transient,
55
                        shutdown => infinity,
56
                        type => supervisor,
57
                        modules => [?MODULE]},
58
                    case supervisor:start_child(ejabberd_db_sup, Spec) of
×
59
                        {ok, _} ->
60
                            ejabberd_sql_schema:start(Host),
×
61
                            ok;
×
62
                        {error, {already_started, Pid}} ->
63
                            %% Wait for the supervisor to fully start
64
                            _ = supervisor:count_children(Pid),
×
65
                            ok;
×
66
                        {error, Why} = Err ->
67
                            ?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]),
×
68
                            Err
×
69
                    end
70
            end
71
    end.
72

73
stop(Host) ->
74
    Proc = gen_mod:get_module_proc(Host, ?MODULE),
×
75
    case supervisor:terminate_child(ejabberd_db_sup, Proc) of
×
76
        ok -> supervisor:delete_child(ejabberd_db_sup, Proc);
×
77
        Err -> Err
×
78
    end.
79

80

81
start_link() ->
82
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
3✔
83

84
start_link(Host) ->
85
    supervisor:start_link({local,
×
86
                           gen_mod:get_module_proc(Host, ?MODULE)},
87
                          ?MODULE, [Host]).
88

89
stop() ->
90
    ejabberd_hooks:delete(host_up, ?MODULE, start, 20),
×
91
    ejabberd_hooks:delete(host_down, ?MODULE, stop, 90),
×
92
    ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20).
×
93

94
init([]) ->
95
    file:delete(ejabberd_sql:odbcinst_config()),
3✔
96
    ejabberd_hooks:add(host_up, ?MODULE, start, 20),
3✔
97
    ejabberd_hooks:add(host_down, ?MODULE, stop, 90),
3✔
98
    ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
3✔
99
    ignore;
3✔
100
init([Host]) ->
101
    Type = ejabberd_option:sql_type(Host),
×
102
    PoolSize = get_pool_size(Type, Host),
×
103
    case Type of
×
104
        sqlite ->
105
            check_sqlite_db(Host);
×
106
        mssql ->
107
            ejabberd_sql:init_mssql(Host);
×
108
        _ ->
109
            ok
×
110
    end,
111
    {ok, {{one_for_one, PoolSize * 10, 1}, child_specs(Host, PoolSize)}}.
×
112

113
-spec config_reloaded() -> ok.
114
config_reloaded() ->
UNCOV
115
    lists:foreach(fun reload/1, ejabberd_option:hosts()).
2✔
116

117
-spec reload(binary()) -> ok.
118
reload(Host) ->
UNCOV
119
    case is_started(Host) of
20✔
120
        true ->
121
            Sup = gen_mod:get_module_proc(Host, ?MODULE),
×
122
            Type = ejabberd_option:sql_type(Host),
×
123
            PoolSize = get_pool_size(Type, Host),
×
124
            lists:foreach(
×
125
              fun(Spec) ->
126
                      supervisor:start_child(Sup, Spec)
×
127
              end, child_specs(Host, PoolSize)),
128
            lists:foreach(
×
129
              fun({Id, _, _, _}) when Id > PoolSize ->
130
                      case supervisor:terminate_child(Sup, Id) of
×
131
                          ok -> supervisor:delete_child(Sup, Id);
×
132
                          _ -> ok
×
133
                      end;
134
                 (_) ->
135
                      ok
×
136
              end, supervisor:which_children(Sup));
137
        false ->
UNCOV
138
            ok
20✔
139
    end.
140

141
-spec is_started(binary()) -> boolean().
142
is_started(Host) ->
UNCOV
143
    whereis(gen_mod:get_module_proc(Host, ?MODULE)) /= undefined.
20✔
144

145
-spec get_pool_size(atom(), binary()) -> pos_integer().
146
get_pool_size(SQLType, Host) ->
147
    PoolSize = ejabberd_option:sql_pool_size(Host),
×
148
    if PoolSize > 1 andalso SQLType == sqlite ->
×
149
            ?WARNING_MSG("It's not recommended to set sql_pool_size > 1 for "
×
150
                         "sqlite, because it may cause race conditions", []);
×
151
       true ->
152
            ok
×
153
    end,
154
    PoolSize.
×
155

156
-spec child_spec(binary(), pos_integer()) -> supervisor:child_spec().
157
child_spec(Host, I) ->
158
    #{id => I,
×
159
      start => {ejabberd_sql, start_link, [Host, I]},
160
      restart => transient,
161
      shutdown => 2000,
162
      type => worker,
163
      modules => [?MODULE]}.
164

165
-spec child_specs(binary(), pos_integer()) -> [supervisor:child_spec()].
166
child_specs(Host, PoolSize) ->
167
    [child_spec(Host, I) || I <- lists:seq(1, PoolSize)].
×
168

169
check_sqlite_db(Host) ->
170
    DB = ejabberd_sql:sqlite_db(Host),
×
171
    File = ejabberd_sql:sqlite_file(Host),
×
172
    Ret = case filelib:ensure_dir(File) of
×
173
              ok ->
174
                  case sqlite3:open(DB, [{file, File}]) of
×
175
                      {ok, _Ref} -> ok;
×
176
                      {error, {already_started, _Ref}} -> ok;
×
177
                      {error, R} -> {error, R}
×
178
                  end;
179
              Err ->
180
                  Err
×
181
          end,
182
    case Ret of
×
183
        ok ->
184
            sqlite3:sql_exec(DB, "pragma foreign_keys = on"),
×
185
            case sqlite3:list_tables(DB) of
×
186
                [] ->
187
                    create_sqlite_tables(DB),
×
188
                    sqlite3:close(DB),
×
189
                    ok;
×
190
                [_H | _] ->
191
                    ok
×
192
            end;
193
        {error, Reason} ->
194
            ?WARNING_MSG("Failed open sqlite database, reason ~p", [Reason])
×
195
    end.
196

197
create_sqlite_tables(DB) ->
198
    SqlDir = misc:sql_dir(),
×
199
    Filename = case ejabberd_sql:use_multihost_schema() of
×
200
        true -> "lite.new.sql";
×
201
        false -> "lite.sql"
×
202
    end,
203
    File = filename:join(SqlDir, Filename),
×
204
    case file:open(File, [read, binary]) of
×
205
        {ok, Fd} ->
206
            Qs = read_lines(Fd, File, []),
×
207
            ok = sqlite3:sql_exec(DB, "begin"),
×
208
            [ok = sqlite3:sql_exec(DB, Q) || Q <- Qs],
×
209
            ok = sqlite3:sql_exec(DB, "commit");
×
210
        {error, Reason} ->
211
            ?WARNING_MSG("Failed to read SQLite schema file: ~ts",
×
212
                         [file:format_error(Reason)])
×
213
    end.
214

215
read_lines(Fd, File, Acc) ->
216
    case file:read_line(Fd) of
×
217
        {ok, Line} ->
218
            NewAcc = case str:strip(str:strip(Line, both, $\r), both, $\n) of
×
219
                         <<"--", _/binary>> ->
220
                             Acc;
×
221
                         <<>> ->
222
                             Acc;
×
223
                         _ ->
224
                             [Line|Acc]
×
225
                     end,
226
            read_lines(Fd, File, NewAcc);
×
227
        eof ->
228
            QueryList = str:tokens(list_to_binary(lists:reverse(Acc)), <<";">>),
×
229
            lists:flatmap(
×
230
              fun(Query) ->
231
                      case str:strip(str:strip(Query, both, $\r), both, $\n) of
×
232
                          <<>> ->
233
                              [];
×
234
                          Q ->
235
                              [<<Q/binary, $;>>]
×
236
                      end
237
              end, QueryList);
238
        {error, _} = Err ->
239
            ?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
×
240
            []
×
241
    end.
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc