25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

Improve compatibility with various db engine versions

This commit is contained in:
Paweł Chmielowski 2022-02-18 20:43:56 +01:00
parent 4247501dc6
commit d4bf29e3ff
2 changed files with 51 additions and 24 deletions

View File

@ -41,6 +41,7 @@
server_host_used = false, server_host_used = false,
used_vars = [], used_vars = [],
use_new_schema, use_new_schema,
need_timestamp_pass = false,
need_array_pass = false}). need_array_pass = false}).
-define(QUERY_RECORD, "sql_query"). -define(QUERY_RECORD, "sql_query").
@ -169,17 +170,24 @@ transform_sql(Arg) ->
Pos, no_server_host), Pos, no_server_host),
[] []
end, end,
case ParseRes#state.need_array_pass of case {ParseRes#state.need_array_pass, ParseRes#state.need_timestamp_pass} of
true -> {true, _} ->
{PR1, PR2} = perform_array_pass(ParseRes), {PR1, PR2} = perform_array_pass(ParseRes),
{PRO1, PRO2} = perform_array_pass(ParseResOld), {PRO1, PRO2} = perform_array_pass(ParseResOld),
set_pos(make_schema_check( set_pos(make_schema_check(
erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(pgsql), make_sql_query(PR2)]), erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(pgsql), make_sql_query(PR2, pgsql)]),
erl_syntax:tuple([erl_syntax:atom(any), make_sql_query(PR1)])]), erl_syntax:tuple([erl_syntax:atom(any), make_sql_query(PR1)])]),
erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(pgsql), make_sql_query(PRO2)]), erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(pgsql), make_sql_query(PRO2, pgsql)]),
erl_syntax:tuple([erl_syntax:atom(any), make_sql_query(PRO1)])])), erl_syntax:tuple([erl_syntax:atom(any), make_sql_query(PRO1)])])),
Pos); Pos);
false -> {_, true} ->
set_pos(make_schema_check(
erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(pgsql), make_sql_query(ParseRes, pgsql)]),
erl_syntax:tuple([erl_syntax:atom(any), make_sql_query(ParseRes)])]),
erl_syntax:list([erl_syntax:tuple([erl_syntax:atom(pgsql), make_sql_query(ParseResOld, pgsql)]),
erl_syntax:tuple([erl_syntax:atom(any), make_sql_query(ParseResOld)])])),
Pos);
_ ->
set_pos( set_pos(
make_schema_check( make_schema_check(
make_sql_query(ParseRes), make_sql_query(ParseRes),
@ -265,6 +273,8 @@ parse1([$@, $( | S], Acc, State) ->
[EVar]); [EVar]);
string -> string ->
EVar; EVar;
timestamp ->
EVar;
boolean -> boolean ->
erl_syntax:application( erl_syntax:application(
erl_syntax:atom(ejabberd_sql), erl_syntax:atom(ejabberd_sql),
@ -295,7 +305,7 @@ parse1([$%, $( | S], Acc, State) ->
erl_syntax:atom(?ESCAPE_RECORD), erl_syntax:atom(?ESCAPE_RECORD),
erl_syntax:atom(string)), erl_syntax:atom(string)),
[erl_syntax:variable(Name)]), [erl_syntax:variable(Name)]),
State3#state{'query' = [{var, Var}, State3#state{'query' = [{var, Var, Type},
{str, "server_host="} | {str, "server_host="} |
State3#state.'query'], State3#state.'query'],
args = [Convert | State3#state.args], args = [Convert | State3#state.args],
@ -327,21 +337,26 @@ parse1([$%, $( | S], Acc, State) ->
erl_syntax:atom(?ESCAPE_RECORD), erl_syntax:atom(?ESCAPE_RECORD),
erl_syntax:atom(IT2)), erl_syntax:atom(IT2)),
erl_syntax:variable(Name)]), erl_syntax:variable(Name)]),
State2#state{'query' = [[{var, Var}] | State2#state.'query'], State2#state{'query' = [[{var, Var, Type}] | State2#state.'query'],
need_array_pass = true, need_array_pass = true,
args = [[Convert, ConvertArr] | State2#state.args], args = [[Convert, ConvertArr] | State2#state.args],
params = [Var | State2#state.params], params = [Var | State2#state.params],
param_pos = State2#state.param_pos + 1, param_pos = State2#state.param_pos + 1,
used_vars = [Name | State2#state.used_vars]}; used_vars = [Name | State2#state.used_vars]};
_ -> _ ->
{TS, Type2} = case Type of
timestamp -> {true, string};
Other -> {State2#state.need_timestamp_pass, Other}
end,
Convert = Convert =
erl_syntax:application( erl_syntax:application(
erl_syntax:record_access( erl_syntax:record_access(
erl_syntax:variable(?ESCAPE_VAR), erl_syntax:variable(?ESCAPE_VAR),
erl_syntax:atom(?ESCAPE_RECORD), erl_syntax:atom(?ESCAPE_RECORD),
erl_syntax:atom(Type)), erl_syntax:atom(Type2)),
[erl_syntax:variable(Name)]), [erl_syntax:variable(Name)]),
State2#state{'query' = [{var, Var} | State2#state.'query'], State2#state{'query' = [{var, Var, Type} | State2#state.'query'],
need_timestamp_pass = TS,
args = [Convert | State2#state.args], args = [Convert | State2#state.args],
params = [Var | State2#state.params], params = [Var | State2#state.params],
param_pos = State2#state.param_pos + 1, param_pos = State2#state.param_pos + 1,
@ -359,7 +374,7 @@ parse1("%ESCAPE" ++ S, Acc, State) ->
[]), []),
Var = State1#state.param_pos, Var = State1#state.param_pos,
State2 = State2 =
State1#state{'query' = [{var, Var} | State1#state.'query'], State1#state{'query' = [{var, Var, string} | State1#state.'query'],
args = [Convert | State1#state.args], args = [Convert | State1#state.args],
params = [Var | State1#state.params], params = [Var | State1#state.params],
param_pos = State1#state.param_pos + 1}, param_pos = State1#state.param_pos + 1},
@ -397,6 +412,7 @@ parse_name([$), T | S], Acc, 0, IsArg, State) ->
$d -> integer; $d -> integer;
$s -> string; $s -> string;
$b -> boolean; $b -> boolean;
$t -> timestamp;
$H when IsArg -> host; $H when IsArg -> host;
_ -> _ ->
throw({error, State#state.loc, throw({error, State#state.loc,
@ -420,10 +436,10 @@ make_var(V) ->
perform_array_pass(State) -> perform_array_pass(State) ->
{NQ, PQ, Rest} = lists:foldl( {NQ, PQ, Rest} = lists:foldl(
fun([{var, _} = Var], {N, P, {str, Str} = Prev}) -> fun([{var, _, _} = Var], {N, P, {str, Str} = Prev}) ->
Str2 = re:replace(Str, "(^|\s+)in\s*$", " = any(", [{return, list}]), Str2 = re:replace(Str, "(^|\s+)in\s*$", " = any(", [{return, list}]),
{[Var, Prev | N], [{str, ")"}, Var, {str, Str2} | P], none}; {[Var, Prev | N], [{str, ")"}, Var, {str, Str2} | P], none};
([{var, _}], _) -> ([{var, _, _}], _) ->
throw({error, State#state.loc, ["List variable not following 'in' operator"]}); throw({error, State#state.loc, ["List variable not following 'in' operator"]});
(Other, {N, P, none}) -> (Other, {N, P, none}) ->
{N, P, Other}; {N, P, Other};
@ -445,16 +461,27 @@ perform_array_pass(State) ->
State#state{query = lists:reverse(PQ2), args = lists:reverse(PA), need_array_pass = false}}. State#state{query = lists:reverse(PQ2), args = lists:reverse(PA), need_array_pass = false}}.
make_sql_query(State) -> make_sql_query(State) ->
make_sql_query(State, unknown).
make_sql_query(State, Type) ->
Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}), Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}),
SHash = <<"Q", (integer_to_binary(Hash))/binary>>, SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
Query = pack_query(State#state.'query'), Query = pack_query(State#state.'query'),
EQuery = EQuery =
lists:map( lists:flatmap(
fun({str, S}) -> fun({str, S}) ->
[erl_syntax:binary(
[erl_syntax:binary_field(
erl_syntax:string(S))])];
({var, V, timestamp}) when Type == pgsql ->
[erl_syntax:binary(
[erl_syntax:binary_field(
erl_syntax:string("to_timestamp("))]),
make_var(V),
erl_syntax:binary( erl_syntax:binary(
[erl_syntax:binary_field( [erl_syntax:binary_field(
erl_syntax:string(S))]); erl_syntax:string(", 'YYYY-MM-DD HH24:MI:SS')"))])];
({var, V}) -> make_var(V) ({var, V, _}) -> [make_var(V)]
end, Query), end, Query),
erl_syntax:record_expr( erl_syntax:record_expr(
erl_syntax:atom(?QUERY_RECORD), erl_syntax:atom(?QUERY_RECORD),
@ -709,7 +736,7 @@ make_sql_upsert_pgsql901(Table, ParseRes0) ->
#state{'query' = [{str, " RETURNING *) "}]}, #state{'query' = [{str, " RETURNING *) "}]},
Insert Insert
]), ]),
Upsert = make_sql_query(State), Upsert = make_sql_query(State, pgsql),
erl_syntax:application( erl_syntax:application(
erl_syntax:atom(ejabberd_sql), erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(sql_query_t), erl_syntax:atom(sql_query_t),
@ -760,7 +787,7 @@ make_sql_upsert_pgsql905(Table, ParseRes0) ->
#state{'query' = [{str, ") DO UPDATE SET "}]}, #state{'query' = [{str, ") DO UPDATE SET "}]},
Set Set
]), ]),
Upsert = make_sql_query(State), Upsert = make_sql_query(State, pgsql),
erl_syntax:application( erl_syntax:application(
erl_syntax:atom(ejabberd_sql), erl_syntax:atom(ejabberd_sql),
erl_syntax:atom(sql_query_t), erl_syntax:atom(sql_query_t),
@ -884,12 +911,12 @@ resolve_vars(ST1, ST2) ->
end, ST1#state.params), end, ST1#state.params),
NewQuery = NewQuery =
lists:map( lists:map(
fun({var, Var}) -> fun({var, Var, Type}) ->
case dict:find(Var, Map) of case dict:find(Var, Map) of
{ok, New} -> {ok, New} ->
{var, New}; {var, New, Type};
error -> error ->
{var, Var} {var, Var, Type}
end; end;
(S) -> S (S) -> S
end, ST1#state.'query'), end, ST1#state.'query'),

View File

@ -67,7 +67,7 @@ store_room(LServer, Host, Name, Opts, ChangesHints) ->
SOpts = misc:term_to_expr(Opts2), SOpts = misc:term_to_expr(Opts2),
Timestamp = case lists:keyfind(hibernation_time, 1, Opts) of Timestamp = case lists:keyfind(hibernation_time, 1, Opts) of
false -> <<"1900-01-01 00:00:00">>; false -> <<"1900-01-01 00:00:00">>;
{_, undefined} -> <<"1900-01-01 00:00:00">>; {_, undefined} -> <<"1970-01-02 00:00:00">>;
{_, Time} -> usec_to_sql_timestamp(Time) {_, Time} -> usec_to_sql_timestamp(Time)
end, end,
F = fun () -> F = fun () ->
@ -77,7 +77,7 @@ store_room(LServer, Host, Name, Opts, ChangesHints) ->
"!host=%(Host)s", "!host=%(Host)s",
"server_host=%(LServer)s", "server_host=%(LServer)s",
"opts=%(SOpts)s", "opts=%(SOpts)s",
"created_at=%(Timestamp)s"]), "created_at=%(Timestamp)t"]),
case ChangesHints of case ChangesHints of
Changes when is_list(Changes) -> Changes when is_list(Changes) ->
[change_room(Host, Name, Change) || Change <- Changes]; [change_room(Host, Name, Change) || Change <- Changes];
@ -191,7 +191,7 @@ get_hibernated_rooms_older_than(LServer, Host, Timestamp) ->
case catch ejabberd_sql:sql_query( case catch ejabberd_sql:sql_query(
LServer, LServer,
?SQL("select @(name)s, @(opts)s from muc_room" ?SQL("select @(name)s, @(opts)s from muc_room"
" where host=%(Host)s and created_at < %(TimestampS)s and created_at > '1900-01-01 00:00:00'")) of " where host=%(Host)s and created_at < %(TimestampS)t and created_at > '1970-01-02 00:00:00'")) of
{selected, RoomOpts} -> {selected, RoomOpts} ->
lists:map( lists:map(
fun({Room, Opts}) -> fun({Room, Opts}) ->