Tests for fold, fold_reverse, select_reverse, etc.
This commit is contained in:
parent
323fcea7a7
commit
04e6063ce0
59
src/mrdb.erl
59
src/mrdb.erl
@ -61,15 +61,17 @@
|
|||||||
, get_batch/1
|
, get_batch/1
|
||||||
, snapshot/1
|
, snapshot/1
|
||||||
, release_snapshot/1
|
, release_snapshot/1
|
||||||
, first/1 , first/2
|
, first/1 , first/2
|
||||||
, next/2 , next/3
|
, next/2 , next/3
|
||||||
, prev/2 , prev/3
|
, prev/2 , prev/3
|
||||||
, last/1 , last/2
|
, last/1 , last/2
|
||||||
, select/2 , select/3
|
, select/2 , select/3
|
||||||
|
, select_reverse/2, select_reverse/3
|
||||||
, select/1
|
, select/1
|
||||||
, fold/3 , fold/4 , fold/5
|
, fold/3 , fold/4 , fold/5
|
||||||
, rdb_fold/4 , rdb_fold/5
|
, fold_reverse/3 , fold_reverse/4, fold_reverse/5
|
||||||
, rdb_rev_fold/4 , rdb_rev_fold/5
|
, rdb_fold/4 , rdb_fold/5
|
||||||
|
, rdb_fold_reverse/4, rdb_fold_reverse/5
|
||||||
, write_info/3
|
, write_info/3
|
||||||
, read_info/2
|
, read_info/2
|
||||||
, read_info/1
|
, read_info/1
|
||||||
@ -541,7 +543,6 @@ re_throw(Cat, Err) ->
|
|||||||
throw -> throw(Err)
|
throw -> throw(Err)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
mnesia_compatible_aborts() ->
|
mnesia_compatible_aborts() ->
|
||||||
mnesia_rocksdb_admin:get_cached_env(mnesia_compatible_aborts, false).
|
mnesia_rocksdb_admin:get_cached_env(mnesia_compatible_aborts, false).
|
||||||
|
|
||||||
@ -662,10 +663,25 @@ maybe_tx_ctxt([#{activity := #{type := Type} = A} = C|_], R) ->
|
|||||||
inherit_ctxt(Ref, R) ->
|
inherit_ctxt(Ref, R) ->
|
||||||
maps:merge(Ref, maps:with([snapshot, activity], R)).
|
maps:merge(Ref, maps:with([snapshot, activity], R)).
|
||||||
|
|
||||||
|
%% @doc Create an iterator on table `Tab' for the duration of `Fun'
|
||||||
|
%%
|
||||||
|
%% The iterator is passed to the provided fun as `Fun(Iterator)', and is
|
||||||
|
%% closed once the fun terminates.
|
||||||
|
%% @equiv with_iterator(Tab, Fun, [])
|
||||||
|
%% @end
|
||||||
-spec with_iterator(ref_or_tab(), fun( (mrdb_iterator()) -> Res )) -> Res.
|
-spec with_iterator(ref_or_tab(), fun( (mrdb_iterator()) -> Res )) -> Res.
|
||||||
with_iterator(Tab, Fun) ->
|
with_iterator(Tab, Fun) ->
|
||||||
with_iterator(Tab, Fun, []).
|
with_iterator(Tab, Fun, []).
|
||||||
|
|
||||||
|
%% @doc Create an iterator on table `Tab' with `ReadOptions' for the duration of `Fun'
|
||||||
|
%%
|
||||||
|
%% The iterator is passed to the provided fun as `Fun(Iterator)', and is
|
||||||
|
%% closed once the fun terminates.
|
||||||
|
%%
|
||||||
|
%% The iterator respects `mnesia_rocksdb' metadata, so accesses through the iterator
|
||||||
|
%% will return `{ok, Obj}' where `Obj' is the complete decoded object.
|
||||||
|
%% For rocksdb-level iterators, see {@link with_rdb_iterator/3}.
|
||||||
|
%% @end
|
||||||
-spec with_iterator(ref_or_tab(), fun( (mrdb_iterator()) -> Res ), read_options()) -> Res.
|
-spec with_iterator(ref_or_tab(), fun( (mrdb_iterator()) -> Res ), read_options()) -> Res.
|
||||||
with_iterator(Tab, Fun, Opts) ->
|
with_iterator(Tab, Fun, Opts) ->
|
||||||
R = ensure_ref(Tab),
|
R = ensure_ref(Tab),
|
||||||
@ -1301,6 +1317,13 @@ select(Tab, Pat, Limit) when Limit == infinity; is_integer(Limit), Limit > 0 ->
|
|||||||
true = valid_limit(Limit),
|
true = valid_limit(Limit),
|
||||||
mrdb_select:select(ensure_ref(Tab), Pat, Limit).
|
mrdb_select:select(ensure_ref(Tab), Pat, Limit).
|
||||||
|
|
||||||
|
select_reverse(Tab, Pat) ->
|
||||||
|
select_reverse(Tab, Pat, infinity).
|
||||||
|
|
||||||
|
select_reverse(Tab, Pat, Limit) when Limit == infinity; is_integer(Limit), Limit > 0 ->
|
||||||
|
true = valid_limit(Limit),
|
||||||
|
mrdb_select:select_reverse(ensure_ref(Tab), Pat, Limit).
|
||||||
|
|
||||||
select(Cont) ->
|
select(Cont) ->
|
||||||
mrdb_select:select(Cont).
|
mrdb_select:select(Cont).
|
||||||
|
|
||||||
@ -1355,6 +1378,16 @@ fold(Tab, Fun, Acc, MatchSpec, Limit) ->
|
|||||||
true = valid_limit(Limit),
|
true = valid_limit(Limit),
|
||||||
mrdb_select:fold(ensure_ref(Tab), Fun, Acc, MatchSpec, Limit).
|
mrdb_select:fold(ensure_ref(Tab), Fun, Acc, MatchSpec, Limit).
|
||||||
|
|
||||||
|
fold_reverse(Tab, Fun, Acc) ->
|
||||||
|
fold_reverse(Tab, Fun, Acc, [{'_', [], ['$_']}]).
|
||||||
|
|
||||||
|
fold_reverse(Tab, Fun, Acc, MatchSpec) ->
|
||||||
|
fold_reverse(Tab, Fun, Acc, MatchSpec, infinity).
|
||||||
|
|
||||||
|
fold_reverse(Tab, Fun, Acc, MatchSpec, Limit) ->
|
||||||
|
true = valid_limit(Limit),
|
||||||
|
mrdb_select:fold_reverse(ensure_ref(Tab), Fun, Acc, MatchSpec, Limit).
|
||||||
|
|
||||||
rdb_fold(Tab, Fun, Acc, Prefix) when is_function(Fun, 3)
|
rdb_fold(Tab, Fun, Acc, Prefix) when is_function(Fun, 3)
|
||||||
, is_binary(Prefix) ->
|
, is_binary(Prefix) ->
|
||||||
rdb_fold(Tab, Fun, Acc, Prefix, infinity).
|
rdb_fold(Tab, Fun, Acc, Prefix, infinity).
|
||||||
@ -1364,14 +1397,14 @@ rdb_fold(Tab, Fun, Acc, Prefix, Limit) when is_function(Fun, 3)
|
|||||||
true = valid_limit(Limit),
|
true = valid_limit(Limit),
|
||||||
mrdb_select:rdb_fold(ensure_ref(Tab), Fun, Acc, Prefix, Limit).
|
mrdb_select:rdb_fold(ensure_ref(Tab), Fun, Acc, Prefix, Limit).
|
||||||
|
|
||||||
rdb_rev_fold(Tab, Fun, Acc, Prefix) when is_function(Fun, 3)
|
rdb_fold_reverse(Tab, Fun, Acc, Prefix) when is_function(Fun, 3)
|
||||||
, is_binary(Prefix) ->
|
, is_binary(Prefix) ->
|
||||||
rdb_rev_fold(Tab, Fun, Acc, Prefix, infinity).
|
rdb_fold_reverse(Tab, Fun, Acc, Prefix, infinity).
|
||||||
|
|
||||||
rdb_rev_fold(Tab, Fun, Acc, Prefix, Limit) when is_function(Fun, 3)
|
rdb_fold_reverse(Tab, Fun, Acc, Prefix, Limit) when is_function(Fun, 3)
|
||||||
, is_binary(Prefix) ->
|
, is_binary(Prefix) ->
|
||||||
true = valid_limit(Limit),
|
true = valid_limit(Limit),
|
||||||
mrdb_select:rdb_rev_fold(ensure_ref(Tab), Fun, Acc, Prefix, Limit).
|
mrdb_select:rdb_fold_reverse(ensure_ref(Tab), Fun, Acc, Prefix, Limit).
|
||||||
|
|
||||||
valid_limit(L) ->
|
valid_limit(L) ->
|
||||||
case L of
|
case L of
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
|
|
||||||
-export([ select/3 %% (Ref, MatchSpec, Limit)
|
-export([ select/3 %% (Ref, MatchSpec, Limit)
|
||||||
, select/4 %% (Ref, MatchSpec, AccKeys, Limit)
|
, select/4 %% (Ref, MatchSpec, AccKeys, Limit)
|
||||||
|
, select_reverse/3
|
||||||
|
, select_reverse/4
|
||||||
, select/1 %% (Cont)
|
, select/1 %% (Cont)
|
||||||
, fold/5 %% (Ref, Fun, Acc, MatchSpec, Limit)
|
, fold/5 %% (Ref, Fun, Acc, MatchSpec, Limit)
|
||||||
|
, fold_reverse/5
|
||||||
, rdb_fold/5 %% (Ref, Fun, Acc, Prefix, Limit)
|
, rdb_fold/5 %% (Ref, Fun, Acc, Prefix, Limit)
|
||||||
, rdb_rev_fold/5 %% (Ref, Fun, Acc, Prefix, Limit)
|
, rdb_fold_reverse/5 %% (Ref, Fun, Acc, Prefix, Limit)
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([continuation_info/2]).
|
-export([continuation_info/2]).
|
||||||
@ -26,18 +29,27 @@
|
|||||||
, compiled_ms
|
, compiled_ms
|
||||||
, limit
|
, limit
|
||||||
, key_only = false % TODO: not used
|
, key_only = false % TODO: not used
|
||||||
, direction = forward % TODO: not used
|
, direction = forward
|
||||||
}).
|
}).
|
||||||
|
|
||||||
select(Ref, MS, Limit) when is_map(Ref), is_list(MS) ->
|
select(Ref, MS, Limit) when is_map(Ref), is_list(MS) ->
|
||||||
select(Ref, MS, false, Limit).
|
select(Ref, MS, false, forward, Limit).
|
||||||
|
|
||||||
select(Ref, MS, AccKeys, Limit)
|
select(Ref, MS, AccKeys, Limit) ->
|
||||||
|
select(Ref, MS, AccKeys, forward, Limit).
|
||||||
|
|
||||||
|
select_reverse(Ref, MS, Limit) ->
|
||||||
|
select(Ref, MS, false, reverse, Limit).
|
||||||
|
|
||||||
|
select_reverse(Ref, MS, AccKeys, Limit) ->
|
||||||
|
select(Ref, MS, AccKeys, reverse, Limit).
|
||||||
|
|
||||||
|
select(Ref, MS, AccKeys, Dir, Limit)
|
||||||
when is_map(Ref), is_list(MS), is_boolean(AccKeys) ->
|
when is_map(Ref), is_list(MS), is_boolean(AccKeys) ->
|
||||||
Sel = mk_sel(Ref, MS, Limit),
|
Sel = mk_sel(Ref, MS, Dir, Limit),
|
||||||
mrdb:with_rdb_iterator(Ref, fun(I) -> i_select(I, Sel, AccKeys, []) end).
|
mrdb:with_rdb_iterator(Ref, fun(I) -> i_select(I, Sel, AccKeys, []) end).
|
||||||
|
|
||||||
mk_sel(#{name := Tab} = Ref, MS, Limit) ->
|
mk_sel(#{name := Tab} = Ref, MS, Dir, Limit) ->
|
||||||
Keypat = keypat(MS, keypos(Tab), Ref),
|
Keypat = keypat(MS, keypos(Tab), Ref),
|
||||||
#sel{tab = Tab,
|
#sel{tab = Tab,
|
||||||
ref = Ref,
|
ref = Ref,
|
||||||
@ -45,6 +57,7 @@ mk_sel(#{name := Tab} = Ref, MS, Limit) ->
|
|||||||
ms = MS,
|
ms = MS,
|
||||||
compiled_ms = ets:match_spec_compile(MS),
|
compiled_ms = ets:match_spec_compile(MS),
|
||||||
key_only = needs_key_only(MS),
|
key_only = needs_key_only(MS),
|
||||||
|
direction = Dir,
|
||||||
limit = Limit}.
|
limit = Limit}.
|
||||||
|
|
||||||
select(Cont) ->
|
select(Cont) ->
|
||||||
@ -65,6 +78,12 @@ continuation_info_(direction, #sel{direction = Dir}) -> Dir;
|
|||||||
continuation_info_(_, _) -> undefined.
|
continuation_info_(_, _) -> undefined.
|
||||||
|
|
||||||
fold(Ref, Fun, Acc, MS, Limit) ->
|
fold(Ref, Fun, Acc, MS, Limit) ->
|
||||||
|
do_fold(Ref, Fun, Acc, MS, forward, Limit).
|
||||||
|
|
||||||
|
fold_reverse(Ref, Fun, Acc, MS, Limit) ->
|
||||||
|
do_fold(Ref, Fun, Acc, MS, reverse, Limit).
|
||||||
|
|
||||||
|
do_fold(Ref, Fun, Acc, MS, Dir, Limit) ->
|
||||||
{AccKeys, F} =
|
{AccKeys, F} =
|
||||||
if is_function(Fun, 3) ->
|
if is_function(Fun, 3) ->
|
||||||
{true, fun({K, Obj}, Acc1) ->
|
{true, fun({K, Obj}, Acc1) ->
|
||||||
@ -75,7 +94,7 @@ fold(Ref, Fun, Acc, MS, Limit) ->
|
|||||||
true ->
|
true ->
|
||||||
mrdb:abort(invalid_fold_fun)
|
mrdb:abort(invalid_fold_fun)
|
||||||
end,
|
end,
|
||||||
fold_(select(Ref, MS, AccKeys, Limit), F, Acc).
|
fold_(select(Ref, MS, AccKeys, Dir, Limit), F, Acc).
|
||||||
|
|
||||||
fold_('$end_of_table', _, Acc) ->
|
fold_('$end_of_table', _, Acc) ->
|
||||||
Acc;
|
Acc;
|
||||||
@ -91,27 +110,29 @@ rdb_fold(Ref, Fun, Acc, Prefix, Limit) ->
|
|||||||
i_rdb_fold(MovRes, I, Prefix, Fun, Acc, Limit)
|
i_rdb_fold(MovRes, I, Prefix, Fun, Acc, Limit)
|
||||||
end).
|
end).
|
||||||
|
|
||||||
rdb_rev_fold(Ref, Fun, Acc, Prefix, Limit) ->
|
rdb_fold_reverse(Ref, Fun, Acc, Prefix, Limit) ->
|
||||||
mrdb:with_rdb_iterator(
|
mrdb:with_rdb_iterator(
|
||||||
Ref, fun(I) ->
|
Ref, fun(I) ->
|
||||||
MovRes = rev_init_seek(I, Prefix),
|
MovRes = rev_init_seek(I, Prefix),
|
||||||
i_rdb_rev_fold(MovRes, I, Prefix, Fun, Acc, Limit)
|
i_rdb_fold_reverse(MovRes, I, Prefix, Fun, Acc, Limit)
|
||||||
end).
|
end).
|
||||||
|
|
||||||
fwd_init_seek(I, <<>>) ->
|
fwd_init_seek(I, Pfx) ->
|
||||||
rocksdb:iterator_move(I, first);
|
rocksdb:iterator_move(I, fwd_init_seek_tgt(Pfx)).
|
||||||
fwd_init_seek(I, Prefix) ->
|
|
||||||
rocksdb:iterator_move(I, {seek, Prefix}).
|
|
||||||
|
|
||||||
rev_init_seek(I, <<>>) ->
|
fwd_init_seek_tgt(<<>> ) -> first;
|
||||||
rocksdb:iterator_move(I, last);
|
fwd_init_seek_tgt(Prefix) -> {seek, Prefix}.
|
||||||
rev_init_seek(I, Prefix) ->
|
|
||||||
Tgt = case incr_prefix(Prefix) of
|
rev_init_seek(I, Pfx) ->
|
||||||
last -> last;
|
rocksdb:iterator_move(I, rev_init_seek_tgt(Pfx)).
|
||||||
Pfx1 when is_binary(Pfx1) ->
|
|
||||||
{seek, Pfx1}
|
rev_init_seek_tgt(<<>>) -> last;
|
||||||
end,
|
rev_init_seek_tgt(Prefix) ->
|
||||||
rocksdb:iterator_move(I, Tgt).
|
case incr_prefix(Prefix) of
|
||||||
|
last -> last;
|
||||||
|
Pfx1 when is_binary(Pfx1) ->
|
||||||
|
{seek, Pfx1}
|
||||||
|
end.
|
||||||
|
|
||||||
incr_prefix(<<>>) -> last;
|
incr_prefix(<<>>) -> last;
|
||||||
incr_prefix(Pfx) when is_binary(Pfx) ->
|
incr_prefix(Pfx) when is_binary(Pfx) ->
|
||||||
@ -134,31 +155,35 @@ i_rdb_fold({ok, K, V}, I, Pfx, Fun, Acc, Limit) when Limit > 0 ->
|
|||||||
i_rdb_fold(_, _, _, _, Acc, _) ->
|
i_rdb_fold(_, _, _, _, Acc, _) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
i_rdb_rev_fold({ok, K, V}, I, Pfx, Fun, Acc, Limit) when Limit > 0 ->
|
i_rdb_fold_reverse({ok, K, V}, I, Pfx, Fun, Acc, Limit) when Limit > 0 ->
|
||||||
case is_prefix(Pfx, K) of
|
case is_prefix(Pfx, K) of
|
||||||
true ->
|
true ->
|
||||||
i_rdb_rev_fold(rocksdb:iterator_move(I, prev), I, Pfx, Fun,
|
i_rdb_fold_reverse(rocksdb:iterator_move(I, prev), I, Pfx, Fun,
|
||||||
Fun(K, V, Acc), decr(Limit));
|
Fun(K, V, Acc), decr(Limit));
|
||||||
false when K > Pfx ->
|
false when K > Pfx ->
|
||||||
i_rdb_rev_fold(rocksdb:iterator_move(I, prev), I, Pfx, Fun, Acc, Limit);
|
i_rdb_fold_reverse(rocksdb:iterator_move(I, prev), I, Pfx, Fun, Acc, Limit);
|
||||||
false ->
|
false ->
|
||||||
Acc
|
Acc
|
||||||
end;
|
end;
|
||||||
i_rdb_rev_fold(_, _, _, _, Acc, _) ->
|
i_rdb_fold_reverse(_, _, _, _, Acc, _) ->
|
||||||
Acc.
|
Acc.
|
||||||
|
|
||||||
i_select(I, #sel{ keypat = Pfx
|
i_select(I, #sel{ keypat = Pfx
|
||||||
, compiled_ms = MS
|
, compiled_ms = MS
|
||||||
, limit = Limit
|
, limit = Limit
|
||||||
, ref = #{vsn := Vsn, encoding := Enc} } = Sel, AccKeys, Acc) ->
|
, direction = Dir
|
||||||
StartKey = case {Pfx, Vsn, Enc} of
|
, ref = #{encoding := Enc} } = Sel0, AccKeys, Acc) ->
|
||||||
{<<>>, 1, {sext, _}} ->
|
{StartKey, Sel} = case Enc of
|
||||||
<<?DATA_START>>;
|
{term, _} ->
|
||||||
{_, _, {term, _}} ->
|
%% No defined ordering - do forward select
|
||||||
<<>>;
|
{first, Sel0#sel{direction = forward}};
|
||||||
_ ->
|
_ ->
|
||||||
Pfx
|
SK = case Dir of
|
||||||
end,
|
forward -> fwd_init_seek_tgt(Pfx);
|
||||||
|
reverse -> rev_init_seek_tgt(Pfx)
|
||||||
|
end,
|
||||||
|
{SK, Sel0}
|
||||||
|
end,
|
||||||
select_traverse(rocksdb:iterator_move(I, StartKey), Limit,
|
select_traverse(rocksdb:iterator_move(I, StartKey), Limit,
|
||||||
Pfx, MS, I, Sel, AccKeys, Acc).
|
Pfx, MS, I, Sel, AccKeys, Acc).
|
||||||
|
|
||||||
@ -236,7 +261,7 @@ map_vars([H|T], P) ->
|
|||||||
map_vars([], _) ->
|
map_vars([], _) ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel,
|
select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R, direction = Dir} = Sel,
|
||||||
AccKeys, Acc) ->
|
AccKeys, Acc) ->
|
||||||
case is_prefix(Pfx, K) of
|
case is_prefix(Pfx, K) of
|
||||||
true ->
|
true ->
|
||||||
@ -245,7 +270,7 @@ select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel,
|
|||||||
case ets:match_spec_run([Rec], MS) of
|
case ets:match_spec_run([Rec], MS) of
|
||||||
[] ->
|
[] ->
|
||||||
select_traverse(
|
select_traverse(
|
||||||
rocksdb:iterator_move(I, next), Limit, Pfx, MS,
|
rocksdb:iterator_move(I, next_or_prev(Dir)), Limit, Pfx, MS,
|
||||||
I, Sel, AccKeys, Acc);
|
I, Sel, AccKeys, Acc);
|
||||||
[Match] ->
|
[Match] ->
|
||||||
Acc1 = if AccKeys ->
|
Acc1 = if AccKeys ->
|
||||||
@ -255,6 +280,9 @@ select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel,
|
|||||||
end,
|
end,
|
||||||
traverse_continue(K, decr(Limit), Pfx, MS, I, Sel, AccKeys, Acc1)
|
traverse_continue(K, decr(Limit), Pfx, MS, I, Sel, AccKeys, Acc1)
|
||||||
end;
|
end;
|
||||||
|
false when Dir == reverse, K > Pfx ->
|
||||||
|
select_traverse(rocksdb:iterator_move(I, prev), Limit, Pfx, MS,
|
||||||
|
I, Sel, AccKeys, Acc);
|
||||||
false when Limit == infinity ->
|
false when Limit == infinity ->
|
||||||
lists:reverse(Acc);
|
lists:reverse(Acc);
|
||||||
false ->
|
false ->
|
||||||
@ -263,6 +291,9 @@ select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel,
|
|||||||
select_traverse({error, _}, Limit, _, _, _, _, _, Acc) ->
|
select_traverse({error, _}, Limit, _, _, _, _, _, Acc) ->
|
||||||
select_return(Limit, {lists:reverse(Acc), '$end_of_table'}).
|
select_return(Limit, {lists:reverse(Acc), '$end_of_table'}).
|
||||||
|
|
||||||
|
next_or_prev(forward) -> next;
|
||||||
|
next_or_prev(reverse) -> prev.
|
||||||
|
|
||||||
select_return(infinity, {L, '$end_of_table'}) ->
|
select_return(infinity, {L, '$end_of_table'}) ->
|
||||||
L;
|
L;
|
||||||
select_return(_, Ret) ->
|
select_return(_, Ret) ->
|
||||||
@ -282,29 +313,34 @@ decr(I) when is_integer(I) ->
|
|||||||
decr(infinity) ->
|
decr(infinity) ->
|
||||||
infinity.
|
infinity.
|
||||||
|
|
||||||
traverse_continue(K, 0, Pfx, MS, _I, #sel{limit = Limit, ref = Ref} = Sel, AccKeys, Acc) ->
|
traverse_continue(K, 0, Pfx, MS, _I, #sel{limit = Limit, ref = Ref, direction = Dir} = Sel, AccKeys, Acc) ->
|
||||||
{lists:reverse(Acc),
|
{lists:reverse(Acc),
|
||||||
fun(sel) -> Sel;
|
fun(sel) -> Sel;
|
||||||
(cont) ->
|
(cont) ->
|
||||||
mrdb:with_rdb_iterator(
|
mrdb:with_rdb_iterator(
|
||||||
Ref,
|
Ref,
|
||||||
fun(NewI) ->
|
fun(NewI) ->
|
||||||
select_traverse(iterator_next(NewI, K),
|
select_traverse(iterator_next(NewI, K, Dir),
|
||||||
Limit, Pfx, MS, NewI, Sel,
|
Limit, Pfx, MS, NewI, Sel,
|
||||||
AccKeys, [])
|
AccKeys, [])
|
||||||
end)
|
end)
|
||||||
end};
|
end};
|
||||||
traverse_continue(_K, Limit, Pfx, MS, I, Sel, AccKeys, Acc) ->
|
traverse_continue(_K, Limit, Pfx, MS, I, #sel{direction = Dir} = Sel, AccKeys, Acc) ->
|
||||||
select_traverse(rocksdb:iterator_move(I, next), Limit, Pfx, MS, I, Sel, AccKeys, Acc).
|
select_traverse(rocksdb:iterator_move(I, next_or_prev(Dir)), Limit, Pfx, MS, I, Sel, AccKeys, Acc).
|
||||||
|
|
||||||
iterator_next(I, K) ->
|
iterator_next(I, K, Dir) ->
|
||||||
case rocksdb:iterator_move(I, K) of
|
case i_move(I, K, Dir) of
|
||||||
{ok, K, _} ->
|
{ok, K, _} ->
|
||||||
rocksdb:iterator_move(I, next);
|
rocksdb:iterator_move(I, next_or_prev(Dir));
|
||||||
Other ->
|
Other ->
|
||||||
Other
|
Other
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
i_move(I, K, reverse) ->
|
||||||
|
rocksdb:iterator_move(I, {seek_for_prev, K});
|
||||||
|
i_move(I, K, forward) ->
|
||||||
|
rocksdb:iterator_move(I, K).
|
||||||
|
|
||||||
keypat([H|T], KeyPos, Ref) ->
|
keypat([H|T], KeyPos, Ref) ->
|
||||||
keypat(T, KeyPos, Ref, keypat_pfx(H, KeyPos, Ref)).
|
keypat(T, KeyPos, Ref, keypat_pfx(H, KeyPos, Ref)).
|
||||||
|
|
||||||
|
@ -12,15 +12,26 @@
|
|||||||
, end_per_testcase/2
|
, end_per_testcase/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ no_prefix_fwd_rdb_fold/1
|
-export([
|
||||||
|
no_prefix_fwd_rdb_fold/1
|
||||||
, prefixed_fwd_rdb_fold/1
|
, prefixed_fwd_rdb_fold/1
|
||||||
, no_prefix_rev_rdb_fold/1
|
, no_prefix_rev_rdb_fold/1
|
||||||
, prefixed_rev_rdb_fold/1
|
, prefixed_rev_rdb_fold/1
|
||||||
, prefixed_rev_rdb_fold_max/1
|
, prefixed_rev_rdb_fold_max/1
|
||||||
|
, fwd_fold/1
|
||||||
|
, filtered_fwd_fold/1
|
||||||
|
, rev_fold/1
|
||||||
|
, filtered_rev_fold/1
|
||||||
|
, fwd_select/1
|
||||||
|
, rev_select/1
|
||||||
|
, select_cont/1
|
||||||
|
, rev_select_cont/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
|
-record(r, {k, v}).
|
||||||
|
|
||||||
suite() ->
|
suite() ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
@ -29,26 +40,37 @@ all() ->
|
|||||||
|
|
||||||
groups() ->
|
groups() ->
|
||||||
[
|
[
|
||||||
{all_tests, [sequence], [ {group, rdb_fwd_fold}
|
{all_tests, [sequence], [ {group, rdb_fold}
|
||||||
, {group, rdb_rev_fold} ]}
|
, {group, fold}
|
||||||
, {rdb_fwd_fold, [sequence], [ no_prefix_fwd_rdb_fold
|
, {group, select} ]}
|
||||||
, prefixed_fwd_rdb_fold ]}
|
, {rdb_fold, [sequence], [ no_prefix_fwd_rdb_fold
|
||||||
, {rdb_rev_fold, [sequence], [ no_prefix_rev_rdb_fold
|
, prefixed_fwd_rdb_fold
|
||||||
, prefixed_rev_rdb_fold
|
, no_prefix_rev_rdb_fold
|
||||||
, prefixed_rev_rdb_fold_max ]}
|
, prefixed_rev_rdb_fold
|
||||||
|
, prefixed_rev_rdb_fold_max ]}
|
||||||
|
, {fold, [sequence], [ fwd_fold
|
||||||
|
, filtered_fwd_fold
|
||||||
|
, rev_fold
|
||||||
|
, filtered_rev_fold ]}
|
||||||
|
, {select, [sequence], [ fwd_select
|
||||||
|
, rev_select
|
||||||
|
, select_cont
|
||||||
|
, rev_select_cont ]}
|
||||||
].
|
].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
mnesia:stop(),
|
||||||
|
ok = mnesia_rocksdb_tlib:start_mnesia(reset),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
|
mnesia:stop(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_per_group(G, Config) ->
|
init_per_group(G, Config) when G==rdb_fold; G==fold ->
|
||||||
mnesia:stop(),
|
mk_tab(G),
|
||||||
ok = mnesia_rocksdb_tlib:start_mnesia(reset),
|
Config;
|
||||||
create_tab(t, G, [{type, ordered_set}]),
|
init_per_group(_, Config) ->
|
||||||
fill_tab(t),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_group(_, _Config) ->
|
end_per_group(_, _Config) ->
|
||||||
@ -60,62 +82,139 @@ init_per_testcase(_, Config) ->
|
|||||||
end_per_testcase(_, _Config) ->
|
end_per_testcase(_, _Config) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
create_tab(Tab, Grp, Opts) ->
|
mk_tab(G) ->
|
||||||
GOpts = case Grp of
|
T = tab_name(G),
|
||||||
_ when Grp==rdb_fwd_fold; Grp==rdb_rev_fold ->
|
case create_tab(T) of
|
||||||
[{user_properties,
|
{atomic, ok} -> fill_tab(T);
|
||||||
[{mrdb_encoding, {raw, raw}}]}];
|
{aborted,{already_exists,T}} -> ok
|
||||||
_ -> []
|
end.
|
||||||
end,
|
|
||||||
{atomic, ok} = mnesia:create_table(Tab, [{rdb, [node()]} | GOpts ++ Opts]),
|
tab_name(fold ) -> r;
|
||||||
ok.
|
tab_name(select ) -> r;
|
||||||
|
tab_name(rdb_fold) -> t.
|
||||||
|
|
||||||
|
create_tab(T) ->
|
||||||
|
Opts = tab_opts(T),
|
||||||
|
mnesia:create_table(T, [{rdb, [node()]} | Opts]).
|
||||||
|
|
||||||
|
tab_opts(r) ->
|
||||||
|
[{attributes, [k, v]}, {type, ordered_set}];
|
||||||
|
tab_opts(t) ->
|
||||||
|
[{attributes, [k, v]}, {user_properties, [{mrdb_encoding, {raw, raw}}]}].
|
||||||
|
|
||||||
no_prefix_fwd_rdb_fold(_Config) ->
|
no_prefix_fwd_rdb_fold(_Config) ->
|
||||||
Res = mrdb:rdb_fold(t, fun simple_rdb_acc/3, [], <<>>, infinity),
|
Res = mrdb:rdb_fold(t, fun simple_rdb_acc/3, [], <<>>, infinity),
|
||||||
|
Expected = lists:reverse(raw_objs()),
|
||||||
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
fwd_fold(_Config) ->
|
||||||
|
Res = mrdb:fold(r, fun simple_acc/2, []),
|
||||||
Expected = lists:reverse(objs()),
|
Expected = lists:reverse(objs()),
|
||||||
{Res, Res} = {Res, Expected}.
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
prefixed_fwd_rdb_fold(_Config) ->
|
prefixed_fwd_rdb_fold(_Config) ->
|
||||||
Pfx = <<"aa">>,
|
Pfx = <<"aa">>,
|
||||||
Res = mrdb:rdb_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity),
|
Res = mrdb:rdb_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity),
|
||||||
Expected = lists:reverse(prefixed_objs(Pfx)),
|
Expected = lists:reverse(prefixed_raw_objs(Pfx)),
|
||||||
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
filtered_fwd_fold(_Config) ->
|
||||||
|
MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}],
|
||||||
|
Res = mrdb:fold(r, fun simple_acc/2, [], MS, infinity),
|
||||||
|
Expected = lists:reverse(filtered_objs(MS)),
|
||||||
{Res, Res} = {Res, Expected}.
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
no_prefix_rev_rdb_fold(_Config) ->
|
no_prefix_rev_rdb_fold(_Config) ->
|
||||||
Res = mrdb:rdb_rev_fold(t, fun simple_rdb_acc/3, [], <<>>, infinity),
|
Res = mrdb:rdb_fold_reverse(t, fun simple_rdb_acc/3, [], <<>>, infinity),
|
||||||
|
Expected = raw_objs(),
|
||||||
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
rev_fold(_Config) ->
|
||||||
|
Res = mrdb:fold_reverse(r, fun simple_acc/2, []),
|
||||||
Expected = objs(),
|
Expected = objs(),
|
||||||
{Res, Res} = {Res, Expected}.
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
prefixed_rev_rdb_fold(_Config) ->
|
prefixed_rev_rdb_fold(_Config) ->
|
||||||
Pfx = <<"aa">>,
|
Pfx = <<"aa">>,
|
||||||
Res = mrdb:rdb_rev_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity),
|
Res = mrdb:rdb_fold_reverse(t, fun simple_rdb_acc/3, [], Pfx, infinity),
|
||||||
Expected = prefixed_objs(Pfx),
|
Expected = prefixed_raw_objs(Pfx),
|
||||||
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
filtered_rev_fold(_Config) ->
|
||||||
|
MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}],
|
||||||
|
Res = mrdb:fold_reverse(r, fun simple_acc/2, [], MS, infinity),
|
||||||
|
Expected = filtered_objs(MS),
|
||||||
{Res, Res} = {Res, Expected}.
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
prefixed_rev_rdb_fold_max(_Config) ->
|
prefixed_rev_rdb_fold_max(_Config) ->
|
||||||
Pfx = <<255,255>>,
|
Pfx = <<255,255>>,
|
||||||
Res = mrdb:rdb_rev_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity),
|
Res = mrdb:rdb_fold_reverse(t, fun simple_rdb_acc/3, [], Pfx, infinity),
|
||||||
Expected = prefixed_objs(Pfx),
|
Expected = prefixed_raw_objs(Pfx),
|
||||||
{Res, Res} = {Res, Expected}.
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
fwd_select(_Config) ->
|
||||||
|
MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}],
|
||||||
|
Res = mrdb:select(r, MS),
|
||||||
|
Expected = filtered_objs(MS),
|
||||||
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
rev_select(_Config) ->
|
||||||
|
MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}],
|
||||||
|
Res = mrdb:select_reverse(r, MS),
|
||||||
|
Expected = lists:reverse(filtered_objs(MS)),
|
||||||
|
{Res, Res} = {Res, Expected}.
|
||||||
|
|
||||||
|
select_cont(_Cont) ->
|
||||||
|
MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}],
|
||||||
|
Expected = filtered_objs(MS),
|
||||||
|
select_cont_loop(mrdb:select(r, MS, 1), Expected).
|
||||||
|
|
||||||
|
rev_select_cont(_Cont) ->
|
||||||
|
MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}],
|
||||||
|
Expected = lists:reverse(filtered_objs(MS)),
|
||||||
|
select_cont_loop(mrdb:select_reverse(r, MS, 1), Expected).
|
||||||
|
|
||||||
|
select_cont_loop({[Obj], Cont}, [Obj|Rest]) ->
|
||||||
|
select_cont_loop(mrdb:select(Cont), Rest);
|
||||||
|
select_cont_loop({[], '$end_of_table'}, []) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
simple_acc(#r{} = Obj, Acc) ->
|
||||||
|
[Obj | Acc].
|
||||||
|
|
||||||
simple_rdb_acc(K, V, Acc) ->
|
simple_rdb_acc(K, V, Acc) ->
|
||||||
[{K,V} | Acc].
|
[{K,V} | Acc].
|
||||||
|
|
||||||
fill_tab(Tab) ->
|
fill_tab(t = Tab) ->
|
||||||
[mrdb:insert(Tab, {Tab, K, V}) || {K,V} <- objs()],
|
[mrdb:insert(Tab, {Tab, K, V}) || {K,V} <- raw_objs()],
|
||||||
|
ok;
|
||||||
|
fill_tab(r = Tab) ->
|
||||||
|
[mrdb:insert(Tab, Obj) || Obj <- objs()],
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
prefixed_objs(Pfx) ->
|
prefixed_raw_objs(Pfx) ->
|
||||||
[Obj || {K,_} = Obj <- objs(),
|
[Obj || {K,_} = Obj <- raw_objs(),
|
||||||
is_prefix(Pfx, K)].
|
is_prefix(Pfx, K)].
|
||||||
|
|
||||||
objs() ->
|
raw_objs() ->
|
||||||
[ {<<"aa">> , <<"1">>}
|
[ {<<"aa">> , <<"1">>}
|
||||||
, {<<"aa1">>, <<"2">>}
|
, {<<"aa1">>, <<"2">>}
|
||||||
, {<<"ab">> , <<"3">>}
|
, {<<"ab">> , <<"3">>}
|
||||||
, {<<"ab1">>, <<"4">>}
|
, {<<"ab1">>, <<"4">>}
|
||||||
, {<<255,255>>, <<"5">>} ].
|
, {<<255,255>>, <<"5">>} ].
|
||||||
|
|
||||||
|
filtered_objs(MS) ->
|
||||||
|
MSC = ets:match_spec_compile(MS),
|
||||||
|
ets:match_spec_run(objs(), MSC).
|
||||||
|
|
||||||
|
objs() ->
|
||||||
|
[ #r{k = {a,a,1}, v = 1}
|
||||||
|
, #r{k = {a,b,2}, v = 2}
|
||||||
|
, #r{k = {a,b,3}, v = 3}
|
||||||
|
, #r{k = {a,c,4}, v = 4}
|
||||||
|
, #r{k = {b,b,5}, v = 5}
|
||||||
|
].
|
||||||
|
|
||||||
%% copied from mrdb_select.erl
|
%% copied from mrdb_select.erl
|
||||||
is_prefix(A, B) when is_binary(A), is_binary(B) ->
|
is_prefix(A, B) when is_binary(A), is_binary(B) ->
|
||||||
Sa = byte_size(A),
|
Sa = byte_size(A),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user