Skip to content

Commit 7a53192

Browse files
committed
Extend timestamp type handling in sql macros
1 parent 6263a93 commit 7a53192

File tree

4 files changed

+82
-44
lines changed

4 files changed

+82
-44
lines changed

include/ejabberd_sql.hrl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@
3636
flags :: non_neg_integer(),
3737
loc :: {module(), {pos_integer(), pos_integer()}}}).
3838

39-
-record(sql_escape, {string :: fun((binary()) -> binary()),
40-
integer :: fun((integer()) -> binary()),
41-
boolean :: fun((boolean()) -> binary()),
42-
in_array_string :: fun((binary()) -> binary()),
43-
like_escape :: fun(() -> binary())}).
39+
-record(sql_escape, {
40+
string :: fun((binary()) -> binary() | atom()),
41+
integer :: fun((integer()) -> binary() | atom()),
42+
boolean :: fun((boolean()) -> binary() | atom()),
43+
timestamp :: fun((calendar:datetime()) -> binary() | atom()),
44+
in_array_string :: fun((binary()) -> binary() | atom()),
45+
like_escape :: fun(() -> binary() | atom())
46+
}).
4447

4548

4649
-record(sql_index, {columns,

src/ejabberd_sql.erl

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
to_string_literal/2,
5353
to_string_literal_t/1,
5454
to_bool/1,
55+
to_timestamp/2,
5556
sqlite_db/1,
5657
sqlite_file/1,
5758
encode_term/1,
@@ -286,6 +287,23 @@ to_bool(true) -> true;
286287
to_bool(1) -> true;
287288
to_bool(_) -> false.
288289

290+
escape_timestamp({{Y, Mo, D}, {H, Mi, S}}) ->
291+
list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0B "
292+
"~2..0B:~2..0B:~2..0B",
293+
[Y, Mo, D, H, Mi, S])).
294+
295+
296+
to_timestamp({Y, {H, M, S, _}}, mysql_prepared) -> {Y, {H, M, S}};
297+
to_timestamp(<<TS:64/signed-big-integer>>, pgsql_prepared) ->
298+
calendar:gregorian_seconds_to_datetime(
299+
calendar:datetime_to_gregorian_seconds({{2000, 1, 1}, {0, 0, 0}}) + TS div 1_000_000);
300+
to_timestamp(<<Y:4/binary, $-, Mo:2/binary, $-, D:2/binary, " ",
301+
H:2/binary, $:, Mi:2/binary, $:, S:2/binary>>,
302+
_) ->
303+
{{binary_to_integer(Y), binary_to_integer(Mo), binary_to_integer(D)},
304+
{binary_to_integer(H), binary_to_integer(Mi), binary_to_integer(S)}}.
305+
306+
289307
to_list(EscapeFun, Val) ->
290308
Escaped = lists:join(<<",">>, lists:map(EscapeFun, Val)),
291309
[<<"(">>, Escaped, <<")">>].
@@ -813,7 +831,8 @@ select_sql_query([{_, _} | Rest], Type, Version, Query) ->
813831
generic_sql_query(SQLQuery) ->
814832
sql_query_format_res(
815833
sql_query_internal(generic_sql_query_format(SQLQuery)),
816-
SQLQuery).
834+
SQLQuery,
835+
generic).
817836

818837
generic_sql_query_format(SQLQuery) ->
819838
Args = (SQLQuery#sql_query.args)(generic_escape()),
@@ -825,14 +844,16 @@ generic_escape() ->
825844
boolean = fun(true) -> <<"1">>;
826845
(false) -> <<"0">>
827846
end,
847+
timestamp = fun(X) -> <<"'", (escape_timestamp(X))/binary, "'">> end,
828848
in_array_string = fun(X) -> <<"'", (escape(X))/binary, "'">> end,
829849
like_escape = fun() -> <<"">> end
830850
}.
831851

832852
pgsql_sql_query(SQLQuery) ->
833853
sql_query_format_res(
834854
sql_query_internal(pgsql_sql_query_format(SQLQuery)),
835-
SQLQuery).
855+
SQLQuery,
856+
pgsql).
836857

837858
pgsql_sql_query_format(SQLQuery) ->
838859
Args = (SQLQuery#sql_query.args)(pgsql_escape()),
@@ -844,14 +865,16 @@ pgsql_escape() ->
844865
boolean = fun(true) -> <<"'t'">>;
845866
(false) -> <<"'f'">>
846867
end,
868+
timestamp = fun(X) -> <<"E'", (escape_timestamp(X))/binary, "'">> end,
847869
in_array_string = fun(X) -> <<"E'", (escape(X))/binary, "'">> end,
848870
like_escape = fun() -> <<"ESCAPE E'\\\\'">> end
849871
}.
850872

