PageRenderTime 147ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rdbms/src/rdbms.erl

https://github.com/babo/jungerl
Erlang | 1539 lines | 934 code | 246 blank | 359 comment | 19 complexity | 541ac40e0e229b28bd93e7bf931a2f47 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
  1. %%%
  2. %%% The contents of this file are subject to the Erlang Public License,
  3. %%% Version 1.0, (the "License"); you may not use this file except in
  4. %%% compliance with the License. You may obtain a copy of the License at
  5. %%% http://www.erlang.org/license/EPL1_0.txt
  6. %%%
  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
  10. %%% under the License.
  11. %%%
  12. %%% The Original Code is rdbms-1.2.
  13. %%%
  14. %%% The Initial Developer of the Original Code is Ericsson Telecom
  15. %%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
  16. %%% Telecom AB. All Rights Reserved.
  17. %%%
  18. %%% Contributor(s): ______________________________________.
  19. %%%----------------------------------------------------------------------
  20. %%% #0. BASIC INFORMATION
  21. %%%----------------------------------------------------------------------
  22. %%% File: rdbms.erl
  23. %%% Author : Ulf Wiger <ulf.wiger@ericsson.com>
  24. %%% Description : Relational constraints checking for mnesia databases
  25. %%%
  26. %%% Modules used : mnesia, mnesia_schema, lists
  27. %%%
  28. %%%----------------------------------------------------------------------
  29. -module(rdbms).
  30. -vsn('1.2').
  31. -date('99-12-18').
  32. -author('ulf.wiger@ericsson.com').
  33. %%%----------------------------------------------------------------------
  34. %%% #2. EXPORT LISTS
  35. %%%----------------------------------------------------------------------
  36. %%% #2.1 EXPORTED INTERFACE FUNCTIONS
  37. %%%----------------------------------------------------------------------
  38. %% Substitute for mnesia:transaction/1.
  39. -export([activity/1]).
  40. -export([begin_activity/4, end_activity/4]).
  41. -export([load_schema/1, load_schema/2]).
  42. -export([create_table/2,
  43. do_create_table/2,
  44. delete_table/1,
  45. do_delete_table/1]).
  46. -export([make_simple_form/1]).
  47. %%% Table metadata
  48. -export([
  49. %%% attributes/1, % (Table)
  50. %%% all_attributes/1, % (Table)
  51. %%% attribute/2, % (Position, Table)
  52. %%% attribute_value/3, % (Table, AttrName, Object)
  53. %%% position/2, % (Attribute, Table)
  54. %%% default_record/1, % (Table)
  55. %%% verify_write/2, % (Table, Object)
  56. %%% verify_delete/2, % (Table, Key)
  57. %%% verify_delete_object/2, % (Table, Object)
  58. register_commit_action/1, % (function/0)
  59. register_rollback_action/1]). % (function/0)
  60. %% utility functions
  61. -export([null_value/0]).
  62. -export([mk_oid/2]).
  63. -export([ix/2, ix_list/2, ix_vals/2]).
  64. %%% Update - Mnesia access module callbacks
  65. -export([lock/4,
  66. write/5,
  67. delete/5,
  68. delete_object/5,
  69. read/5,
  70. select/5,
  71. match_object/5,
  72. all_keys/4,
  73. index_match_object/6,
  74. index_read/6,
  75. table_info/4,
  76. first/3,
  77. last/3,
  78. next/4,
  79. prev/4,
  80. foldl/6,
  81. foldr/6,
  82. select/5,
  83. select/6,
  84. select_cont/3]).
  85. -export([onload_fun/2]).
  86. %% Used by other rdbms modules (includes VMod)
  87. -export([read/6,
  88. do_select/6]).
  89. -export([fetch_verification_module/0,
  90. default_verification_module/0]). % for remote procedure calls
  91. -export([patch_mnesia/0]).
  92. %%% extra retrieval functions
  93. %%%-export([fetch_objects/3]). % (Tab, SelectAttr, SelectKey)
  94. %%%----------------------------------------------------------------------
  95. %%% #2.2 EXPORTED INTERNAL FUNCTIONS
  96. %%%----------------------------------------------------------------------
  97. -include("rdbms.hrl").
  98. -include_lib("mnesia/src/mnesia.hrl").
  99. -import(mnesia, [abort/1]).
  100. -import(lists, [keysearch/3, foreach/2, foldl/3, foldr/3]).
  101. -define(KEYPOS, 2).
  102. -define(vmod, fetch_verification_module()).
  103. -define(JIT_MODULE, rdbms_verify_jit).
  104. -record(rdbms_activity, {is_schema_transaction = false,
  105. verification_module = {rdbms_verify, vrec(false)},
  106. refs_logs = [],
  107. funs = []}).
  108. %%%----------------------------------------------------------------------
  109. %%% #3. CODE
  110. %%%----------------------------------------------------------------------
  111. %%% #3.1 CODE FOR EXPORTED INTERFACE FUNCTIONS
  112. %%%----------------------------------------------------------------------
  113. patch_mnesia() ->
  114. application:load(mnesia),
  115. {ok,Ms} = application:get_key(mnesia, modules),
  116. ToPatch = Ms -- [mnesia, mnesia_controller, mnesia_frag,
  117. mnesia_lib, mnesia_loader, mnesia_log,
  118. mnesia_schema, mnesia_tm],
  119. OrigDir = filename:join(code:lib_dir(mnesia), "ebin"),
  120. lists:foreach(
  121. fun(M) ->
  122. F = filename:join(
  123. OrigDir,atom_to_list(M) ++ code:objfile_extension()),
  124. {ok,{_M,[{abstract_code,{raw_abstract_v1,Forms}}]}} =
  125. beam_lib:chunks(F, [abstract_code]),
  126. [_|TailF] = Forms,
  127. io:format("Transforming ~p ... ", [M]),
  128. NewTailF = transform_mod(TailF),
  129. {ok, Module, Bin} = compile:forms(NewTailF, []),
  130. io:format("ok.~n", []),
  131. case code:load_binary(Module, foo, Bin) of
  132. {module, Module} ->
  133. ok;
  134. Error ->
  135. erlang:error({Error,Module})
  136. end
  137. end, ToPatch).
  138. transform_mod(Fs) ->
  139. lists:map(fun({attribute,L,record,{cstruct,Flds}}) ->
  140. {attribute,L,record,{cstruct, insert_attr(Flds)}};
  141. (X) -> X
  142. end, Fs).
  143. insert_attr([{record_field,L,{atom,_,load_order},_} = H|T]) ->
  144. [{record_field,L,{atom,L,external_copies}, {nil,L}},H|T];
  145. insert_attr([H|T]) ->
  146. [H|insert_attr(T)];
  147. insert_attr([]) ->
  148. [].
  149. %%%----------------------------------------------------------------------
  150. %%% -type activity(Fun : function())->
  151. %%% ResultFromFun.
  152. %%% Input: Function object for Mnesia transaction
  153. %%% Output: Result from
  154. %%% Exceptions: EXIT if transaction aborted
  155. %%% Description: This is a wrapper around mnesia:activity/1.
  156. %%% It starts a mnesia activity with this module as the activity
  157. %%% module. This enables all RDBMS integrity checks.
  158. %%%----------------------------------------------------------------------
  159. begin_activity(_Type, Factor, Tid, Ts) ->
  160. {?MODULE, Tid, Ts} = get(mnesia_activity_state), % assertion
  161. if Factor > 1 ->
  162. %% transaction restarted
  163. #rdbms_activity{refs_logs = Logs} = A =
  164. get_activity_state(),
  165. case Logs of
  166. [] ->
  167. ok;
  168. [_|_] ->
  169. foreach(fun(Log) -> ets:delete(Log) end, Logs),
  170. put_activity_state(
  171. A#rdbms_activity{refs_logs = []})
  172. end;
  173. Factor == 1 ->
  174. if Ts#tidstore.level == 1 ->
  175. undefined = get_activity_state(),
  176. {IsSchemaTrans, VMod} = verification_module(),
  177. put_activity_state(
  178. #rdbms_activity{is_schema_transaction = IsSchemaTrans,
  179. verification_module = VMod});
  180. Ts#tidstore.level > 1 ->
  181. #rdbms_activity{refs_logs = Logs} = A =
  182. get_activity_state(),
  183. case Logs of
  184. [] ->
  185. ok;
  186. [PrevLog|_] ->
  187. NewLog = new_refs_log(),
  188. ets:insert(NewLog, ets:tab2list(PrevLog)),
  189. put_activity_state(
  190. A#rdbms_activity{refs_logs = [NewLog|Logs]})
  191. end
  192. end
  193. end.
  194. %%% TODO: mnesia_schema:schema_transaction() doesn't use
  195. %%% mnesia:activity(), so the default access module isn't used.
  196. %%%
  197. end_activity(Result, Type, _Tid, Ts) ->
  198. #rdbms_activity{is_schema_transaction = IsSchemaTrans,
  199. refs_logs = Logs,
  200. funs = Funs} = A =
  201. get_activity_state(),
  202. case Ts#tidstore.level of
  203. 1 ->
  204. case Logs of
  205. [] -> ok;
  206. [RefsLog] ->
  207. %% there should never be more than at most one level
  208. %% of refs_log here. Otherwise, something's gone wrong.
  209. ets:delete(RefsLog)
  210. end,
  211. case Result of
  212. {atomic, _} ->
  213. foreach(fun({F, _Level, commit}) ->
  214. catch_run(F, commit);
  215. (_) ->
  216. ok
  217. end, lists:reverse(Funs)),
  218. if Type == read_only ->
  219. case erlang:module_loaded(?JIT_MODULE) of
  220. false ->
  221. io:format("generating (read-only)~n",
  222. []),
  223. GenRes =
  224. (catch rdbms_codegen:regenerate()),
  225. io:format("GenRes = ~p~n", [GenRes]);
  226. true ->
  227. ok
  228. end;
  229. IsSchemaTrans; Type == asym_trans ->
  230. case ets:select_count(
  231. Ts#tidstore.store,
  232. [{'$1',[{'==',{element,1,'$1'},'op'}],
  233. [true]}]) of
  234. 0 ->
  235. io:format("no need to regenerate~n", []);
  236. N when N > 0 ->
  237. io:format("TidStore = ~n~p~n",
  238. [ets:tab2list(
  239. Ts#tidstore.store)]),
  240. io:format(
  241. "Schema updated - regenerate!~n", []),
  242. GenRes =
  243. (catch rdbms_codegen:regenerate()),
  244. io:format("GenRes = ~p~n", [GenRes])
  245. end;
  246. true ->
  247. ok
  248. end;
  249. {aborted, _} ->
  250. foreach(fun({F, _Level, rollback}) ->
  251. catch_run(F, rollback);
  252. (_) ->
  253. ok
  254. end, lists:reverse(Funs))
  255. end,
  256. erase_activity_state();
  257. L when L > 1 ->
  258. case Result of
  259. {aborted, _} ->
  260. NewFuns = [{F,Level,FType} ||
  261. {F,Level,FType} <- Funs,
  262. Level =/= L],
  263. put_activity_state(A#rdbms_activity{funs = NewFuns});
  264. {atomic,_} ->
  265. case Logs of
  266. [ThisLog,PrevLog|Rest] ->
  267. Refs = ets:tab2list(ThisLog),
  268. ets:delete(ThisLog),
  269. ets:insert(PrevLog, Refs),
  270. put_activity_state(
  271. A#rdbms_activity{refs_logs = [PrevLog|Rest]});
  272. [_SingleLog] ->
  273. ok;
  274. [] ->
  275. ok
  276. end
  277. end
  278. end.
  279. activity(Fun) ->
  280. mnesia:activity(transaction, Fun, [], ?MODULE).
  281. create_table(Name, Opts) ->
  282. mnesia_schema:schema_transaction(
  283. fun() ->
  284. do_create_table(Name, Opts)
  285. end).
  286. do_create_table(Name, Opts) ->
  287. case keysearch(rdbms, 1, Opts) of
  288. {value, {_, Props}} ->
  289. Options = lists:keydelete(rdbms,1,Opts),
  290. Cs = mnesia_schema:list2cs([{name,Name}|Options]),
  291. mnesia_schema:do_create_table(Cs),
  292. io:format("created ~p~n" , [Name]),
  293. {Indexes, Props1} =
  294. case keysearch(indexes, 1, Props) of
  295. {value, {_, I}} ->
  296. {I, lists:keydelete(indexes, 1, Props)};
  297. false ->
  298. {[], Props}
  299. end,
  300. lists:foreach(
  301. fun({K,V}) ->
  302. rdbms_props:do_set_property(Name,K,V)
  303. end, Props1),
  304. %%% rdbms_props:do_add_properties(Props1, Name),
  305. rdbms_index:do_add_indexes(Name, Indexes);
  306. false ->
  307. Cs = mnesia_schema:list2cs([{name,Name}|Opts]),
  308. mnesia_schema:do_create_table(Cs)
  309. end.
  310. delete_table(Name) ->
  311. mnesia_schema:schema_transaction(
  312. fun() ->
  313. do_delete_table(Name)
  314. end).
  315. do_delete_table(Name) ->
  316. mnesia_schema:do_delete_table(Name),
  317. rdbms_groups:do_drop_membership(Name),
  318. rdbms_props:do_drop_references(Name),
  319. case rdbms_props:indexes(Name) of
  320. [_|_] = Indexes ->
  321. rdbms_index:do_delete_indexes(Indexes);
  322. [] ->
  323. ok
  324. end.
  325. load_schema(File) ->
  326. load_schema(File, erl_eval:new_bindings()).
  327. load_schema(File, Bindings0) ->
  328. Vars = [{'SchemaNodes', mnesia:table_info(schema, disc_copies)}],
  329. Bindings =
  330. foldl(
  331. fun({K, V}, Bs) ->
  332. erl_eval:add_binding(K, V, Bs)
  333. end, Bindings0, Vars),
  334. case file:script(File, Bindings) of
  335. {ok, Dictionary} ->
  336. mnesia_schema:schema_transaction(
  337. fun() ->
  338. load_dictionary(Dictionary, [])
  339. end);
  340. Error ->
  341. Error
  342. end.
  343. load_dictionary(Dict, Parent) when is_list(Dict) ->
  344. foreach(
  345. fun({table, Tab, Opts}) ->
  346. case keysearch(rdbms, 1, Opts) of
  347. false -> do_create_table(Tab, Opts);
  348. {value,{_,[]}} -> do_create_table(Tab, Opts);
  349. {value, {_, [_|_] = ROpts}} ->
  350. case keysearch(membership, 1, ROpts) of
  351. false ->
  352. do_create_table(Tab, Opts);
  353. {value, {_, P}} when Parent =/= P, Parent =/= [] ->
  354. abort({conflicting_membership, Tab});
  355. {value, _} ->
  356. Opts1 = lists:keyreplace(
  357. rdbms, 1, Opts,
  358. lists:keydelete(membership, 1, ROpts)),
  359. do_create_table(Tab, Opts1)
  360. end
  361. end;
  362. ({group, Group, Members}) ->
  363. load_dictionary(Members, Group),
  364. MemberNames = lists:map(
  365. fun({table, T, _}) -> {table, T};
  366. ({group, G, _}) -> {group, G}
  367. end, Members),
  368. rdbms_group:do_add_group(Group, MemberNames)
  369. end, Dict).
  370. %% mnesia callbacks =====================================
  371. %% for each callback, an internal function is implemented.
  372. %% the internal functions are not exported, since they are only
  373. %% meant to be used for operations generated by the dictionary.
  374. %% No integrity checks are performed on the internal functions.
  375. lock(ActivityId, Opaque, LockItem, LockKind) ->
  376. Module = case LockItem of
  377. {record, T, _} -> get_module(T);
  378. {table, T} -> get_module(T);
  379. _ -> mnesia
  380. end,
  381. Module:lock(ActivityId, Opaque, LockItem, LockKind).
  382. write(Tid, Ts, Tab, Rec, LockKind) ->
  383. ?dbg("verify_write(~p, ~p)~n", [Tab, Rec]),
  384. VMod = ?vmod,
  385. validate_rec(Tab, Rec, VMod),
  386. do_write(Tid, Ts, Tab, Rec, LockKind, VMod),
  387. check_references(Tab, Rec, write, VMod).
  388. do_write(ActivityId, Opaque, WTab, WRec, LockKind, VMod) ->
  389. AMod = access_module(WTab, VMod),
  390. AMod:write(ActivityId, Opaque, WTab, WRec, LockKind),
  391. rdbms_index:update_index(
  392. ActivityId, Opaque, WTab, write, WRec, LockKind, VMod).
  393. %% non-exported function -- used by rdbms internally, no verification
  394. %%
  395. write(Tab, Rec, VMod) ->
  396. {ActivityId, Opaque} = get_mnesia_transaction_data(),
  397. do_write(ActivityId, Opaque, Tab, Rec, write, VMod).
  398. delete(Tid, Ts, Tab, Key, LockKind) ->
  399. delete(Tid, Ts, Tab, Key, LockKind, ?vmod).
  400. delete(Tid, Ts, Tab, Key, LockKind, VMod) ->
  401. Objs = read(Tid, Ts, Tab, Key, LockKind, VMod),
  402. AMod = access_module(Tab, VMod),
  403. foreach(fun(Obj) ->
  404. verify_delete_object(Tab, Obj, VMod),
  405. AMod:delete_object(Tid, Ts, Tab, Obj, LockKind),
  406. rdbms_index:update_index(
  407. Tid, Ts, Tab, delete_object, Obj, LockKind, VMod)
  408. end, Objs).
  409. delete_object(Tab, Obj, VMod) ->
  410. {ActivityId, Opaque} = get_mnesia_transaction_data(),
  411. delete_object(ActivityId, Opaque, Tab, Obj, write, VMod).
  412. delete_object(ActivityId, Opaque, Tab, Obj, LockKind) ->
  413. VMod = ?vmod,
  414. delete_object(ActivityId, Opaque, Tab, Obj, LockKind, VMod).
  415. delete_object(ActivityId, Opaque, Tab, Obj, LockKind, VMod) ->
  416. verify_delete_object(Tab, Obj, VMod),
  417. do_delete_object(ActivityId, Opaque, Tab, Obj, LockKind, VMod).
  418. do_delete_object(Tid, Ts, Tab, Obj, LockKind, VMod) ->
  419. AMod = access_module(Tab, VMod),
  420. AMod:delete_object(Tid, Ts, Tab, Obj, LockKind),
  421. rdbms_index:update_index(Tid, Ts, Tab, delete_object, Obj, LockKind, VMod).
  422. read(Tid, Ts, Tab, Key, LockKind) ->
  423. read(Tid, Ts, Tab, Key, LockKind, ?vmod).
  424. read(Tid, Ts, Tab, Key, LockKind, VMod) ->
  425. AMod = access_module(Tab, VMod),
  426. on_read(Tab, AMod:read(Tid, Ts, Tab, Key, LockKind), VMod).
  427. read(Tab, Key, VMod) ->
  428. {Tid, Ts} = get_mnesia_transaction_data(),
  429. read(Tid, Ts, Tab, Key, read, VMod).
  430. match_object(ActivityId, Opaque, Tab, Pattern, _LockKind) ->
  431. VMod = ?vmod,
  432. AMod = access_module(Tab, VMod),
  433. AMod:match_object(ActivityId, Opaque, Tab, Pattern, read).
  434. match_object(Tab, Pattern, VMod) ->
  435. {ActivityId, Opaque} = get_mnesia_transaction_data(),
  436. VMod = ?vmod,
  437. AMod = access_module(Tab, VMod),
  438. AMod:match_object(ActivityId, Opaque, Tab, Pattern, read).
  439. all_keys(ActivityId, Opaque, Tab, LockKind) ->
  440. VMod = ?vmod,
  441. AMod = access_module(Tab, VMod),
  442. AMod:all_keys(ActivityId, Opaque, Tab, LockKind).
  443. index_match_object(ActivityId, Opaque, Tab, Pattern, Attr, LockKind) ->
  444. VMod = ?vmod,
  445. AMod = access_module(Tab, VMod),
  446. AMod:index_match_object(ActivityId, Opaque, Tab,
  447. Pattern, Attr, LockKind).
  448. index_read(Tid, Ts, Tab, SecondaryKey, Attr, LockKind) ->
  449. index_read(Tid, Ts, Tab, SecondaryKey, Attr, LockKind, ?vmod).
  450. index_read(Tid, Ts, Tab, SecondaryKey, Attr, LockKind, VMod) ->
  451. rdbms_index:read(Tid, Ts, Tab, SecondaryKey, Attr, LockKind, VMod).
  452. index_read(Tab, SecondaryKey, Attr, VMod) ->
  453. {Tid, Ts} = get_mnesia_transaction_data(),
  454. index_read(Tid, Ts, Tab, SecondaryKey, Attr, VMod).
  455. table_info(_ActivityId, _Opaque, Tab, InfoItem) ->
  456. VMod = ?vmod,
  457. table_info(Tab, InfoItem, VMod).
  458. first(Tid, Ts, Tab) ->
  459. first(Tid, Ts, Tab, ?vmod).
  460. first(Tid, Ts, Tab, VMod) ->
  461. AMod = access_module(Tab, VMod),
  462. AMod:first(Tid, Ts, Tab).
  463. last(Tid, Ts, Tab) ->
  464. last(Tid, Ts, Tab, ?vmod).
  465. last(Tid, Ts, Tab, VMod) ->
  466. AMod = access_module(Tab, VMod),
  467. AMod:last(Tid, Ts, Tab).
  468. next(Tid, Ts, Tab, Key) ->
  469. next(Tid, Ts, Tab, Key, ?vmod).
  470. next(Tid, Ts, Tab, Key, VMod) ->
  471. AMod = access_module(Tab, VMod),
  472. AMod:next(Tid, Ts, Tab, Key).
  473. prev(Tid, Ts, Tab, Key) ->
  474. prev(Tid, Ts, Tab, Key, ?vmod).
  475. prev(Tid, Ts, Tab, Key, VMod) ->
  476. AMod = access_module(Tab, VMod),
  477. AMod:prev(Tid, Ts, Tab, Key).
  478. foldl(Tid, Ts, Fun, Acc, Tab, LockKind) ->
  479. foldl(Tid, Ts, Fun, Acc, Tab, LockKind, ?vmod).
  480. foldl(Tid, Ts, Fun, Acc, Tab, LockKind, VMod) ->
  481. AMod = access_module(Tab, VMod),
  482. AMod:foldl(Tid, Ts, Fun, Acc, Tab, LockKind).
  483. foldr(Tid, Ts, Fun, Acc, Tab, LockKind) ->
  484. foldr(Tid, Ts, Fun, Acc, Tab, LockKind, ?vmod).
  485. foldr(Tid, Ts, Fun, Acc, Tab, LockKind, VMod) ->
  486. AMod = access_module(Tab, VMod),
  487. AMod:foldr(Tid, Ts, Fun, Acc, Tab, LockKind).
  488. select(Tid, Ts, Tab, Pat, LockKind) ->
  489. do_select(Tid, Ts, Tab, Pat, LockKind, ?vmod).
  490. do_select(Tid, Ts, Tab, Pat, LockKind, VMod) ->
  491. AMod = access_module(Tab, VMod),
  492. AMod:select(Tid, Ts, Tab, Pat, LockKind).
  493. select(Tid, Ts, Tab, Pat, NObjects, LockKind) ->
  494. do_select(Tid, Ts, Tab, Pat, NObjects, LockKind, ?vmod).
  495. do_select(Tid, Ts, Tab, Pat, NObjects, LockKind, VMod) ->
  496. AMod = access_module(Tab, VMod),
  497. case AMod:select(Tid, Ts, Tab, Pat, NObjects, LockKind) of
  498. '$end_of_table' = Result ->
  499. Result;
  500. {Objs, Cont} ->
  501. {Objs, {AMod, fun(Tid1, Ts1) ->
  502. AMod:select_cont(Tid1, Ts1, Cont)
  503. end}}
  504. end.
  505. %%% Cont is required to be a fun/2 as returned from rdbms:do_select/7 above.
  506. %%%
  507. select_cont(Tid, Ts, {AMod, Cont}) when is_function(Cont, 2) ->
  508. case Cont(Tid, Ts) of
  509. {Objs, Cont1} ->
  510. {Objs, {AMod, fun(Tid1, Ts1) ->
  511. AMod:select_cont(Tid1, Ts1, Cont1)
  512. end}};
  513. '$end_of_table' ->
  514. '$end_of_table'
  515. end.
  516. onload_fun(Table, LoadReason) ->
  517. rdbms_index:index_init_fun(Table, LoadReason).
  518. %% end mnesia callbacks =====================================
  519. %%% fetch_objects(Tab, Attr, Keys) ->
  520. %%% fetch_objects(Tab, Attr, Keys, ?vmod).
  521. fetch_objects(Tab, Attr, Keys, VMod) ->
  522. case key_type(Tab, Attr, VMod) of
  523. primary ->
  524. lists:flatmap(
  525. fun(K) ->
  526. read(Tab, K, VMod)
  527. end, Keys);
  528. secondary ->
  529. lists:flatmap(
  530. fun(K) ->
  531. opt_index_read(Tab, K, Attr, VMod)
  532. end, Keys);
  533. attribute ->
  534. Wild = table_info(Tab, wild_pattern, VMod),
  535. Apos = attribute_position(Tab, Attr, VMod),
  536. Pat = [{setelement(Apos, Wild, Key), [], ['$_']} ||
  537. Key <- Keys],
  538. select(Tab, Pat, VMod);
  539. %%% Found = match_object(Tab, setelement(Apos, Wild, Key), VMod);
  540. {compound, Attrs} ->
  541. Wild = table_info(Tab, wild_pattern, VMod),
  542. Pat =
  543. [{build_compound_pattern(Attrs,Key,Wild,Tab,VMod),[],['$_']} ||
  544. Key <- Keys],
  545. select(Tab, Pat, VMod)
  546. %%% match_object(
  547. %%% Tab, build_compound_pattern(
  548. %%% Attrs, Key, Wild, Tab, VMod), VMod)
  549. end.
  550. select(Tab, Pattern, VMod) ->
  551. {Tid, Ts} = get_mnesia_transaction_data(),
  552. VMod = ?vmod,
  553. AMod = access_module(Tab, VMod),
  554. AMod:select(Tid, Ts, Tab, Pattern, write).
  555. opt_index_read(Tab, Key, Attr, VMod) ->
  556. index_read(Tab, Key, Attr, VMod).
  557. %% build_compound_pattern([AttrName], Key, WildPattern, TableName)
  558. %%
  559. %% This is used when fetching objects based on a compound attribute
  560. %% Example: Given a record #person{surname, lastname, ...}, and if
  561. %% Example: 'name' is defined as a compound attribute: [surname, lastname],
  562. %% we can specify 'name' := ["ulf", "wiger"], and this function will translate
  563. %% it to #person{surname = "ulf", lastname = "wiger"}.
  564. %%
  565. build_compound_pattern(Attrs, Key, Wild, Tab, VMod) when tuple(Key) ->
  566. build_compound_pattern1(Attrs, tuple_to_list(Key), Wild, Tab, VMod);
  567. build_compound_pattern(Attrs, Key, Wild, Tab, VMod) ->
  568. build_compound_pattern1(Attrs, Key, Wild, Tab, VMod).
  569. build_compound_pattern1([Attr|Attrs], [Value|Vals], Pat, Tab, VMod) ->
  570. Pos = attribute_position(Tab, Attr, VMod),
  571. build_compound_pattern1(
  572. Attrs, Vals, setelement(Pos, Pat, Value), Tab, VMod);
  573. build_compound_pattern1([], [], Pat, _, _) ->
  574. Pat.
  575. %% null_value()
  576. %%
  577. %% Erlang doesn't have a real null value that can be used.
  578. %% I think this is a pity, but not enough other people agreed...
  579. %%
  580. %% The null value is not configureable, but I don't consider this a big
  581. %% problem. Having a dynamic null value could cause severe problems, and would
  582. %% also incur extra overhead.
  583. %%
  584. null_value() -> ?NULL.
  585. mk_oid(_Tab, _Attr) ->
  586. {node(), erlang:now()}.
  587. %%% ix(Value, [])
  588. %%%
  589. %%% indexing callback, can be used if all you want is the value of
  590. %%% a single attribute (much like mnesia's built-in indexes, but
  591. %%% here you can get ordered indexes, unique indexes, etc.)
  592. ix(Value, []) ->
  593. [Value].
  594. %%% ix_list(Value, [])
  595. %%%
  596. %%% indexing callback, if the attribute is a list of values, and each
  597. %%% value in the list is to be used as index values.
  598. ix_list(Val, []) when is_list(Val) ->
  599. Val.
  600. %%% ix_vals(Object, PosList)
  601. %%%
  602. %%% indexing callback, similar to the snmp oids. The 2nd argument
  603. %%% must be a list of attribute position integers, and the 1st arg
  604. %%% must be a tuple (normally the whole object)
  605. ix_vals(Obj, PosList) ->
  606. [[element(P,Obj) || P <- PosList]].
  607. %% if functions are called during a transaction which have side-effects,
  608. %% these functions may be used to "commit" or "undo" the effect.
  609. %% multiple calls can be made during one transaction; the functions will
  610. %% be called LIFO *after* the transaction is aborted or commited.
  611. %%
  612. %% TODO - changed call order to FIFO
  613. register_rollback_action(Fun) ->
  614. register_action(rollback, Fun).
  615. register_commit_action(Fun) ->
  616. register_action(commit, Fun).
  617. %%%----------------------------------------------------------------------
  618. %%% #3.2 CODE FOR EXPORTED INTERNAL FUNCTIONS
  619. %%%----------------------------------------------------------------------
  620. %%%----------------------------------------------------------------------
  621. %%% #3.3 CODE FOR INTERNAL FUNCTIONS
  622. %%%----------------------------------------------------------------------
  623. %%%----------------------------------------------------------------------
  624. %%% #3.3.1 Code for Additional Actions (post-transaction triggers).
  625. %%%----------------------------------------------------------------------
  626. register_action(Type, Fun) when Type==rollback; Type==commit ->
  627. case get(mnesia_activity_state) of
  628. {_M, _Tid, Ts} ->
  629. #rdbms_activity{funs = Funs} = A =
  630. get_activity_state(),
  631. Funs1 = [{Fun, Ts#tidstore.level, Type}|Funs],
  632. put_activity_state(A#rdbms_activity{funs = Funs1});
  633. undefined ->
  634. exit(no_transaction)
  635. end.
  636. get_activity_state() ->
  637. get(rdbms_activity_state).
  638. put_activity_state(#rdbms_activity{} = State) ->
  639. put(rdbms_activity_state, State);
  640. put_activity_state(Other) ->
  641. exit({bad_activity_state, Other}).
  642. erase_activity_state() ->
  643. erase(rdbms_activity_state).
  644. catch_run(F, Type) ->
  645. case catch F() of
  646. {'EXIT', Reason} ->
  647. error_logger:error_report([{?MODULE, caught_exception},
  648. {additional_action, Type},
  649. {'EXIT', Reason}]),
  650. ok;
  651. _ ->
  652. ok
  653. end.
  654. %%%----------------------------------------------------------------------
  655. %%% #3.3.1 Code for RDBMS verification functions.
  656. %%%----------------------------------------------------------------------
  657. %%% verify_write(Tab, Rec) ->
  658. %%% validate_rec(Tab, Rec, ?vmod).
  659. validate_rec(Tab, Rec, ?JIT_MODULE) ->
  660. try ?JIT_MODULE:validate_rec(Tab, Rec)
  661. catch
  662. error:Reason ->
  663. erlang:error({Reason, erlang:get_stacktrace()})
  664. end;
  665. validate_rec(Tab, Rec, {rdbms_verify, VRec}) ->
  666. rdbms_verify:validate_rec(Tab, Rec, VRec).
  667. access_module(Tab, ?JIT_MODULE) ->
  668. ?JIT_MODULE:module(Tab);
  669. access_module(Tab, {rdbms_verify, VRec}) ->
  670. rdbms_verify:module(Tab, VRec).
  671. check_access(Tab, Obj, Mode, VMode) ->
  672. rdbms_verify:check_access(acl(Tab, VMode), Mode, Obj, Tab).
  673. on_read(Tab, Objs, ?JIT_MODULE) ->
  674. ?JIT_MODULE:on_read(Tab, Objs);
  675. on_read(Tab, Objs, {rdbms_verify,VRec}) ->
  676. rdbms_verify:on_read(Objs, Tab, VRec).
  677. acl(Tab, ?JIT_MODULE) ->
  678. ?JIT_MODULE:acl(Tab);
  679. acl(Tab, {rdbms_verify, VRec}) ->
  680. rdbms_verify:acl(Tab, VRec).
  681. references(Tab, ?JIT_MODULE) ->
  682. ?JIT_MODULE:references(Tab);
  683. references(Tab, {rdbms_verify, VRec}) ->
  684. rdbms_verify:references(Tab, VRec).
  685. %%% rec_type(Tab, ?JIT_MODULE) ->
  686. %%% ?JIT_MODULE:rec_type(Tab);
  687. %%% rec_type(Tab, {rdbms_verify, VRec}) ->
  688. %%% rdbms_verify:rec_type(Tab, VRec).
  689. %%% acl(Tab, ?JIT_MODULE) ->
  690. %%% ?JIT_MODULE:acl(Tab);
  691. %%% acl(Tab, {rdbms_verify, VRec}) ->
  692. %%% rdbms_verify:acl(Tab, VRec).
  693. %%% global_types(?JIT_MODULE) ->
  694. %%% ?JIT_MODULE:global_types();
  695. %%% global_types({rdbms_verify, #verify{is_schema_trans = IsSchemaTrans}}) ->
  696. %%% if IsSchemaTrans ->
  697. %%% rdbms_props:schema_global_types();
  698. %%% true ->
  699. %%% rdbms_props:global_types()
  700. %%% end.
  701. attr_property(Tab, Attr, Prop, VMod) ->
  702. attr_property(Tab, Attr, Prop, VMod, undefined).
  703. attr_property(Tab, Attr, Prop, ?JIT_MODULE, Default) ->
  704. ?JIT_MODULE:attr_property(Tab, Attr, Prop, Default);
  705. attr_property(Tab, Attr, Prop, {_, #verify{attr_property = AP}}, Default) ->
  706. AP(Tab, Attr, Prop, Default).
  707. table_info(Tab, InfoItem, ?JIT_MODULE) ->
  708. ?JIT_MODULE:table_info(Tab, InfoItem);
  709. table_info(Tab, InfoItem, {_, #verify{table_info = TI}}) ->
  710. TI(Tab, InfoItem).
  711. fetch_verification_module() ->
  712. case get_activity_state() of
  713. undefined ->
  714. {rdbms_verify, vrec(false)};
  715. #rdbms_activity{verification_module = VMod} ->
  716. VMod
  717. end.
  718. default_verification_module() ->
  719. {rdbms_verify, vrec(false)}.
  720. verification_module() ->
  721. IsSchemaTrans = is_schema_transaction(),
  722. Mod =
  723. case IsSchemaTrans of
  724. true ->
  725. {rdbms_verify, vrec(true)};
  726. false ->
  727. Jit = ?JIT_MODULE,
  728. case erlang:module_loaded(Jit) of
  729. true ->
  730. Jit;
  731. false ->
  732. {rdbms_verify, vrec(false)}
  733. end
  734. end,
  735. {IsSchemaTrans, Mod}.
  736. is_schema_transaction() ->
  737. case process_info(self(), initial_call) of
  738. {_, {mnesia_schema, schema_coordinator, _}} ->
  739. true;
  740. _ ->
  741. false
  742. end.
  743. %%% IDEA: Two "verification modules": ?JIT_MODULE, and rdbms_verify.
  744. %%% rdbms_verify takes a verification record containing funs to access
  745. %%% metadata. These funs are different depending on whether the transaction
  746. %%% is a schema transaction or a normal transaction.
  747. %%%
  748. vrec(_IsSchemaTrans = false) ->
  749. {Tid,Ts} = get_tid_ts(),
  750. #verify{is_schema_trans = false,
  751. tab_property = fun(Tab, P, Default) ->
  752. rdbms_props:table_property(Tab, P, Default)
  753. end,
  754. attr_property = fun(Tab, A, P, Def) ->
  755. rdbms_props:attr_property(Tab, A, P, Def)
  756. end,
  757. global_property = fun(P, Def) ->
  758. rdbms_props:global_property(P, Def)
  759. end,
  760. table_info = fun(Tab, I) ->
  761. mnesia:table_info(Tid, Ts, Tab, I)
  762. end};
  763. vrec(_IsSchemaTrans = true) ->
  764. #verify{is_schema_trans = true,
  765. tab_property = fun(Tab, P, Def) ->
  766. rdbms_props:schema_table_property(
  767. Tab, P, Def)
  768. end,
  769. attr_property = fun(Tab, Attr, P, Def) ->
  770. rdbms_props:schema_attr_property(
  771. Tab, Attr, P, Def)
  772. end,
  773. global_property = fun(P, Def) ->
  774. rdbms_props:schema_global_property(
  775. P, Def)
  776. end,
  777. table_info = fun(Tab, I) ->
  778. rdbms_props:schema_table_info(Tab, I)
  779. end}.
  780. get_tid_ts() ->
  781. case get(mnesia_activity_state) of
  782. undefined ->
  783. {{async,self()}, non_transaction};
  784. {_, Tid, Ts} ->
  785. {Tid, Ts}
  786. end.
  787. get_module(Tab) ->
  788. case mnesia:table_info(Tab, frag_properties) of
  789. [] ->
  790. mnesia;
  791. [_|_] ->
  792. mnesia_frag
  793. end.
  794. %% verify_delete(Tab, Key, DelF)
  795. %% This is called in order to verify a mnesia:delete({Tab, Key})
  796. %% It triggers verification of referential integrity.
  797. %%
  798. %%% verify_delete(Tab, Key) ->
  799. %%% verify_delete(Tab, Key, ?vmod).
  800. %%% verify_delete(Tab, Key, VMod) ->
  801. %%% Objs = read(Tab, Key, VMod),
  802. %%% foreach(fun(Obj) -> verify_delete_object(Tab, Obj, VMod) end, Objs).
  803. %% verify_delete_object(Tab, Obj)
  804. %% Similar to verify_delete(), but for delete_object/1
  805. %%
  806. %%% verify_delete_object(Tab, Obj) when record(Obj, rdbms_obj) ->
  807. %%% verify_delete_object(Tab, rdbms_obj_to_record(Obj));
  808. %%% verify_delete_object(Tab, Obj) ->
  809. %%% verify_delete_object(Tab, Obj, ?vmod).
  810. verify_delete_object(Tab, Obj, VMod) ->
  811. check_access(Tab, Obj, delete, VMod),
  812. check_references(Tab, Obj, delete, VMod).
  813. %% violation(Which, Data)
  814. %% called if an integrity check fails (not consistently used yet)
  815. %% Which : which type of check was violated
  816. %% Data : helpful information
  817. %%
  818. violation(Which, Data) ->
  819. ?dbg("~p violation (~p)~n", [Which, Data]),
  820. exit({violation, {Which, Data}}).
  821. %%
  822. %% Functions for accessing metadata.
  823. %% -- metadata values are stored in the table 'sysMnesiaDict'
  824. %% which has the structure -record(sysMnesiaDict, {key, value}).
  825. %% where key ::= {Obj_type, Obj, Type}, e.g. {attr, {Table, Attr}, Type},
  826. %% {attr, Attr, Type}, {table, Table, Type}, or {record, Rec, Type}.
  827. %%
  828. %%
  829. %% check_references(Tab, Attribute, Value, Context) -> [RelatedObject]
  830. %%
  831. %% This function checks referential integrity.
  832. %% Referential integrity rules are stored as metadata in the following way:
  833. %% {{attr, {Tab, Attr}, references}, [{Tab2, Attr2, RefActions}]}, where
  834. %% Tab : the referencing table
  835. %% Attr : the referencing attribute
  836. %% Tab2 : the referenced table
  837. %% Attr2 : the referenced attribute(s) -
  838. %% atom() | {atom()} | function(Object, Value)
  839. %% RefActions : {Match, DeleteAction : Action, UpdateAction : Action}
  840. %% Match : handling of null values - partial | full
  841. %% Action : referential action -
  842. %% no_action | cascade | set_default | set_null
  843. %%
  844. check_references(Tab, Rec, Context, VMod) ->
  845. case references(Tab, VMod) of
  846. [] ->
  847. true;
  848. [_|_] = Refs ->
  849. Log = get_refs_log(),
  850. foreach(
  851. fun({How, What, Refs1}) ->
  852. Values =
  853. case How of
  854. attr ->
  855. [attribute_value(Tab, What, Rec, VMod)];
  856. index ->
  857. rdbms_index:index_values(
  858. Tab,What,Rec,VMod);
  859. eval ->
  860. {Mod, Fun, Arg} = What,
  861. Mod:Fun(Rec, Arg)
  862. end,
  863. foreach(
  864. fun({_Tab2, _Attr2, {_,_,ignore}})
  865. when Context==write ->
  866. true;
  867. ({_Tab2, _Attr2, {_,ignore,_}})
  868. when Context==delete ->
  869. true;
  870. ({Tab2, Attr2, Actions}) ->
  871. do_follow_refs(
  872. Tab2,Attr2,Actions,
  873. Tab, How, What, Values, Context,
  874. Log, VMod)
  875. end, Refs1)
  876. end, Refs),
  877. true
  878. end.
  879. do_follow_refs(Tab2, {via_index, Ix}=Via, Actions,
  880. _Tab, _How, _What, Values, Context, Log, VMod) ->
  881. {Tid, Ts} = get_mnesia_transaction_data(),
  882. Objs = lists:flatmap(
  883. fun(Value) ->
  884. index_read(Tid, Ts, Tab2, Value, Ix, write, VMod)
  885. end, Values),
  886. perform_ref_actions(
  887. Actions, Objs, Context, Tab2, Via, Values, Log, VMod);
  888. do_follow_refs(Tab2, Attr2, Actions,
  889. Tab, How, What, Values, Context, Log, VMod) ->
  890. {Match, _, _} = Actions,
  891. Objs = ref_match(Tab2, Attr2, Match, Values, Tab, How, What, VMod),
  892. perform_ref_actions(
  893. Actions, Objs, Context, Tab2, Attr2, Values, Log, VMod).
  894. %%% inspect_references(Refs, Tab, Obj, delete, VMod) ->
  895. %%% %% to avoid a possible endless loop, delete Obj first
  896. %%% do_delete_object(Tab, Obj, VMod),
  897. %%% foreach(
  898. %%% fun({Attr, Refs1}) ->
  899. %%% foreach(
  900. %%% fun({_Tab2, _Attr2, {_
  901. %%% fun({Attr,Val, Refs1}) ->
  902. %%% do_follow_refs(
  903. %%% follow_refs(Refs1, Val, delete, Tab, Attr) ++ Acc
  904. %%% end, [], Refs1)
  905. %%% end, Refs).
  906. %%% perform_ref_actions(_, Objs, read, _, _, _, Acc, _) ->
  907. %%% lists:foldl(fun(O, A) -> [O|A] end, Acc, Objs);
  908. perform_ref_actions(Actions, Objs, delete, Tab2, Attr2, Val, Log, VMod) ->
  909. {_Match, OnDelete, _OnUpdate} = Actions,
  910. case OnDelete of
  911. cascade ->
  912. foreach(
  913. fun(Obj) ->
  914. log_ref(Tab2, Obj, delete, Log),
  915. delete_object(Tab2, Obj, VMod)
  916. end, Objs),
  917. true;
  918. set_null ->
  919. cascade_update(Objs, Tab2, Attr2, ?NULL, Log, VMod);
  920. set_default ->
  921. cascade_update(Objs, Tab2, Attr2,
  922. default(Tab2, Attr2, VMod), Log, VMod);
  923. no_action ->
  924. if Objs == [] ->
  925. [];
  926. true ->
  927. violation(ref_integrity, {delete, [Tab2,Attr2,Val]})
  928. end
  929. end;
  930. perform_ref_actions(Actions, Objs, write, Tab2, Attr2, Val, Log, VMod) ->
  931. {_Match, _OnDelete, OnUpdate} = Actions,
  932. case OnUpdate of
  933. cascade ->
  934. cascade_update(Objs, Tab2, Attr2, Val, Log, VMod);
  935. set_null ->
  936. cascade_update(Objs, Tab2, Attr2, ?NULL, Log, VMod);
  937. set_default ->
  938. cascade_update(Objs, Tab2, Attr2,
  939. default(Tab2, Attr2, VMod), Log, VMod);
  940. no_action ->
  941. %% here we should probably also use the MATCH condition...
  942. %% but since MATCH only matters for composite values, we skip it
  943. %% for now.
  944. if Objs == [] ->
  945. violation(ref_integrity, {write, [Tab2, Attr2, Val]});
  946. true ->
  947. true
  948. end
  949. end.
  950. %% ref_match(...)
  951. %%
  952. %% This has to do with the handling of null values.
  953. %% If the attribute used in the referencing action is a compound attribute,
  954. %% we must deal with the issue of partial matches
  955. %% - thus the (Match : full | partial) stuff
  956. %% For a more exhaustive explanation, see the SQL Standard
  957. %%
  958. ref_match(Tab2, Attr2, _Match, Val, _Tab1, _H, _W, VMod) when atom(Attr2) ->
  959. fetch_objects(Tab2, Attr2, Val, VMod);
  960. ref_match(Tab2, Attr2, Match, Vals,
  961. _Tab1, _H, _W, VMod) when is_list(Attr2) ->
  962. Wild = table_info(Tab2, wild_pattern, VMod),
  963. Objs = match_object(Tab2, Wild, VMod),
  964. AttrL = [attribute_position(Tab2, A, VMod) || A <- Attr2],
  965. lists:flatmap(
  966. fun(V) ->
  967. ValL = tuple_to_list(V),
  968. case {has_nulls(ValL), Match} of
  969. {all, partial} -> Objs;
  970. {some, full} -> Objs;
  971. _ -> ref_compound_match(Objs, AttrL, ValL)
  972. end
  973. end, Vals);
  974. ref_match(Tab2, AttrF, _, Vals, _, _, _, VMod) when function(AttrF) ->
  975. %% TODO optimize. Check whether something more efficient than full
  976. %% linear search is possible (e.g. if target is a bag table.)
  977. Wild = table_info(Tab2, wild_pattern, VMod),
  978. Objs = match_object(Tab2, Wild, VMod),
  979. lists:foldl(
  980. fun(Obj, Acc) ->
  981. lists:foldl(
  982. fun(Val, Acc1) ->
  983. case AttrF(Obj, Val) of
  984. true ->
  985. [Obj|Acc1];
  986. false ->
  987. Acc1
  988. end
  989. end, Acc, Vals)
  990. end, [], Objs).
  991. ref_compound_match([O|Objs], Attrs, Vals) ->
  992. case match_compound_obj(O, Attrs, Vals) of
  993. true -> [O|ref_compound_match(Objs, Attrs, Vals)];
  994. false -> ref_compound_match(Objs, Attrs, Vals)
  995. end;
  996. ref_compound_match([], _, _) -> [].
  997. match_compound_obj(Obj, [_|Attrs], [?NULL|Vals]) ->
  998. match_compound_obj(Obj, Attrs, Vals);
  999. match_compound_obj(Obj, [A|Attrs], [V|Vals]) ->
  1000. if element(A, Obj) == V ->
  1001. match_compound_obj(Obj, Attrs, Vals);
  1002. true ->
  1003. false
  1004. end;
  1005. match_compound_obj(_, _, _) -> true.
  1006. %% We allow for a 'fun' version of match
  1007. %% F(Object) should return true or false.
  1008. %%
  1009. %%% ref_fun_match([Obj|Objs], F, Value) ->
  1010. %%% case F(Obj, Value) of
  1011. %%% true -> [Obj|ref_fun_match(Objs, F, Value)];
  1012. %%% false -> ref_fun_match(Objs, F, Value)
  1013. %%% end;
  1014. %%% ref_fun_match([], _, _) -> [].
  1015. %% has_nulls([Value]) -> none | all | some
  1016. %% This function tells whether a list of values contains nulls.
  1017. %%
  1018. has_nulls([_|_] = Vals) ->
  1019. Res = lists:foldl(fun(X, {_,Miss}) when X==?NULL -> {true,Miss};
  1020. (X, {Hit, _}) when X=/=?NULL -> {Hit,true}
  1021. end, {false, false}, Vals),
  1022. case Res of
  1023. {true, true} -> some;
  1024. {true, false} -> all;
  1025. {false,true} -> none
  1026. end.
  1027. %%% has_nulls([?NULL|Vals]) -> has_nulls(Vals, all);
  1028. %%% has_nulls([_|Vals]) -> has_nulls(Vals, none);
  1029. %%% has_nulls([]) -> all.
  1030. %%% has_nulls([?NULL|T], all) -> has_nulls(T, all);
  1031. %%% has_nulls([?NULL|T], none) -> has_nulls(T, some);
  1032. %%% has_nulls([_|T], all) -> has_nulls(T, some);
  1033. %%% has_nulls([_|T], Acc) -> has_nulls(T, Acc);
  1034. %%% has_nulls([], Acc) -> Acc.
  1035. %% cascade_update(...)
  1036. %%
  1037. %% Used in connection with referential integrity checks
  1038. %% set_default | set_null | (cascading update)
  1039. cascade_update(Objs, Tab, Attr, Value, Log, VMod) ->
  1040. Attrs = table_info(Tab, attributes, VMod),
  1041. Pos = pos(Attrs, Attr, 2, Tab),
  1042. foreach(
  1043. fun(Obj) ->
  1044. log_ref(Tab, Obj, write, Log),
  1045. write(Tab, setelement(Pos, Obj, Value), VMod)
  1046. end, Objs).
  1047. new_refs_log() ->
  1048. ets:new(refs_log, [set]).
  1049. get_refs_log() ->
  1050. case get_activity_state() of
  1051. #rdbms_activity{refs_logs = [Log|_]} ->
  1052. Log;
  1053. #rdbms_activity{refs_logs = []} = A ->
  1054. Log = new_refs_log(),
  1055. put_activity_state(A#rdbms_activity{refs_logs = [Log]}),
  1056. Log
  1057. end.
  1058. log_ref(Tab, Obj, Op, Log) when Op==write; Op==delete ->
  1059. Key = element(2, Obj),
  1060. case ets:insert_new(Log, {{Tab, Key, Op}}) of
  1061. false ->
  1062. mnesia:abort({cyclical_reference, {Op, [Tab, Key]}});
  1063. true ->
  1064. true
  1065. end.
  1066. %%% ==========================================================
  1067. %%% Data Representation
  1068. %%%
  1069. %%% We define a data representation called rdbms_obj()
  1070. %%% (defined in rdbms.hrl)
  1071. %%%
  1072. %%% make_object(atom() | record() | rdbms_obj()) -> rdbms_obj()
  1073. %%% For all attributes where a value is not provided, specified defaults
  1074. %%% will be inserted.
  1075. %%% ==========================================================
  1076. %%% make_object(Tab) ->
  1077. %%% make_object(Tab, []).
  1078. %%% make_object(Tab, Values) when atom(Tab) ->
  1079. %%% VMod = ?vmod,
  1080. %%% AttrNames = table_info(Tab, attributes, VMod),
  1081. %%% Attrs = [{N, table_info({Tab,N}, type, VMod),
  1082. %%% table_info({Tab,N}, default, VMod)} || N <- AttrNames],
  1083. %%% replace_values(Values, #rdbms_obj{name = Tab, attributes = Attrs});
  1084. %%% make_object(Data, Values) ->
  1085. %%% case is_rdbms_obj(Data) of
  1086. %%% true -> replace_values(Values, Data); % no need to convert
  1087. %%% false ->
  1088. %%% replace_values(Values, record_to_rdbms_obj(Data))
  1089. %%% end.
  1090. make_simple_form(Tab) ->
  1091. VMod = ?vmod,
  1092. case attributes(Tab, VMod) of
  1093. undefined ->
  1094. mnesia:abort({no_exists, Tab, simple_form});
  1095. AttrNames ->
  1096. Props = table_info(Tab, user_properties, VMod),
  1097. Refs = proplists:get_value(references, Props, []),
  1098. Acl = proplists:get_value(acl, Props, []),
  1099. RecType = proplists:get_value(rec_type, Props, []),
  1100. TypeDefs = [{T,D} || {{typedef,T},D} <- Props],
  1101. Content = [{attrs, [],
  1102. foldr(
  1103. fun(N, Acc) ->
  1104. [{N, describe_attribute(Tab, N, VMod) ++
  1105. [{references,
  1106. describe_references(N, Refs)}], []}|Acc]
  1107. end, [], AttrNames)},
  1108. {acl, [], Acl},
  1109. {rec_type, [], RecType},
  1110. {typedefs, [], TypeDefs},
  1111. {external_copies, [],
  1112. table_info(Tab, external_copies, VMod)}],
  1113. {Tab, describe_generic_attributes(Tab, VMod), Content}
  1114. end.
  1115. describe_attribute(Tab, Attr, VMod) ->
  1116. [
  1117. {type, attr_property(Tab, Attr, type, VMod)},
  1118. {key_type, key_type(Tab, Attr, VMod)},
  1119. {default, attr_property(Tab, Attr, default, VMod)}].
  1120. %%% describe_global_type(Attr, VMod) ->
  1121. %%% [{What, global_property({attr, Attr, What}, VMod)} ||
  1122. %%% What <- [type, key_type, required, bounds, default, references]].
  1123. describe_generic_attributes(Tab, VMod) ->
  1124. [{A, table_info(Tab, A, VMod)} || A <- [record_name,
  1125. attributes,
  1126. ram_copies,
  1127. disc_copies,
  1128. disc_only_copies,
  1129. local_content,
  1130. index,
  1131. snmp]].
  1132. describe_references(Name, Refs) ->
  1133. case lists:keysearch(Name, 1, Refs) of
  1134. {value, {_, R}} ->
  1135. describe_references(R);
  1136. false ->
  1137. []
  1138. end.
  1139. describe_references(Refs) ->
  1140. lists:map(
  1141. fun({Tab, Attr, Action}) ->
  1142. {Match, OnDelete, OnUpdate} = Action,
  1143. {Tab, Attr, [{match, Match},
  1144. {update, OnUpdate},
  1145. {delete, OnDelete}]}
  1146. end, Refs).
  1147. %% attribute_value(Tab, Attr, Object, VMod)
  1148. %%
  1149. %% This is used to figure out the value of an attribute.
  1150. %% Needed since the attribute could be compound.
  1151. attribute_value(Tab, SubAttrs, Object, VMod) when is_list(SubAttrs) ->
  1152. [element(attribute_position(Tab, A, VMod), Object) ||
  1153. A <- SubAttrs];
  1154. attribute_value(Tab, Attr, Object, VMod) when is_atom(Attr) ->
  1155. element(attribute_position(Tab, Attr, VMod), Object).
  1156. %%
  1157. %% type ::= atom | list | tuple | string | number | integer | float |
  1158. %% any | term | record
  1159. %%
  1160. %%% type(Attr) ->
  1161. %%% attr_property(Attr, type).
  1162. %%% access(_Type, _Attr) -> undefined.
  1163. %%
  1164. %% required ::= true | false
  1165. %%
  1166. %%% required(Attr) ->
  1167. %%% case type(Attr) of
  1168. %%% oid ->
  1169. %%% %% We enforce required==true for attributes of type oid.
  1170. %%% true;
  1171. %%% _ ->
  1172. %%% case attr_property(Attr, required) of
  1173. %%% undefined ->
  1174. %%% false;
  1175. %%% Other ->
  1176. %%% Other
  1177. %%% end
  1178. %%% end.
  1179. %%
  1180. %% default ::= Value
  1181. %%
  1182. %%% default(Tab, Attr) ->
  1183. %%% default(Tab, Attr, ?vmod).
  1184. default(Tab, Attr, VMod) ->
  1185. case attr_property(Tab, Attr, default, VMod) of
  1186. undefined ->
  1187. case attr_property(Tab, Attr, type, VMod) of
  1188. {record, Rec} ->
  1189. default_record(Rec, VMod);
  1190. oid ->
  1191. {node(), erlang:now()};
  1192. _ ->
  1193. ?NULL
  1194. end;
  1195. Val -> Val
  1196. end.
  1197. default_record(Tab, VMod) ->
  1198. Defs = [default(Tab, Attr, VMod) || Attr <- attributes(Tab, VMod)],
  1199. list_to_tuple([Tab|Defs]).
  1200. %% position(Attribute, Record) -> integer()
  1201. %% This calculates the element position of a specified attribute
  1202. %% Note that compound attributes have no position
  1203. %% position(CompoundAttr) will result in exit({invalid_attribute, ...})
  1204. %%
  1205. %%% position(Attr, Rec) ->
  1206. %%% pos(attributes(Rec), Attr, 1, Rec).
  1207. pos([Attr|_], Attr, N, _Tab) ->
  1208. N;
  1209. pos([_|T], Attr, N, Tab) ->
  1210. pos(T, Attr, N+1, Tab);
  1211. pos([], Attr, _, Tab) ->
  1212. violation(type, {invalid_attribute, {Tab, Attr}}).
  1213. %%
  1214. %% attrs ::= [attr]
  1215. %% -- list of attrs in correct order
  1216. %%
  1217. %%% attributes(Tab) ->
  1218. %%% attributes(Tab, ?vmod).
  1219. attributes(Tab, VMod) ->
  1220. case catch table_info(Tab, attributes, VMod) of
  1221. {'EXIT', _} ->
  1222. global_property({record, Tab, attributes}, VMod);
  1223. Attrs when list(Attrs) ->
  1224. Attrs
  1225. end.
  1226. %% all_attributes(Tab)
  1227. %% {PhysicalAttributes, LogicalAttributes}
  1228. %% where
  1229. %% LogicalAttributes ::= [{Attr, compound, [SubAttr]}]
  1230. %%
  1231. %%% all_attributes(Tab) ->
  1232. %%% VMod = ?vmod,
  1233. %%% PhysicalAttrs = attributes(Tab, VMod),
  1234. %%% case catch table_info(Tab, user_properties, VMod) of
  1235. %%% {'EXIT', _} ->
  1236. %%% {PhysicalAttrs, []};
  1237. %%% Props ->
  1238. %%% CompoundAttrs =
  1239. %%% [{A, {compound, Sub}} ||
  1240. %%% {{attr,A,type},{compound,Sub}} <- Props],
  1241. %%% {PhysicalAttrs, CompoundAttrs}
  1242. %%% end.
  1243. attribute_position(Tab, Attr, VMod) ->
  1244. pos(table_info(Tab, attributes, VMod), Attr, 2, Tab).
  1245. %% key_type(Tab, Attribute, VMod)
  1246. %% This checks whether Attribute of Tab is a key, and if so, which type of key
  1247. %% Possible return values are:
  1248. %% - primary - the primary (unique) key
  1249. %% - secondary - non-unique key (has an index attached to it)
  1250. %% - {compound, Sub} - compound attribute (may contain keys)
  1251. %% - attribute - not a key
  1252. key_type(Tab, Attr, VMod) ->
  1253. case attr_property(Tab, Attr, key_type, VMod) of
  1254. undefined ->
  1255. case attr_property(Tab, Attr, type, VMod) of
  1256. {compound, SubAttrs} ->
  1257. {compound, SubAttrs};
  1258. _ ->
  1259. Apos = attribute_position(Tab, Attr, VMod),
  1260. case (Apos == ?KEYPOS) of
  1261. true -> primary;
  1262. false ->
  1263. case table_info(Tab, index, VMod) of
  1264. %% TODO - also check extended indexes
  1265. [] -> attribute;
  1266. Ix ->
  1267. case lists:member(Apos, Ix) of
  1268. true -> secondary;
  1269. false ->
  1270. attribute
  1271. end
  1272. end
  1273. end
  1274. end;
  1275. Type ->
  1276. Type
  1277. end.
  1278. %%% attr_property(Tab, Attr, Prop) ->
  1279. %%% rdbms_props:attr_property(Tab, Attr, Prop).
  1280. global_property(Prop, ?JIT_MODULE) ->
  1281. ?JIT_MODULE:global_property(Prop);
  1282. global_property(Prop, {_, #verify{global_property = GP}}) ->
  1283. GP(Prop).
  1284. get_mnesia_transaction_data() ->
  1285. case get(mnesia_activity_state) of
  1286. {_, ActivityId, Opaque} ->
  1287. {ActivityId, Opaque};
  1288. _ ->
  1289. abort(no_transaction)
  1290. end.
  1291. %%%----------------------------------------------------------------------
  1292. %%% #4 CODE FOR TEMPORARY CORRECTIONS
  1293. %%%----------------------------------------------------------------------