mnesia_rocksdb/test/mnesia_rocksdb_indexes_SUITE.erl
2024-08-13 10:36:31 +02:00

394 lines
14 KiB
Erlang

%%----------------------------------------------------------------
%% Copyright (c) 2013-2016 Klarna AB
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%----------------------------------------------------------------
-module(mnesia_rocksdb_indexes_SUITE).
-export([
all/0
, groups/0
, suite/0
, init_per_suite/1
, end_per_suite/1
, init_per_group/2
, end_per_group/2
, init_per_testcase/2
, end_per_testcase/2
]).
-export([
index_plugin_mgmt/1
, add_indexes/1
, delete_indexes/1
, create_bag_index/1
, create_ordered_index/1
, test_1_ram_copies/1
, test_1_disc_copies/1
, fail_1_disc_only/1
, plugin_ram_copies1/1
, plugin_ram_copies2/1
, plugin_ram_copies3/1
, plugin_disc_copies/1
, fail_plugin_disc_only/1
, plugin_disc_copies_bag/1
, plugin_rdb_ordered/1
, plugin_rdb_none/1
, index_iterator/1
]).
-include_lib("common_test/include/ct.hrl").
-export([run/0,
run/1,
r1/0]).
-define(TAB(T), list_to_atom(lists:flatten(io_lib:fwrite("~w_~w", [T, ?LINE])))).
run() ->
run([]).
run(Config) ->
mnesia:stop(),
maybe_set_dir(Config),
ok = mnesia_rocksdb_tlib:start_mnesia(reset),
test(1, ram_copies, r1),
test(1, disc_copies, d1),
fail(test, [1, disc_only_copies, do1]), % doesn't support ordered
test(2, disc_only_copies, do1),
fail(test, [1, rdb, l1]), % doesn't support bag
test(3, rdb, l1),
add_del_indexes(),
{atomic,ok} = mnesia_schema:add_index_plugin(
{pfx},mnesia_rocksdb, ix_prefixes),
test_index_plugin(cfg([pr1, ram_copies, ordered], Config)),
test_index_plugin(cfg([pr2, ram_copies, bag], Config)),
test_index_plugin(cfg([pr3, ram_copies, none], Config)),
test_index_plugin(cfg([pd1, disc_copies, ordered], Config)),
fail(test_index_plugin, [cfg([pd2, disc_only_copies, ordered], Config)]),
test_index_plugin(cfg([pd2, disc_copies, bag], Config)),
test_index_plugin(cfg([pl2, rdb, ordered], Config)),
test_index_plugin(cfg([pl3, rdb, none], Config)),
index_plugin_mgmt(Config),
ok.
suite() ->
[].
all() ->
[{group, all_tests}].
groups() ->
[
{all_tests, [sequence], [ {group, mgmt}, {group, access}, {group, plugin} ]}
, {mgmt, [sequence], [
create_bag_index
, create_ordered_index
, index_plugin_mgmt
, add_indexes
, delete_indexes
]}
, {access, [sequence], [
test_1_ram_copies
, test_1_disc_copies
, fail_1_disc_only
, index_iterator
]}
, {plugin, [sequence], [
plugin_ram_copies1
, plugin_ram_copies2
, plugin_ram_copies3
, plugin_disc_copies
, fail_plugin_disc_only
, plugin_disc_copies_bag
, plugin_rdb_ordered
, plugin_rdb_none
]}
].
%% ======================================================================
init_per_suite(Config) ->
mnesia:stop(),
maybe_set_dir(Config),
Config.
end_per_suite(_) ->
ok.
init_per_group(Grp, Config) ->
mnesia_rocksdb_tlib:restart_reset_mnesia(),
case Grp of
plugin ->
{atomic,ok} = mnesia_schema:add_index_plugin(
{pfx},mnesia_rocksdb, ix_prefixes);
_ ->
ok
end,
Config.
end_per_group(_, _) ->
ok.
init_per_testcase(_, Config) ->
Config.
end_per_testcase(_, _) ->
ok.
%% ======================================================================
cfg([Tab, Type, IxType], Config) ->
[{my_config, #{tab => Tab, type => Type, ixtype => IxType}} | Config];
cfg(Cfg, Config) when is_map(Cfg) -> [{my_config, Cfg} | Config].
cfg(Config) -> ?config(my_config, Config).
%% ======================================================================
r1() ->
mnesia:stop(),
ok = mnesia_rocksdb_tlib:start_mnesia(reset),
{atomic,ok} = mnesia_schema:add_index_plugin(
{pfx},mnesia_rocksdb, ix_prefixes),
dbg:tracer(),
dbg:tpl(mnesia_schema,x),
dbg:tpl(mnesia_index,x),
dbg:p(all,[c]),
test_index_plugin(cfg([pd2, disc_only_copies, ordered], [])).
fail(F, Args) ->
try apply(?MODULE, F, Args),
error(should_fail)
catch
error:R when R =/= should_fail ->
io:fwrite("apply(~p, ~p, ~p) -> fails as expected~n",
[?MODULE, F, Args])
end.
test_1_ram_copies( _Cfg) -> test(1, ram_copies, r1).
test_1_disc_copies(_Cfg) -> test(1, disc_copies, d1).
fail_1_disc_only( _Cfg) -> fail(test, [1, disc_only_copies, do1]).
plugin_ram_copies1(Cfg) -> test_index_plugin(cfg([pr1, ram_copies, ordered], Cfg)).
plugin_ram_copies2(Cfg) -> test_index_plugin(cfg([pr2, ram_copies, bag], Cfg)).
plugin_ram_copies3(Cfg) -> test_index_plugin(cfg([pr3, ram_copies, none], Cfg)).
plugin_disc_copies(Cfg) -> test_index_plugin(cfg([pd1, disc_copies, ordered], Cfg)).
fail_plugin_disc_only(Cfg) -> fail(test_index_plugin, [cfg([pd2, disc_only_copies, ordered], Cfg)]).
plugin_disc_copies_bag(Cfg) -> test_index_plugin(cfg([pd2, disc_copies, bag], Cfg)).
plugin_rdb_ordered(Cfg) -> test_index_plugin(cfg([pl2, rdb, ordered], Cfg)).
plugin_rdb_none(Cfg) -> test_index_plugin(cfg([pl3, rdb, none], Cfg)).
test(N, Type, T) ->
{atomic, ok} = mnesia:create_table(T, [{Type,[node()]},
{attributes,[k,a,b,c]},
{index, indexes(N)}]),
ok = test_index(N, T).
add_del_indexes() ->
{atomic, ok} = mnesia:del_table_index(r1, a),
{aborted, _} = mnesia:del_table_index(r1, a),
{atomic, ok} = mnesia:add_table_index(r1, a),
{aborted, _} = mnesia:add_table_index(r1, a),
{atomic, ok} = mnesia:del_table_index(d1, a),
{atomic, ok} = mnesia:add_table_index(d1, a),
{atomic, ok} = mnesia:del_table_index(do1, a),
{atomic, ok} = mnesia:add_table_index(do1, a),
{atomic, ok} = mnesia:del_table_index(l1, a),
{atomic, ok} = mnesia:add_table_index(l1, a),
io:fwrite("add_del_indexes() -> ok~n", []).
test_index_plugin(Config) ->
#{tab := Tab, type := Type, ixtype := IxType} = cfg(Config),
{atomic, ok} = mnesia:create_table(Tab, [{Type, [node()]},
{index, [ixtype(IxType)]}]),
mnesia:dirty_write({Tab, "foobar", "sentence"}),
mnesia:dirty_write({Tab, "yellow", "sensor"}),
mnesia:dirty_write({Tab, "truth", "white"}),
mnesia:dirty_write({Tab, "fulcrum", "white"}),
Res1 = [{Tab, "foobar", "sentence"}, {Tab, "yellow", "sensor"}],
Res2 = [{Tab, "fulcrum", "white"}, {Tab, "truth", "white"}],
if IxType == bag ->
Res1 = lists:sort(mnesia:dirty_index_read(Tab,<<"sen">>, {pfx})),
Res2 = lists:sort(mnesia:dirty_index_read(Tab,<<"whi">>, {pfx})),
[{Tab,"foobar","sentence"}] = mnesia:dirty_index_read(
Tab, <<"foo">>, {pfx});
IxType == ordered; IxType == none ->
Res1 = lists:sort(mnesia:dirty_index_read(Tab,<<"sen">>, {pfx})),
Res2 = lists:sort(mnesia:dirty_index_read(Tab,<<"whi">>, {pfx})),
[{Tab,"foobar","sentence"}] = mnesia:dirty_index_read(
Tab, <<"foo">>, {pfx})
end,
if Type == rdb ->
Res1 = lists:sort(mrdb:index_read(Tab,<<"sen">>, {pfx})),
Res2 = lists:sort(mrdb:index_read(Tab,<<"whi">>, {pfx})),
[{Tab,"foobar","sentence"}] = mrdb:index_read(
Tab, <<"foo">>, {pfx});
true ->
ok
end.
ixtype(T) when T==bag;
T==ordered ->
{{pfx}, T};
ixtype(none) ->
{pfx}.
create_bag_index(_Config) ->
{aborted, {combine_error, _, _}} =
mnesia:create_table(bi, [{rdb, [node()]}, {index, [{val, bag}]}]),
ok.
create_ordered_index(_Config) ->
{atomic, ok} =
mnesia:create_table(oi, [{rdb, [node()]}, {index, [{val, ordered}]}]),
ok.
add_indexes(_Config) ->
T = ?TAB(t1),
{atomic, ok} = mnesia:create_table(T, [{rdb, [node()]}, {attributes, [k, a, b, c]}]),
{atomic, ok} = mnesia:add_table_index(T, a),
ok.
delete_indexes(_Config) ->
T = ?TAB(t1),
{atomic, ok} = mnesia:create_table(T, [{rdb, [node()]}, {attributes, [k, a, b]}]),
ok = mrdb:insert(T, {T, a, 1, 1}),
ok = mrdb:insert(T, {T, b, 1, 2}),
{atomic, ok} = mnesia:add_table_index(T, a),
[{1, {T,a,1,1}}, {1, {T,b,1,2}}] =
mrdb_index:rev_fold(T, a, fun ix_fold_acc/3, []),
{atomic, ok} = mnesia:del_table_index(T, a),
ok = mrdb:delete(T, b),
ok = mrdb:insert(T, {T, c, 2, 3}),
{atomic, ok} = mnesia:add_table_index(T, a),
[{1, {T,a,1,1}}, {2, {T,c,2,3}}] =
mrdb_index:rev_fold(T, a, fun ix_fold_acc/3, []),
ok.
ix_fold_acc(K, V, Acc) ->
[{K, V} | Acc].
index_plugin_mgmt(_Config) ->
{aborted,_} = mnesia:create_table(x, [{index,[{unknown}]}]),
{aborted,_} = mnesia:create_table(x, [{index,[{{unknown},bag}]}]),
{aborted,_} = mnesia:create_table(x, [{index,[{{unknown},ordered}]}]),
{atomic,ok} = mnesia_schema:add_index_plugin(
{t}, mnesia_rocksdb,ix_prefixes),
{atomic,ok} = mnesia_schema:delete_index_plugin({t}),
{aborted,{bad_type,x,_}} =
mnesia:create_table(x, [{index,[{{t},ordered}]}]),
%% re-add plugin
{atomic,ok} = mnesia_schema:add_index_plugin(
{t}, mnesia_rocksdb,ix_prefixes),
{atomic,ok} =
mnesia:create_table(x, [{index,[{{t},ordered}]}]),
{aborted,{plugin_in_use,{t}}} =
mnesia_schema:delete_index_plugin({t}).
test_index(1, T) ->
L2 = [{T,K,x,y,z} || K <- lists:seq(4,6)],
L1 = [{T,K,a,b,c} || K <- lists:seq(1,3)],
true = lists:all(fun(X) -> X == ok end,
[mnesia:dirty_write(Obj) || Obj <- L1 ++ L2]),
L1 = lists:sort(mnesia:dirty_index_read(T,a,a)),
L1 = lists:sort(mnesia:dirty_index_read(T,a,3)),
L1 = mnesia:dirty_index_read(T,b,b),
L1 = lists:sort(mnesia:dirty_index_read(T,c,c)),
L2 = lists:sort(mnesia:dirty_index_read(T,x,a)),
L2 = lists:sort(mnesia:dirty_index_read(T,x,3)),
L2 = mnesia:dirty_index_read(T,y,b),
L2 = lists:sort(mnesia:dirty_index_read(T,z,c)),
io:fwrite("test_index(1, ~p) -> ok~n", [T]),
ok;
test_index(2, T) ->
L1 = [{T,K,a,b,c} || K <- lists:seq(1,3)],
L2 = [{T,K,x,y,z} || K <- lists:seq(4,6)],
true = lists:all(fun(X) -> X == ok end,
[mnesia:dirty_write(Obj) || Obj <- L1 ++ L2]),
L1 = lists:sort(mnesia:dirty_index_read(T,a,a)),
L1 = lists:sort(mnesia:dirty_index_read(T,a,3)),
L1 = lists:sort(mnesia:dirty_index_read(T,b,b)),
L1 = lists:sort(mnesia:dirty_index_read(T,c,c)),
L2 = lists:sort(mnesia:dirty_index_read(T,x,a)),
L2 = lists:sort(mnesia:dirty_index_read(T,x,3)),
L2 = lists:sort(mnesia:dirty_index_read(T,y,b)),
L2 = lists:sort(mnesia:dirty_index_read(T,z,c)),
io:fwrite("test_index(1, ~p) -> ok~n", [T]),
ok;
test_index(3, T) ->
L2 = [{T,K,x,y,z} || K <- lists:seq(4,6)],
L1 = [{T,K,a,b,c} || K <- lists:seq(1,3)],
true = lists:all(fun(X) -> X == ok end,
[mnesia:dirty_write(Obj) || Obj <- L1 ++ L2]),
L1 = mnesia:dirty_index_read(T,a,a),
L1 = mnesia:dirty_index_read(T,a,3),
L1 = mnesia:dirty_index_read(T,b,b),
L1 = mnesia:dirty_index_read(T,c,c),
L2 = mnesia:dirty_index_read(T,x,a),
L2 = mnesia:dirty_index_read(T,x,3),
L2 = mnesia:dirty_index_read(T,y,b),
L2 = mnesia:dirty_index_read(T,z,c),
io:fwrite("test_index(1, ~p) -> ok~n", [T]),
ok.
index_iterator(_Cfg) ->
T = ?TAB(it),
Attrs = [ {rdb,[node()]}
, {record_name, i}
, {attributes, [k,a,b]}
, {index, [a,b]} ],
{atomic, ok} = mnesia:create_table(T, Attrs),
ct:log("created tab T=~p: ~p", [T, Attrs]),
L1 = [{i,K,a,y} || K <- lists:seq(4,6)],
L2 = [{i,K,b,x} || K <- lists:seq(1,3)],
true = lists:all(fun(X) -> X == ok end,
[mnesia:dirty_write(T, Obj) || Obj <- L1 ++ L2]),
ct:log("inserted ~p", [L1 ++ L2]),
ResA = [{a,X} || X <- L1] ++ [{b,Y} || Y <- L2],
ResB = [{x,X} || X <- L2] ++ [{y,Y} || Y <- L1],
F = fun iter_all/1,
ResA = mrdb_index:with_iterator(T, a, F),
ct:log("mrdb_index:with_iterator(T, a, F) -> ~p", [ResA]),
ResB = mrdb_index:with_iterator(T, b, F),
ct:log("mrdb_index:with_iterator(T, b, F) -> ~p", [ResB]),
ok.
iter_all(I) ->
iter_all(mrdb_index:iterator_move(I, first), I).
iter_all({ok, IxVal, Obj}, I) ->
[{IxVal, Obj} | iter_all(mrdb_index:iterator_move(I, next), I)];
iter_all(_, _) ->
[].
indexes(1) ->
[a,{b,ordered},{c,bag}];
indexes(2) ->
[a,b,{c,bag}];
indexes(3) ->
[a,{b,ordered},{c,ordered}].
maybe_set_dir(Config) ->
case proplists:get_value(priv_dir, Config) of
undefined ->
ok;
PDir ->
Dir = filename:join(PDir, "mnesia_indexes"),
application:set_env(mnesia, dir, Dir)
end.