Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepared queries for modules #3039

Merged
merged 88 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
0afdd09
MAM: Use prepared queries for message retraction #2961 - first commit
arcusfelis Nov 25, 2020
257b122
Make lookups prepared in mod_mam
arcusfelis Nov 27, 2020
0e37eb0
Reorder function in mod_mam_rdbms_arch.erl
arcusfelis Nov 27, 2020
fccb037
Convert gdpr query to prepared
arcusfelis Nov 27, 2020
90f1c94
Add maybe_reserve helper
arcusfelis Nov 27, 2020
8a1ce37
Compactify code for do_lookup_messages
arcusfelis Nov 27, 2020
e91f797
Adress review commits
arcusfelis Dec 1, 2020
5e53699
Use lookup_field record in mod_mam_rdbms_arch
arcusfelis Dec 1, 2020
c659c15
Refactor opt_count logic (less black magic)
arcusfelis Dec 1, 2020
c4b3c3a
Add Index Hint
arcusfelis Dec 1, 2020
d089389
Reorder code
arcusfelis Dec 1, 2020
a608dbf
Add db_mappings
arcusfelis Dec 1, 2020
b7fb067
Remove GDPR helpers - use standard functions
arcusfelis Dec 1, 2020
8a77ec7
Make prepare_value pure by introducing env_vars
arcusfelis Dec 1, 2020
aeb81dc
Use env_vars for lookups
arcusfelis Dec 1, 2020
9f7d0eb
Use env_vars for retraction
arcusfelis Dec 1, 2020
dc5f3c5
Use generic lookup for GDPR data extraction in mod_mam
arcusfelis Dec 1, 2020
4edd718
Shorten calc_count specs
arcusfelis Dec 1, 2020
ed85595
Add specs for with_env functions
arcusfelis Dec 1, 2020
8b76586
Use lookup_query for archive_size
arcusfelis Dec 1, 2020
a808845
Pass env into lookup_query
arcusfelis Dec 2, 2020
fc3e0c9
Put RSM logic into mam_lookup
arcusfelis Dec 2, 2020
69f8c72
Prettify mam_lookup
arcusfelis Dec 2, 2020
8d8e574
Put code on one line where is possible in mod_mam_rdbms_arch
arcusfelis Dec 2, 2020
595fa97
Move some code into encode_search_body
arcusfelis Dec 2, 2020
4431279
Reorder some functions
arcusfelis Dec 2, 2020
cf23924
Move unescape_binary into decode_packet
arcusfelis Dec 2, 2020
67bec6d
Move some code into mam_filter and mam_lookup_sql
arcusfelis Dec 2, 2020
6ff66da
Rename some env fields
arcusfelis Dec 2, 2020
69ba044
Put env getters on top of mam_lookup_sql
arcusfelis Dec 2, 2020
4ed2310
Split make_tombstone function
arcusfelis Dec 2, 2020
33a9fb9
Move some code into mam_encoder
arcusfelis Dec 2, 2020
10b2a83
Use Ext prefix in decoders
arcusfelis Dec 2, 2020
3bc2887
Move common code into mam_decoder
arcusfelis Dec 2, 2020
f219b9e
Reorder code in mod_mam_rdbms_arch
arcusfelis Dec 2, 2020
63c2f6f
Use generic logic for mod_mam_muc_rdbms_arch
arcusfelis Dec 2, 2020
2e1113d
Address review comments
arcusfelis Dec 2, 2020
4d92b6d
Pass Offset and Limit as a separate arg
arcusfelis Dec 3, 2020
2e9b77a
Fix GDPR MAM MUC query
arcusfelis Dec 3, 2020
fbd6816
Improve type specs
arcusfelis Dec 3, 2020
1cee3ce
Remove unused column ids
arcusfelis Dec 3, 2020
5ac689c
Order MAM MUC GDPR messages by ID
arcusfelis Dec 3, 2020
b947736
Rename limit_offset_to_args to offset_limit_to_args
arcusfelis Dec 3, 2020
bd285c5
Fix mam_muc_extract_gdpr_messages on MSSQL
arcusfelis Dec 3, 2020
2f09123
Define mod_mam_rdbms_arch:env_vars() type
arcusfelis Dec 3, 2020
a51c52b
Replace lower with less
arcusfelis Dec 9, 2020
0d5fedc
Use boolean type for lookup_field.required field
arcusfelis Dec 9, 2020
935ba50
Add specs for decoder/encoder
arcusfelis Dec 9, 2020
9f77738
Remove mod_mam_utils:get_retract_id/3
arcusfelis Dec 9, 2020
d69b867
Apply code review comments
arcusfelis Dec 10, 2020
617ece1
Add mongoose_rdbms:execute_successfully
arcusfelis Dec 14, 2020
536d086
Pass binary into mongoose_rdbms:prepare/4 from mam modules
arcusfelis Dec 14, 2020
2339544
MAM: Tweak prepared_statements ETS table #2961 - final commit
arcusfelis Dec 14, 2020
dc81ee7
MAM2: Use prepared queries in mod_mam_rdbms_user #2977 - first commit
arcusfelis Dec 10, 2020
401122d
Convert mod_mam_rdbms_prefs to prepared queries
arcusfelis Dec 10, 2020
e8d6939
Declare queries in INSERT, SELECT, DELETE order
arcusfelis Dec 10, 2020
39e4821
Do not start mod_mam_rdbms_user for elasticsearch_and_cassandra_mnesi…
arcusfelis Dec 16, 2020
428fd77
MAM2: Move transaction_with_delayed_retry/3 #2977 - final commit
arcusfelis Dec 16, 2020
ecfac0c
ROSTER: Move sql queries into mod_roster_rdbms #2985 - first commit
arcusfelis Dec 18, 2020
c3dae11
Remove mod_mam_utils:success_sql_query/2
arcusfelis Dec 18, 2020
c561809
Use prepared queries in mod_roster
arcusfelis Dec 18, 2020
528204b
Properly decode subscription and ask fields in mod_roster_rdbms
arcusfelis Jan 4, 2021
2a4e62d
Remove unused roster_sub_get_by_jid query
arcusfelis Jan 7, 2021
5b12c22
Simplify error handling in mod_roster_rdbms
arcusfelis Jan 7, 2021
4f3a28b
ROSTER: Edit comment for character_to_integer #2985 - final commit
arcusfelis Jan 8, 2021
c406764
Use prepared queries in mod_privacy_rdbms #3004
arcusfelis Jan 12, 2021
88f4394
MUC-light: Make select_room_id prepared #3011 - first commit
arcusfelis Jan 18, 2021
69cbb98
Split create_room function into two
arcusfelis Jan 18, 2021
463f358
Use prepared query for update_room_version and delete_room functions
arcusfelis Jan 18, 2021
975cc35
Use prepared function for select_affs/2 in MUC-light
arcusfelis Jan 18, 2021
d07bad7
Use prepared function for select_affs/1 in MUC-light
arcusfelis Jan 18, 2021
a63262d
Add decode_affs/1 function
arcusfelis Jan 18, 2021
93d349f
User prepared insert_aff function
arcusfelis Jan 18, 2021
55d581e
User prepared update_aff function
arcusfelis Jan 18, 2021
84d73f1
Use prepared delete_aff/delete_affs functions
arcusfelis Jan 18, 2021
456d6cd
Make get_config function prepared
arcusfelis Jan 18, 2021
f64038e
Use prepared functions for insert, update and delete room configs
arcusfelis Jan 18, 2021
c5d3348
Use prepared queries for muc-light blocking
arcusfelis Jan 18, 2021
b8cabbe
Fix types for affiliations
arcusfelis Jan 19, 2021
1ff7f42
Fix muc-light queries for ODBC
arcusfelis Jan 19, 2021
d575c88
Fix comment and typespec based on review
arcusfelis Jan 26, 2021
c7783ab
Fix indention in mod_muc_light_db_rdbms
arcusfelis Jan 26, 2021
5935d79
Replace find_key with field_to_odbc_type
arcusfelis Jan 27, 2021
2bf2ec3
MUC-light: Match everything in create_room_transaction LCs #3011 - fi…
arcusfelis Jan 27, 2021
d6df02b
Use prepared queries for mod_last #3019
arcusfelis Feb 1, 2021
cd9ffe7
Insert only a diff of privacy_data #3004
arcusfelis Feb 18, 2021
d5ebedc
Use prepared queries for mod_offline #3040
arcusfelis Feb 4, 2021
a8d9cab
Use prepared queries for mod_muc_db_rdbms #3028
arcusfelis Feb 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions big_tests/tests/mam_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -616,15 +616,19 @@ init_per_group(muc_rsm04, Config) ->
init_per_group(Group, ConfigIn) ->
C = configuration(Group),
B = basic_group(Group),
case init_modules(C, B, ConfigIn) of
try init_modules(C, B, ConfigIn) of
skip ->
{skip, print_configuration_not_supported(C, B)};
Config0 ->
ct:pal("Init per group ~p; configuration ~p; basic group ~p",
[Group, C, B]),
Config1 = do_init_per_group(C, Config0),
[{basic_group, B}, {configuration, C} | init_state(C, B, Config1)]
end.
catch Class:Reason:Stacktrace ->
ct:pal("Failed to start configuration=~p basic_group=~p",
[C, B]),
erlang:raise(Class, Reason, Stacktrace)
end.

backup_module_opts(Module) ->
{{params_backup, Module}, rpc_apply(gen_mod, get_module_opts, [host(), mod_mam_muc])}.
Expand Down Expand Up @@ -753,7 +757,11 @@ init_modules(rdbms_mnesia_cache, C, Config) when C =:= muc_all;
Config;
init_modules(BackendType, muc_light, Config) ->
Config1 = init_modules_for_muc_light(BackendType, Config),
init_module(host(), mod_mam_rdbms_user, [muc, pm]),
case BackendType of
cassandra -> ok;
elasticsearch -> ok;
_ -> init_module(host(), mod_mam_rdbms_user, [muc, pm])
end,
Config1;
init_modules(rdbms, C, Config) ->
init_module(host(), mod_mam, addin_mam_options(C, Config)),
Expand Down
2 changes: 2 additions & 0 deletions include/mongoose_mam.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-record(db_mapping, {column :: atom(), param :: atom(), format :: atom()}).
-record(lookup_field, {op :: atom(), column :: atom(), param :: atom(), required = false :: boolean(), value_maker :: atom()}).
2 changes: 1 addition & 1 deletion priv/azuresql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ CREATE TABLE [dbo].[privacy_list_data](
[t] [char](1) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[value] [varchar](max) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[action] [char](1) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[ord] [bigint] NOT NULL,
[ord] [int] NOT NULL,
[match_all] [smallint] NOT NULL,
[match_iq] [smallint] NOT NULL,
[match_message] [smallint] NOT NULL,
Expand Down
2 changes: 1 addition & 1 deletion priv/mssql2012.sql
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ CREATE TABLE [dbo].[privacy_list_data](
[t] [char](1) NOT NULL,
[value] [nvarchar](max) NOT NULL,
[action] [char](1) NOT NULL,
[ord] [bigint] NOT NULL,
[ord] [int] NOT NULL,
[match_all] [smallint] NOT NULL,
[match_iq] [smallint] NOT NULL,
[match_message] [smallint] NOT NULL,
Expand Down
4 changes: 2 additions & 2 deletions priv/mysql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ CREATE INDEX i_last_seconds ON last(seconds);

CREATE TABLE rosterusers (
username varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
jid varchar(250) NOT NULL, -- must be a parsable jid
nick text NOT NULL,
subscription character(1) NOT NULL,
ask character(1) NOT NULL,
Expand Down Expand Up @@ -157,7 +157,7 @@ CREATE TABLE privacy_list_data (
t character(1) NOT NULL,
value text NOT NULL,
action character(1) NOT NULL,
ord bigint NOT NULL,
ord INT NOT NULL,
match_all boolean NOT NULL,
match_iq boolean NOT NULL,
match_message boolean NOT NULL,
Expand Down
2 changes: 1 addition & 1 deletion priv/pg.sql
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ CREATE TABLE privacy_list_data (
t character(1) NOT NULL,
value text NOT NULL,
action character(1) NOT NULL,
ord NUMERIC NOT NULL,
ord INT NOT NULL,
match_all boolean NOT NULL,
match_iq boolean NOT NULL,
match_message boolean NOT NULL,
Expand Down
2 changes: 1 addition & 1 deletion rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
{<<"eini">>,{pkg,<<"eini">>,<<"1.2.7">>},1},
{<<"eodbc">>,
{git,"https://github.com/arcusfelis/eodbc.git",
{ref,"612461e4d8e12c0947a67e0bfdeb854010db0b2d"}},
{ref,"1823d8fe6f5fbe2d8724a9649b75ebd5b8738661"}},
0},
{<<"eper">>,
{git,"http://github.com/basho/eper.git",
Expand Down
53 changes: 53 additions & 0 deletions src/mam/mam_decoder.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-module(mam_decoder).
-export([decode_row/2]).
-export([decode_muc_row/2]).
-export([decode_muc_gdpr_row/2]).
-export([decode_retraction_info/2]).

-type ext_mess_id() :: non_neg_integer() | binary().
-type env_vars() :: mod_mam_rdbms_arch:env_vars().
-type db_row() :: {ext_mess_id(), ExtSrcJID :: binary(), ExtData :: binary()}.
-type db_muc_row() :: {ext_mess_id(), Nick :: binary(), ExtData :: binary()}.
-type db_muc_gdpr_row() :: {ext_mess_id(), ExtData :: binary()}.
-type decoded_muc_gdpr_row() :: {ext_mess_id(), exml:element()}.
-type retraction_info() :: #{packet := exml:element(), message_id := mod_mam:message_id()}.

-spec decode_row(db_row(), env_vars()) -> mod_mam:message_row().
decode_row({ExtMessID, ExtSrcJID, ExtData}, Env) ->
MessID = mongoose_rdbms:result_to_integer(ExtMessID),
SrcJID = decode_jid(ExtSrcJID, Env),
Packet = decode_packet(ExtData, Env),
{MessID, SrcJID, Packet}.

-spec decode_muc_row(db_muc_row(), env_vars()) -> mod_mam:message_row().
decode_muc_row({ExtMessID, Nick, ExtData}, Env = #{archive_jid := RoomJID}) ->
MessID = mongoose_rdbms:result_to_integer(ExtMessID),
SrcJID = jid:replace_resource(RoomJID, Nick),
Packet = decode_packet(ExtData, Env),
{MessID, SrcJID, Packet}.

-spec decode_muc_gdpr_row(db_muc_gdpr_row(), env_vars()) -> decoded_muc_gdpr_row().
decode_muc_gdpr_row({ExtMessID, ExtData}, Env) ->
Packet = decode_packet(ExtData, Env),
{ExtMessID, Packet}.

-spec decode_retraction_info(env_vars(), [] | [{mod_mam:message_id(), binary()}]) ->
skip | retraction_info().
decode_retraction_info(_Env, []) -> skip;
decode_retraction_info(Env, [{ResMessID, Data}]) ->
Packet = decode_packet(Data, Env),
MessID = mongoose_rdbms:result_to_integer(ResMessID),
#{packet => Packet, message_id => MessID}.

-spec decode_jid(binary(), env_vars()) -> jid:jid().
decode_jid(ExtJID, #{db_jid_codec := Codec, archive_jid := ArcJID}) ->
mam_jid:decode(Codec, ArcJID, ExtJID).

-spec decode_packet(binary(), env_vars()) -> exml:element().
decode_packet(ExtBin, Env = #{db_message_codec := Codec}) ->
Bin = unescape_binary(ExtBin, Env),
mam_message:decode(Codec, Bin).

-spec unescape_binary(binary(), env_vars()) -> binary().
unescape_binary(Bin, #{host := Host}) ->
mongoose_rdbms:unescape_binary(Host, Bin).
89 changes: 89 additions & 0 deletions src/mam/mam_encoder.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
-module(mam_encoder).
-export([encode_message/3]).
-export([encode_jid/2]).
-export([encode_direction/1]).
-export([encode_packet/2]).
-export([extend_lookup_params/2]).

-include("mongoose.hrl").
-include("jlib.hrl").
-include_lib("exml/include/exml.hrl").
-include("mongoose_mam.hrl").

-type value_type() :: int | maybe_string | direction | bare_jid | jid | jid_resource | xml | search.
-type env_vars() :: mod_mam_rdbms_arch:env_vars().
-type db_mapping() :: #db_mapping{}.
-type encoded_field_value() :: term().

-spec extend_lookup_params(mam_iq:lookup_params(), env_vars()) -> mam_iq:lookup_params().
extend_lookup_params(#{start_ts := Start, end_ts := End, with_jid := WithJID,
borders := Borders, search_text := SearchText} = Params, Env) ->
Params#{norm_search_text => mod_mam_utils:normalize_search_text(SearchText),
start_id => make_start_id(Start, Borders),
end_id => make_end_id(End, Borders),
remote_bare_jid => maybe_encode_bare_jid(WithJID, Env),
remote_resource => jid_to_non_empty_resource(WithJID)}.

-spec encode_message(mod_mam:archive_message_params(), env_vars(), list(db_mapping())) ->
[encoded_field_value()].
encode_message(Params, Env, Mappings) ->
[encode_value_using_mapping(Params, Env, Mapping) || Mapping <- Mappings].

encode_value_using_mapping(Params, Env, #db_mapping{param = Param, format = Format}) ->
Value = maps:get(Param, Params),
encode_value(Format, Value, Env).

-spec encode_value(value_type(), term(), env_vars()) -> encoded_field_value().
encode_value(int, Value, _Env) when is_integer(Value) ->
Value;
encode_value(maybe_string, none, _Env) ->
null;
encode_value(maybe_string, Value, _Env) when is_binary(Value) ->
Value;
encode_value(direction, Value, _Env) ->
encode_direction(Value);
encode_value(bare_jid, Value, Env) ->
encode_jid(jid:to_bare(Value), Env);
encode_value(jid, Value, Env) ->
encode_jid(Value, Env);
encode_value(jid_resource, #jid{lresource = Res}, _Env) ->
Res;
encode_value(xml, Value, Env) ->
encode_packet(Value, Env);
encode_value(search, Value, Env) ->
encode_search_body(Value, Env).

encode_direction(incoming) -> <<"I">>;
encode_direction(outgoing) -> <<"O">>.

make_start_id(Start, Borders) ->
StartID = maybe_encode_compact_uuid(Start, 0),
mod_mam_utils:apply_start_border(Borders, StartID).

make_end_id(End, Borders) ->
EndID = maybe_encode_compact_uuid(End, 255),
mod_mam_utils:apply_end_border(Borders, EndID).

maybe_encode_compact_uuid(undefined, _) ->
undefined;
maybe_encode_compact_uuid(Microseconds, NodeID) ->
mod_mam_utils:encode_compact_uuid(Microseconds, NodeID).

jid_to_non_empty_resource(undefined) -> undefined;
jid_to_non_empty_resource(#jid{lresource = <<>>}) -> undefined;
jid_to_non_empty_resource(#jid{lresource = Res}) -> Res.

-spec encode_jid(jid:jid(), env_vars()) -> binary().
encode_jid(JID, #{db_jid_codec := Codec, archive_jid := ArcJID}) ->
mam_jid:encode(Codec, ArcJID, JID).

maybe_encode_bare_jid(undefined, _Env) -> undefined;
maybe_encode_bare_jid(JID, Env) -> encode_jid(jid:to_bare(JID), Env).

-spec encode_packet(exml:element(), env_vars()) -> binary().
encode_packet(Packet, #{db_message_codec := Codec}) ->
mam_message:encode(Codec, Packet).

-spec encode_search_body(exml:element(), env_vars()) -> binary().
encode_search_body(Packet, #{has_full_text_search := SearchEnabled}) ->
mod_mam_utils:packet_to_search_body(SearchEnabled, Packet).
52 changes: 52 additions & 0 deletions src/mam/mam_filter.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
%% Produces filters based on lookup params
-module(mam_filter).
-export([produce_filter/2]).
-include("mongoose_mam.hrl").

-type column() :: atom().

-type filter_field() :: {like, column(), binary()}
| {le, column(), integer()}
| {ge, column(), integer()}
| {equal, column(), integer() | binary()}
| {less, column(), integer()}
| {greater, column(), integer()}.

-type filter() :: [filter_field()].
-type fields() :: [#lookup_field{}].
-type params() :: map().

-export_type([filter_field/0]).
-export_type([filter/0]).

-define(SEARCH_WORDS_LIMIT, 10).

-spec produce_filter(params(), fields()) -> list(filter_field()).
produce_filter(Params, Fields) ->
[new_filter(Field, Value)
|| Field <- Fields,
Value <- field_to_values(Field, Params)].

field_to_values(#lookup_field{param = Param, value_maker = ValueMaker, required = Required} = Field, Params) ->
case maps:find(Param, Params) of
{ok, Value} when Value =/= undefined ->
make_value(ValueMaker, Value);
Other when Required ->
error(#{reason => missing_required_field, field => Field, params => Params, result => Other});
_ ->
[]
end.

make_value(search_words, Value) -> search_words(Value);
make_value(undefined, Value) -> [Value]. %% Default value_maker

new_filter(#lookup_field{op = Op, column = Column}, Value) ->
{Op, Column, Value}.

%% Constructs a separate LIKE filter for each word.
%% SearchText example is "word1%word2%word3".
%% Order of words does not matter (they can go in any order).
-spec search_words(binary()) -> list(binary()).
search_words(SearchText) ->
Words = binary:split(SearchText, <<"%">>, [global]),
[<<"%", Word/binary, "%">> || Word <- lists:sublist(Words, ?SEARCH_WORDS_LIMIT)].
Loading