394 lines
14 KiB
Erlang
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.
|