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.''
18
-module(snmpa_general_db).
21
%%%-----------------------------------------------------------------
22
%%% This module implements a very simple "generic" MIB database
23
%%% interface. It contains the most common functions performed.
24
%%% It is generic in the sense that it implements both an interface
25
%%% to mnesia and ets.
27
%%% Note that this module has nothing to do with the snmp_generic
28
%%% and snmp_generic_mnesia modules.
29
%%%-----------------------------------------------------------------
31
-export([open/5, close/1, read/2, write/2, delete/1, delete/2]).
32
-export([sync/1, backup/2]).
33
-export([match_object/2, match_delete/2]).
34
-export([tab2list/1, info/1, info/2]).
37
-define(VMODULE,"GDB").
38
-include("snmp_verbosity.hrl").
41
%% ---------------------------------------------------------------
42
%% open(Info,Name,RecName,Attr,Type) -> term()
43
%% Info -> ets | {ets, Dir} |
44
%% {dets, Dir} | {dets, Dir, Action} |
45
%% {mnesia, Nodes} | {mnesia, Nodes, Action}
47
%% RecName -> Name of the record to store
48
%% Attr -> Attributes of the record stored in the table
52
%% Action -> keep | clear
54
%% Open or create a database table. In the mnesia/dets case,
55
%% where the table is stored on disc, it could be of interest
56
%% to clear the table. This is controlled by the Action parameter.
57
%% An empty node list ([]), is traslated into a list containing
59
%% ---------------------------------------------------------------
60
open({mnesia,Nodes,clear}, Name, RecName, Attr, Type) when list(Nodes) ->
61
?vtrace("[mnesia] open ~p database ~p for ~p on ~p; clear",
62
[Type, Name, RecName, Nodes]),
63
mnesia_open(mnesia_table_check(Name), Nodes, RecName, Attr, Type, clear);
64
open({mnesia,Nodes,_}, Name, RecName, Attr, Type) ->
65
?vtrace("[mnesia] open ~p database ~p for ~p on ~p; keep",
66
[Type, Name, RecName, Nodes]),
67
open({mnesia,Nodes}, Name, RecName, Attr, Type);
68
open({mnesia,Nodes}, Name, RecName, Attr, Type) when list(Nodes) ->
69
?vtrace("[mnesia] open ~p database ~p for ~p on ~p",
70
[Type, Name, RecName, Nodes]),
71
mnesia_open(mnesia_table_check(Name), Nodes, RecName, Attr, Type, keep);
73
open({dets, Dir, Action}, Name, _RecName, _Attr, Type) ->
74
dets_open(Name, dets_filename(Name, Dir), Type, Action);
75
open({dets, Dir}, Name, _RecName, _Attr, Type) ->
76
dets_open(Name, dets_filename(Name, Dir), Type, keep);
78
%% This function creates the ets table
79
open(ets, Name, _RecName, _Attr, Type) ->
80
?vtrace("[ets] open ~p database ~p", [Type, Name]),
81
Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
82
{ets, Ets, undefined};
83
open({ets, Dir}, Name, _RecName, _Attr, Type) ->
84
ets_open(Name, Dir, keep, Type);
85
open({ets, Dir, Action}, Name, _RecName, _Attr, Type) ->
86
ets_open(Name, Dir, Action, Type).
88
ets_open(Name, Dir, keep, Type) ->
89
?vtrace("[ets] open ~p database ~p", [Type, Name]),
90
File = filename:join(Dir, atom_to_list(Name) ++ ".db"),
91
case file:read_file_info(File) of
93
case ets:file2tab(File) of
97
user_err("failed converting file to (ets-) tab: "
99
"~n~p", [File, Reason]),
100
Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
104
Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
107
ets_open(Name, Dir, clear, Type) ->
108
File = filename:join(Dir, atom_to_list(Name) ++ ".db"),
109
Ets = ets:new(Name, [Type, protected, {keypos, 2}]),
114
mnesia_open({table_exist,Name},_Nodes,_RecName,_Attr,_Type,clear) ->
115
?vtrace("[mnesia] database ~p already exists; clear content",[Name]),
118
Recs = mnesia:match_object(Name,Pattern,read),
119
lists:foreach(fun(Rec) ->
120
mnesia:delete_object(Name,Rec,write)
124
case mnesia:transaction(F) of
126
exit({aborted,Reason});
130
mnesia_open({table_exist,Name},_Nodes,_RecName,_Attr,_Type,keep) ->
131
?vtrace("[mnesia] database ~p already exists; keep content",[Name]),
133
mnesia_open({no_table,Name},[],Type,RecName,Attr,Action) ->
134
mnesia_open({no_table,Name},[node()],Type,RecName,Attr,Action);
135
mnesia_open({no_table,Name},Nodes,RecName,Attr,Type,_) ->
136
?vtrace("[mnesia] no database ~p: create for ~p of type ~p",
137
[Name,RecName,Type]),
138
%% Ok, we assume that this means that the table does not exist
139
Args = [{record_name,RecName}, {attributes,Attr},
140
{type,Type}, {disc_copies,Nodes}],
141
case mnesia:create_table(Name,Args) of
145
%% ?vinfo("[mnesia] aborted: ~p", [Reason]),
146
exit({failed_create_mnesia_table,Reason})
150
mnesia_table_check(Name) ->
151
?vtrace("[mnesia] check existens of database ~p",[Name]),
152
case (catch mnesia:table_info(Name,type)) of
160
dets_open(Name, File, Type, Action) ->
161
?vtrace("[dets] open database ~p (~p)", [Name, Action]),
162
N = dets_open1(Name, File, Type),
163
dets_open2(N, Action).
165
dets_open1(Name, File, Type) ->
166
?vtrace("[dets] open database ~p of type ~p",[Name, Type]),
167
{ok, N} = dets:open_file(Name, [{file, File}, {type, Type}, {keypos, 2}]),
170
dets_open2(N, clear) ->
171
dets:match_delete(N,'_'),
176
%% dets_table_check(Name, Dir) ->
177
%% Filename = dets_filename(Name, Dir),
178
%% ?vtrace("[dets] check existens of database: "
183
%% [Name, Filename, file:list_dir(Dir), file:read_file_info(Filename)]),
184
%% case (catch dets:info(Filename, size)) of
185
%% {'EXIT', Reason} ->
186
%% {no_table, Name, Filename};
187
%% undefined -> %% Introduced in R8
188
%% {no_table, Name, Filename};
190
%% {table_exist, Name, Filename}
194
dets_filename(Name, Dir) ->
195
Dir1 = dets_filename1(Dir),
196
Dir2 = string:strip(Dir1, right, $/),
197
io_lib:format("~s/~p.dat", [Dir2, Name]).
199
dets_filename1([]) -> ".";
200
dets_filename1(Dir) -> Dir.
203
%% ---------------------------------------------------------------
207
%% Close the database. This does nothing in the mnesia case, but
208
%% deletes the table in the ets case.
209
%% ---------------------------------------------------------------
211
?vtrace("[mnesia] close database: NO ACTION",[]),
213
close({dets, Name}) ->
214
?vtrace("[dets] close database ~p",[Name]),
216
close({ets, Name, undefined}) ->
217
?vtrace("[ets] close (delete) table ~p",[Name]),
219
close({ets, Name, File}) ->
220
?vtrace("[ets] close (delete) table ~p",[Name]),
221
write_ets_file(Name, File),
225
%% ---------------------------------------------------------------
226
%% read(DbRef,Key) -> false | {value,Rec}
230
%% Retrieve a record from the database.
231
%% ---------------------------------------------------------------
232
read({mnesia, Name}, Key) ->
233
?vtrace("[mnesia] read (dirty) from database ~p: ~p",[Name,Key]),
234
case (catch mnesia:dirty_read(Name,Key)) of
235
[Rec|_] -> {value,Rec};
238
read({dets, Name}, Key) ->
239
?vtrace("[dets] read from table ~p: ~p",[Name,Key]),
240
case dets:lookup(Name, Key) of
241
[Rec|_] -> {value, Rec};
244
read({ets, Name, _}, Key) ->
245
?vtrace("[ets] read from table ~p: ~p",[Name,Key]),
246
case ets:lookup(Name, Key) of
247
[Rec|_] -> {value, Rec};
252
%% ---------------------------------------------------------------
253
%% write(DbRef,Rec) -> ok
257
%% Write a record to the database.
258
%% ---------------------------------------------------------------
259
write({mnesia, Name}, Rec) ->
260
?vtrace("[mnesia] write to database ~p",[Name]),
261
F = fun() -> mnesia:write(Name, Rec, write) end,
262
case mnesia:transaction(F) of
264
exit({aborted, Reason});
268
write({dets, Name}, Rec) ->
269
?vtrace("[dets] write to table ~p",[Name]),
270
dets:insert(Name, Rec);
271
write({ets, Name, _}, Rec) ->
272
?vtrace("[ets] write to table ~p",[Name]),
273
ets:insert(Name, Rec).
276
%% ---------------------------------------------------------------
280
%% Delete the database.
281
%% ---------------------------------------------------------------
282
delete({mnesia, Name}) ->
283
?vtrace("[mnesia] delete database: ~p",[Name]),
284
mnesia:delete_table(Name);
285
delete({dets, Name}) ->
286
?vtrace("[dets] delete database ~p",[Name]),
287
File = dets:info(Name, filename),
288
case dets:close(Name) of
294
delete({ets, Name, undefined}) ->
295
?vtrace("[dets] delete table ~p",[Name]),
297
delete({ets, Name, File}) ->
298
?vtrace("[dets] delete table ~p",[Name]),
303
%% ---------------------------------------------------------------
304
%% delete(DbRef, Key) -> ok
308
%% Delete a record from the database.
309
%% ---------------------------------------------------------------
310
delete({mnesia, Name}, Key) ->
311
?vtrace("[mnesia] delete from database ~p: ~p", [Name, Key]),
312
F = fun() -> mnesia:delete(Name, Key, write) end,
313
case mnesia:transaction(F) of
315
exit({aborted,Reason});
319
delete({dets, Name}, Key) ->
320
?vtrace("[dets] delete from table ~p: ~p", [Name, Key]),
321
dets:delete(Name, Key);
322
delete({ets, Name, _}, Key) ->
323
?vtrace("[ets] delete from table ~p: ~p", [Name, Key]),
324
ets:delete(Name, Key).
327
%% ---------------------------------------------------------------
328
%% match_object(DbRef,Pattern) -> [tuple()]
330
%% Pattern -> tuple()
332
%% Search the database for records witch matches the pattern.
333
%% ---------------------------------------------------------------
334
match_object({mnesia, Name}, Pattern) ->
335
?vtrace("[mnesia] match_object in ~p of ~p",[Name, Pattern]),
336
F = fun() -> mnesia:match_object(Name, Pattern, read) end,
337
case mnesia:transaction(F) of
339
exit({aborted, Reason});
343
match_object({dets, Name}, Pattern) ->
344
?vtrace("[dets] match_object in ~p of ~p",[Name, Pattern]),
345
dets:match_object(Name, Pattern);
346
match_object({ets, Name, _}, Pattern) ->
347
?vtrace("[ets] match_object in ~p of ~p",[Name, Pattern]),
348
ets:match_object(Name, Pattern).
351
%% ---------------------------------------------------------------
352
%% match_delete(DbRef,Pattern) ->
354
%% Pattern -> tuple()
356
%% Search the database for records witch matches the pattern and
357
%% deletes them from the database.
358
%% ---------------------------------------------------------------
359
match_delete({mnesia, Name}, Pattern) ->
360
?vtrace("[mnesia] match_delete in ~p with pattern ~p",[Name,Pattern]),
362
Recs = mnesia:match_object(Name, Pattern, read),
363
lists:foreach(fun(Rec) ->
364
mnesia:delete_object(Name, Rec, write)
368
case mnesia:transaction(F) of
370
exit({aborted, Reason});
374
match_delete({dets, Name}, Pattern) ->
375
?vtrace("[dets] match_delete in ~p with pattern ~p",[Name,Pattern]),
376
Recs = dets:match_object(Name, Pattern),
377
dets:match_delete(Name, Pattern),
379
match_delete({ets, Name, _}, Pattern) ->
380
?vtrace("[ets] match_delete in ~p with pattern ~p",[Name,Pattern]),
381
Recs = ets:match_object(Name, Pattern),
382
ets:match_delete(Name, Pattern),
386
%% ---------------------------------------------------------------
387
%% tab2list(DbRef) -> [tuple()]
390
%% Return all records in the table in the form of a list.
391
%% ---------------------------------------------------------------
392
tab2list({mnesia, Name}) ->
393
?vtrace("[mnesia] tab2list -> list of ~p", [Name]),
394
match_object({mnesia, Name}, mnesia:table_info(Name, wild_pattern));
395
tab2list({dets, Name}) ->
396
?vtrace("[dets] tab2list -> list of ~p", [Name]),
397
match_object({dets, Name}, '_');
398
tab2list({ets, Name, _}) ->
399
?vtrace("[ets] tab2list -> list of ~p", [Name]),
404
%% ---------------------------------------------------------------
405
%% info(Db) -> taglist()
406
%% info(Db, Item) -> Info
409
%% tablist() -> [{key(),value()}]
413
%% Retrieve table information.
414
%% ---------------------------------------------------------------
415
info({mnesia, Name}) ->
416
case (catch mnesia:table_info(Name, all)) of
417
Info when list(Info) ->
419
{'EXIT', {aborted, Reason}} ->
424
info({dets, Name}) ->
426
info({ets, Name, _}) ->
427
case ets:info(Name) of
435
info({mnesia, Name}, Item) ->
436
case (catch mnesia:table_info(Name, Item)) of
437
{'EXIT', {aborted, Reason}} ->
442
info({dets, Name}, memory) ->
443
dets:info(Name, file_size);
444
info({dets, Name}, Item) ->
445
dets:info(Name, Item);
446
info({ets, Name, _}, Item) ->
447
ets:info(Name, Item).
450
%% ---------------------------------------------------------------
451
%% sync(Db) -> ok | {error, Reason}
455
%% Dump table to disc (if possible)
456
%% ---------------------------------------------------------------
460
sync({dets, Name}) ->
462
sync({ets, _Name, undefined}) ->
464
sync({ets, Name, File}) ->
465
write_ets_file(Name, File).
468
%% ---------------------------------------------------------------
469
%% backup(Db, BackupDir) -> ok | {error, Reason}
473
%% Make a backup copy of the DB (only valid for det and ets)
474
%% ---------------------------------------------------------------
476
backup({mnesia, _}, _) ->
478
backup({dets, Name}, BackupDir) ->
479
case dets:info(Name, filename) of
483
case filename:dirname(Filename) of
487
Type = dets:info(Name, type),
488
KP = dets:info(Name, keypos),
490
filename:basename(Filename),
494
backup({ets, _Name, undefined}, _BackupDir) ->
496
backup({ets, Name, File}, BackupDir) ->
497
Filename = filename:basename(File),
498
case filename:join(BackupDir, Filename) of
500
%% Oups: backup-dir and db-dir the same
503
write_ets_file(Name, BackupFile)
507
dets_backup(Name, Filename, BackupDir, Type, KP) ->
508
?vtrace("dets_backup -> entry with"
513
"~n KP: ~p", [Name, Filename, BackupDir, Type, KP]),
514
BackupFile = filename:join(BackupDir, Filename),
515
?vtrace("dets_backup -> "
516
"~n BackupFile: ~p", [BackupFile]),
517
Backup = list_to_atom(atom_to_list(Name) ++ "_backup"),
518
Opts = [{file, BackupFile}, {type, Type}, {keypos, KP}],
519
case dets:open_file(Backup, Opts) of
521
?vtrace("dets_backup -> create fun", []),
523
dets_backup(Arg, start, Name, B)
525
dets:safe_fixtable(Name, true),
526
Res = dets:init_table(Backup, F, [{format, bchunk}]),
527
dets:safe_fixtable(Name, false),
528
?vtrace("dets_backup -> Res: ~p", [Res]),
531
?vinfo("dets_backup -> open_file failed: "
537
dets_backup(close, _Cont, _Name, B) ->
540
dets_backup(read, Cont1, Name, B) ->
541
case dets:bchunk(Name, Cont1) of
544
dets_backup(Arg, Cont2, Name, B)
555
%%----------------------------------------------------------------------
557
write_ets_file(Name, File) ->
558
TmpFile = File ++ ".tmp",
559
case ets:tab2file(Name, TmpFile) of
561
case file:rename(TmpFile, File) of
565
user_err("Warning: could not move file ~p"
566
" (~p)", [File, Else])
569
user_err("Warning: could not save file ~p (~p)",
574
%%----------------------------------------------------------------------
577
snmpa_error:user_err(F, A).