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 - DRAFT 2 #3025

Closed
wants to merge 90 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
1eaf9d6
Use prepared queries for message retraction
arcusfelis Nov 25, 2020
5deea9b
Make lookups prepared in mod_mam
arcusfelis Nov 27, 2020
fa55d70
Reorder function in mod_mam_rdbms_arch.erl
arcusfelis Nov 27, 2020
82d5a26
Convert gdpr query to prepared
arcusfelis Nov 27, 2020
d2dff9e
ADd maybe_reserve helper
arcusfelis Nov 27, 2020
bf7e379
Compactify code for do_lookup_messages
arcusfelis Nov 27, 2020
cba1489
Adress review commits
arcusfelis Dec 1, 2020
9c1cd65
Use lookup_field record in mod_mam_rdbms_arch
arcusfelis Dec 1, 2020
075f01c
Refactor opt_count logic (less black magic)
arcusfelis Dec 1, 2020
12f38fd
Add Index Hint
arcusfelis Dec 1, 2020
7662cd7
Reorder code
arcusfelis Dec 1, 2020
449a697
Add db_mappings
arcusfelis Dec 1, 2020
eccad30
Remove GDPR helpers - use standard functions
arcusfelis Dec 1, 2020
9a03005
Make prepare_value pure by introducing env_vars
arcusfelis Dec 1, 2020
c34bc62
Use env_vars for lookups
arcusfelis Dec 1, 2020
a44b2c7
Use env_vars for retraction
arcusfelis Dec 1, 2020
60abce3
Use generic lookup for GDPR data extraction in mod_mam
arcusfelis Dec 1, 2020
e432f9a
Shorten calc_count specs
arcusfelis Dec 1, 2020
11bb92c
Add specs for with_env functions
arcusfelis Dec 1, 2020
0074bcf
Use lookup_query for archive_size
arcusfelis Dec 1, 2020
c260175
Pass env into lookup_query
arcusfelis Dec 2, 2020
4047ef4
Put RSM logic into mam_lookup
arcusfelis Dec 2, 2020
10b89b8
Prettify mam_lookup
arcusfelis Dec 2, 2020
45fc7ef
Put code on one line where is possible in mod_mam_rdbms_arch
arcusfelis Dec 2, 2020
82f06a5
Move some code into encode_search_body
arcusfelis Dec 2, 2020
a004b4a
Reorder some functions
arcusfelis Dec 2, 2020
d00636b
Move unescape_binary into decode_packet
arcusfelis Dec 2, 2020
a63e56e
Move some code into mam_filter and mam_lookup_sql
arcusfelis Dec 2, 2020
f73537b
Rename some env fields
arcusfelis Dec 2, 2020
e5ab9dc
Put env getters on top of mam_lookup_sql
arcusfelis Dec 2, 2020
329da67
Split make_tombstone function
arcusfelis Dec 2, 2020
5c4610f
Move some code into mam_encoder
arcusfelis Dec 2, 2020
f124125
Use Ext prefix in decoders
arcusfelis Dec 2, 2020
26256d7
Move common code into mam_decoder
arcusfelis Dec 2, 2020
9d9f458
Reorder code in mod_mam_rdbms_arch
arcusfelis Dec 2, 2020
6a791c4
Use generic logic for mod_mam_muc_rdbms_arch
arcusfelis Dec 2, 2020
f185a92
Address review comments
arcusfelis Dec 2, 2020
446f625
Pass Offset and Limit as a separate arg
arcusfelis Dec 3, 2020
0667308
Fix GDPR MAM MUC query
arcusfelis Dec 3, 2020
c97fa58
Improve type specs
arcusfelis Dec 3, 2020
87b0ae2
Remove unused column ids
arcusfelis Dec 3, 2020
18e4468
Order MAM MUC GDPR messages by ID
arcusfelis Dec 3, 2020
c03619c
Rename limit_offset_to_args to offset_limit_to_args
arcusfelis Dec 3, 2020
1d7603b
Fix mam_muc_extract_gdpr_messages on MSSQL
arcusfelis Dec 3, 2020
1093095
Define mod_mam_rdbms_arch:env_vars() type
arcusfelis Dec 3, 2020
fe77da1
Replace lower with less
arcusfelis Dec 9, 2020
a6edd28
Use boolean type for lookup_field.required field
arcusfelis Dec 9, 2020
a214f42
ADd specs for decoder/encoder
arcusfelis Dec 9, 2020
2f4d8e4
Remove mod_mam_utils:get_retract_id/3
arcusfelis Dec 9, 2020
8cbfb25
Apply code review comments
arcusfelis Dec 10, 2020
b5e0d64
Add mongoose_rdbms:execute_successfully
arcusfelis Dec 14, 2020
9f4d1c9
Pass binary into mongoose_rdbms:prepare/4 from mam modules
arcusfelis Dec 14, 2020
33a85ba
Store statement as binary in prepared_statements ETS table
arcusfelis Dec 14, 2020
c9e98e7
Use prepared queries in mod_mam_rdbms_user
arcusfelis Dec 10, 2020
49275d8
Convert mod_mam_rdbms_prefs to prepared queries
arcusfelis Dec 10, 2020
a203b7f
Declare queries in INSERT, SELECT, DELETE order
arcusfelis Dec 10, 2020
77f4964
Do not start mod_mam_rdbms_user for elasticsearch_and_cassandra_mnesi…
arcusfelis Dec 16, 2020
214ff4d
Move transaction_with_delayed_retry/3 into mongoose_rdbms
arcusfelis Dec 16, 2020
64e1e78
Move sql queries into mod_roster_rdbms
arcusfelis Dec 18, 2020
367dbb3
Remove mod_mam_utils:success_sql_query/2
arcusfelis Dec 18, 2020
2adf351
Use prepared queries in mod_roster
arcusfelis Dec 18, 2020
48a2600
Properly decode subscription and ask fields in mod_roster_rdbms
arcusfelis Jan 4, 2021
8a6bc38
Remove unused roster_sub_get_by_jid query
arcusfelis Jan 7, 2021
5f2e6d7
Simplify error handling in mod_roster_rdbms
arcusfelis Jan 7, 2021
3c0ccb1
Edit comment for character_to_integer
arcusfelis Jan 8, 2021
45f94ff
Use prepared queries in mod_privacy_rdbms
arcusfelis Jan 12, 2021
0a8789e
Make select_room_id/select_room_id_and_version prepared
arcusfelis Jan 18, 2021
0530760
Split create_room function into two
arcusfelis Jan 18, 2021
ccac7a3
Use prepared query for update_room_version and delete_room functions
arcusfelis Jan 18, 2021
243c616
Use prepared function for select_affs/2 in MUC-light
arcusfelis Jan 18, 2021
d7ec037
Use prepared function for select_affs/1 in MUC-light
arcusfelis Jan 18, 2021
fb3bf67
Add decode_affs/1 function
arcusfelis Jan 18, 2021
0d0caf5
User prepared insert_aff function
arcusfelis Jan 18, 2021
943dbc3
User prepared update_aff function
arcusfelis Jan 18, 2021
a03955b
Use prepared delete_aff/delete_affs functions
arcusfelis Jan 18, 2021
261e5ae
Make get_config function prepared
arcusfelis Jan 18, 2021
8ae524b
Use prepared functions for insert, update and delete room configs
arcusfelis Jan 18, 2021
6ed98cf
Use prepared queries for muc-light blocking
arcusfelis Jan 18, 2021
93114ee
Fix types for affiliations
arcusfelis Jan 19, 2021
c91fdf7
Fix muc-light queries for ODBC
arcusfelis Jan 19, 2021
c3cb438
Fix comment and typespec based on review
arcusfelis Jan 26, 2021
1d4f143
Fix indention in mod_muc_light_db_rdbms
arcusfelis Jan 26, 2021
3b6a716
Replace find_key with field_to_odbc_type
arcusfelis Jan 27, 2021
7fd536c
Ensure we match everything in create_room_transaction LCs
arcusfelis Jan 27, 2021
abc5a1a
Use prepared queries for mod_last
arcusfelis Feb 1, 2021
c7974d7
Remove unused rdbms_queries:update/5 function
arcusfelis Feb 4, 2021
6b3306f
Insert only a diff of privacy_data
arcusfelis Feb 18, 2021
4cfbea6
Merge pull request #3034 from esl/mu-prepared-queries-privacy-set-fix
chrzaszcz Feb 19, 2021
03006ce
Use prepared queries for mod_muc_db_rdbms
arcusfelis Feb 4, 2021
872c8f2
Merge pull request #3028 from esl/mu-prepared-queries-muc-2
DenysGonchar Feb 23, 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