25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-20 16:15:59 +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, -record(sql_index, {columns,
unique = false :: boolean()}). unique = false :: boolean(),
meta = #{}}).
-record(sql_column, {name :: binary(), -record(sql_column, {name :: binary(),
type, type,
default = false, default = false,

View File

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