25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-22 17:28:25 +01:00

Use the first unique index as a primary key in ejabberd_sql_schema

This commit is contained in:
Alexey Shchepin 2023-11-16 15:58:41 +03:00
parent 4ea46c5765
commit b0a9b58958
2 changed files with 193 additions and 63 deletions

View File

@ -53,7 +53,8 @@
-record(sql_index, {columns,
unique = false :: boolean()}).
unique = false :: boolean(),
meta = #{}}).
-record(sql_column, {name :: binary(),
type,
default = false,

View File

@ -351,12 +351,11 @@ guess_version(Host, Schemas) ->
fun(Schema) ->
lists:all(
fun(Table) ->
Table2 = filter_table_sh(Table),
CurrentColumns =
get_table_schema(
Host, Table2#sql_table.name),
Host, Table#sql_table.name),
check_columns_compatibility(
Table2#sql_table.columns,
Table#sql_table.columns,
CurrentColumns)
end, Schema#sql_schema.tables)
end, Schemas),
@ -568,6 +567,56 @@ format_create_index(mysql, _DBVersion, Table, Index) ->
end, Index#sql_index.columns)),
<<");">>].
format_primary_key(mysql, _DBVersion, Table) ->
case lists:filter(
fun(#sql_index{meta = #{primary_key := true}}) -> true;
(_) -> false
end, Table#sql_table.indices) of
[] -> [];
[I] ->
[[<<" ">>,
<<"PRIMARY KEY (">>,
lists:join(
<<", ">>,
lists:map(
fun(Col) ->
format_mysql_index_column(Table, Col)
end, I#sql_index.columns)),
<<")">>]]
end;
format_primary_key(_DBType, _DBVersion, Table) ->
case lists:filter(
fun(#sql_index{meta = #{primary_key := true}}) -> true;
(_) -> false
end, Table#sql_table.indices) of
[] -> [];
[I] ->
[[<<" ">>,
<<"PRIMARY KEY (">>,
lists:join(<<", ">>, I#sql_index.columns),
<<")">>]]
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) ->
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) ->
TableName = Table#sql_table.name,
[<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>,
lists:join(
<<", ">>,
lists:map(
fun(Col) ->
format_mysql_index_column(Table, Col)
end, Index#sql_index.columns)),
<<");">>].
format_create_table(pgsql = DBType, DBVersion, Table) ->
TableName = Table#sql_table.name,
[iolist_to_binary(
@ -576,13 +625,18 @@ format_create_table(pgsql = DBType, DBVersion, Table) ->
<<",\n">>,
lists:map(
fun(C) -> format_column_def(DBType, DBVersion, C) end,
Table#sql_table.columns)),
Table#sql_table.columns) ++
format_primary_key(DBType, DBVersion, Table)),
<<"\n);\n">>])] ++
lists:map(
fun(I) ->
iolist_to_binary(
[format_create_index(DBType, DBVersion, Table, I),
<<"\n">>])
lists:flatmap(
fun(#sql_index{meta = #{primary_key := true}}) ->
[];
(#sql_index{meta = #{ignore := true}}) ->
[];
(I) ->
[iolist_to_binary(
[format_create_index(DBType, DBVersion, Table, I),
<<"\n">>])]
end,
Table#sql_table.indices);
format_create_table(sqlite = DBType, DBVersion, Table) ->
@ -593,13 +647,18 @@ format_create_table(sqlite = DBType, DBVersion, Table) ->
<<",\n">>,
lists:map(
fun(C) -> format_column_def(DBType, DBVersion, C) end,
Table#sql_table.columns)),
Table#sql_table.columns) ++
format_primary_key(DBType, DBVersion, Table)),
<<"\n);\n">>])] ++
lists:map(
fun(I) ->
iolist_to_binary(
[format_create_index(DBType, DBVersion, Table, I),
<<"\n">>])
lists:flatmap(
fun(#sql_index{meta = #{primary_key := true}}) ->
[];
(#sql_index{meta = #{ignore := true}}) ->
[];
(I) ->
[iolist_to_binary(
[format_create_index(DBType, DBVersion, Table, I),
<<"\n">>])]
end,
Table#sql_table.indices);
format_create_table(mysql = DBType, DBVersion, Table) ->
@ -610,13 +669,18 @@ format_create_table(mysql = DBType, DBVersion, Table) ->
<<",\n">>,
lists:map(
fun(C) -> format_column_def(DBType, DBVersion, C) end,
Table#sql_table.columns)),
Table#sql_table.columns) ++
format_primary_key(DBType, DBVersion, Table)),
<<"\n) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n">>])] ++
lists:map(
fun(I) ->
iolist_to_binary(
[format_create_index(DBType, DBVersion, Table, I),
<<"\n">>])
lists:flatmap(
fun(#sql_index{meta = #{primary_key := true}}) ->
[];
(#sql_index{meta = #{ignore := true}}) ->
[];
(I) ->
[iolist_to_binary(
[format_create_index(DBType, DBVersion, Table, I),
<<"\n">>])]
end,
Table#sql_table.indices).
%format_create_table(DBType, _DBVersion, Table) ->
@ -644,12 +708,11 @@ create_table(Host, Table) ->
create_tables(Host, Module, Schema) ->
lists:foreach(
fun(Table) ->
Table2 = filter_table_sh(Table),
Res = create_table(Host, Table2),
Res = create_table(Host, Table),
case Res of
{error, Error} ->
?ERROR_MSG("Failed to create table ~s: ~p",
[Table2#sql_table.name, Error]),
[Table#sql_table.name, Error]),
error(Error);
_ ->
ok
@ -677,9 +740,60 @@ should_update_schema(Host) ->
false
end.
update_schema(Host, Module, Schemas) ->
preprocess_table(Host, Table) ->
Table1 = filter_table_sh(Table),
ImplicitPK =
case ejabberd_option:sql_type(Host) of
pgsql -> false;
sqlite ->
case lists:keyfind(bigserial, #sql_column.type,
Table1#sql_table.columns) of
false -> false;
#sql_column{name = Name} -> {ok, Name}
end;
mysql ->
case lists:keyfind(bigserial, #sql_column.type,
Table1#sql_table.columns) of
false -> false;
#sql_column{name = Name} -> {ok, Name}
end
end,
Indices =
case ImplicitPK of
false ->
{Inds, _} =
lists:mapfoldl(
fun(#sql_index{unique = true} = I, false) ->
{I#sql_index{
meta = (I#sql_index.meta)#{primary_key => true}},
true};
(I, Acc) ->
{I, Acc}
end, false, Table1#sql_table.indices),
Inds;
{ok, CN} ->
lists:map(
fun(#sql_index{columns = [CN1]} = I) when CN == CN1 ->
I#sql_index{
meta = (I#sql_index.meta)#{ignore => true}};
(I) -> I
end,
Table1#sql_table.indices)
end,
Table1#sql_table{indices = Indices}.
preprocess_schemas(Host, Schemas) ->
lists:map(
fun(Schema) ->
Schema#sql_schema{
tables = lists:map(fun(T) -> preprocess_table(Host, 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),
Version = get_current_version(Host, Module, Schemas),
LastSchema = lists:max(Schemas),
LastVersion = LastSchema#sql_schema.version,
@ -730,7 +844,7 @@ do_update_schema(Host, Module, Schema) ->
<<" DEFAULT ">>,
Default, <<";\n">>]] ++
case Column#sql_column.default of
false ->
false when DBType /= sqlite ->
[[<<"ALTER TABLE ">>,
TableName,
<<" ALTER COLUMN ">>,
@ -779,45 +893,57 @@ do_update_schema(Host, Module, Schema) ->
_ ->
ok
end;
({create_index, TableName, Columns}) ->
{value, Table1} =
lists:keysearch(
TableName, #sql_table.name, Schema#sql_schema.tables),
{value, Index1} =
lists:keysearch(
Columns, #sql_index.columns, Table1#sql_table.indices),
Table = filter_table_sh(Table1),
Index =
({create_index, TableName, Columns1}) ->
Columns =
case ejabberd_sql:use_new_schema() of
true ->
Index1;
Columns1;
false ->
Index1#sql_index{
columns =
lists:delete(
<<"server_host">>, Index1#sql_index.columns)
}
lists:delete(
<<"server_host">>, Columns1)
end,
Res =
ejabberd_sql:sql_query(
Host,
fun(DBType, DBVersion) ->
SQL1 = format_create_index(
DBType, DBVersion, Table, Index),
SQL = iolist_to_binary(SQL1),
?INFO_MSG("Create index ~s/~p:~n~s~n",
[Table#sql_table.name,
Index#sql_index.columns,
SQL]),
ejabberd_sql:sql_query_t(SQL)
end),
case Res of
{error, Error} ->
?ERROR_MSG("Failed to update table ~s: ~p",
[TableName, Error]),
error(Error);
{value, Table} =
lists:keysearch(
TableName, #sql_table.name, Schema#sql_schema.tables),
{value, Index} =
lists:keysearch(
Columns, #sql_index.columns, Table#sql_table.indices),
case Index#sql_index.meta of
#{ignore := true} -> ok;
_ ->
ok
Res =
ejabberd_sql:sql_query(
Host,
fun(DBType, DBVersion) ->
case Index#sql_index.meta of
#{primary_key := true} ->
SQL1 = format_add_primary_key(
DBType, DBVersion, Table, Index),
SQL = iolist_to_binary(SQL1),
?INFO_MSG("Add primary key ~s/~p:~n~s~n",
[Table#sql_table.name,
Index#sql_index.columns,
SQL]),
ejabberd_sql:sql_query_t(SQL);
_ ->
SQL1 = format_create_index(
DBType, DBVersion, Table, Index),
SQL = iolist_to_binary(SQL1),
?INFO_MSG("Create index ~s/~p:~n~s~n",
[Table#sql_table.name,
Index#sql_index.columns,
SQL]),
ejabberd_sql:sql_query_t(SQL)
end
end),
case Res of
{error, Error} ->
?ERROR_MSG("Failed to update table ~s: ~p",
[TableName, Error]),
error(Error);
_ ->
ok
end
end;
({drop_index, TableName, Columns1}) ->
Columns =
@ -888,7 +1014,9 @@ test() ->
#sql_column{name = <<"type">>, type = text},
#sql_column{name = <<"created_at">>, type = timestamp,
default = true}],
indices = [#sql_index{
indices = [#sql_index{columns = [<<"id">>],
unique = true},
#sql_index{
columns = [<<"server_host">>, <<"username">>, <<"timestamp">>]},
#sql_index{
columns = [<<"server_host">>, <<"username">>, <<"peer">>]},
@ -905,7 +1033,8 @@ test() ->
[<<"server_host">>, <<"origin_id">>]},
{drop_index, <<"archive2">>,
[<<"server_host">>, <<"origin_id">>]},
{drop_column, <<"archive2">>, <<"origin_id">>}
{drop_column, <<"archive2">>, <<"origin_id">>},
{create_index, <<"archive2">>, [<<"id">>]}
]},
#sql_schema{
version = 1,