Skip to content

Commit cd4c8f2

Browse files
committed
Add abilty to change column types in sql schema updater
1 parent 2c8bbe5 commit cd4c8f2

3 files changed

Lines changed: 189 additions & 9 deletions

File tree

src/ejabberd_sql_schema.erl

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -863,24 +863,29 @@ update_schema(Host, Module, RawSchemas) ->
863863
LastVersion = LastSchema#sql_schema.version,
864864
case Version of
865865
_ when Version < 0 ->
866-
?ERROR_MSG("Can't update SQL schema for module ~p, please do it manually", [Module]);
866+
?ERROR_MSG("Can't update SQL schema for module ~p, please do it manually", [Module]),
867+
error;
867868
0 ->
868869
create_tables(Host, Module, SchemaInfo, LastSchema);
869870
LastVersion ->
870871
ok;
871872
_ when LastVersion < Version ->
872-
?ERROR_MSG("The current SQL schema for module ~p is ~p, but the latest known schema in the module is ~p", [Module, Version, LastVersion]);
873+
?ERROR_MSG("The current SQL schema for module ~p is ~p, but the latest known schema in the module is ~p", [Module, Version, LastVersion]),
874+
error;
873875
_ ->
874-
lists:foreach(
875-
fun(Schema) ->
876+
lists:foldl(
877+
fun(Schema, Res) ->
876878
if
877879
Schema#sql_schema.version > Version ->
878-
do_update_schema(Host, Module,
879-
SchemaInfo, Schema);
880+
case do_update_schema(Host, Module,
881+
SchemaInfo, Schema) of
882+
{atomic, _} -> Res;
883+
_ -> error
884+
end;
880885
true ->
881-
ok
886+
Res
882887
end
883-
end, lists:sort(Schemas))
888+
end, ok, lists:sort(Schemas))
884889
end;
885890
false ->
886891
ok
@@ -957,6 +962,69 @@ do_update_schema(Host, Module, SchemaInfo, Schema) ->
957962
_ ->
958963
ok
959964
end;
965+
({change_column_type, TableName, ColumnName}) ->
966+
{value, Table} =
967+
lists:keysearch(
968+
TableName, #sql_table.name, Schema#sql_schema.tables),
969+
{value, Column} =
970+
lists:keysearch(
971+
ColumnName, #sql_column.name, Table#sql_table.columns),
972+
Res =
973+
ejabberd_sql:sql_query_t(
974+
fun(DBType, _DBVersion) ->
975+
SQL =
976+
case DBType of
977+
mysql ->
978+
Def = format_column_def(SchemaInfo, Column),
979+
[<<"ALTER TABLE ">>,
980+
TableName,
981+
<<" MODIFY COLUMN ">>,
982+
Def,
983+
<<";">>];
984+
sqlite ->
985+
sqlite_table_copy_t(SchemaInfo, Table);
986+
mssql ->
987+
Type = format_type(SchemaInfo, Column),
988+
[<<"ALTER TABLE ">>,
989+
TableName,
990+
<<" ALTER COLUMN ">>,
991+
ColumnName,
992+
<<" ">>,
993+
Type,
994+
<<";">>];
995+
pgsql ->
996+
Type = format_type(SchemaInfo, Column),
997+
[<<"ALTER TABLE ">>,
998+
TableName,
999+
<<" ALTER COLUMN ">>,
1000+
ColumnName,
1001+
<<" TYPE ">>,
1002+
Type,
1003+
<<" USING ">>,
1004+
ColumnName, <<"::">>, Type,
1005+
<<";">>]
1006+
end,
1007+
case SQL of
1008+
_ when is_list(SQL) ->
1009+
?INFO_MSG("Change column type ~s/~s:~n~s~n",
1010+
[TableName,
1011+
ColumnName,
1012+
SQL]),
1013+
ejabberd_sql:sql_query_t(SQL);
1014+
_ ->
1015+
?INFO_MSG("Change column type ~s/~s:~n",
1016+
[TableName,
1017+
ColumnName])
1018+
end
1019+
end),
1020+
case Res of
1021+
{error, Error} ->
1022+
?ERROR_MSG("Failed to update table ~s: ~p",
1023+
[TableName, Error]),
1024+
error(Error);
1025+
_ ->
1026+
ok
1027+
end;
9601028
({create_index, TableName, Columns1}) ->
9611029
Columns =
9621030
case ejabberd_sql:use_multihost_schema() of

test/ejabberd_SUITE.erl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ init_per_testcase(TestCase, OrigConfig) ->
348348
bind(auth(connect(Config)));
349349
"replaced" ++ _ ->
350350
auth(connect(Config));
351+
"sqlschemaupdater_" ++ _ ->
352+
Config;
351353
_ when TestGroup == s2s_tests ->
352354
auth(connect(starttls(connect(Config))));
353355
_ ->
@@ -466,6 +468,11 @@ db_tests(DB) when DB == mnesia; DB == redis ->
466468
csi_tests:master_slave_cases(),
467469
push_tests:master_slave_cases()];
468470
db_tests(DB) ->
471+
SqlUpdate = if
472+
DB == sqlite; DB == mysql; DB == pgsql; DB == mssql ->
473+
[sqlschemaupdater_tests:single_cases()];
474+
true -> []
475+
end,
469476
[{single_user, [sequence],
470477
[test_register,
471478
legacy_auth_tests(),
@@ -486,7 +493,7 @@ db_tests(DB) ->
486493
push_tests:single_cases(),
487494
invites_tests:single_cases(),
488495
test_pass_change,
489-
test_unregister]},
496+
test_unregister] ++ SqlUpdate},
490497
muc_tests:master_slave_cases(),
491498
privacy_tests:master_slave_cases(),
492499
pubsub_tests:master_slave_cases(),

test/sqlschemaupdater_tests.erl

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
%%%-------------------------------------------------------------------
2+
%%% Author : Pawel Chmielowski <pawel@process-one.net>
3+
%%% Created : 20 Jul 2026 by Pawel Chmielowski <pawel@process-one.net>p
4+
%%%
5+
%%%
6+
%%% ejabberd, Copyright (C) 2002-2026 ProcessOne
7+
%%%
8+
%%% This program is free software; you can redistribute it and/or
9+
%%% modify it under the terms of the GNU General Public License as
10+
%%% published by the Free Software Foundation; either version 2 of the
11+
%%% License, or (at your option) any later version.
12+
%%%
13+
%%% This program is distributed in the hope that it will be useful,
14+
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
%%% General Public License for more details.
17+
%%%
18+
%%% You should have received a copy of the GNU General Public License along
19+
%%% with this program; if not, write to the Free Software Foundation, Inc.,
20+
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21+
%%%
22+
%%%-------------------------------------------------------------------
23+
24+
-module(sqlschemaupdater_tests).
25+
26+
-compile(export_all).
27+
28+
-include("suite.hrl").
29+
-include("ejabberd_sql_pt.hrl").
30+
31+
%%%==================================
32+
33+
34+
single_cases() ->
35+
{sqlschemaupdater_single,
36+
[sequence],
37+
[single_test(add_column),
38+
single_test(change_column)]}.
39+
40+
41+
%% Interactions
42+
43+
44+
apply_schemas(Config, Schemas) ->
45+
Server = ?config(server, Config),
46+
ejabberd_sql:sql_query(Server, <<"drop table if exists schemaupdate;">>),
47+
ejabberd_sql:sql_query(Server, <<"delete from schema_version where module='schemaupdate';">>),
48+
lists:foreach(
49+
fun(#sql_schema{} = Schema) ->
50+
?match(ok, ejabberd_sql_schema:update_schema(Server, schemaupdate, [Schema]));
51+
(ToExec) when is_binary(ToExec) ->
52+
ejabberd_sql:sql_query(Server, ToExec);
53+
([ToExec, Result]) when is_binary(ToExec) ->
54+
?match({selected, _, Result}, ejabberd_sql:sql_query(Server, ToExec))
55+
end,
56+
Schemas).
57+
58+
59+
add_column(Config) ->
60+
Schemas = [#sql_schema{
61+
version = 1,
62+
tables = [#sql_table{
63+
name = <<"schemaupdate">>,
64+
columns = [#sql_column{name = <<"first">>, type = integer}]
65+
}]
66+
},
67+
<<"insert into schemaupdate (first) values (1)">>,
68+
#sql_schema{
69+
version = 2,
70+
tables = [#sql_table{
71+
name = <<"schemaupdate">>,
72+
columns = [#sql_column{name = <<"first">>, type = integer}#sql_column{name = <<"second">>, type = integer}]
73+
}],
74+
update = [{add_column, <<"schemaupdate">>, <<"second">>}]
75+
},
76+
<<"insert into schemaupdate (first, second) values (1, 2)">>,
77+
[<<"select first, second from schemaupdate order by first, second">>, [[<<"1">>, <<"0">>], [<<"1">>, <<"2">>]]]],
78+
apply_schemas(Config, Schemas).
79+
80+
81+
change_column(Config) ->
82+
Schemas = [#sql_schema{
83+
version = 1,
84+
tables = [#sql_table{
85+
name = <<"schemaupdate">>,
86+
columns = [#sql_column{name = <<"first">>, type = boolean}]
87+
}]
88+
},
89+
<<"insert into schemaupdate (first) values (false)">>,
90+
#sql_schema{
91+
version = 2,
92+
tables = [#sql_table{
93+
name = <<"schemaupdate">>,
94+
columns = [#sql_column{name = <<"first">>, type = integer}]
95+
}],
96+
update = [{change_column_type, <<"schemaupdate">>, <<"first">>}]
97+
},
98+
[<<"select first from schemaupdate">>, [[<<"0">>]]],
99+
<<"update schemaupdate set first=2">>,
100+
[<<"select first from schemaupdate">>, [[<<"2">>]]]],
101+
apply_schemas(Config, Schemas).
102+
103+
104+
single_test(T) ->
105+
list_to_atom("sqlschemaupdater_" ++ atom_to_list(T)).

0 commit comments

Comments
 (0)