851873
sqlite_sql_query(SQLQuery) ->
852874
sql_query_format_res(
853875
sql_query_internal(sqlite_sql_query_format(SQLQuery)),
854-
SQLQuery).
876+
SQLQuery,
877+
sqlite).
855878

856879
sqlite_sql_query_format(SQLQuery) ->
857880
Args = (SQLQuery#sql_query.args)(sqlite_escape()),
@@ -863,6 +886,7 @@ sqlite_escape() ->
863886
boolean = fun(true) -> <<"1">>;
864887
(false) -> <<"0">>
865888
end,
889+
timestamp = fun(X) -> <<"'", (escape_timestamp(X))/binary, "'">> end,
866890
in_array_string = fun(X) -> <<"'", (standard_escape(X))/binary, "'">> end,
867891
like_escape = fun() -> <<"ESCAPE '\\'">> end
868892
}.
@@ -894,12 +918,15 @@ pgsql_prepare(SQLQuery, State) ->
894918
Query = (SQLQuery#sql_query.format_query)(Args),
895919
pgsql:prepare(State#state.db_ref, SQLQuery#sql_query.hash, Query).
896920

921+
897922
pgsql_execute_escape() ->
898-
#sql_escape{string = fun(X) -> X end,
899-
integer = fun(X) -> [misc:i2l(X)] end,
900-
boolean = fun(true) -> "1";
901-
(false) -> "0"
902-
end,
923+
#sql_escape{
924+
string = fun(X) -> X end,
925+
integer = fun(X) -> misc:i2l(X) end,
926+
boolean = fun(true) -> <<"1">>;
927+
(false) -> <<"0">>
928+
end,
929+
timestamp = fun escape_timestamp/1,
903930
in_array_string = fun(X) -> <<"\"", (escape(X))/binary, "\"">> end,
904931
like_escape = fun() -> ignore end
905932
}.
@@ -913,33 +940,46 @@ pgsql_execute_sql_query(SQLQuery, State) ->
913940
% timer:tc(pgsql, execute, [State#state.db_ref, SQLQuery#sql_query.hash, Args]),
914941
% io:format("T ~ts ~p~n", [SQLQuery#sql_query.hash, T]),
915942
Res = pgsql_execute_to_odbc(ExecuteRes),
916-
sql_query_format_res(Res, SQLQuery).
943+
sql_query_format_res(Res, SQLQuery, pgsql_prepared).
944+
917945

918946
mysql_prepared_execute(#sql_query{hash = Hash} = Query, State) ->
919-
ValEsc = #sql_escape{like_escape = fun() -> ignore end, _ = fun(X) -> X end},
920-
TypesEsc = #sql_escape{string = fun(_) -> string end,
921-
integer = fun(_) -> integer end,
922-
boolean = fun(_) -> bool end,
947+
ValEsc = #sql_escape{
948+
like_escape = fun() -> ignore end,
949+
timestamp = fun escape_timestamp/1,
950+
_ = fun(X) -> X end
951+
},
952+
TypesEsc = #sql_escape{
953+
string = fun(_) -> string end,
954+
integer = fun(_) -> integer end,
955+
boolean = fun(_) -> bool end,
956+
timestamp = fun(_) -> string end,
923957
in_array_string = fun(_) -> string end,
924958
like_escape = fun() -> ignore end},
925959
Val = [X || X <- (Query#sql_query.args)(ValEsc), X /= ignore],
926960
Types = [X || X <- (Query#sql_query.args)(TypesEsc), X /= ignore],
927961
QueryFn = fun() ->
928-
PrepEsc = #sql_escape{like_escape = fun() -> <<>> end, _ = fun(_) -> <<"?">> end},
929-
(Query#sql_query.format_query)((Query#sql_query.args)(PrepEsc))
930-
end,
962+
PrepEsc = #sql_escape{like_escape = fun() -> <<>> end, _ = fun(_) -> <<"?">> end},
963+
(Query#sql_query.format_query)((Query#sql_query.args)(PrepEsc))
964+
end,
931965
QueryTimeout = query_timeout(State#state.host),
932-
Res = p1_mysql_conn:prepared_query(State#state.db_ref, QueryFn, Hash, Val, Types,
933-
self(), [{timeout, QueryTimeout - 1000}]),
966+
Res = p1_mysql_conn:prepared_query(State#state.db_ref,
967+
QueryFn,
968+
Hash,
969+
Val,
970+
Types,
971+
self(),
972+
[{timeout, QueryTimeout - 1000}]),
934973
Res2 = mysql_to_odbc(Res),
935-
sql_query_format_res(Res2, Query).
974+
sql_query_format_res(Res2, Query, mysql_prepared).
975+
936976

937-
sql_query_format_res({selected, _, Rows}, SQLQuery) ->
977+
sql_query_format_res({selected, _, Rows}, SQLQuery, DbType) ->
938978
Res =
939979
lists:flatmap(
940980
fun(Row) ->
941981
try
942-
[(SQLQuery#sql_query.format_res)(Row)]
982+
[(SQLQuery#sql_query.format_res)(Row, DbType)]
943983
catch
944984
Class:Reason:StackTrace ->
945985
?ERROR_MSG("Error while processing SQL query result:~n"
@@ -950,7 +990,7 @@ sql_query_format_res({selected, _, Rows}, SQLQuery) ->
950990
end
951991
end, Rows),
952992
{selected, Res};
953-
sql_query_format_res(Res, _SQLQuery) ->
993+
sql_query_format_res(Res, _SQLQuery, _DbType) ->
954994
Res.
955995

956996
sql_query_to_iolist(SQLQuery) ->

src/ejabberd_sql_pt.erl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,10 @@ parse1([$@, $( | S], Acc, State) ->
293293
string ->
294294
EVar;
295295
timestamp ->
296-
EVar;
296+
erl_syntax:application(
297+
erl_syntax:atom(ejabberd_sql),
298+
erl_syntax:atom(to_timestamp),
299+
[EVar, erl_syntax:variable("__DbType")]);
297300
boolean ->
298301
erl_syntax:application(
299302
erl_syntax:atom(ejabberd_sql),
@@ -364,19 +367,16 @@ parse1([$%, $( | S], Acc, State) ->
364367
param_pos = State2#state.param_pos + 1,
365368
used_vars = [Name | State2#state.used_vars]};
366369
_ ->
367-
{TS, Type2} = case Type of
368-
timestamp -> {true, string};
369-
Other -> {State2#state.need_timestamp_pass, Other}
370-
end,
371370
Convert =
372371
erl_syntax:application(
373372
erl_syntax:record_access(
374373
erl_syntax:variable(?ESCAPE_VAR),
375374
erl_syntax:atom(?ESCAPE_RECORD),
376-
erl_syntax:atom(Type2)),
375+
erl_syntax:atom(Type)),
377376
[erl_syntax:variable(Name)]),
378-
State2#state{'query' = [{var, Var, Type} | State2#state.'query'],
379-
need_timestamp_pass = TS,
377+
State2#state{
378+
'query' = [{var, Var, Type} | State2#state.'query'],
379+
need_timestamp_pass = Type == timestamp orelse State2#state.need_timestamp_pass,
380380
args = [Convert | State2#state.args],
381381
params = [Var | State2#state.params],
382382
param_pos = State2#state.param_pos + 1,
@@ -532,7 +532,7 @@ make_sql_query(State, Type) ->
532532
erl_syntax:atom(format_res),
533533
erl_syntax:fun_expr(
534534
[erl_syntax:clause(
535-
[erl_syntax:list(State#state.res_vars)],
535+
[erl_syntax:list(State#state.res_vars), erl_syntax:variable("__DbType")],
536536
none,
537537
[erl_syntax:tuple(State#state.res)]
538538
)])),

src/mod_muc_sql.erl

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ store_room(LServer, Host, Name, Opts, ChangesHints) ->
147147
end,
148148
SOpts = misc:term_to_expr(Opts2),
149149
Timestamp = case lists:keyfind(hibernation_time, 1, Opts) of
150-
false -> <<"1970-01-02 00:00:00">>;
151-
{_, undefined} -> <<"1970-01-02 00:00:00">>;
150+
false -> {{1970, 1, 2}, {0, 0, 0}};
151+
{_, undefined} -> {{1970, 1, 2}, {0, 0, 0}};
152152
{_, Time} -> usec_to_sql_timestamp(Time)
153153
end,
154154
F = fun () ->
@@ -759,9 +759,4 @@ clean_tables(ServerHost) ->
759759

760760
usec_to_sql_timestamp(Timestamp) ->
761761
TS = misc:usec_to_now(Timestamp),
762-
case calendar:now_to_universal_time(TS) of
763-
{{Year, Month, Day}, {Hour, Minute, Second}} ->
764-
list_to_binary(io_lib:format("~4..0B-~2..0B-~2..0B "
765-
"~2..0B:~2..0B:~2..0B",
766-
[Year, Month, Day, Hour, Minute, Second]))
767-
end.
762+
calendar:now_to_universal_time(TS).

0 commit comments

Comments
 (0)