From 66d701e788af7d1f86c253481ab2d8411bc92a3c Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 24 Jan 2024 22:49:59 +0300 Subject: [PATCH] Add print_sql_schema ejabberdctl command --- include/ejabberd_sql.hrl | 5 + src/ejabberd_auth_sql.erl | 5 +- src/ejabberd_ctl.erl | 6 +- src/ejabberd_oauth_sql.erl | 5 +- src/ejabberd_router_sql.erl | 5 +- src/ejabberd_sm_sql.erl | 5 +- src/ejabberd_sql.erl | 53 +++++--- src/ejabberd_sql_schema.erl | 244 +++++++++++++++++++++++++--------- src/mod_announce_sql.erl | 5 +- src/mod_bosh_sql.erl | 5 +- src/mod_caps_sql.erl | 5 +- src/mod_last_sql.erl | 5 +- src/mod_mam_sql.erl | 18 +-- src/mod_mix_pam_sql.erl | 5 +- src/mod_mix_sql.erl | 5 +- src/mod_mqtt_sql.erl | 5 +- src/mod_muc_sql.erl | 5 +- src/mod_offline_sql.erl | 5 +- src/mod_privacy_sql.erl | 6 +- src/mod_private_sql.erl | 5 +- src/mod_proxy65_sql.erl | 5 +- src/mod_pubsub_sql.erl | 5 +- src/mod_push_sql.erl | 5 +- src/mod_roster_sql.erl | 5 +- src/mod_shared_roster_sql.erl | 5 +- src/mod_vcard_sql.erl | 5 +- 26 files changed, 293 insertions(+), 139 deletions(-) diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index 608ff3459..b5a4933fc 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -68,3 +68,8 @@ update = []}). -record(sql_references, {table :: binary(), column :: binary()}). + +-record(sql_schema_info, + {db_type :: pgsql | mysql | sqlite, + db_version :: any(), + new_schema = true :: boolean()}). diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index a0e69cdb4..dd58f0af5 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -34,6 +34,7 @@ get_users/2, count_users/2, get_password/2, remove_user/2, store_type/1, plain_password_required/1, export/1, which_users_exists/2]). +-export([sql_schemas/0]). -include_lib("xmpp/include/scram.hrl"). -include("logger.hrl"). @@ -46,10 +47,10 @@ %%% API %%%---------------------------------------------------------------------- start(Host) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index a0feb49d8..4d243a392 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -157,6 +157,9 @@ process(["mnesia", Arg], _Version) -> end, ?STATUS_SUCCESS; +process(["print_sql_schema", DBType, DBVersion, NewSchema], _Version) -> + ejabberd_sql_schema:print_schema(DBType, DBVersion, NewSchema); + %% The arguments --long and --dual are not documented because they are %% automatically selected depending in the number of columns of the shell process(["help" | Mode], Version) -> @@ -555,7 +558,8 @@ print_usage(HelpMode, MaxC, ShCode, Version) -> {"status", [], "Get ejabberd status"}, {"stop", [], "Stop ejabberd"}, {"restart", [], "Restart ejabberd"}, - {"mnesia", ["[info]"], "show information of Mnesia system"}] ++ + {"mnesia", ["[info]"], "show information of Mnesia system"}, + {"print_sql_schema", ["db_type", "db_version", "new_schema"], "print SQL schema for the given RDBMS"}] ++ get_list_commands(Version), print( diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl index ffd571bec..17c38f20c 100644 --- a/src/ejabberd_oauth_sql.erl +++ b/src/ejabberd_oauth_sql.erl @@ -34,6 +34,7 @@ lookup_client/1, store_client/1, remove_client/1, revoke/1]). +-export([sql_schemas/0]). -include("ejabberd_oauth.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -42,10 +43,10 @@ init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index d334e0f6f..03e617654 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -27,6 +27,7 @@ %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, get_all_routes/0]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -38,7 +39,7 @@ %%%=================================================================== init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'route' table...", []), case ejabberd_sql:sql_query( @@ -50,7 +51,7 @@ init() -> Err end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl index 96c640b90..df8e0970d 100644 --- a/src/ejabberd_sm_sql.erl +++ b/src/ejabberd_sm_sql.erl @@ -34,6 +34,7 @@ get_sessions/0, get_sessions/1, get_sessions/2]). +-export([sql_schemas/0]). -include("ejabberd_sm.hrl"). -include("logger.hrl"). @@ -48,7 +49,7 @@ init() -> ?DEBUG("Cleaning SQL SM table...", []), lists:foldl( fun(Host, ok) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), case ejabberd_sql:sql_query( Host, ?SQL("delete from sm where node=%(Node)s")) of {updated, _} -> @@ -61,7 +62,7 @@ init() -> Err end, ok, ejabberd_sm:get_vh_by_backend(?MODULE)). -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 1b9a07769..8c192a784 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -56,7 +56,8 @@ init_mssql/1, keep_alive/2, to_list/2, - to_array/2]). + to_array/2, + parse_mysql_version/2]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, @@ -1134,6 +1135,31 @@ to_odbc({error, Reason}) when is_list(Reason) -> to_odbc(Res) -> Res. +parse_mysql_version(SVersion, DefaultUpsert) -> + case re:run(SVersion, <<"(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-([^-]*))?">>, + [{capture, all_but_first, binary}]) of + {match, [V1, V2, V3, Type]} -> + V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), + TypeA = binary_to_atom(Type, utf8), + Flags = case TypeA of + 'MariaDB' -> DefaultUpsert; + _ when V >= 5007026 andalso V < 8000000 -> 1; + _ when V >= 8000020 -> 1; + _ -> DefaultUpsert + end, + {ok, {V, TypeA, Flags}}; + {match, [V1, V2, V3]} -> + V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), + Flags = case V of + _ when V >= 5007026 andalso V < 8000000 -> 1; + _ when V >= 8000020 -> 1; + _ -> DefaultUpsert + end, + {ok, {V, unknown, Flags}}; + _ -> + error + end. + get_db_version(#state{db_type = pgsql} = State) -> case pgsql:squery(State#state.db_ref, <<"select current_setting('server_version_num')">>) of @@ -1159,27 +1185,10 @@ get_db_version(#state{db_type = mysql, host = Host} = State) -> [{timeout, 5000}, {result_type, binary}])) of {selected, _, [SVersion]} -> - case re:run(SVersion, <<"(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-([^-]*))?">>, - [{capture, all_but_first, binary}]) of - {match, [V1, V2, V3, Type]} -> - V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), - TypeA = binary_to_atom(Type, utf8), - Flags = case TypeA of - 'MariaDB' -> DefaultUpsert; - _ when V >= 5007026 andalso V < 8000000 -> 1; - _ when V >= 8000020 -> 1; - _ -> DefaultUpsert - end, - State#state{db_version = {V, TypeA, Flags}}; - {match, [V1, V2, V3]} -> - V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), - Flags = case V of - _ when V >= 5007026 andalso V < 8000000 -> 1; - _ when V >= 8000020 -> 1; - _ -> DefaultUpsert - end, - State#state{db_version = {V, unknown, Flags}}; - _ -> + case parse_mysql_version(SVersion, DefaultUpsert) of + {ok, V} -> + State#state{db_version = V}; + error -> ?WARNING_MSG("Error parsing mysql version: ~p", [SVersion]), State end; diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 5c6d95868..29bcc55b1 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -28,10 +28,12 @@ -author('alexey@process-one.net'). -export([start/1, update_schema/3, - get_table_schema/2, get_table_indices/2, test/0]). + get_table_schema/2, get_table_indices/2, print_schema/3, + test/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). +-include("ejabberd_ctl.hrl"). start(Host) -> case should_update_schema(Host) of @@ -40,8 +42,17 @@ start(Host) -> true -> ok; false -> - Table = filter_table_sh(schema_table()), - Res = create_table(Host, Table), + SchemaInfo = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + #sql_schema_info{ + db_type = DBType, + db_version = DBVersion, + new_schema = ejabberd_sql:use_new_schema()} + end), + Table = filter_table_sh(SchemaInfo, schema_table()), + Res = create_table(Host, SchemaInfo, Table), case Res of {error, Error} -> ?ERROR_MSG("Failed to create table ~s: ~p", @@ -249,8 +260,8 @@ table_exists(Host, Table) -> end end). -filter_table_sh(Table) -> - case {ejabberd_sql:use_new_schema(), Table#sql_table.name} of +filter_table_sh(SchemaInfo, Table) -> + case {SchemaInfo#sql_schema_info.new_schema, Table#sql_table.name} of {true, _} -> Table; {_, <<"route">>} -> @@ -383,7 +394,7 @@ get_current_version(Host, Module, Schemas) -> Version end. -format_type(pgsql, _DBVersion, Column) -> +format_type(#sql_schema_info{db_type = pgsql}, Column) -> case Column#sql_column.type of text -> <<"text">>; {text, _} -> <<"text">>; @@ -397,7 +408,7 @@ format_type(pgsql, _DBVersion, Column) -> {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; bigserial -> <<"bigserial">> end; -format_type(sqlite, _DBVersion, Column) -> +format_type(#sql_schema_info{db_type = sqlite}, Column) -> case Column#sql_column.type of text -> <<"text">>; {text, _} -> <<"text">>; @@ -411,7 +422,7 @@ format_type(sqlite, _DBVersion, Column) -> {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; bigserial -> <<"integer primary key autoincrement">> end; -format_type(mysql, _DBVersion, Column) -> +format_type(#sql_schema_info{db_type = mysql}, Column) -> case Column#sql_column.type of text -> <<"text">>; {text, big} -> <<"mediumtext">>; @@ -429,7 +440,7 @@ format_type(mysql, _DBVersion, Column) -> bigserial -> <<"bigint auto_increment primary key">> end. -format_default(pgsql, _DBVersion, Column) -> +format_default(#sql_schema_info{db_type = pgsql}, Column) -> case Column#sql_column.type of text -> <<"''">>; {text, _} -> <<"''">>; @@ -443,7 +454,7 @@ format_default(pgsql, _DBVersion, Column) -> %{char, N} -> <<"''">>; %bigserial -> <<"0">> end; -format_default(sqlite, _DBVersion, Column) -> +format_default(#sql_schema_info{db_type = sqlite}, Column) -> case Column#sql_column.type of text -> <<"''">>; {text, _} -> <<"''">>; @@ -457,7 +468,7 @@ format_default(sqlite, _DBVersion, Column) -> %{char, N} -> <<"''">>; %bigserial -> <<"0">> end; -format_default(mysql, _DBVersion, Column) -> +format_default(#sql_schema_info{db_type = mysql}, Column) -> case Column#sql_column.type of text -> <<"('')">>; {text, _} -> <<"('')">>; @@ -472,20 +483,20 @@ format_default(mysql, _DBVersion, Column) -> %bigserial -> <<"0">> end. -escape_name(pgsql, _DBVersion, <<"type">>) -> +escape_name(#sql_schema_info{db_type = pgsql}, <<"type">>) -> <<"\"type\"">>; -escape_name(_DBType, _DBVersion, ColumnName) -> +escape_name(_SchemaInfo, ColumnName) -> ColumnName. -format_column_def(DBType, DBVersion, Column) -> +format_column_def(SchemaInfo, Column) -> [<<" ">>, - escape_name(DBType, DBVersion, Column#sql_column.name), <<" ">>, - format_type(DBType, DBVersion, Column), + escape_name(SchemaInfo, Column#sql_column.name), <<" ">>, + format_type(SchemaInfo, Column), <<" NOT NULL">>, case Column#sql_column.default of false -> []; true -> - [<<" DEFAULT ">>, format_default(DBType, DBVersion, Column)] + [<<" DEFAULT ">>, format_default(SchemaInfo, Column)] end, case lists:keyfind(sql_references, 1, Column#sql_column.opts) of false -> []; @@ -511,7 +522,7 @@ format_mysql_index_column(Table, ColumnName) -> ColumnName end. -format_create_index(pgsql, _DBVersion, Table, Index) -> +format_create_index(#sql_schema_info{db_type = pgsql}, Table, Index) -> TableName = Table#sql_table.name, Unique = case Index#sql_index.unique of @@ -528,7 +539,7 @@ format_create_index(pgsql, _DBVersion, Table, Index) -> <<", ">>, Index#sql_index.columns), <<");">>]; -format_create_index(sqlite, _DBVersion, Table, Index) -> +format_create_index(#sql_schema_info{db_type = sqlite}, Table, Index) -> TableName = Table#sql_table.name, Unique = case Index#sql_index.unique of @@ -545,7 +556,7 @@ format_create_index(sqlite, _DBVersion, Table, Index) -> <<", ">>, Index#sql_index.columns), <<");">>]; -format_create_index(mysql, _DBVersion, Table, Index) -> +format_create_index(#sql_schema_info{db_type = mysql}, Table, Index) -> TableName = Table#sql_table.name, Unique = case Index#sql_index.unique of @@ -567,7 +578,7 @@ format_create_index(mysql, _DBVersion, Table, Index) -> end, Index#sql_index.columns)), <<");">>]. -format_primary_key(mysql, _DBVersion, Table) -> +format_primary_key(#sql_schema_info{db_type = mysql}, Table) -> case lists:filter( fun(#sql_index{meta = #{primary_key := true}}) -> true; (_) -> false @@ -584,7 +595,7 @@ format_primary_key(mysql, _DBVersion, Table) -> end, I#sql_index.columns)), <<")">>]] end; -format_primary_key(_DBType, _DBVersion, Table) -> +format_primary_key(_SchemaInfo, Table) -> case lists:filter( fun(#sql_index{meta = #{primary_key := true}}) -> true; (_) -> false @@ -597,16 +608,17 @@ format_primary_key(_DBType, _DBVersion, Table) -> <<")">>]] end. -format_add_primary_key(sqlite = DBType, DBVersion, Table, Index) -> - format_create_index(DBType, DBVersion, Table, Index); -format_add_primary_key(mysql, _DBVersion, Table, Index) -> +format_add_primary_key(#sql_schema_info{db_type = sqlite} = SchemaInfo, + Table, Index) -> + format_create_index(SchemaInfo, Table, Index); +format_add_primary_key(#sql_schema_info{db_type = pgsql}, Table, Index) -> TableName = Table#sql_table.name, [<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>, lists:join( <<", ">>, Index#sql_index.columns), <<");">>]; -format_add_primary_key(_DBType, _DBVersion, Table, Index) -> +format_add_primary_key(#sql_schema_info{db_type = mysql}, Table, Index) -> TableName = Table#sql_table.name, [<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>, lists:join( @@ -617,16 +629,16 @@ format_add_primary_key(_DBType, _DBVersion, Table, Index) -> end, Index#sql_index.columns)), <<");">>]. -format_create_table(pgsql = DBType, DBVersion, Table) -> +format_create_table(#sql_schema_info{db_type = pgsql} = SchemaInfo, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( [<<"CREATE TABLE ">>, TableName, <<" (\n">>, lists:join( <<",\n">>, lists:map( - fun(C) -> format_column_def(DBType, DBVersion, C) end, + fun(C) -> format_column_def(SchemaInfo, C) end, Table#sql_table.columns) ++ - format_primary_key(DBType, DBVersion, Table)), + format_primary_key(SchemaInfo, Table)), <<"\n);\n">>])] ++ lists:flatmap( fun(#sql_index{meta = #{primary_key := true}}) -> @@ -635,20 +647,20 @@ format_create_table(pgsql = DBType, DBVersion, Table) -> []; (I) -> [iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), + [format_create_index(SchemaInfo, Table, I), <<"\n">>])] end, Table#sql_table.indices); -format_create_table(sqlite = DBType, DBVersion, Table) -> +format_create_table(#sql_schema_info{db_type = sqlite} = SchemaInfo, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( [<<"CREATE TABLE ">>, TableName, <<" (\n">>, lists:join( <<",\n">>, lists:map( - fun(C) -> format_column_def(DBType, DBVersion, C) end, + fun(C) -> format_column_def(SchemaInfo, C) end, Table#sql_table.columns) ++ - format_primary_key(DBType, DBVersion, Table)), + format_primary_key(SchemaInfo, Table)), <<"\n);\n">>])] ++ lists:flatmap( fun(#sql_index{meta = #{primary_key := true}}) -> @@ -657,20 +669,20 @@ format_create_table(sqlite = DBType, DBVersion, Table) -> []; (I) -> [iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), + [format_create_index(SchemaInfo, Table, I), <<"\n">>])] end, Table#sql_table.indices); -format_create_table(mysql = DBType, DBVersion, Table) -> +format_create_table(#sql_schema_info{db_type = mysql} = SchemaInfo, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( [<<"CREATE TABLE ">>, TableName, <<" (\n">>, lists:join( <<",\n">>, lists:map( - fun(C) -> format_column_def(DBType, DBVersion, C) end, + fun(C) -> format_column_def(SchemaInfo, C) end, Table#sql_table.columns) ++ - format_primary_key(DBType, DBVersion, Table)), + format_primary_key(SchemaInfo, Table)), <<"\n) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n">>])] ++ lists:flatmap( fun(#sql_index{meta = #{primary_key := true}}) -> @@ -679,20 +691,16 @@ format_create_table(mysql = DBType, DBVersion, Table) -> []; (I) -> [iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), + [format_create_index(SchemaInfo, Table, I), <<"\n">>])] end, Table#sql_table.indices). -%format_create_table(DBType, _DBVersion, Table) -> -% ?ERROR_MSG("Can't create SQL table ~p on ~p", -% [Table#sql_table.name, DBType]), -% error. -create_table(Host, Table) -> +create_table(Host, SchemaInfo, Table) -> ejabberd_sql:sql_query( Host, - fun(DBType, DBVersion) -> - SQLs = format_create_table(DBType, DBVersion, Table), + fun() -> + SQLs = format_create_table(SchemaInfo, Table), ?INFO_MSG("Creating table ~s:~n~s~n", [Table#sql_table.name, SQLs]), lists:foreach( @@ -700,15 +708,18 @@ create_table(Host, Table) -> case Table#sql_table.post_create of undefined -> ok; - F -> - F(DBType, DBVersion) + F when is_function(F, 1) -> + PostSQLs = F(SchemaInfo), + lists:foreach( + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, + PostSQLs) end end). -create_tables(Host, Module, Schema) -> +create_tables(Host, Module, SchemaInfo, Schema) -> lists:foreach( fun(Table) -> - Res = create_table(Host, Table), + Res = create_table(Host, SchemaInfo, Table), case Res of {error, Error} -> ?ERROR_MSG("Failed to create table ~s: ~p", @@ -740,10 +751,10 @@ should_update_schema(Host) -> false end. -preprocess_table(Host, Table) -> - Table1 = filter_table_sh(Table), +preprocess_table(SchemaInfo, Table) -> + Table1 = filter_table_sh(SchemaInfo, Table), ImplicitPK = - case ejabberd_option:sql_type(Host) of + case SchemaInfo#sql_schema_info.db_type of pgsql -> false; sqlite -> case lists:keyfind(bigserial, #sql_column.type, @@ -782,18 +793,30 @@ preprocess_table(Host, Table) -> end, Table1#sql_table{indices = Indices}. -preprocess_schemas(Host, Schemas) -> +preprocess_schemas(SchemaInfo, Schemas) -> lists:map( fun(Schema) -> Schema#sql_schema{ - tables = lists:map(fun(T) -> preprocess_table(Host, T) end, - Schema#sql_schema.tables)} + tables = lists:map( + fun(T) -> + preprocess_table(SchemaInfo, T) + end, + Schema#sql_schema.tables)} end, Schemas). update_schema(Host, Module, RawSchemas) -> case should_update_schema(Host) of true -> - Schemas = preprocess_schemas(Host, RawSchemas), + SchemaInfo = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + #sql_schema_info{ + db_type = DBType, + db_version = DBVersion, + new_schema = ejabberd_sql:use_new_schema()} + end), + Schemas = preprocess_schemas(SchemaInfo, RawSchemas), Version = get_current_version(Host, Module, Schemas), LastSchema = lists:max(Schemas), LastVersion = LastSchema#sql_schema.version, @@ -801,7 +824,7 @@ update_schema(Host, Module, RawSchemas) -> _ when Version < 0 -> ?ERROR_MSG("Can't update SQL schema for module ~p, please do it manually", [Module]); 0 -> - create_tables(Host, Module, LastSchema); + create_tables(Host, Module, SchemaInfo, LastSchema); LastVersion -> ok; _ when LastVersion < Version -> @@ -811,7 +834,8 @@ update_schema(Host, Module, RawSchemas) -> fun(Schema) -> if Schema#sql_schema.version > Version -> - do_update_schema(Host, Module, Schema); + do_update_schema(Host, Module, + SchemaInfo, Schema); true -> ok end @@ -821,7 +845,7 @@ update_schema(Host, Module, RawSchemas) -> ok end. -do_update_schema(Host, Module, Schema) -> +do_update_schema(Host, Module, SchemaInfo, Schema) -> lists:foreach( fun({add_column, TableName, ColumnName}) -> {value, Table} = @@ -833,9 +857,9 @@ do_update_schema(Host, Module, Schema) -> Res = ejabberd_sql:sql_query( Host, - fun(DBType, DBVersion) -> - Def = format_column_def(DBType, DBVersion, Column), - Default = format_default(DBType, DBVersion, Column), + fun(DBType, _DBVersion) -> + Def = format_column_def(SchemaInfo, Column), + Default = format_default(SchemaInfo, Column), SQLs = [[<<"ALTER TABLE ">>, TableName, @@ -914,11 +938,11 @@ do_update_schema(Host, Module, Schema) -> Res = ejabberd_sql:sql_query( Host, - fun(DBType, DBVersion) -> + fun() -> case Index#sql_index.meta of #{primary_key := true} -> SQL1 = format_add_primary_key( - DBType, DBVersion, Table, Index), + SchemaInfo, Table, Index), SQL = iolist_to_binary(SQL1), ?INFO_MSG("Add primary key ~s/~p:~n~s~n", [Table#sql_table.name, @@ -927,7 +951,7 @@ do_update_schema(Host, Module, Schema) -> ejabberd_sql:sql_query_t(SQL); _ -> SQL1 = format_create_index( - DBType, DBVersion, Table, Index), + SchemaInfo, Table, Index), SQL = iolist_to_binary(SQL1), ?INFO_MSG("Create index ~s/~p:~n~s~n", [Table#sql_table.name, @@ -993,6 +1017,96 @@ do_update_schema(Host, Module, Schema) -> end, Schema#sql_schema.update), store_version(Host, Module, Schema#sql_schema.version). + +print_schema(SDBType, SDBVersion, SNewSchema) -> + {DBType, DBVersion} = + case SDBType of + "pgsql" -> + case string:split(SDBVersion, ".") of + [SMajor, SMinor] -> + try {list_to_integer(SMajor), list_to_integer(SMinor)} of + {Major, Minor} -> + {pgsql, Major * 10000 + Minor} + catch _:_ -> + io:format("pgsql version be in the form of " + "Major.Minor, e.g. 16.1~n"), + {error, error} + end; + _ -> + io:format("pgsql version be in the form of " + "Major.Minor, e.g. 16.1~n"), + {error, error} + end; + "mysql" -> + case ejabberd_sql:parse_mysql_version(SDBVersion, 0) of + {ok, V} -> + {mysql, V}; + error -> + io:format("pgsql version be in the same form as " + "SELECT VERSION() returns~n"), + {error, error} + end; + "sqlite" -> + {sqlite, undefined}; + _ -> + io:format("db_type must be one of the following: " + "'pgsql', 'mysql', 'sqlite'~n"), + {error, error} + end, + NewSchema = + case SNewSchema of + "0" -> false; + "1" -> true; + "false" -> false; + "true" -> true; + _ -> + io:format("new_schema must be one of the following: " + "'0', '1', 'false', 'true'~n"), + error + end, + case {DBType, NewSchema} of + {error, _} -> ?STATUS_ERROR; + {_, error} -> ?STATUS_ERROR; + _ -> + SchemaInfo = + #sql_schema_info{ + db_type = DBType, + db_version = DBVersion, + new_schema = NewSchema}, + Mods = ejabberd_config:beams(all), + lists:foreach( + fun(Mod) -> + case erlang:function_exported(Mod, sql_schemas, 0) of + true -> + Schemas = Mod:sql_schemas(), + Schemas2 = preprocess_schemas(SchemaInfo, Schemas), + Schema = lists:max(Schemas2), + SQLs = + lists:flatmap( + fun(Table) -> + SQLs = format_create_table(SchemaInfo, Table), + PostSQLs = + case Table#sql_table.post_create of + undefined -> + []; + F when is_function(F, 1) -> + PSQLs = F(SchemaInfo), + lists:map( + fun(S) -> + [S, <<"\n">>] + end, PSQLs) + end, + SQLs ++ PostSQLs + end, Schema#sql_schema.tables), + io:format("~s~n", [SQLs]); + false -> + ok + end + end, Mods), + ?STATUS_SUCCESS + end. + + test() -> Schemas = [#sql_schema{ diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl index 230b4035a..7b1f9bbc9 100644 --- a/src/mod_announce_sql.erl +++ b/src/mod_announce_sql.erl @@ -31,6 +31,7 @@ -export([init/2, set_motd_users/2, set_motd/2, delete_motd/1, get_motd/1, is_motd_user/2, set_motd_user/2, import/3, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_announce.hrl"). @@ -41,10 +42,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl index da943da9a..0e54ee56c 100644 --- a/src/mod_bosh_sql.erl +++ b/src/mod_bosh_sql.erl @@ -29,6 +29,7 @@ %% API -export([init/0, open_session/2, close_session/1, find_session/1]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -38,7 +39,7 @@ %%%=================================================================== init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'bosh' table...", []), case ejabberd_sql:sql_query( @@ -50,7 +51,7 @@ init() -> Err end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl index 2a40dc497..06449175a 100644 --- a/src/mod_caps_sql.erl +++ b/src/mod_caps_sql.erl @@ -29,6 +29,7 @@ %% API -export([init/2, caps_read/2, caps_write/3, export/1, import/3]). +-export([sql_schemas/0]). -include("mod_caps.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -38,10 +39,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl index 0c8f0a222..317dc1daa 100644 --- a/src/mod_last_sql.erl +++ b/src/mod_last_sql.erl @@ -30,6 +30,7 @@ %% API -export([init/2, get_last/2, store_last_info/4, remove_user/2, import/2, export/1]). +-export([sql_schemas/0]). -include("mod_last.hrl"). -include("logger.hrl"). @@ -39,10 +40,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index f923b3e0b..8bc27b65d 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -32,6 +32,7 @@ extended_fields/0, store/10, write_prefs/4, get_prefs/2, select/7, export/1, remove_from_archive/3, is_empty_for_user/2, is_empty_for_room/3, select_with_mucsub/6, delete_old_messages_batch/4, count_messages_to_delete/3]). +-export([sql_schemas/0]). -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -44,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 2, tables = @@ -79,11 +80,10 @@ schemas() -> columns = [<<"server_host">>, <<"username">>, <<"origin_id">>]} ], post_create = - fun(mysql, _) -> - ejabberd_sql:sql_query_t( - <<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>); - (_, _) -> - ok + fun(#sql_schema_info{db_type = mysql}) -> + [<<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>]; + (_) -> + [] end}, #sql_table{ name = <<"archive_prefs">>, @@ -131,10 +131,10 @@ schemas() -> columns = [<<"server_host">>, <<"timestamp">>]} ], post_create = - fun(mysql, _) -> + fun(#sql_schema_info{db_type = mysql}) -> ejabberd_sql:sql_query_t( <<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>); - (_, _) -> + (_) -> ok end}, #sql_table{ diff --git a/src/mod_mix_pam_sql.erl b/src/mod_mix_pam_sql.erl index 606d306ce..af22c74f4 100644 --- a/src/mod_mix_pam_sql.erl +++ b/src/mod_mix_pam_sql.erl @@ -26,6 +26,7 @@ %% API -export([init/2, add_channel/3, get_channel/2, get_channels/1, del_channel/2, del_channels/1]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -34,10 +35,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_mix_sql.erl b/src/mod_mix_sql.erl index 74f341fb8..5d0c3d95d 100644 --- a/src/mod_mix_sql.erl +++ b/src/mod_mix_sql.erl @@ -27,6 +27,7 @@ -export([set_channel/6, get_channels/2, get_channel/3, del_channel/3]). -export([set_participant/6, get_participant/4, get_participants/3, del_participant/4]). -export([subscribe/5, unsubscribe/4, unsubscribe/5, get_subscribed/4]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -35,10 +36,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl index 9e12e30cb..a57628372 100644 --- a/src/mod_mqtt_sql.erl +++ b/src/mod_mqtt_sql.erl @@ -25,6 +25,7 @@ -export([init/0]). -export([subscribe/4, unsubscribe/2, find_subscriber/2]). -export([open_session/1, close_session/1, lookup_session/1, get_sessions/2]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -37,10 +38,10 @@ init() -> {error, db_failure}. init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index ea0c416b3..9e193a7df 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -42,6 +42,7 @@ find_online_room_by_pid/2, remove_user/2]). -export([set_affiliation/6, set_affiliations/4, get_affiliation/5, get_affiliations/3, search_affiliation/4]). +-export([sql_schemas/0]). -include_lib("xmpp/include/jid.hrl"). -include("mod_muc.hrl"). @@ -52,7 +53,7 @@ %%% API %%%=================================================================== init(Host, Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), case gen_mod:ram_db_mod(Opts, mod_muc) of ?MODULE -> clean_tables(Host); @@ -60,7 +61,7 @@ init(Host, Opts) -> ok end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index b1eb4ed89..1f48b7ea9 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -31,6 +31,7 @@ remove_old_messages/2, remove_user/2, read_message_headers/2, read_message/3, remove_message/3, read_all_messages/2, remove_all_messages/2, count_messages/2, import/1, export/1, remove_old_messages_batch/3]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_offline.hrl"). @@ -41,10 +42,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index 2811c6a8e..b00b62741 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -34,6 +34,8 @@ -export([item_to_raw/1, raw_to_item/1]). +-export([sql_schemas/0]). + -include_lib("xmpp/include/xmpp.hrl"). -include("mod_privacy.hrl"). -include("logger.hrl"). @@ -43,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl index 806867965..2c242a78a 100644 --- a/src/mod_private_sql.erl +++ b/src/mod_private_sql.erl @@ -28,6 +28,7 @@ %% API -export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2, import/3, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_private.hrl"). @@ -38,10 +39,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl index f1f9ab322..67ceeac21 100644 --- a/src/mod_proxy65_sql.erl +++ b/src/mod_proxy65_sql.erl @@ -26,6 +26,7 @@ %% API -export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -35,7 +36,7 @@ %%%=================================================================== init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), NodeS = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'proxy65' table...", []), case ejabberd_sql:sql_query( @@ -49,7 +50,7 @@ init() -> Err end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl index 8fd3865de..f0b65a6b2 100644 --- a/src/mod_pubsub_sql.erl +++ b/src/mod_pubsub_sql.erl @@ -20,6 +20,7 @@ %% API -export([init/3]). +-export([sql_schemas/0]). -include("ejabberd_sql_pt.hrl"). @@ -27,13 +28,13 @@ %%% API %%%=================================================================== init(_Host, ServerHost, _Opts) -> - ejabberd_sql_schema:update_schema(ServerHost, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(ServerHost, ?MODULE, sql_schemas()), ok. %%%=================================================================== %%% Internal functions %%%=================================================================== -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index 13de8c406..8e4cc1f1c 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -30,6 +30,7 @@ -export([init/2, store_session/6, lookup_session/4, lookup_session/3, lookup_sessions/3, lookup_sessions/2, lookup_sessions/1, delete_session/3, delete_old_sessions/2, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -40,10 +41,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 8012fca83..565861751 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -34,6 +34,7 @@ update_roster/4, del_roster/3, transaction/2, process_rosteritems/5, import/3, export/1, raw_to_record/2]). +-export([sql_schemas/0]). -include("mod_roster.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -44,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl index 9c5233d25..0bdd531d9 100644 --- a/src/mod_shared_roster_sql.erl +++ b/src/mod_shared_roster_sql.erl @@ -34,6 +34,7 @@ get_user_displayed_groups/3, is_user_in_group/3, add_user_to_group/3, remove_user_from_group/3, import/3, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/jid.hrl"). -include("mod_roster.hrl"). @@ -44,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index cb8a1e441..c67338f51 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -31,6 +31,7 @@ -export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4, remove_user/2, search_fields/1, search_reported/1, import/3, export/1]). -export([is_search_supported/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_vcard.hrl"). @@ -42,10 +43,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables =