1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(ext_test).
22
23%% Initializations
24-export([init_backend/0, add_aliases/1, remove_aliases/1,
25	 check_definition/4, semantics/2]).
26
27-export([
28	 create_table/3, load_table/4,
29	 delete_table/2, close_table/2, sync_close_table/2,
30
31	 sender_init/4,
32	 receiver_first_message/4, receive_data/5, receive_done/4,
33
34	 index_is_consistent/3, is_index_consistent/2,
35
36	 real_suffixes/0, tmp_suffixes/0,
37
38	 info/3,
39	 fixtable/3,
40	 validate_key/6, validate_record/6,
41
42	 first/2, last/2, next/3, prev/3, slot/3,
43
44	 insert/3, update_counter/4,
45	 lookup/3,
46	 delete/3, match_delete/3,
47	 select/1, select/3, select/4, repair_continuation/2
48	]).
49
50-ifdef(DEBUG).
51-define(DBG(DATA), io:format("~p:~p: ~p~n",[?MODULE, ?LINE, DATA])).
52-define(DBG(FORMAT, ARGS), io:format("~p:~p: " ++ FORMAT,[?MODULE, ?LINE] ++ ARGS)).
53-else.
54-define(DBG(DATA), ok).
55-define(DBG(FORMAT, ARGS), ok).
56-endif.
57
58%% types() ->
59%%     [{fs_copies, ?MODULE},
60%%      {raw_fs_copies, ?MODULE}].
61
62semantics(ext_ets, storage) -> ram_copies;
63semantics(ext_ets, types  ) -> [set, ordered_set, bag];
64semantics(ext_ets, index_types) -> [ordered];
65semantics(_Alias, _) ->
66    undefined.
67
68%% valid_op(_, _) ->
69%%     true.
70
71init_backend() ->
72    ?DBG(init_backend),
73    ct:log("init_backend ~p", [?MODULE]),
74    %% cheat and stuff a marker in mnesia_gvar
75    K = backend_init_marker(),
76    case try mnesia_lib:val(K) catch _:_ -> error end of
77        error ->
78            ct:log("BACKEND marker will be inserted (~p)", [?MODULE]),
79            mnesia_lib:set(K, true);
80        Other ->
81            ct:log("BACKEND marker already present (~p)", [?MODULE]),
82            error({backend_already_initialized, {?MODULE, Other}})
83    end,
84    ok.
85
86backend_init_marker() ->
87    {test, ?MODULE, backend_init}.
88
89add_aliases(_As) ->
90    ?DBG(_As),
91    ct:log("add_aliases(~p)", [_As]),
92    true = mnesia_lib:val(backend_init_marker()),
93    ok.
94
95remove_aliases(_) ->
96    ok.
97
98
99%% Table operations
100
101check_definition(ext_ets, _Tab, _Nodes, _Props) ->
102    ?DBG("~p ~p ~p~n", [_Tab, _Nodes, _Props]),
103    ok.
104
105create_table(ext_ets, Tab, Props) when is_atom(Tab) ->
106    Tid = ets:new(Tab, [public, proplists:get_value(type, Props, set), {keypos, 2}]),
107    ?DBG("~p Create: ~p(~p) ~p~n", [self(), Tab, Tid, Props]),
108    mnesia_lib:set({?MODULE, Tab}, Tid),
109    ok;
110create_table(_, Tag={Tab, index, {_Where, Type0}}, _Opts) ->
111    Type = case Type0 of
112	       ordered -> ordered_set;
113	       _ -> Type0
114	   end,
115    Tid = ets:new(Tab, [public, Type]),
116    ?DBG("~p(~p) ~p~n", [Tab, Tid, Tag]),
117    mnesia_lib:set({?MODULE, Tag}, Tid),
118    ok;
119create_table(_, Tag={_Tab, retainer, ChkPName}, _Opts) ->
120    Tid = ets:new(ChkPName, [set, public, {keypos, 2}]),
121    ?DBG("~p(~p) ~p~n", [_Tab, Tid, Tag]),
122    mnesia_lib:set({?MODULE, Tag}, Tid),
123    ok.
124
125delete_table(ext_ets, Tab) ->
126    try
127      ets:delete(mnesia_lib:val({?MODULE,Tab})),
128      mnesia_lib:unset({?MODULE,Tab}),
129      ok
130    catch _:_ ->
131	    ?DBG({double_delete, Tab}),
132	    ok
133    end.
134
135load_table(ext_ets, _Tab, init_index, _Cs) -> ok;
136load_table(ext_ets, _Tab, _LoadReason, _Cs) ->
137    ?DBG("Load ~p ~p~n", [_Tab, _LoadReason]),
138    ok.
139%%     mnesia_monitor:unsafe_create_external(Tab, ext_ets, ?MODULE, Cs).
140
141sender_init(Alias, Tab, _RemoteStorage, _Pid) ->
142    KeysPerTransfer = 100,
143    {standard,
144     fun() -> mnesia_lib:db_init_chunk({ext,Alias,?MODULE}, Tab, KeysPerTransfer) end,
145     fun(Cont) -> mnesia_lib:db_chunk({ext,Alias,?MODULE}, Cont) end}.
146
147receiver_first_message(Sender, {first, Size}, _Alias, Tab) ->
148    ?DBG({first,Size}),
149    {Size, {Tab, Sender}}.
150
151receive_data(Data, ext_ets, Name, _Sender, {Name, Tab, _Sender}=State) ->
152    ?DBG({Data,State}),
153    true = ets:insert(Tab, Data),
154    {more, State};
155receive_data(Data, Alias, Tab, Sender, {Name, Sender}) ->
156    receive_data(Data, Alias, Tab, Sender, {Name, mnesia_lib:val({?MODULE,Tab}), Sender}).
157
158receive_done(_Alias, _Tab, _Sender, _State) ->
159    ?DBG({done,_State}),
160    ok.
161
162close_table(Alias, Tab) -> sync_close_table(Alias, Tab).
163
164sync_close_table(ext_ets, _Tab) ->
165    ?DBG(_Tab).
166
167fixtable(ext_ets, Tab, Bool) ->
168    ?DBG({Tab,Bool}),
169    ets:safe_fixtable(mnesia_lib:val({?MODULE,Tab}), Bool).
170
171info(ext_ets, Tab, Type) ->
172    ?DBG({Tab,Type}),
173    Tid = mnesia_lib:val({?MODULE,Tab}),
174    try ets:info(Tid, Type) of
175	Val -> Val
176    catch _:_ ->
177	    undefined
178    end.
179
180real_suffixes() ->
181    [".dat"].
182
183tmp_suffixes() ->
184    [].
185
186%% Index
187
188index_is_consistent(_Alias, _Ix, _Bool) -> ok.  % Ignore for now
189is_index_consistent(_Alias, _Ix) -> false.      % Always rebuild
190
191%% Record operations
192
193validate_record(_Alias, _Tab, RecName, Arity, Type, _Obj) ->
194    {RecName, Arity, Type}.
195
196validate_key(_Alias, _Tab, RecName, Arity, Type, _Key) ->
197    {RecName, Arity, Type}.
198
199insert(ext_ets, Tab, Obj) ->
200    ?DBG({Tab,Obj}),
201    try
202	ets:insert(mnesia_lib:val({?MODULE,Tab}), Obj),
203	ok
204    catch _:Reason ->
205	    io:format("CRASH ~p ~p~n",[Reason, mnesia_lib:val({?MODULE,Tab})])
206    end.
207
208lookup(ext_ets, Tab, Key) ->
209    ets:lookup(mnesia_lib:val({?MODULE,Tab}), Key).
210
211delete(ext_ets, Tab, Key) ->
212    ets:delete(mnesia_lib:val({?MODULE,Tab}), Key).
213
214match_delete(ext_ets, Tab, Pat) ->
215    ets:match_delete(mnesia_lib:val({?MODULE,Tab}), Pat).
216
217first(ext_ets, Tab) ->
218    ets:first(mnesia_lib:val({?MODULE,Tab})).
219
220last(Alias, Tab) -> first(Alias, Tab).
221
222next(ext_ets, Tab, Key) ->
223    ets:next(mnesia_lib:val({?MODULE,Tab}), Key).
224
225prev(Alias, Tab, Key) ->
226    next(Alias, Tab, Key).
227
228slot(ext_ets, Tab, Pos) ->
229    ets:slot(mnesia_lib:val({?MODULE,Tab}), Pos).
230
231update_counter(ext_ets, Tab, C, Val) ->
232    ets:update_counter(mnesia_lib:val({?MODULE,Tab}), C, Val).
233
234select('$end_of_table' = End) -> End;
235select({ext_ets, C}) ->  ets:select(C).
236
237select(Alias, Tab, Ms) ->
238    Res = select(Alias, Tab, Ms, 100000),
239    select_1(Res).
240
241select_1('$end_of_table') -> [];
242select_1({Acc, C}) ->
243    case ets:select(C) of
244	'$end_of_table' -> Acc;
245	{New, Cont} ->
246	    select_1({New ++ Acc, Cont})
247    end.
248
249select(ext_ets, Tab, Ms, Limit) when is_integer(Limit); Limit =:= infinity ->
250    ets:select(mnesia_lib:val({?MODULE,Tab}), Ms, Limit).
251
252repair_continuation(Cont, Ms) ->
253    ets:repair_continuation(Cont, Ms).
254