1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
16
%% $Id: mnesia_index.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
18
%% Purpose: Handles index functionality in mnesia
20
-module(mnesia_index).
45
-import(mnesia_lib, [verbose/2]).
46
-include("mnesia.hrl").
48
-record(index, {setorbag, pos_list}).
51
case ?catch_val(Var) of
52
{'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
56
%% read an object list throuh its index table
57
%% we assume that table Tab has index on attribute number Pos
59
read(Tid, Store, Tab, IxKey, Pos) ->
60
ResList = mnesia_locker:ixrlock(Tid, Store, Tab, IxKey, Pos),
61
%% Remove all tuples which don't include Ixkey, happens when Tab is a bag
62
case val({Tab, setorbag}) of
64
mnesia_lib:key_search_all(IxKey, Pos, ResList);
69
add_index(Index, Tab, Key, Obj, Old) ->
70
add_index2(Index#index.pos_list, Index#index.setorbag, Tab, Key, Obj, Old).
72
add_index2([{Pos, Ixt} |Tail], bag, Tab, K, Obj, OldRecs) ->
73
db_put(Ixt, {element(Pos, Obj), K}),
74
add_index2(Tail, bag, Tab, K, Obj, OldRecs);
75
add_index2([{Pos, Ixt} |Tail], Type, Tab, K, Obj, OldRecs) ->
76
%% Remove old tuples in index if Tab is updated
79
Old = mnesia_lib:db_get(Tab, K),
80
del_ixes(Ixt, Old, Pos, K);
82
del_ixes(Ixt, Old, Pos, K)
84
db_put(Ixt, {element(Pos, Obj), K}),
85
add_index2(Tail, Type, Tab, K, Obj, OldRecs);
86
add_index2([], _, _Tab, _K, _Obj, _) -> ok.
88
delete_index(Index, Tab, K) ->
89
delete_index2(Index#index.pos_list, Tab, K).
91
delete_index2([{Pos, Ixt} | Tail], Tab, K) ->
92
DelObjs = mnesia_lib:db_get(Tab, K),
93
del_ixes(Ixt, DelObjs, Pos, K),
94
delete_index2(Tail, Tab, K);
95
delete_index2([], _Tab, _K) -> ok.
98
del_ixes(_Ixt, [], _Pos, _L) -> ok;
99
del_ixes(Ixt, [Obj | Tail], Pos, Key) ->
100
db_match_erase(Ixt, {element(Pos, Obj), Key}),
101
del_ixes(Ixt, Tail, Pos, Key).
103
del_object_index(Index, Tab, K, Obj, Old) ->
104
del_object_index2(Index#index.pos_list, Index#index.setorbag, Tab, K, Obj, Old).
106
del_object_index2([], _, _Tab, _K, _Obj, _Old) -> ok;
107
del_object_index2([{Pos, Ixt} | Tail], SoB, Tab, K, Obj, Old) ->
110
del_object_bag(Tab, K, Obj, Pos, Ixt, Old);
111
_ -> %% If set remove the tuple in index table
112
del_ixes(Ixt, [Obj], Pos, K)
114
del_object_index2(Tail, SoB, Tab, K, Obj, Old).
116
del_object_bag(Tab, Key, Obj, Pos, Ixt, undefined) ->
117
Old = mnesia_lib:db_get(Tab, Key),
118
del_object_bag(Tab, Key, Obj, Pos, Ixt, Old);
119
%% If Tab type is bag we need remove index identifier if Tab
120
%% contains less than 2 elements.
121
del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when length(Old) < 2 ->
122
del_ixes(Ixt, [Obj], Pos, Key);
123
del_object_bag(_Tab, _Key, _Obj, _Pos, _Ixt, _Old) -> ok.
125
clear_index(Index, Tab, K, Obj) ->
126
clear_index2(Index#index.pos_list, Tab, K, Obj).
128
clear_index2([], _Tab, _K, _Obj) -> ok;
129
clear_index2([{_Pos, Ixt} | Tail], Tab, K, Obj) ->
130
db_match_erase(Ixt, Obj),
131
clear_index2(Tail, Tab, K, Obj).
133
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135
dirty_match_object(Tab, Pat, Pos) ->
136
%% Assume that we are on the node where the replica is
137
case element(2, Pat) of
139
IxKey = element(Pos, Pat),
140
RealKeys = realkeys(Tab, Pos, IxKey),
141
merge(RealKeys, Tab, Pat, []);
143
mnesia_lib:db_match_object(Tab, Pat)
146
merge([{_IxKey, RealKey} | Tail], Tab, Pat, Ack) ->
147
%% Assume that we are on the node where the replica is
148
Pat2 = setelement(2, Pat, RealKey),
149
Recs = mnesia_lib:db_match_object(Tab, Pat2),
150
merge(Tail, Tab, Pat, Recs ++ Ack);
151
merge([], _, _, Ack) ->
154
realkeys(Tab, Pos, IxKey) ->
155
Index = get_index_table(Tab, Pos),
156
db_get(Index, IxKey). % a list on the form [{IxKey, RealKey1} , ....
158
dirty_select(Tab, Spec, Pos) ->
159
%% Assume that we are on the node where the replica is
160
%% Returns the records without applying the match spec
161
%% The actual filtering is handled by the caller
162
IxKey = element(Pos, Spec),
163
RealKeys = realkeys(Tab, Pos, IxKey),
164
StorageType = val({Tab, storage_type}),
165
lists:append([mnesia_lib:db_get(StorageType, Tab, Key) || Key <- RealKeys]).
167
dirty_read(Tab, IxKey, Pos) ->
168
ResList = mnesia:dirty_rpc(Tab, ?MODULE, dirty_read2,
170
case val({Tab, setorbag}) of
172
%% Remove all tuples which don't include Ixkey
173
mnesia_lib:key_search_all(IxKey, Pos, ResList);
178
dirty_read2(Tab, IxKey, Pos) ->
179
Ix = get_index_table(Tab, Pos),
180
Keys = db_match(Ix, {IxKey, '$1'}),
181
r_keys(Keys, Tab, []).
183
r_keys([[H]|T],Tab,Ack) ->
184
V = mnesia_lib:db_get(Tab, H),
185
r_keys(T, Tab, V ++ Ack);
186
r_keys([], _, Ack) ->
190
%%%%%%% Creation, Init and deletion routines for index tables
191
%% We can have several indexes on the same table
192
%% this can be a fairly costly operation if table is *very* large
194
tab2filename(Tab, Pos) ->
195
mnesia_lib:dir(Tab) ++ "_" ++ integer_to_list(Pos) ++ ".DAT".
197
tab2tmp_filename(Tab, Pos) ->
198
mnesia_lib:dir(Tab) ++ "_" ++ integer_to_list(Pos) ++ ".TMP".
200
init_index(Tab, Storage) ->
201
PosList = val({Tab, index}),
202
init_indecies(Tab, Storage, PosList).
204
init_indecies(Tab, Storage, PosList) ->
209
init_disc_index(Tab, PosList);
211
make_ram_index(Tab, PosList);
213
make_ram_index(Tab, PosList)
216
%% works for both ram and disc indexes
218
del_index_table(_, unknown, _) ->
220
del_index_table(Tab, Storage, Pos) ->
221
delete_transient_index(Tab, Pos, Storage),
222
mnesia_lib:del({Tab, index}, Pos).
224
del_transient(Tab, Storage) ->
225
PosList = val({Tab, index}),
226
del_transient(Tab, PosList, Storage).
228
del_transient(_, [], _) -> done;
229
del_transient(Tab, [Pos | Tail], Storage) ->
230
delete_transient_index(Tab, Pos, Storage),
231
del_transient(Tab, Tail, Storage).
233
delete_transient_index(Tab, Pos, disc_only_copies) ->
234
Tag = {Tab, index, Pos},
235
mnesia_monitor:unsafe_close_dets(Tag),
236
file:delete(tab2filename(Tab, Pos)),
237
del_index_info(Tab, Pos), %% Uses val(..)
238
mnesia_lib:unset({Tab, {index, Pos}});
240
delete_transient_index(Tab, Pos, _Storage) ->
241
Ixt = val({Tab, {index, Pos}}),
242
?ets_delete_table(Ixt),
243
del_index_info(Tab, Pos),
244
mnesia_lib:unset({Tab, {index, Pos}}).
246
%%%%% misc functions for the index create/init/delete functions above
248
%% assuming that the file exists.
249
init_disc_index(_Tab, []) ->
251
init_disc_index(Tab, [Pos | Tail]) when integer(Pos) ->
252
Fn = tab2filename(Tab, Pos),
253
IxTag = {Tab, index, Pos},
255
Args = [{file, Fn}, {keypos, 1}, {type, bag}],
256
mnesia_monitor:open_dets(IxTag, Args),
257
Storage = disc_only_copies,
258
Key = mnesia_lib:db_first(Storage, Tab),
259
Recs = mnesia_lib:db_get(Storage, Tab, Key),
260
BinSize = size(term_to_binary(Recs)),
261
KeysPerChunk = (4000 div BinSize) + 1,
262
Init = {start, KeysPerChunk},
263
mnesia_lib:db_fixtable(Storage, Tab, true),
264
ok = dets:init_table(IxTag, create_fun(Init, Tab, Pos)),
265
mnesia_lib:db_fixtable(Storage, Tab, false),
266
mnesia_lib:set({Tab, {index, Pos}}, IxTag),
267
add_index_info(Tab, val({Tab, setorbag}), {Pos, {dets, IxTag}}),
268
init_disc_index(Tab, Tail).
270
create_fun(Cont, Tab, Pos) ->
274
{start, KeysPerChunk} ->
275
mnesia_lib:db_init_chunk(disc_only_copies, Tab, KeysPerChunk);
279
mnesia_lib:db_chunk(disc_only_copies, Cont)
285
IdxElems = [{element(Pos, Obj), element(2, Obj)} || Obj <- Recs],
286
{IdxElems, create_fun(Next, Tab, Pos)}
292
make_ram_index(_, []) ->
294
make_ram_index(Tab, [Pos | Tail]) ->
295
add_ram_index(Tab, Pos),
296
make_ram_index(Tab, Tail).
298
add_ram_index(Tab, Pos) when integer(Pos) ->
299
verbose("Creating index for ~w ~n", [Tab]),
300
Index = mnesia_monitor:mktab(mnesia_index, [bag, public]),
301
Insert = fun(Rec, _Acc) ->
302
true = ?ets_insert(Index, {element(Pos, Rec), element(2, Rec)})
304
mnesia_lib:db_fixtable(ram_copies, Tab, true),
305
true = ets:foldl(Insert, true, Tab),
306
mnesia_lib:db_fixtable(ram_copies, Tab, false),
307
mnesia_lib:set({Tab, {index, Pos}}, Index),
308
add_index_info(Tab, val({Tab, setorbag}), {Pos, {ram, Index}});
309
add_ram_index(_Tab, snmp) ->
312
add_index_info(Tab, Type, IxElem) ->
313
Commit = val({Tab, commit_work}),
314
case lists:keysearch(index, 1, Commit) of
316
Index = #index{setorbag = Type,
317
pos_list = [IxElem]},
318
%% Check later if mnesia_tm is sensative about the order
319
mnesia_lib:set({Tab, commit_work},
320
mnesia_lib:sort_commit([Index | Commit]));
322
%% We could check for consistency here
323
Index = Old#index{pos_list = [IxElem | Old#index.pos_list]},
324
NewC = lists:keyreplace(index, 1, Commit, Index),
325
mnesia_lib:set({Tab, commit_work},
326
mnesia_lib:sort_commit(NewC))
329
del_index_info(Tab, Pos) ->
330
Commit = val({Tab, commit_work}),
331
case lists:keysearch(index, 1, Commit) of
333
%% Something is wrong ignore
336
case lists:keydelete(Pos, 1, Old#index.pos_list) of
338
NewC = lists:keydelete(index, 1, Commit),
339
mnesia_lib:set({Tab, commit_work},
340
mnesia_lib:sort_commit(NewC));
342
Index = Old#index{pos_list = New},
343
NewC = lists:keyreplace(index, 1, Commit, Index),
344
mnesia_lib:set({Tab, commit_work},
345
mnesia_lib:sort_commit(NewC))
349
db_put({ram, Ixt}, V) ->
350
true = ?ets_insert(Ixt, V);
351
db_put({dets, Ixt}, V) ->
352
ok = dets:insert(Ixt, V).
354
db_get({ram, Ixt}, K) ->
356
db_get({dets, Ixt}, K) ->
359
db_match_erase({ram, Ixt}, Pat) ->
360
true = ?ets_match_delete(Ixt, Pat);
361
db_match_erase({dets, Ixt}, Pat) ->
362
ok = dets:match_delete(Ixt, Pat).
364
db_match({ram, Ixt}, Pat) ->
365
?ets_match(Ixt, Pat);
366
db_match({dets, Ixt}, Pat) ->
367
dets:match(Ixt, Pat).
369
get_index_table(Tab, Pos) ->
370
get_index_table(Tab, val({Tab, storage_type}), Pos).
372
get_index_table(Tab, ram_copies, Pos) ->
373
{ram, val({Tab, {index, Pos}})};
374
get_index_table(Tab, disc_copies, Pos) ->
375
{ram, val({Tab, {index, Pos}})};
376
get_index_table(Tab, disc_only_copies, Pos) ->
377
{dets, val({Tab, {index, Pos}})};
378
get_index_table(_Tab, unknown, _Pos) ->