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

processone / ejabberd / 1258

12 Dec 2025 03:57PM UTC coverage: 33.638% (-0.006%) from 33.644%
1258

push

github

badlop
Container: Apply commit a22c88a

ejabberdctl.template: Show meaningful error when ERL_DIST_PORT is in use

15554 of 46240 relevant lines covered (33.64%)

1078.28 hits per line

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

65.05
/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
44,318✔
38
        true -> ok;
44,312✔
39
        false ->
40
            case lists:member(Host, ejabberd_option:hosts()) of
6✔
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
6✔
46
                              mysql -> p1_mysql;
2✔
47
                              pgsql -> p1_pgsql;
2✔
48
                              sqlite -> sqlite3;
2✔
49
                              _ -> odbc
×
50
                          end,
51
                    ejabberd:start_app(App),
6✔
52
                    Spec = #{id => gen_mod:get_module_proc(Host, ?MODULE),
6✔
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
6✔
59
                        {ok, _} ->
60
                            ejabberd_sql_schema:start(Host),
6✔
61
                            ok;
6✔
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, []).
11✔
83

84
start_link(Host) ->
85
    supervisor:start_link({local,
6✔
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()),
11✔
96
    ejabberd_hooks:add(host_up, ?MODULE, start, 20),
11✔
97
    ejabberd_hooks:add(host_down, ?MODULE, stop, 90),
11✔
98
    ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
11✔
99
    ignore;
11✔
100
init([Host]) ->
101
    Type = ejabberd_option:sql_type(Host),
6✔
102
    PoolSize = get_pool_size(Type, Host),
6✔
103
    case Type of
6✔
104
        sqlite ->
105
            check_sqlite_db(Host);
2✔
106
        mssql ->
107
            ejabberd_sql:init_mssql(Host);
×
108
        _ ->
109
            ok
4✔
110
    end,
111
    {ok, {{one_for_one, PoolSize * 10, 1}, child_specs(Host, PoolSize)}}.
6✔
112

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

117
-spec reload(binary()) -> ok.
118
reload(Host) ->
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 ->
138
            ok
20✔
139
    end.
140

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

145
-spec get_pool_size(atom(), binary()) -> pos_integer().
146
get_pool_size(SQLType, Host) ->
147
    PoolSize = ejabberd_option:sql_pool_size(Host),
6✔
148
    if PoolSize > 1 andalso SQLType == sqlite ->
6✔
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
6✔
153
    end,
154
    PoolSize.
6✔
155

156
-spec child_spec(binary(), pos_integer()) -> supervisor:child_spec().
157
child_spec(Host, I) ->
158
    #{id => I,
6✔
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)].
6✔
168

169
check_sqlite_db(Host) ->
170
    DB = ejabberd_sql:sqlite_db(Host),
2✔
171
    File = ejabberd_sql:sqlite_file(Host),
2✔
172
    Ret = case filelib:ensure_dir(File) of
2✔
173
              ok ->
174
                  case sqlite3:open(DB, [{file, File}]) of
2✔
175
                      {ok, _Ref} -> ok;
2✔
176
                      {error, {already_started, _Ref}} -> ok;
×
177
                      {error, R} -> {error, R}
×
178
                  end;
179
              Err ->
180
                  Err
×
181
          end,
182
    case Ret of
2✔
183
        ok ->
184
            sqlite3:sql_exec(DB, "pragma foreign_keys = on"),
2✔
185
            case sqlite3:list_tables(DB) of
2✔
186
                [] ->
187
                    create_sqlite_tables(DB),
2✔
188
                    sqlite3:close(DB),
2✔
189
                    ok;
2✔
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(),
2✔
199
    Filename = case ejabberd_sql:use_multihost_schema() of
2✔
200
        true -> "lite.new.sql";
1✔
201
        false -> "lite.sql"
1✔
202
    end,
203
    File = filename:join(SqlDir, Filename),
2✔
204
    case file:open(File, [read, binary]) of
2✔
205
        {ok, Fd} ->
206
            Qs = read_lines(Fd, File, []),
2✔
207
            ok = sqlite3:sql_exec(DB, "begin"),
2✔
208
            [ok = sqlite3:sql_exec(DB, Q) || Q <- Qs],
2✔
209
            ok = sqlite3:sql_exec(DB, "commit");
2✔
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
952✔
217
        {ok, Line} ->
218
            NewAcc = case str:strip(str:strip(Line, both, $\r), both, $\n) of
950✔
219
                         <<"--", _/binary>> ->
220
                             Acc;
40✔
221
                         <<>> ->
222
                             Acc;
140✔
223
                         _ ->
224
                             [Line|Acc]
770✔
225
                     end,
226
            read_lines(Fd, File, NewAcc);
950✔
227
        eof ->
228
            QueryList = str:tokens(list_to_binary(lists:reverse(Acc)), <<";">>),
2✔
229
            lists:flatmap(
2✔
230
              fun(Query) ->
231
                      case str:strip(str:strip(Query, both, $\r), both, $\n) of
202✔
232
                          <<>> ->
233
                              [];
2✔
234
                          Q ->
235
                              [<<Q/binary, $;>>]
200✔
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

© 2025 Coveralls, Inc