PageRenderTime 72ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/rdbms/mnesia_patches/src/mnesia.erl

http://github.com/gebi/jungerl
Erlang | 2779 lines | 2285 code | 310 blank | 184 comment | 41 complexity | 74adda6f0a42b3a008e541753cbd3c19 MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause
  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/.
  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 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.''
  15. %%
  16. %% $Id$
  17. %%
  18. %% This module exports the public interface of the Mnesia DBMS engine
  19. -module(mnesia).
  20. %-behaviour(mnesia_access).
  21. -export([
  22. %% Start, stop and debugging
  23. start/0, start/1, stop/0, % Not for public use
  24. set_debug_level/1, lkill/0, kill/0, % Not for public use
  25. ms/0, nc/0, nc/1, ni/0, ni/1, % Not for public use
  26. change_config/2,
  27. %% Activity mgt
  28. abort/1, transaction/1, transaction/2, transaction/3,
  29. sync_transaction/1, sync_transaction/2, sync_transaction/3,
  30. async_dirty/1, async_dirty/2, sync_dirty/1, sync_dirty/2, ets/1, ets/2,
  31. activity/2, activity/3, activity/4, % Not for public use
  32. %% Access within an activity - Lock acquisition
  33. lock/2, lock/4,
  34. read_lock_table/1,
  35. write_lock_table/1,
  36. %% Access within an activity - Updates
  37. write/1, s_write/1, write/3, write/5,
  38. delete/1, s_delete/1, delete/3, delete/5,
  39. delete_object/1, s_delete_object/1, delete_object/3, delete_object/5,
  40. %% Access within an activity - Reads
  41. read/1, wread/1, read/3, read/5,
  42. match_object/1, match_object/3, match_object/5,
  43. select/1,select/2,select/3,select/4,select/5,select/6,
  44. all_keys/1, all_keys/4,
  45. index_match_object/2, index_match_object/4, index_match_object/6,
  46. index_read/3, index_read/6,
  47. first/1, next/2, last/1, prev/2,
  48. %% Iterators within an activity
  49. foldl/3, foldl/4, foldr/3, foldr/4,
  50. %% Dirty access regardless of activities - Updates
  51. dirty_write/1, dirty_write/2,
  52. dirty_delete/1, dirty_delete/2,
  53. dirty_delete_object/1, dirty_delete_object/2,
  54. dirty_update_counter/2, dirty_update_counter/3,
  55. %% Dirty access regardless of activities - Read
  56. dirty_read/1, dirty_read/2,
  57. dirty_select/2,
  58. dirty_match_object/1, dirty_match_object/2, dirty_all_keys/1,
  59. dirty_index_match_object/2, dirty_index_match_object/3,
  60. dirty_index_read/3, dirty_slot/2,
  61. dirty_first/1, dirty_next/2, dirty_last/1, dirty_prev/2,
  62. %% Info
  63. table_info/2, table_info/4, schema/0, schema/1,
  64. error_description/1, info/0, system_info/1,
  65. system_info/0, % Not for public use
  66. %% Database mgt
  67. create_schema/1, delete_schema/1,
  68. backup/1, backup/2, traverse_backup/4, traverse_backup/6,
  69. install_fallback/1, install_fallback/2,
  70. uninstall_fallback/0, uninstall_fallback/1,
  71. activate_checkpoint/1, deactivate_checkpoint/1,
  72. backup_checkpoint/2, backup_checkpoint/3, restore/2,
  73. %% Table mgt
  74. create_table/1, create_table/2, delete_table/1,
  75. add_table_copy/3, del_table_copy/2, move_table_copy/3,
  76. add_table_index/2, del_table_index/2,
  77. transform_table/3, transform_table/4,
  78. change_table_copy_type/3,
  79. read_table_property/2, write_table_property/2, delete_table_property/2,
  80. change_table_frag/2,
  81. clear_table/1,
  82. %% Table load
  83. dump_tables/1, wait_for_tables/2, force_load_table/1,
  84. change_table_access_mode/2, change_table_load_order/2,
  85. set_master_nodes/1, set_master_nodes/2,
  86. onload_fun/2, % UW 2005-12-22
  87. %% Misc admin
  88. dump_log/0, subscribe/1, unsubscribe/1, report_event/1,
  89. %% Snmp
  90. snmp_open_table/2, snmp_close_table/1,
  91. snmp_get_row/2, snmp_get_next_index/2, snmp_get_mnesia_key/2,
  92. %% Textfile access
  93. load_textfile/1, dump_to_textfile/1,
  94. %% QLC functions
  95. table/1, table/2,
  96. %% Mnemosyne exclusive
  97. get_activity_id/0, put_activity_id/1, % Not for public use
  98. %% Mnesia internal functions
  99. dirty_rpc/4, % Not for public use
  100. has_var/1, fun_select/7, fun_select/10, select_cont/3, dirty_sel_init/5,
  101. foldl/6, foldr/6,
  102. %% Module internal callback functions
  103. raw_table_info/2, % Not for public use
  104. remote_dirty_match_object/2, % Not for public use
  105. remote_dirty_select/2 % Not for public use
  106. ]).
  107. -export([begin_activity/4, end_activity/4]).
  108. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  109. -include("mnesia.hrl").
  110. -import(mnesia_lib, [verbose/2]).
  111. -define(DEFAULT_ACCESS, ?MODULE).
  112. %% Select
  113. -define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]).
  114. -define(PATTERN_TO_BINDINGS_MATCH_SPEC(Pat), [{Pat,[],['$$']}]).
  115. begin_activity(_Type, _Factor, _Tid, _Ts) ->
  116. ok.
  117. end_activity(_Result, _Type, _Tid, _Ts) ->
  118. ok.
  119. %% Local function in order to avoid external function call
  120. val(Var) ->
  121. case ?catch_val(Var) of
  122. {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
  123. Value -> Value
  124. end.
  125. is_dollar_digits(Var) ->
  126. case atom_to_list(Var) of
  127. [$$ | Digs] ->
  128. is_digits(Digs);
  129. _ ->
  130. false
  131. end.
  132. is_digits([Dig | Tail]) ->
  133. if
  134. $0 =< Dig, Dig =< $9 ->
  135. is_digits(Tail);
  136. true ->
  137. false
  138. end;
  139. is_digits([]) ->
  140. true.
  141. has_var(X) when atom(X) ->
  142. if
  143. X == '_' ->
  144. true;
  145. atom(X) ->
  146. is_dollar_digits(X);
  147. true ->
  148. false
  149. end;
  150. has_var(X) when tuple(X) ->
  151. e_has_var(X, size(X));
  152. has_var([H|T]) ->
  153. case has_var(H) of
  154. false -> has_var(T);
  155. Other -> Other
  156. end;
  157. has_var(_) -> false.
  158. e_has_var(_, 0) -> false;
  159. e_has_var(X, Pos) ->
  160. case has_var(element(Pos, X))of
  161. false -> e_has_var(X, Pos-1);
  162. Other -> Other
  163. end.
  164. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  165. %% Start and stop
  166. start() ->
  167. {Time , Res} = timer:tc(application, start, [?APPLICATION, temporary]),
  168. Secs = Time div 1000000,
  169. case Res of
  170. ok ->
  171. verbose("Mnesia started, ~p seconds~n",[ Secs]),
  172. ok;
  173. {error, {already_started, mnesia}} ->
  174. verbose("Mnesia already started, ~p seconds~n",[ Secs]),
  175. ok;
  176. {error, R} ->
  177. verbose("Mnesia failed to start, ~p seconds: ~p~n",[ Secs, R]),
  178. {error, R}
  179. end.
  180. start(ExtraEnv) when list(ExtraEnv) ->
  181. case mnesia_lib:ensure_loaded(?APPLICATION) of
  182. ok ->
  183. patched_start(ExtraEnv);
  184. Error ->
  185. Error
  186. end;
  187. start(ExtraEnv) ->
  188. {error, {badarg, ExtraEnv}}.
  189. patched_start([{Env, Val} | Tail]) when atom(Env) ->
  190. case mnesia_monitor:patch_env(Env, Val) of
  191. {error, Reason} ->
  192. {error, Reason};
  193. _NewVal ->
  194. patched_start(Tail)
  195. end;
  196. patched_start([Head | _]) ->
  197. {error, {bad_type, Head}};
  198. patched_start([]) ->
  199. start().
  200. stop() ->
  201. case application:stop(?APPLICATION) of
  202. ok -> stopped;
  203. {error, {not_started, ?APPLICATION}} -> stopped;
  204. Other -> Other
  205. end.
  206. change_config(extra_db_nodes, Ns) when list(Ns) ->
  207. mnesia_controller:connect_nodes(Ns);
  208. change_config(BadKey, _BadVal) ->
  209. {error, {badarg, BadKey}}.
  210. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  211. %% Debugging
  212. set_debug_level(Level) ->
  213. mnesia_subscr:set_debug_level(Level).
  214. lkill() ->
  215. mnesia_sup:kill().
  216. kill() ->
  217. rpc:multicall(mnesia_sup, kill, []).
  218. ms() ->
  219. [
  220. mnesia,
  221. mnesia_backup,
  222. mnesia_bup,
  223. mnesia_checkpoint,
  224. mnesia_checkpoint_sup,
  225. mnesia_controller,
  226. mnesia_dumper,
  227. mnesia_loader,
  228. mnesia_frag,
  229. mnesia_frag_hash,
  230. mnesia_frag_old_hash,
  231. mnesia_index,
  232. mnesia_kernel_sup,
  233. mnesia_late_loader,
  234. mnesia_lib,
  235. mnesia_log,
  236. mnesia_registry,
  237. mnesia_schema,
  238. mnesia_snmp_hook,
  239. mnesia_snmp_sup,
  240. mnesia_subscr,
  241. mnesia_sup,
  242. mnesia_text,
  243. mnesia_tm,
  244. mnesia_recover,
  245. mnesia_locker,
  246. %% Keep these last in the list, so
  247. %% mnesia_sup kills these last
  248. mnesia_monitor,
  249. mnesia_event
  250. ].
  251. nc() ->
  252. Mods = ms(),
  253. nc(Mods).
  254. nc(Mods) when list(Mods)->
  255. [Mod || Mod <- Mods, ok /= load(Mod, compile)].
  256. ni() ->
  257. Mods = ms(),
  258. ni(Mods).
  259. ni(Mods) when list(Mods) ->
  260. [Mod || Mod <- Mods, ok /= load(Mod, interpret)].
  261. load(Mod, How) when atom(Mod) ->
  262. case try_load(Mod, How) of
  263. ok ->
  264. ok;
  265. _ ->
  266. mnesia_lib:show( "~n RETRY ~p FROM: ", [Mod]),
  267. Abs = mod2abs(Mod),
  268. load(Abs, How)
  269. end;
  270. load(Abs, How) ->
  271. case try_load(Abs, How) of
  272. ok ->
  273. ok;
  274. {error, Reason} ->
  275. mnesia_lib:show( " *** ERROR *** ~p~n", [Reason]),
  276. {error, Reason}
  277. end.
  278. try_load(Mod, How) ->
  279. mnesia_lib:show( " ~p ", [Mod]),
  280. Flags = [{d, debug}],
  281. case How of
  282. compile ->
  283. case catch c:nc(Mod, Flags) of
  284. {ok, _} -> ok;
  285. Other -> {error, Other}
  286. end;
  287. interpret ->
  288. case catch int:ni(Mod, Flags) of
  289. {module, _} -> ok;
  290. Other -> {error, Other}
  291. end
  292. end.
  293. mod2abs(Mod) ->
  294. ModString = atom_to_list(Mod),
  295. SubDir =
  296. case lists:suffix("test", ModString) of
  297. true -> test;
  298. false -> src
  299. end,
  300. filename:join([code:lib_dir(?APPLICATION), SubDir, ModString]).
  301. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  302. %% Activity mgt
  303. abort(Reason) ->
  304. exit({aborted, Reason}).
  305. transaction(Fun) ->
  306. transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, async).
  307. transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
  308. transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
  309. transaction(Fun, Retries) when Retries == infinity ->
  310. transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
  311. transaction(Fun, Args) ->
  312. transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, async).
  313. transaction(Fun, Args, Retries) ->
  314. transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, async).
  315. sync_transaction(Fun) ->
  316. transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, sync).
  317. sync_transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
  318. transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
  319. sync_transaction(Fun, Retries) when Retries == infinity ->
  320. transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
  321. sync_transaction(Fun, Args) ->
  322. transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, sync).
  323. sync_transaction(Fun, Args, Retries) ->
  324. transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, sync).
  325. transaction(State, Fun, Args, Retries, Mod, Kind)
  326. when function(Fun), list(Args), Retries == infinity, atom(Mod) ->
  327. mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
  328. transaction(State, Fun, Args, Retries, Mod, Kind)
  329. when function(Fun), list(Args), integer(Retries), Retries >= 0, atom(Mod) ->
  330. mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
  331. transaction(_State, Fun, Args, Retries, Mod, _Kind) ->
  332. {aborted, {badarg, Fun, Args, Retries, Mod}}.
  333. non_transaction(State, Fun, Args, ActivityKind, Mod)
  334. when function(Fun), list(Args), atom(Mod) ->
  335. mnesia_tm:non_transaction(State, Fun, Args, ActivityKind, Mod);
  336. non_transaction(_State, Fun, Args, _ActivityKind, _Mod) ->
  337. {aborted, {badarg, Fun, Args}}.
  338. async_dirty(Fun) ->
  339. async_dirty(Fun, []).
  340. async_dirty(Fun, Args) ->
  341. non_transaction(get(mnesia_activity_state), Fun, Args, async_dirty, ?DEFAULT_ACCESS).
  342. sync_dirty(Fun) ->
  343. sync_dirty(Fun, []).
  344. sync_dirty(Fun, Args) ->
  345. non_transaction(get(mnesia_activity_state), Fun, Args, sync_dirty, ?DEFAULT_ACCESS).
  346. ets(Fun) ->
  347. ets(Fun, []).
  348. ets(Fun, Args) ->
  349. non_transaction(get(mnesia_activity_state), Fun, Args, ets, ?DEFAULT_ACCESS).
  350. activity(Kind, Fun) ->
  351. activity(Kind, Fun, []).
  352. activity(Kind, Fun, Args) when list(Args) ->
  353. activity(Kind, Fun, Args, mnesia_monitor:get_env(access_module));
  354. activity(Kind, Fun, Mod) ->
  355. activity(Kind, Fun, [], Mod).
  356. activity(Kind, Fun, Args, Mod) ->
  357. State = get(mnesia_activity_state),
  358. case Kind of
  359. ets -> non_transaction(State, Fun, Args, Kind, Mod);
  360. async_dirty -> non_transaction(State, Fun, Args, Kind, Mod);
  361. sync_dirty -> non_transaction(State, Fun, Args, Kind, Mod);
  362. transaction -> wrap_trans(State, Fun, Args, infinity, Mod, async);
  363. {transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, async);
  364. sync_transaction -> wrap_trans(State, Fun, Args, infinity, Mod, sync);
  365. {sync_transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, sync);
  366. _ -> {aborted, {bad_type, Kind}}
  367. end.
  368. wrap_trans(State, Fun, Args, Retries, Mod, Kind) ->
  369. case transaction(State, Fun, Args, Retries, Mod, Kind) of
  370. {atomic, GoodRes} -> GoodRes;
  371. BadRes -> exit(BadRes)
  372. end.
  373. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  374. %% Access within an activity - lock acquisition
  375. %% Grab a lock on an item in the global lock table
  376. %% Item may be any term. Lock may be write or read.
  377. %% write lock is set on all the given nodes
  378. %% read lock is only set on the first node
  379. %% Nodes may either be a list of nodes or one node as an atom
  380. %% Mnesia on all Nodes must be connected to each other, but
  381. %% it is not neccessary that they are up and running.
  382. lock(LockItem, LockKind) ->
  383. case get(mnesia_activity_state) of
  384. {?DEFAULT_ACCESS, Tid, Ts} ->
  385. lock(Tid, Ts, LockItem, LockKind);
  386. {Mod, Tid, Ts} ->
  387. Mod:lock(Tid, Ts, LockItem, LockKind);
  388. _ ->
  389. abort(no_transaction)
  390. end.
  391. lock(Tid, Ts, LockItem, LockKind) ->
  392. case element(1, Tid) of
  393. tid ->
  394. case LockItem of
  395. {record, Tab, Key} ->
  396. lock_record(Tid, Ts, Tab, Key, LockKind);
  397. {table, Tab} ->
  398. lock_table(Tid, Ts, Tab, LockKind);
  399. {global, GlobalKey, Nodes} ->
  400. global_lock(Tid, Ts, GlobalKey, LockKind, Nodes);
  401. _ ->
  402. abort({bad_type, LockItem})
  403. end;
  404. _Protocol ->
  405. []
  406. end.
  407. %% Grab a read lock on a whole table
  408. read_lock_table(Tab) ->
  409. lock({table, Tab}, read),
  410. ok.
  411. %% Grab a write lock on a whole table
  412. write_lock_table(Tab) ->
  413. lock({table, Tab}, write),
  414. ok.
  415. lock_record(Tid, Ts, Tab, Key, LockKind) when atom(Tab) ->
  416. Store = Ts#tidstore.store,
  417. Oid = {Tab, Key},
  418. case LockKind of
  419. read ->
  420. mnesia_locker:rlock(Tid, Store, Oid);
  421. write ->
  422. mnesia_locker:wlock(Tid, Store, Oid);
  423. sticky_write ->
  424. mnesia_locker:sticky_wlock(Tid, Store, Oid);
  425. none ->
  426. [];
  427. _ ->
  428. abort({bad_type, Tab, LockKind})
  429. end;
  430. lock_record(_Tid, _Ts, Tab, _Key, _LockKind) ->
  431. abort({bad_type, Tab}).
  432. lock_table(Tid, Ts, Tab, LockKind) when atom(Tab) ->
  433. Store = Ts#tidstore.store,
  434. case LockKind of
  435. read ->
  436. mnesia_locker:rlock_table(Tid, Store, Tab);
  437. write ->
  438. mnesia_locker:wlock_table(Tid, Store, Tab);
  439. sticky_write ->
  440. mnesia_locker:sticky_wlock_table(Tid, Store, Tab);
  441. none ->
  442. [];
  443. _ ->
  444. abort({bad_type, Tab, LockKind})
  445. end;
  446. lock_table(_Tid, _Ts, Tab, _LockKind) ->
  447. abort({bad_type, Tab}).
  448. global_lock(Tid, Ts, Item, Kind, Nodes) when list(Nodes) ->
  449. case element(1, Tid) of
  450. tid ->
  451. Store = Ts#tidstore.store,
  452. GoodNs = good_global_nodes(Nodes),
  453. if
  454. Kind /= read, Kind /= write ->
  455. abort({bad_type, Kind});
  456. true ->
  457. mnesia_locker:global_lock(Tid, Store, Item, Kind, GoodNs)
  458. end;
  459. _Protocol ->
  460. []
  461. end;
  462. global_lock(_Tid, _Ts, _Item, _Kind, Nodes) ->
  463. abort({bad_type, Nodes}).
  464. good_global_nodes(Nodes) ->
  465. Recover = [node() | val(recover_nodes)],
  466. mnesia_lib:intersect(Nodes, Recover).
  467. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  468. %% Access within an activity - updates
  469. write(Val) when tuple(Val), size(Val) > 2 ->
  470. Tab = element(1, Val),
  471. write(Tab, Val, write);
  472. write(Val) ->
  473. abort({bad_type, Val}).
  474. s_write(Val) when tuple(Val), size(Val) > 2 ->
  475. Tab = element(1, Val),
  476. write(Tab, Val, sticky_write).
  477. write(Tab, Val, LockKind) ->
  478. case get(mnesia_activity_state) of
  479. {?DEFAULT_ACCESS, Tid, Ts} ->
  480. write(Tid, Ts, Tab, Val, LockKind);
  481. {Mod, Tid, Ts} ->
  482. Mod:write(Tid, Ts, Tab, Val, LockKind);
  483. _ ->
  484. abort(no_transaction)
  485. end.
  486. write(Tid, Ts, Tab, Val, LockKind)
  487. when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
  488. case element(1, Tid) of
  489. ets ->
  490. ?ets_insert(Tab, Val),
  491. ok;
  492. tid ->
  493. Store = Ts#tidstore.store,
  494. Oid = {Tab, element(2, Val)},
  495. case LockKind of
  496. write ->
  497. mnesia_locker:wlock(Tid, Store, Oid);
  498. sticky_write ->
  499. mnesia_locker:sticky_wlock(Tid, Store, Oid);
  500. _ ->
  501. abort({bad_type, Tab, LockKind})
  502. end,
  503. write_to_store(Tab, Store, Oid, Val);
  504. Protocol ->
  505. do_dirty_write(Protocol, Tab, Val)
  506. end;
  507. write(_Tid, _Ts, Tab, Val, LockKind) ->
  508. abort({bad_type, Tab, Val, LockKind}).
  509. write_to_store(Tab, Store, Oid, Val) ->
  510. case ?catch_val({Tab, record_validation}) of
  511. {RecName, Arity, Type}
  512. when size(Val) == Arity, RecName == element(1, Val) ->
  513. case Type of
  514. bag ->
  515. ?ets_insert(Store, {Oid, Val, write});
  516. _ ->
  517. ?ets_delete(Store, Oid),
  518. ?ets_insert(Store, {Oid, Val, write})
  519. end,
  520. ok;
  521. {'EXIT', _} ->
  522. abort({no_exists, Tab});
  523. _ ->
  524. abort({bad_type, Val})
  525. end.
  526. delete({Tab, Key}) ->
  527. delete(Tab, Key, write);
  528. delete(Oid) ->
  529. abort({bad_type, Oid}).
  530. s_delete({Tab, Key}) ->
  531. delete(Tab, Key, sticky_write);
  532. s_delete(Oid) ->
  533. abort({bad_type, Oid}).
  534. delete(Tab, Key, LockKind) ->
  535. case get(mnesia_activity_state) of
  536. {?DEFAULT_ACCESS, Tid, Ts} ->
  537. delete(Tid, Ts, Tab, Key, LockKind);
  538. {Mod, Tid, Ts} ->
  539. Mod:delete(Tid, Ts, Tab, Key, LockKind);
  540. _ ->
  541. abort(no_transaction)
  542. end.
  543. delete(Tid, Ts, Tab, Key, LockKind)
  544. when atom(Tab), Tab /= schema ->
  545. case element(1, Tid) of
  546. ets ->
  547. ?ets_delete(Tab, Key),
  548. ok;
  549. tid ->
  550. Store = Ts#tidstore.store,
  551. Oid = {Tab, Key},
  552. case LockKind of
  553. write ->
  554. mnesia_locker:wlock(Tid, Store, Oid);
  555. sticky_write ->
  556. mnesia_locker:sticky_wlock(Tid, Store, Oid);
  557. _ ->
  558. abort({bad_type, Tab, LockKind})
  559. end,
  560. ?ets_delete(Store, Oid),
  561. ?ets_insert(Store, {Oid, Oid, delete}),
  562. ok;
  563. Protocol ->
  564. do_dirty_delete(Protocol, Tab, Key)
  565. end;
  566. delete(_Tid, _Ts, Tab, _Key, _LockKind) ->
  567. abort({bad_type, Tab}).
  568. delete_object(Val) when tuple(Val), size(Val) > 2 ->
  569. Tab = element(1, Val),
  570. delete_object(Tab, Val, write);
  571. delete_object(Val) ->
  572. abort({bad_type, Val}).
  573. s_delete_object(Val) when tuple(Val), size(Val) > 2 ->
  574. Tab = element(1, Val),
  575. delete_object(Tab, Val, sticky_write);
  576. s_delete_object(Val) ->
  577. abort({bad_type, Val}).
  578. delete_object(Tab, Val, LockKind) ->
  579. case get(mnesia_activity_state) of
  580. {?DEFAULT_ACCESS, Tid, Ts} ->
  581. delete_object(Tid, Ts, Tab, Val, LockKind);
  582. {Mod, Tid, Ts} ->
  583. Mod:delete_object(Tid, Ts, Tab, Val, LockKind);
  584. _ ->
  585. abort(no_transaction)
  586. end.
  587. delete_object(Tid, Ts, Tab, Val, LockKind)
  588. when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
  589. case element(1, Tid) of
  590. ets ->
  591. ?ets_match_delete(Tab, Val),
  592. ok;
  593. tid ->
  594. Store = Ts#tidstore.store,
  595. Oid = {Tab, element(2, Val)},
  596. case LockKind of
  597. write ->
  598. mnesia_locker:wlock(Tid, Store, Oid);
  599. sticky_write ->
  600. mnesia_locker:sticky_wlock(Tid, Store, Oid);
  601. _ ->
  602. abort({bad_type, Tab, LockKind})
  603. end,
  604. case val({Tab, setorbag}) of
  605. bag ->
  606. ?ets_match_delete(Store, {Oid, Val, '_'}),
  607. ?ets_insert(Store, {Oid, Val, delete_object});
  608. _ ->
  609. case ?ets_match_object(Store, {Oid, '_', write}) of
  610. [] ->
  611. ?ets_match_delete(Store, {Oid, Val, '_'}),
  612. ?ets_insert(Store, {Oid, Val, delete_object});
  613. _ ->
  614. ?ets_delete(Store, Oid),
  615. ?ets_insert(Store, {Oid, Oid, delete})
  616. end
  617. end,
  618. ok;
  619. Protocol ->
  620. do_dirty_delete_object(Protocol, Tab, Val)
  621. end;
  622. delete_object(_Tid, _Ts, Tab, _Key, _LockKind) ->
  623. abort({bad_type, Tab}).
  624. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  625. %% Access within an activity - read
  626. read({Tab, Key}) ->
  627. read(Tab, Key, read);
  628. read(Oid) ->
  629. abort({bad_type, Oid}).
  630. wread({Tab, Key}) ->
  631. read(Tab, Key, write);
  632. wread(Oid) ->
  633. abort({bad_type, Oid}).
  634. read(Tab, Key, LockKind) ->
  635. case get(mnesia_activity_state) of
  636. {?DEFAULT_ACCESS, Tid, Ts} ->
  637. read(Tid, Ts, Tab, Key, LockKind);
  638. {Mod, Tid, Ts} ->
  639. Mod:read(Tid, Ts, Tab, Key, LockKind);
  640. _ ->
  641. abort(no_transaction)
  642. end.
  643. read(Tid, Ts, Tab, Key, LockKind)
  644. when atom(Tab), Tab /= schema ->
  645. case element(1, Tid) of
  646. ets ->
  647. ?ets_lookup(Tab, Key);
  648. tid ->
  649. Store = Ts#tidstore.store,
  650. Oid = {Tab, Key},
  651. Objs =
  652. case LockKind of
  653. read ->
  654. mnesia_locker:rlock(Tid, Store, Oid);
  655. write ->
  656. mnesia_locker:rwlock(Tid, Store, Oid);
  657. sticky_write ->
  658. mnesia_locker:sticky_rwlock(Tid, Store, Oid);
  659. _ ->
  660. abort({bad_type, Tab, LockKind})
  661. end,
  662. add_written(?ets_lookup(Store, Oid), Tab, Objs);
  663. _Protocol ->
  664. dirty_read(Tab, Key)
  665. end;
  666. read(_Tid, _Ts, Tab, _Key, _LockKind) ->
  667. abort({bad_type, Tab}).
  668. first(Tab) ->
  669. case get(mnesia_activity_state) of
  670. {?DEFAULT_ACCESS, Tid, Ts} ->
  671. first(Tid, Ts, Tab);
  672. {Mod, Tid, Ts} ->
  673. Mod:first(Tid, Ts, Tab);
  674. _ ->
  675. abort(no_transaction)
  676. end.
  677. first(Tid, Ts, Tab)
  678. when atom(Tab), Tab /= schema ->
  679. case element(1, Tid) of
  680. ets ->
  681. ?ets_first(Tab);
  682. tid ->
  683. lock_table(Tid, Ts, Tab, read),
  684. do_fixtable(Tab,Ts),
  685. Key = dirty_first(Tab),
  686. stored_keys(Tab,Key,'$end_of_table',Ts,next,
  687. val({Tab, setorbag}));
  688. _Protocol ->
  689. dirty_first(Tab)
  690. end;
  691. first(_Tid, _Ts,Tab) ->
  692. abort({bad_type, Tab}).
  693. last(Tab) ->
  694. case get(mnesia_activity_state) of
  695. {?DEFAULT_ACCESS, Tid, Ts} ->
  696. last(Tid, Ts, Tab);
  697. {Mod, Tid, Ts} ->
  698. Mod:last(Tid, Ts, Tab);
  699. _ ->
  700. abort(no_transaction)
  701. end.
  702. last(Tid, Ts, Tab)
  703. when atom(Tab), Tab /= schema ->
  704. case element(1, Tid) of
  705. ets ->
  706. ?ets_last(Tab);
  707. tid ->
  708. lock_table(Tid, Ts, Tab, read),
  709. do_fixtable(Tab,Ts),
  710. Key = dirty_last(Tab),
  711. stored_keys(Tab,Key,'$end_of_table',Ts,prev,
  712. val({Tab, setorbag}));
  713. _Protocol ->
  714. dirty_last(Tab)
  715. end;
  716. last(_Tid, _Ts,Tab) ->
  717. abort({bad_type, Tab}).
  718. next(Tab,Key) ->
  719. case get(mnesia_activity_state) of
  720. {?DEFAULT_ACCESS,Tid,Ts} ->
  721. next(Tid,Ts,Tab,Key);
  722. {Mod,Tid,Ts} ->
  723. Mod:next(Tid,Ts,Tab,Key);
  724. _ ->
  725. abort(no_transaction)
  726. end.
  727. next(Tid,Ts,Tab,Key)
  728. when atom(Tab), Tab /= schema ->
  729. case element(1, Tid) of
  730. ets ->
  731. ?ets_next(Tab,Key);
  732. tid ->
  733. lock_table(Tid, Ts, Tab, read),
  734. do_fixtable(Tab,Ts),
  735. New = (catch dirty_next(Tab,Key)),
  736. stored_keys(Tab,New,Key,Ts,next,
  737. val({Tab, setorbag}));
  738. _Protocol ->
  739. dirty_next(Tab,Key)
  740. end;
  741. next(_Tid, _Ts,Tab,_) ->
  742. abort({bad_type, Tab}).
  743. prev(Tab,Key) ->
  744. case get(mnesia_activity_state) of
  745. {?DEFAULT_ACCESS,Tid,Ts} ->
  746. prev(Tid,Ts,Tab,Key);
  747. {Mod,Tid,Ts} ->
  748. Mod:prev(Tid,Ts,Tab,Key);
  749. _ ->
  750. abort(no_transaction)
  751. end.
  752. prev(Tid,Ts,Tab,Key)
  753. when atom(Tab), Tab /= schema ->
  754. case element(1, Tid) of
  755. ets ->
  756. ?ets_prev(Tab,Key);
  757. tid ->
  758. lock_table(Tid, Ts, Tab, read),
  759. do_fixtable(Tab,Ts),
  760. New = (catch dirty_prev(Tab,Key)),
  761. stored_keys(Tab,New,Key,Ts,prev,
  762. val({Tab, setorbag}));
  763. _Protocol ->
  764. dirty_prev(Tab,Key)
  765. end;
  766. prev(_Tid, _Ts,Tab,_) ->
  767. abort({bad_type, Tab}).
  768. %% Compensate for transaction written and/or deleted records
  769. stored_keys(Tab,'$end_of_table',Prev,Ts,Op,Type) ->
  770. case ts_keys(Ts#tidstore.store,Tab,Op,Type,[]) of
  771. [] -> '$end_of_table';
  772. Keys when Type == ordered_set->
  773. get_ordered_tskey(Prev,Keys,Op);
  774. Keys ->
  775. get_next_tskey(Prev,Keys,Tab)
  776. end;
  777. stored_keys(Tab,{'EXIT',{aborted,R={badarg,[Tab,Key]}}},
  778. Key,#tidstore{store=Store},Op,Type) ->
  779. %% Had to match on error, ouch..
  780. case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
  781. [] -> abort(R);
  782. Ops ->
  783. case lists:last(Ops) of
  784. [delete] -> abort(R);
  785. _ ->
  786. case ts_keys(Store,Tab,Op,Type,[]) of
  787. [] -> '$end_of_table';
  788. Keys -> get_next_tskey(Key,Keys,Tab)
  789. end
  790. end
  791. end;
  792. stored_keys(_,{'EXIT',{aborted,R}},_,_,_,_) ->
  793. abort(R);
  794. stored_keys(Tab,Key,Prev,#tidstore{store=Store},Op,ordered_set) ->
  795. case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
  796. [] ->
  797. Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
  798. get_ordered_tskey(Prev,Keys,Op);
  799. Ops ->
  800. case lists:last(Ops) of
  801. [delete] ->
  802. mnesia:Op(Tab,Key);
  803. _ ->
  804. Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
  805. get_ordered_tskey(Prev,Keys,Op)
  806. end
  807. end;
  808. stored_keys(Tab,Key,_,#tidstore{store=Store},Op,_) ->
  809. case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
  810. [] -> Key;
  811. Ops ->
  812. case lists:last(Ops) of
  813. [delete] -> mnesia:Op(Tab,Key);
  814. _ -> Key
  815. end
  816. end.
  817. get_ordered_tskey('$end_of_table', [First|_],_) -> First;
  818. get_ordered_tskey(Prev, [First|_], next) when Prev < First -> First;
  819. get_ordered_tskey(Prev, [First|_], prev) when Prev > First -> First;
  820. get_ordered_tskey(Prev, [_|R],Op) -> get_ordered_tskey(Prev,R,Op);
  821. get_ordered_tskey(_, [],_) -> '$end_of_table'.
  822. get_next_tskey(_, [],_) -> '$end_of_table';
  823. get_next_tskey(Key,Keys,Tab) ->
  824. Next =
  825. if Key == '$end_of_table' -> hd(Keys);
  826. true ->
  827. case lists:dropwhile(fun(A) -> A /= Key end, Keys) of
  828. [] -> hd(Keys); %% First stored key
  829. [Key] -> '$end_of_table';
  830. [Key,Next2|_] -> Next2
  831. end
  832. end,
  833. case Next of
  834. '$end_of_table' -> '$end_of_table';
  835. _ -> %% Really slow anybody got another solution??
  836. case dirty_read(Tab, Next) of
  837. [] -> Next;
  838. _ ->
  839. %% Updated value we already returned this key
  840. get_next_tskey(Next,Keys,Tab)
  841. end
  842. end.
  843. ts_keys(Store, Tab, Op, Type, Def) ->
  844. All = ?ets_match(Store, {{Tab,'$1'},'_','$2'}),
  845. Keys = ts_keys_1(All, Def),
  846. if
  847. Type == ordered_set, Op == prev ->
  848. lists:reverse(lists:sort(Keys));
  849. Type == ordered_set ->
  850. lists:sort(Keys);
  851. Op == next ->
  852. lists:reverse(Keys);
  853. true ->
  854. Keys
  855. end.
  856. ts_keys_1([[Key, write]|R], []) ->
  857. ts_keys_1(R, [Key]);
  858. ts_keys_1([[Key, write]|R], Acc=[Key|_]) ->
  859. ts_keys_1(R, Acc);
  860. ts_keys_1([[Key, write]|R], Acc) ->
  861. ts_keys_1(R, [Key|Acc]);
  862. ts_keys_1([[Key, delete]|R], [Key|Acc]) ->
  863. ts_keys_1(R, Acc);
  864. ts_keys_1([_|R], Acc) ->
  865. ts_keys_1(R, Acc);
  866. ts_keys_1([], Acc) ->
  867. Acc.
  868. %%%%%%%%%%%%%%%%%%%%%
  869. %% Iterators
  870. foldl(Fun, Acc, Tab) ->
  871. foldl(Fun, Acc, Tab, read).
  872. foldl(Fun, Acc, Tab, LockKind) when function(Fun) ->
  873. case get(mnesia_activity_state) of
  874. {?DEFAULT_ACCESS, Tid, Ts} ->
  875. foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
  876. {Mod, Tid, Ts} ->
  877. Mod:foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
  878. _ ->
  879. abort(no_transaction)
  880. end.
  881. foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
  882. {Type, Prev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
  883. Res = (catch do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)),
  884. close_iteration(Res, Tab).
  885. do_foldl(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
  886. lists:foldl(fun(Key, Acc) ->
  887. lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
  888. end, RAcc, Stored);
  889. do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
  890. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
  891. {_, Tid, Ts} = get(mnesia_activity_state),
  892. do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, Stored);
  893. do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
  894. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
  895. {_, Tid, Ts} = get(mnesia_activity_state),
  896. do_foldl(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
  897. do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
  898. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
  899. {_, Tid, Ts} = get(mnesia_activity_state),
  900. do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
  901. do_foldl(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag
  902. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
  903. NewStored = ordsets:del_element(Key, Stored),
  904. {_, Tid, Ts} = get(mnesia_activity_state),
  905. do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, Type, NewStored).
  906. foldr(Fun, Acc, Tab) ->
  907. foldr(Fun, Acc, Tab, read).
  908. foldr(Fun, Acc, Tab, LockKind) when function(Fun) ->
  909. case get(mnesia_activity_state) of
  910. {?DEFAULT_ACCESS, Tid, Ts} ->
  911. foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
  912. {Mod, Tid, Ts} ->
  913. Mod:foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
  914. _ ->
  915. abort(no_transaction)
  916. end.
  917. foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
  918. {Type, TempPrev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
  919. Prev =
  920. if
  921. Type == ordered_set ->
  922. lists:reverse(TempPrev);
  923. true -> %% Order doesn't matter for set and bag
  924. TempPrev %% Keep the order so we can use ordsets:del_element
  925. end,
  926. Res = (catch do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)),
  927. close_iteration(Res, Tab).
  928. do_foldr(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
  929. lists:foldl(fun(Key, Acc) ->
  930. lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
  931. end, RAcc, Stored);
  932. do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
  933. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
  934. {_, Tid, Ts} = get(mnesia_activity_state),
  935. do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, Stored);
  936. do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
  937. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
  938. {_, Tid, Ts} = get(mnesia_activity_state),
  939. do_foldr(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
  940. do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
  941. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
  942. {_, Tid, Ts} = get(mnesia_activity_state),
  943. do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
  944. do_foldr(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag
  945. NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
  946. NewStored = ordsets:del_element(Key, Stored),
  947. {_, Tid, Ts} = get(mnesia_activity_state),
  948. do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, Type, NewStored).
  949. init_iteration(ActivityId, Opaque, Tab, LockKind) ->
  950. lock(ActivityId, Opaque, {table, Tab}, LockKind),
  951. Type = val({Tab, setorbag}),
  952. Previous = add_previous(ActivityId, Opaque, Type, Tab),
  953. St = val({Tab, storage_type}),
  954. if
  955. St == unknown ->
  956. ignore;
  957. true ->
  958. mnesia_lib:db_fixtable(St, Tab, true)
  959. end,
  960. {Type, Previous}.
  961. close_iteration(Res, Tab) ->
  962. case val({Tab, storage_type}) of
  963. unknown ->
  964. ignore;
  965. St ->
  966. mnesia_lib:db_fixtable(St, Tab, false)
  967. end,
  968. case Res of
  969. {'EXIT', {aborted, What}} ->
  970. abort(What);
  971. {'EXIT', What} ->
  972. abort(What);
  973. _ ->
  974. Res
  975. end.
  976. add_previous(_ActivityId, non_transaction, _Type, _Tab) ->
  977. [];
  978. add_previous(_Tid, Ts, _Type, Tab) ->
  979. Previous = ?ets_match(Ts#tidstore.store, {{Tab, '$1'}, '_', write}),
  980. lists:sort(lists:concat(Previous)).
  981. %% This routine fixes up the return value from read/1 so that
  982. %% it is correct with respect to what this particular transaction
  983. %% has already written, deleted .... etc
  984. add_written([], _Tab, Objs) ->
  985. Objs; % standard normal fast case
  986. add_written(Written, Tab, Objs) ->
  987. case val({Tab, setorbag}) of
  988. bag ->
  989. add_written_to_bag(Written, Objs, []);
  990. _ ->
  991. add_written_to_set(Written)
  992. end.
  993. add_written_to_set(Ws) ->
  994. case lists:last(Ws) of
  995. {_, _, delete} -> [];
  996. {_, Val, write} -> [Val];
  997. {_, _, delete_object} -> []
  998. end.
  999. add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
  1000. add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
  1001. add_written_to_bag([], Objs, Ack) ->
  1002. Objs ++ lists:reverse(Ack); %% Oldest write first as in ets
  1003. add_written_to_bag([{_, _ , delete} | Tail], _Objs, _Ack) ->
  1004. %% This transaction just deleted all objects
  1005. %% with this key
  1006. add_written_to_bag(Tail, [], []);
  1007. add_written_to_bag([{_, Val, delete_object} | Tail], Objs, Ack) ->
  1008. add_written_to_bag(Tail, lists:delete(Val, Objs), lists:delete(Val, Ack)).
  1009. match_object(Pat) when tuple(Pat), size(Pat) > 2 ->
  1010. Tab = element(1, Pat),
  1011. match_object(Tab, Pat, read);
  1012. match_object(Pat) ->
  1013. abort({bad_type, Pat}).
  1014. match_object(Tab, Pat, LockKind) ->
  1015. case get(mnesia_activity_state) of
  1016. {?DEFAULT_ACCESS, Tid, Ts} ->
  1017. match_object(Tid, Ts, Tab, Pat, LockKind);
  1018. {Mod, Tid, Ts} ->
  1019. Mod:match_object(Tid, Ts, Tab, Pat, LockKind);
  1020. _ ->
  1021. abort(no_transaction)
  1022. end.
  1023. match_object(Tid, Ts, Tab, Pat, LockKind)
  1024. when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
  1025. case element(1, Tid) of
  1026. ets ->
  1027. mnesia_lib:db_match_object(ram_copies, Tab, Pat);
  1028. tid ->
  1029. Key = element(2, Pat),
  1030. case has_var(Key) of
  1031. false -> lock_record(Tid, Ts, Tab, Key, LockKind);
  1032. true -> lock_table(Tid, Ts, Tab, LockKind)
  1033. end,
  1034. Objs = dirty_match_object(Tab, Pat),
  1035. add_written_match(Ts#tidstore.store, Pat, Tab, Objs);
  1036. _Protocol ->
  1037. dirty_match_object(Tab, Pat)
  1038. end;
  1039. match_object(_Tid, _Ts, Tab, Pat, _LockKind) ->
  1040. abort({bad_type, Tab, Pat}).
  1041. add_written_match(S, Pat, Tab, Objs) ->
  1042. Ops = find_ops(S, Tab, Pat),
  1043. add_match(Ops, Objs, val({Tab, setorbag})).
  1044. find_ops(S, Tab, Pat) ->
  1045. GetWritten = [{{{Tab, '_'}, Pat, write}, [], ['$_']},
  1046. {{{Tab, '_'}, '_', delete}, [], ['$_']},
  1047. {{{Tab, '_'}, Pat, delete_object}, [], ['$_']}],
  1048. ets:select(S, GetWritten).
  1049. add_match([], Objs, _Type) ->
  1050. Objs;
  1051. add_match(Written, Objs, ordered_set) ->
  1052. %% Must use keysort which is stable
  1053. add_ordered_match(lists:keysort(1,Written), Objs, []);
  1054. add_match([{Oid, _, delete}|R], Objs, Type) ->
  1055. add_match(R, deloid(Oid, Objs), Type);
  1056. add_match([{_Oid, Val, delete_object}|R], Objs, Type) ->
  1057. add_match(R, lists:delete(Val, Objs), Type);
  1058. add_match([{_Oid, Val, write}|R], Objs, bag) ->
  1059. add_match(R, [Val | lists:delete(Val, Objs)], bag);
  1060. add_match([{Oid, Val, write}|R], Objs, set) ->
  1061. add_match(R, [Val | deloid(Oid,Objs)],set).
  1062. %% For ordered_set only !!
  1063. add_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs], Acc)
  1064. when Key > element(2, Obj) ->
  1065. add_ordered_match(Written, Objs, [Obj|Acc]);
  1066. add_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_], Acc)
  1067. when Key < element(2, Obj) ->
  1068. add_ordered_match(Rest, [Val|Objs],Acc);
  1069. add_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
  1070. when Key < element(2, Obj) ->
  1071. add_ordered_match(Rest,Objs,Acc);
  1072. %% Greater than last object
  1073. add_ordered_match([{_, Val, write}|Rest], [], Acc) ->
  1074. add_ordered_match(Rest, [Val], Acc);
  1075. add_ordered_match([_|Rest], [], Acc) ->
  1076. add_ordered_match(Rest, [], Acc);
  1077. %% Keys are equal from here
  1078. add_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
  1079. add_ordered_match(Rest, [Val|Objs], Acc);
  1080. add_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
  1081. add_ordered_match(Rest, Objs, Acc);
  1082. add_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
  1083. add_ordered_match(Rest, Objs, Acc);
  1084. add_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
  1085. add_ordered_match(Rest, Objs, Acc);
  1086. add_ordered_match([], Objs, Acc) ->
  1087. lists:reverse(Acc, Objs).
  1088. %% For select chunk
  1089. add_sel_match(Sorted, Objs, ordered_set) ->
  1090. add_sel_ordered_match(Sorted, Objs, []);
  1091. add_sel_match(Written, Objs, Type) ->
  1092. add_sel_match(Written, Objs, Type, []).
  1093. add_sel_match([], Objs, _Type, Acc) ->
  1094. {Objs,lists:reverse(Acc)};
  1095. add_sel_match([Op={Oid, _, delete}|R], Objs, Type, Acc) ->
  1096. case deloid(Oid, Objs) of
  1097. Objs ->
  1098. add_sel_match(R, Objs, Type, [Op|Acc]);
  1099. NewObjs when Type == set ->
  1100. add_sel_match(R, NewObjs, Type, Acc);
  1101. NewObjs -> %% If bag we may get more in next chunk
  1102. add_sel_match(R, NewObjs, Type, [Op|Acc])
  1103. end;
  1104. add_sel_match([Op = {_Oid, Val, delete_object}|R], Objs, Type, Acc) ->
  1105. case lists:delete(Val, Objs) of
  1106. Objs ->
  1107. add_sel_match(R, Objs, Type, [Op|Acc]);
  1108. NewObjs when Type == set ->
  1109. add_sel_match(R, NewObjs, Type, Acc);
  1110. NewObjs ->
  1111. add_sel_match(R, NewObjs, Type, [Op|Acc])
  1112. end;
  1113. add_sel_match([Op={Oid={_,Key}, Val, write}|R], Objs, bag, Acc) ->
  1114. case lists:keymember(Key, 2, Objs) of
  1115. true ->
  1116. add_sel_match(R,[Val|lists:delete(Val,Objs)],bag,
  1117. [{Oid,Val,delete_object}|Acc]);
  1118. false ->
  1119. add_sel_match(R,Objs,bag,[Op|Acc])
  1120. end;
  1121. add_sel_match([Op={Oid, Val, write}|R], Objs, set, Acc) ->
  1122. case deloid(Oid,Objs) of
  1123. Objs ->
  1124. add_sel_match(R, Objs,set, [Op|Acc]);
  1125. NewObjs ->
  1126. add_sel_match(R, [Val | NewObjs],set, Acc)
  1127. end.
  1128. %% For ordered_set only !!
  1129. add_sel_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs],Acc)
  1130. when Key > element(2, Obj) ->
  1131. add_sel_ordered_match(Written, Objs, [Obj|Acc]);
  1132. add_sel_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_],Acc)
  1133. when Key < element(2, Obj) ->
  1134. add_sel_ordered_match(Rest,[Val|Objs],Acc);
  1135. add_sel_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
  1136. when Key < element(2, Obj) ->
  1137. add_sel_ordered_match(Rest,Objs,Acc);
  1138. %% Greater than last object
  1139. add_sel_ordered_match(Ops1, [], Acc) ->
  1140. {lists:reverse(Acc), Ops1};
  1141. %% Keys are equal from here
  1142. add_sel_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
  1143. add_sel_ordered_match(Rest, [Val|Objs], Acc);
  1144. add_sel_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
  1145. add_sel_ordered_match(Rest, Objs, Acc);
  1146. add_sel_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
  1147. add_sel_ordered_match(Rest, Objs, Acc);
  1148. add_sel_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
  1149. add_sel_ordered_match(Rest, Objs, Acc);
  1150. add_sel_ordered_match([], Objs, Acc) ->
  1151. {lists:reverse(Acc, Objs),[]}.
  1152. deloid(_Oid, []) ->
  1153. [];
  1154. deloid({Tab, Key}, [H | T]) when element(2, H) == Key ->
  1155. deloid({Tab, Key}, T);
  1156. deloid(Oid, [H | T]) ->
  1157. [H | deloid(Oid, T)].
  1158. %%%%%%%%%%%%%%%%%%
  1159. % select
  1160. select(Tab, Pat) ->
  1161. select(Tab, Pat, read).
  1162. select(Tab, Pat, LockKind)
  1163. when atom(Tab), Tab /= schema, list(Pat) ->
  1164. case get(mnesia_activity_state) of
  1165. {?DEFAULT_ACCESS, Tid, Ts} ->
  1166. select(Tid, Ts, Tab, Pat, LockKind);
  1167. {Mod, Tid, Ts} ->
  1168. Mod:select(Tid, Ts, Tab, Pat, LockKind);
  1169. _ ->
  1170. abort(no_transaction)
  1171. end;
  1172. select(Tab, Pat, _Lock) ->
  1173. abort({badarg, Tab, Pat}).
  1174. select(Tid, Ts, Tab, Spec, LockKind) ->
  1175. SelectFun = fun(FixedSpec) -> dirty_select(Tab, FixedSpec) end,
  1176. fun_select(Tid, Ts, Tab, Spec, LockKind, Tab, SelectFun).
  1177. fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, SelectFun) ->
  1178. case element(1, Tid) of
  1179. ets ->
  1180. mnesia_lib:db_select(ram_copies, Tab, Spec);
  1181. tid ->
  1182. select_lock(Tid,Ts,LockKind,Spec,Tab),
  1183. Store = Ts#tidstore.store,
  1184. Written = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
  1185. case Written of
  1186. [] ->
  1187. %% Nothing changed in the table during this transaction,
  1188. %% Simple case get results from [d]ets
  1189. SelectFun(Spec);
  1190. _ ->
  1191. %% Hard (slow case) records added or deleted earlier
  1192. %% in the transaction, have to cope with that.
  1193. Type = val({Tab, setorbag}),
  1194. FixedSpec = get_record_pattern(Spec),
  1195. TabRecs = SelectFun(FixedSpec),
  1196. FixedRes = add_match(Written, TabRecs, Type),
  1197. CMS = ets:match_spec_compile(Spec),
  1198. ets:match_spec_run(FixedRes, CMS)
  1199. end;
  1200. _Protocol ->
  1201. SelectFun(Spec)
  1202. end.
  1203. select_lock(Tid,Ts,LockKind,Spec,Tab) ->
  1204. %% Avoid table lock if possible
  1205. case Spec of
  1206. [{HeadPat,_, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
  1207. Key = element(2, HeadPat),
  1208. case has_var(Key) of
  1209. false -> lock_record(Tid, Ts, Tab, Key, LockKind);
  1210. true -> lock_table(Tid, Ts, Tab, LockKind)
  1211. end;
  1212. _ ->
  1213. lock_table(Tid, Ts, Tab, LockKind)
  1214. end.
  1215. %% Breakable Select
  1216. select(Tab, Pat, NObjects, LockKind)
  1217. when atom(Tab), Tab /= schema, list(Pat), number(NObjects) ->
  1218. case get(mnesia_activity_state) of
  1219. {?DEFAULT_ACCESS, Tid, Ts} ->
  1220. select(Tid, Ts, Tab, Pat, NObjects, LockKind);
  1221. {Mod, Tid, Ts} ->
  1222. Mod:select(Tid, Ts, Tab, Pat, NObjects, LockKind);
  1223. _ ->
  1224. abort(no_transaction)
  1225. end;
  1226. select(Tab, Pat, NObjects, _Lock) ->
  1227. abort({badarg, Tab, Pat, NObjects}).
  1228. select(Tid, Ts, Tab, Spec, NObjects, LockKind) ->
  1229. Where = val({Tab,where_to_read}),
  1230. Type = mnesia_lib:storage_type_at_node(Where,Tab),
  1231. InitFun = fun(FixedSpec) -> dirty_sel_init(Where,Tab,FixedSpec,NObjects,Type) end,
  1232. fun_select(Tid,Ts,Tab,Spec,LockKind,Tab,InitFun,NObjects,Where,Type).
  1233. -record(mnesia_select, {tab,tid,node,storage,cont,written=[],spec,type,orig}).
  1234. fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, Init, NObjects, Node, Storage) ->
  1235. Def = #mnesia_select{tid=Tid,node=Node,storage=Storage,tab=Tab,orig=Spec},
  1236. case element(1, Tid) of
  1237. ets ->
  1238. select_state(mnesia_lib:db_select_init(ram_copies,Tab,Spec,NObjects),Def);
  1239. tid ->
  1240. select_lock(Tid,Ts,LockKind,Spec,Tab),
  1241. Store = Ts#tidstore.store,
  1242. do_fixtable(Tab, Store),
  1243. Written0 = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
  1244. case Written0 of
  1245. [] ->
  1246. %% Nothing changed in the table during this transaction,
  1247. %% Simple case get results from [d]ets
  1248. select_state(Init(Spec),Def);
  1249. _ ->
  1250. %% Hard (slow case) records added or deleted earlier
  1251. %% in the transaction, have to cope with that.
  1252. Type = val({Tab, setorbag}),
  1253. Written =
  1254. if Type == ordered_set -> %% Sort stable
  1255. lists:keysort(1,Written0);
  1256. true ->
  1257. Written0
  1258. end,
  1259. FixedSpec = get_record_pattern(Spec),
  1260. CMS = ets:match_spec_compile(Spec),
  1261. trans_select(Init(FixedSpec),
  1262. Def#mnesia_select{written=Written,spec=CMS,type=Type})
  1263. end;
  1264. _Protocol ->
  1265. select_state(Init(Spec),Def)
  1266. end.
  1267. select(Cont) ->
  1268. case get(mnesia_activity_state) of
  1269. {?DEFAULT_ACCESS, Tid, Ts} ->
  1270. select_cont(Tid,Ts,Cont);
  1271. {Mod, Tid, Ts} ->
  1272. Mod:select_cont(Tid,Ts,Cont);
  1273. _ ->
  1274. abort(no_transaction)
  1275. end.
  1276. select_cont(_Tid,_Ts,'$end_of_table') ->
  1277. '$end_of_table';
  1278. select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid,cont=Cont, orig=Ms})
  1279. when element(1,Tid) == ets ->
  1280. case Cont of
  1281. '$end_of_table' -> '$end_of_table';
  1282. _ -> select_state(mnesia_lib:db_select_cont(ram_copies,Cont,Ms),State)
  1283. end;
  1284. select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) ->
  1285. select_state(dirty_sel_cont(State),State);
  1286. select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid}) ->
  1287. trans_select(dirty_sel_cont(State), State);
  1288. select_cont(_Tid2,_,#mnesia_select{tid=_Tid1}) -> % Missmatching tids
  1289. abort(wrong_transaction);
  1290. select_cont(_,_,Cont) ->
  1291. abort({badarg, Cont}).
  1292. trans_select('$end_of_table', #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
  1293. Written = add_match(Written0, [], Type),
  1294. {ets:match_spec_run(Written, CMS), '$end_of_table'};
  1295. trans_select({TabRecs,Cont}, State = #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
  1296. {FixedRes,Written} = add_sel_match(Written0, TabRecs, Type),
  1297. select_state({ets:match_spec_run(FixedRes, CMS),Cont},
  1298. State#mnesia_select{written=Written}).
  1299. select_state({Matches, Cont}, MS) ->
  1300. {Matches, MS#mnesia_select{cont=Cont}};
  1301. select_state('$end_of_table',_) -> '$end_of_table'.
  1302. get_record_pattern([]) -> [];
  1303. get_record_pattern([{M,C,_B}|R]) ->
  1304. [{M,C,['$_']} | get_record_pattern(R)].
  1305. all_keys(Tab) ->
  1306. case get(mnesia_activity_state) of
  1307. {?DEFAULT_ACCESS, Tid, Ts} ->
  1308. all_keys(Tid, Ts, Tab, read);
  1309. {Mod, Tid, Ts} ->
  1310. Mod:all_keys(Tid, Ts, Tab, read);
  1311. _ ->
  1312. abort(no_transaction)
  1313. end.
  1314. all_keys(Tid, Ts, Tab, LockKind)
  1315. when atom(Tab), Tab /= schema ->
  1316. Pat0 = val({Tab, wild_pattern}),
  1317. Pat = setelement(2, Pat0, '$1'),
  1318. Keys = select(Tid, Ts, Tab, [{Pat, [], ['$1']}], LockKind),
  1319. case val({Tab, setorbag}) of
  1320. bag ->
  1321. mnesia_lib:uniq(Keys);
  1322. _ ->
  1323. Keys
  1324. end;
  1325. all_keys(_Tid, _Ts, Tab, _LockKind) ->
  1326. abort({bad_type, Tab}).
  1327. index_match_object(Pat, Attr) when tuple(Pat), size(Pat) > 2 ->
  1328. Tab = element(1, Pat),
  1329. index_match_object(Tab, Pat, Attr, read);
  1330. index_match_object(Pat, _Attr) ->
  1331. abort({bad_type, Pat}).
  1332. index_match_object(Tab, Pat, Attr, LockKind) ->
  1333. case get(mnesia_activity_state) of
  1334. {?DEFAULT_ACCESS, Tid, Ts} ->
  1335. index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
  1336. {Mod, Tid, Ts} ->
  1337. Mod:index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
  1338. _ ->
  1339. abort(no_transaction)
  1340. end.
  1341. index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind)
  1342. when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
  1343. case element(1, Tid) of
  1344. ets ->
  1345. dirty_index_match_object(Tab, Pat, Attr); % Should be optimized?
  1346. tid ->
  1347. case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
  1348. Pos when Pos =< size(Pat) ->
  1349. case LockKind of
  1350. read ->
  1351. Store = Ts#tidstore.store,
  1352. mnesia_locker:rlock_table(Tid, Store, Tab),
  1353. Objs = dirty_index_match_object(Tab, Pat, Attr),
  1354. add_written_match(Store, Pat, Tab, Objs);
  1355. _ ->
  1356. abort({bad_type, Tab, LockKind})
  1357. end;
  1358. BadPos ->
  1359. abort({bad_type, Tab, BadPos})
  1360. end;
  1361. _Protocol ->
  1362. dirty_index_match_object(Tab, Pat, Attr)
  1363. end;
  1364. index_match_object(_Tid, _Ts, Tab, Pat, _Attr, _LockKind) ->
  1365. abort({bad_type, Tab, Pat}).
  1366. index_read(Tab, Key, Attr) ->
  1367. case get(mnesia_activity_state) of
  1368. {?DEFAULT_ACCESS, Tid, Ts} ->
  1369. index_read(Tid, Ts, Tab, Key, Attr, read);
  1370. {Mod, Tid, Ts} ->
  1371. Mod:index_read(Tid, Ts, Tab, Key, Attr, read);
  1372. _ ->
  1373. abort(no_transaction)
  1374. end.
  1375. index_read(Tid, Ts, Tab, Key, Attr, LockKind)
  1376. when atom(Tab), Tab /= schema ->
  1377. case element(1, Tid) of
  1378. ets ->
  1379. dirty_index_read(Tab, Key, Attr); % Should be optimized?
  1380. tid ->
  1381. Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
  1382. case LockKind of
  1383. read ->
  1384. case has_var(Key) of
  1385. false ->
  1386. Store = Ts#tidstore.store,
  1387. Objs = mnesia_index:read(Tid, Store, Tab, Key, Pos),
  1388. Pat = setelement(Pos, val({Tab, wild_pattern}), Key),
  1389. add_written_match(Store, Pat, Tab, Objs);
  1390. true ->
  1391. abort({bad_type, Tab, Attr, Key})
  1392. end;
  1393. _ ->
  1394. abort({bad_type, Tab, LockKind})
  1395. end;
  1396. _Protocol ->
  1397. dirty_index_read(Tab, Key, Attr)
  1398. end;
  1399. index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind) ->
  1400. abort({bad_type, Tab}).
  1401. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1402. %% Dirty access regardless of activities - updates
  1403. dirty_write(Val) when tuple(Val), size(Val) > 2 ->
  1404. Tab = element(1, Val),
  1405. dirty_write(Tab, Val);
  1406. dirty_write(Val) ->
  1407. abort({bad_type, Val}).
  1408. dirty_write(Tab, Val) ->
  1409. do_dirty_write(async_dirty, Tab, Val).
  1410. do_dirty_write(SyncMode, Tab, Val)
  1411. when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
  1412. case ?catch_val({Tab, record_validation}) of
  1413. {RecName, Arity, _Type}
  1414. when size(Val) == Arity, RecName == element(1, Val) ->
  1415. Oid = {Tab, element(2, Val)},
  1416. mnesia_tm:dirty(SyncMode, {Oid, Val, write});
  1417. {'EXIT', _} ->
  1418. abort({no_exists, Tab});
  1419. _ ->
  1420. abort({bad_type, Val})
  1421. end;
  1422. do_dirty_write(_SyncMode, Tab, Val) ->
  1423. abort({bad_type, Tab, Val}).
  1424. dirty_delete({Tab, Key}) ->
  1425. dirty_delete(Tab, Key);
  1426. dirty_delete(Oid) ->
  1427. abort({bad_type, Oid}).
  1428. dirty_delete(Tab, Key) ->
  1429. do_dirty_delete(async_dirty, Tab, Key).
  1430. do_dirty_delete(SyncMode, Tab, Key) when atom(Tab), Tab /= schema ->
  1431. Oid = {Tab, Key},
  1432. mnesia_tm:dirty(SyncMode, {Oid, Oid, delete});
  1433. do_dirty_delete(_SyncMode, Tab, _Key) ->
  1434. abort({bad_type, Tab}).
  1435. dirty_delete_object(Val) when tuple(Val), size(Val) > 2 ->
  1436. Tab = element(1, Val),
  1437. dirty_delete_object(Tab, Val);
  1438. dirty_delete_object(Val) ->
  1439. abort({bad_type, Val}).
  1440. dirty_delete_object(Tab, Val) ->
  1441. do_dirty_delete_object(async_dirty, Tab, Val).
  1442. do_dirty_delete_object(SyncMode, Tab, Val)
  1443. when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
  1444. Oid = {Tab, element(2, Val)},
  1445. mnesia_tm:dirty(SyncMode, {Oid, Val, delete_object});
  1446. do_dirty_delete_object(_SyncMode, Tab, Val) ->
  1447. abort({bad_type, Tab, Val}).
  1448. %% A Counter is an Oid being {CounterTab, CounterName}
  1449. dirty_update_counter({Tab, Key}, Incr) ->
  1450. dirty_update_counter(Tab, Key, Incr);
  1451. dirty_update_counter(Counter, _Incr) ->
  1452. abort({bad_type, Counter}).
  1453. dirty_update_counter(Tab, Key, Incr) ->
  1454. do_dirty_update_counter(async_dirty, Tab, Key, Incr).
  1455. do_dirty_update_counter(SyncMode, Tab, Key, Incr)
  1456. when atom(Tab), Tab /= schema, integer(Incr) ->
  1457. case ?catch_val({Tab, record_validation}) of
  1458. {RecName, 3, set} ->
  1459. Oid = {Tab, Key},
  1460. mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
  1461. _ ->
  1462. abort({combine_error, Tab, update_counter})
  1463. end;
  1464. do_dirty_update_counter(_SyncMode, Tab, _Key, Incr) ->
  1465. abort({bad_type, Tab, Incr}).
  1466. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1467. %% Dirty access regardless of activities - read
  1468. dirty_read({Tab, Key}) ->
  1469. dirty_read(Tab, Key);
  1470. dirty_read(Oid) ->
  1471. abort({bad_type, Oid}).
  1472. dirty_read(Tab, Key)
  1473. when atom(Tab), Tab /= schema ->
  1474. %% case catch ?ets_lookup(Tab, Key) of
  1475. %% {'EXIT', _} ->
  1476. %% Bad luck, we have to perform a real lookup
  1477. dirty_rpc(Tab, mnesia_lib, db_get, [Tab, Key]);
  1478. %% Val ->
  1479. %% Val
  1480. %% end;
  1481. dirty_read(Tab, _Key) ->
  1482. abort({bad_type, Tab}).
  1483. dirty_match_object(Pat) when tuple(Pat), size(Pat) > 2 ->
  1484. Tab = element(1, Pat),
  1485. dirty_match_object(Tab, Pat);
  1486. dirty_match_object(Pat) ->
  1487. abort({bad_type, Pat}).
  1488. dirty_match_object(Tab, Pat)
  1489. when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
  1490. dirty_rpc(Tab, ?MODULE, remote_dirty_match_object, [Tab, Pat]);
  1491. dirty_match_object(Tab, Pat) ->
  1492. abort({bad_type, Tab, Pat}).
  1493. remote_dirty_match_object(Tab, Pat) ->
  1494. Key = element(2, Pat),
  1495. case has_var(Key) of
  1496. false ->
  1497. mnesia_lib:db_match_object(Tab, Pat);
  1498. true ->
  1499. PosList = val({Tab, index}),
  1500. remote_dirty_match_object(Tab, Pat, PosList)
  1501. end.
  1502. remote_dirty_match_object(Tab, Pat, [Pos | Tail]) when Pos =< size(Pat) ->
  1503. IxKey = element(Pos, Pat),
  1504. case has_var(IxKey) of
  1505. false ->
  1506. mnesia_index:dirty_match_object(Tab, Pat, Pos);
  1507. true ->
  1508. remote_dirty_match_object(Tab, Pat, Tail)
  1509. end;
  1510. remote_dirty_match_object(Tab, Pat, []) ->
  1511. mnesia_lib:db_match_object(Tab, Pat);
  1512. remote_dirty_match_object(Tab, Pat, _PosList) ->
  1513. abort({bad_type, Tab, Pat}).
  1514. dirty_select(Tab, Spec) when atom(Tab), Tab /= schema, list(Spec) ->
  1515. dirty_rpc(Tab, ?MODULE, remote_dirty_select, [Tab, Spec]);
  1516. dirty_select(Tab, Spec) ->
  1517. abort({bad_type, Tab, Spec}).
  1518. remote_dirty_select(Tab, Spec) ->
  1519. case Spec of
  1520. [{HeadPat, _, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
  1521. Key = element(2, HeadPat),
  1522. case has_var(Key) of
  1523. false ->
  1524. mnesia_lib:db_select(Tab, Spec);
  1525. true ->
  1526. PosList = val({Tab, index}),
  1527. remote_dirty_select(Tab, Spec, PosList)
  1528. end;
  1529. _ ->
  1530. mnesia_lib:db_select(Tab, Spec)
  1531. end.
  1532. remote_dirty_select(Tab, [{HeadPat,_, _}] = Spec, [Pos | Tail])
  1533. when tuple(HeadPat), size(HeadPat) > 2, Pos =< size(HeadPat) ->
  1534. Key = element(Pos, HeadPat),
  1535. case has_var(Key) of
  1536. false ->
  1537. Recs = mnesia_index:dirty_select(Tab, HeadPat, Pos),
  1538. %% Returns the records without applying the match spec
  1539. %% The actual filtering is handled by the caller
  1540. CMS = ets:match_spec_compile(Spec),
  1541. case val({Tab, setorbag}) of
  1542. ordered_set ->
  1543. ets:match_spec_run(lists:sort(Recs), CMS);
  1544. _ ->
  1545. ets:match_spec_run(Recs, CMS)
  1546. end;
  1547. true ->
  1548. remote_dirty_select(Tab, Spec, Tail)
  1549. end;
  1550. remote_dirty_select(Tab, Spec, _) ->
  1551. mnesia_lib:db_select(Tab, Spec).
  1552. dirty_sel_init(Node,Tab,Spec,NObjects,Type) ->
  1553. do_dirty_rpc(Tab,Node,mnesia_lib,db_select_init,[Type,Tab,Spec,NObjects]).
  1554. dirty_sel_cont(#mnesia_select{cont='$end_of_table'}) -> '$end_of_table';
  1555. dirty_sel_cont(#mnesia_select{node=Node,tab=Tab,storage=Type,cont=Cont,orig=Ms}) ->
  1556. do_dirty_rpc(Tab,Node,mnesia_lib,db_select_cont,[Type,Cont,Ms]).
  1557. dirty_all_keys(Tab) when atom(Tab), Tab /= schema ->
  1558. case ?catch_val({Tab, wild_pattern}) of
  1559. {'EXIT', _} ->
  1560. abort({no_exists, Tab});
  1561. Pat0 ->
  1562. Pat = setelement(2, Pat0, '$1'),
  1563. Keys = dirty_select(Tab, [{Pat, [], ['$1']}]),
  1564. case val({Tab, setorbag}) of
  1565. bag -> mnesia_lib:uniq(Keys);
  1566. _ -> Keys
  1567. end
  1568. end;
  1569. dirty_all_keys(Tab) ->
  1570. abort({bad_type, Tab}).
  1571. dirty_index_match_object(Pat, Attr) when tuple(Pat), size(Pat) > 2 ->
  1572. Tab = element(1, Pat),
  1573. dirty_index_match_object(Tab, Pat, Attr);
  1574. dirty_index_match_object(Pat, _Attr) ->
  1575. abort({bad_type, Pat}).
  1576. dirty_index_match_object(Tab, Pat, Attr)
  1577. when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
  1578. case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
  1579. Pos when Pos =< size(Pat) ->
  1580. case has_var(element(2, Pat)) of
  1581. false ->
  1582. dirty_match_object(Tab, Pat);
  1583. true ->
  1584. Elem = element(Pos, Pat),
  1585. case has_var(Elem) of
  1586. false ->
  1587. dirty_rpc(Tab, mnesia_index, dirty_match_object,
  1588. [Tab, Pat, Pos]);
  1589. true ->
  1590. abort({bad_type, Tab, Attr, Elem})
  1591. end
  1592. end;
  1593. BadPos ->
  1594. abort({bad_type, Tab, BadPos})
  1595. end;
  1596. dirty_index_match_object(Tab, Pat, _Attr) ->
  1597. abort({bad_type, Tab, Pat}).
  1598. dirty_index_read(Tab, Key, Attr) when atom(Tab), Tab /= schema ->
  1599. Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
  1600. case has_var(Key) of
  1601. false ->
  1602. mnesia_index:dirty_read(Tab, Key, Pos);
  1603. true ->
  1604. abort({bad_type, Tab, Attr, Key})
  1605. end;
  1606. dirty_index_read(Tab, _Key, _Attr) ->
  1607. abort({bad_type, Tab}).
  1608. dirty_slot(Tab, Slot) when atom(Tab), Tab /= schema, integer(Slot) ->
  1609. dirty_rpc(Tab, mnesia_lib, db_slot, [Tab, Slot]);
  1610. dirty_slot(Tab, Slot) ->
  1611. abort({bad_type, Tab, Slot}).
  1612. dirty_first(Tab) when atom(Tab), Tab /= schema ->
  1613. dirty_rpc(Tab, mnesia_lib, db_first, [Tab]);
  1614. dirty_first(Tab) ->
  1615. abort({bad_type, Tab}).
  1616. dirty_last(Tab) when atom(Tab), Tab /= schema ->
  1617. dirty_rpc(Tab, mnesia_lib, db_last, [Tab]);
  1618. dirty_last(Tab) ->
  1619. abort({bad_type, Tab}).
  1620. dirty_next(Tab, Key) when atom(Tab), Tab /= schema ->
  1621. dirty_rpc(Tab, mnesia_lib, db_next_key, [Tab, Key]);
  1622. dirty_next(Tab, _Key) ->
  1623. abort({bad_type, Tab}).
  1624. dirty_prev(Tab, Key) when atom(Tab), Tab /= schema ->
  1625. dirty_rpc(Tab, mnesia_lib, db_prev_key, [Tab, Key]);
  1626. dirty_prev(Tab, _Key) ->
  1627. abort({bad_type, Tab}).
  1628. dirty_rpc(Tab, M, F, Args) ->
  1629. Node = val({Tab, where_to_read}),
  1630. do_dirty_rpc(Tab, Node, M, F, Args).
  1631. do_dirty_rpc(_Tab, nowhere, _, _, Args) ->
  1632. mnesia:abort({no_exists, Args});
  1633. do_dirty_rpc(Tab, Node, M, F, Args) ->
  1634. case rpc:call(Node, M, F, Args) of
  1635. {badrpc, Reason} ->
  1636. erlang:yield(), %% Do not be too eager
  1637. case mnesia_controller:call({check_w2r, Node, Tab}) of % Sync
  1638. NewNode when NewNode == Node ->
  1639. ErrorTag = mnesia_lib:dirty_rpc_error_tag(Reason),
  1640. mnesia:abort({ErrorTag, Args});
  1641. NewNode ->
  1642. case get(mnesia_activity_state) of
  1643. {_Mod, Tid, _Ts} when record(Tid, tid) ->
  1644. %% In order to perform a consistent
  1645. %% retry of a transaction we need
  1646. %% to acquire the lock on the NewNode.
  1647. %% In this context we do neither know
  1648. %% the kind or granularity of the lock.
  1649. %% --> Abort the transaction
  1650. mnesia:abort({node_not_running, Node});
  1651. _ ->
  1652. %% Splendid! A dirty retry is safe
  1653. %% 'Node' probably went down now
  1654. %% Let mnesia_controller get broken link message first
  1655. do_dirty_rpc(Tab, NewNode, M, F, Args)
  1656. end
  1657. end;
  1658. Other ->
  1659. Other
  1660. end.
  1661. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1662. %% Info
  1663. %% Info about one table
  1664. table_info(Tab, Item) ->
  1665. case get(mnesia_activity_state) of
  1666. undefined ->
  1667. any_table_info(Tab, Item);
  1668. {?DEFAULT_ACCESS, _Tid, _Ts} ->
  1669. any_table_info(Tab, Item);
  1670. {Mod, Tid, Ts} ->
  1671. Mod:table_info(Tid, Ts, Tab, Item);
  1672. _ ->
  1673. abort(no_transaction)
  1674. end.
  1675. table_info(_Tid, _Ts, Tab, Item) ->
  1676. any_table_info(Tab, Item).
  1677. any_table_info(Tab, Item) when atom(Tab) ->
  1678. case Item of
  1679. master_nodes ->
  1680. mnesia_recover:get_master_nodes(Tab);
  1681. % checkpoints ->
  1682. % case ?catch_val({Tab, commit_work}) of
  1683. % [{checkpoints, List} | _] -> List;
  1684. % No_chk when list(No_chk) -> [];
  1685. % Else -> info_reply(Else, Tab, Item)
  1686. % end;
  1687. size ->
  1688. raw_table_info(Tab, Item);
  1689. memory ->
  1690. raw_table_info(Tab, Item);
  1691. type ->
  1692. case ?catch_val({Tab, setorbag}) of
  1693. {'EXIT', _} ->
  1694. bad_info_reply(Tab, Item);
  1695. Val ->
  1696. Val
  1697. end;
  1698. all ->
  1699. case mnesia_schema:get_table_properties(Tab) of
  1700. [] ->
  1701. abort({no_exists, Tab, Item});
  1702. Props ->
  1703. lists:map(fun({setorbag, Type}) -> {type, Type};
  1704. (Prop) -> Prop end,
  1705. Props)
  1706. end;
  1707. _ ->
  1708. case ?catch_val({Tab, Item}) of
  1709. {'EXIT', _} ->
  1710. bad_info_reply(Tab, Item);
  1711. Val ->
  1712. Val
  1713. end
  1714. end;
  1715. any_table_info(Tab, _Item) ->
  1716. abort({bad_type, Tab}).
  1717. raw_table_info(Tab, Item) ->
  1718. case ?catch_val({Tab, storage_type}) of
  1719. ram_copies ->
  1720. info_reply(catch ?ets_info(Tab, Item), Tab, Item);
  1721. disc_copies ->
  1722. info_reply(catch ?ets_info(Tab, Item), Tab, Item);
  1723. disc_only_copies ->
  1724. info_reply(catch dets:info(Tab, Item), Tab, Item);
  1725. {external_copies, Mod} ->
  1726. info_reply(catch Mod:table_info(Tab, Item), Tab, Item);
  1727. unknown ->
  1728. bad_info_reply(Tab, Item);
  1729. {'EXIT', _} ->
  1730. bad_info_reply(Tab, Item)
  1731. end.
  1732. info_reply({'EXIT', _Reason}, Tab, Item) ->
  1733. bad_info_reply(Tab, Item);
  1734. info_reply({error, _Reason}, Tab, Item) ->
  1735. bad_info_reply(Tab, Item);
  1736. info_reply(Val, _Tab, _Item) ->
  1737. Val.
  1738. bad_info_reply(_Tab, size) -> 0;
  1739. bad_info_reply(_Tab, memory) -> 0;
  1740. bad_info_reply(Tab, Item) -> abort({no_exists, Tab, Item}).
  1741. %% Raw info about all tables
  1742. schema() ->
  1743. mnesia_schema:info().
  1744. %% Raw info about one tables
  1745. schema(Tab) ->
  1746. mnesia_schema:info(Tab).
  1747. error_description(Err) ->
  1748. mnesia_lib:error_desc(Err).
  1749. info() ->
  1750. case mnesia_lib:is_running() of
  1751. yes ->
  1752. TmInfo = mnesia_tm:get_info(10000),
  1753. Held = system_info(held_locks),
  1754. Queued = system_info(lock_queue),
  1755. io:format("---> Processes holding locks <--- ~n", []),
  1756. lists:foreach(fun(L) -> io:format("Lock: ~p~n", [L]) end,
  1757. Held),
  1758. io:format( "---> Processes waiting for locks <--- ~n", []),
  1759. lists:foreach(fun({Oid, Op, _Pid, Tid, OwnerTid}) ->
  1760. io:format("Tid ~p waits for ~p lock "
  1761. "on oid ~p owned by ~p ~n",
  1762. [Tid, Op, Oid, OwnerTid])
  1763. end, Queued),
  1764. mnesia_tm:display_info(group_leader(), TmInfo),
  1765. Pat = {'_', unclear, '_'},
  1766. Uncertain = ets:match_object(mnesia_decision, Pat),
  1767. io:format( "---> Uncertain transactions <--- ~n", []),
  1768. lists:foreach(fun({Tid, _, Nodes}) ->
  1769. io:format("Tid ~w waits for decision "
  1770. "from ~w~n",
  1771. [Tid, Nodes])
  1772. end, Uncertain),
  1773. mnesia_controller:info(),
  1774. display_system_info(Held, Queued, TmInfo, Uncertain);
  1775. _ ->
  1776. mini_info()
  1777. end,
  1778. ok.
  1779. mini_info() ->
  1780. io:format("===> System info in version ~p, debug level = ~p <===~n",
  1781. [system_info(version), system_info(debug)]),
  1782. Not =
  1783. case system_info(use_dir) of
  1784. true -> "";
  1785. false -> "NOT "
  1786. end,
  1787. io:format("~w. Directory ~p is ~sused.~n",
  1788. [system_info(schema_location), system_info(directory), Not]),
  1789. io:format("use fallback at restart = ~w~n",
  1790. [system_info(fallback_activated)]),
  1791. Running = system_info(running_db_nodes),
  1792. io:format("running db nodes = ~w~n", [Running]),
  1793. All = mnesia_lib:all_nodes(),
  1794. io:format("stopped db nodes = ~w ~n", [All -- Running]).
  1795. display_system_info(Held, Queued, TmInfo, Uncertain) ->
  1796. mini_info(),
  1797. display_tab_info(),
  1798. S = fun(Items) -> [system_info(I) || I <- Items] end,
  1799. io:format("~w transactions committed, ~w aborted, "
  1800. "~w restarted, ~w logged to disc~n",
  1801. S([transaction_commits, transaction_failures,
  1802. transaction_restarts, transaction_log_writes])),
  1803. {Active, Pending} =
  1804. case TmInfo of
  1805. {timeout, _} -> {infinity, infinity};
  1806. {info, P, A} -> {length(A), length(P)}
  1807. end,
  1808. io:format("~w held locks, ~w in queue; "
  1809. "~w local transactions, ~w remote~n",
  1810. [length(Held), length(Queued), Active, Pending]),
  1811. Ufold = fun({_, _, Ns}, {C, Old}) ->
  1812. New = [N || N <- Ns, not lists:member(N, Old)],
  1813. {C + 1, New ++ Old}
  1814. end,
  1815. {Ucount, Unodes} = lists:foldl(Ufold, {0, []}, Uncertain),
  1816. io:format("~w transactions waits for other nodes: ~p~n",
  1817. [Ucount, Unodes]).
  1818. display_tab_info() ->
  1819. MasterTabs = mnesia_recover:get_master_node_tables(),
  1820. io:format("master node tables = ~p~n", [lists:sort(MasterTabs)]),
  1821. Tabs = system_info(tables),
  1822. {Unknown, Ram, Disc, DiscOnly} =
  1823. lists:foldl(fun storage_count/2, {[], [], [], []}, Tabs),
  1824. io:format("remote = ~p~n", [lists:sort(Unknown)]),
  1825. io:format("ram_copies = ~p~n", [lists:sort(Ram)]),
  1826. io:format("disc_copies = ~p~n", [lists:sort(Disc)]),
  1827. io:format("disc_only_copies = ~p~n", [lists:sort(DiscOnly)]),
  1828. Rfoldl = fun(T, Acc) ->
  1829. Rpat =
  1830. case val({T, access_mode}) of
  1831. read_only ->
  1832. lists:sort([{A, read_only} || A <- val({T, active_replicas})]);
  1833. read_write ->
  1834. table_info(T, where_to_commit)
  1835. end,
  1836. case lists:keysearch(Rpat, 1, Acc) of
  1837. {value, {_Rpat, Rtabs}} ->
  1838. lists:keyreplace(Rpat, 1, Acc, {Rpat, [T | Rtabs]});
  1839. false ->
  1840. [{Rpat, [T]} | Acc]
  1841. end
  1842. end,
  1843. Repl = lists:foldl(Rfoldl, [], Tabs),
  1844. Rdisp = fun({Rpat, Rtabs}) -> io:format("~p = ~p~n", [Rpat, Rtabs]) end,
  1845. lists:foreach(Rdisp, lists:sort(Repl)).
  1846. storage_count(T, {U, R, D, DO}) ->
  1847. case table_info(T, storage_type) of
  1848. unknown -> {[T | U], R, D, DO};
  1849. ram_copies -> {U, [T | R], D, DO};
  1850. disc_copies -> {U, R, [T | D], DO};
  1851. disc_only_copies -> {U, R, D, [T | DO]}
  1852. end.
  1853. system_info(Item) ->
  1854. case catch system_info2(Item) of
  1855. {'EXIT',Error} -> abort(Error);
  1856. Other -> Other
  1857. end.
  1858. system_info2(all) ->
  1859. Items = system_info_items(mnesia_lib:is_running()),
  1860. [{I, system_info(I)} || I <- Items];
  1861. system_info2(db_nodes) ->
  1862. DiscNs = ?catch_val({schema, disc_copies}),
  1863. RamNs = ?catch_val({schema, ram_copies}),
  1864. if
  1865. list(DiscNs), list(RamNs) ->
  1866. DiscNs ++ RamNs;
  1867. true ->
  1868. case mnesia_schema:read_nodes() of
  1869. {ok, Nodes} -> Nodes;
  1870. {error,Reason} -> exit(Reason)
  1871. end
  1872. end;
  1873. system_info2(running_db_nodes) ->
  1874. case ?catch_val({current, db_nodes}) of
  1875. {'EXIT',_} ->
  1876. %% Ensure that we access the intended Mnesia
  1877. %% directory. This function may not be called
  1878. %% during startup since it will cause the
  1879. %% application_controller to get into deadlock
  1880. load_mnesia_or_abort(),
  1881. mnesia_lib:running_nodes();
  1882. Other ->
  1883. Other
  1884. end;
  1885. system_info2(extra_db_nodes) ->
  1886. case ?catch_val(extra_db_nodes) of
  1887. {'EXIT',_} ->
  1888. %% Ensure that we access the intended Mnesia
  1889. %% directory. This function may not be called
  1890. %% during startup since it will cause the
  1891. %% application_controller to get into deadlock
  1892. load_mnesia_or_abort(),
  1893. mnesia_monitor:get_env(extra_db_nodes);
  1894. Other ->
  1895. Other
  1896. end;
  1897. system_info2(directory) ->
  1898. case ?catch_val(directory) of
  1899. {'EXIT',_} ->
  1900. %% Ensure that we access the intended Mnesia
  1901. %% directory. This function may not be called
  1902. %% during startup since it will cause the
  1903. %% application_controller to get into deadlock
  1904. load_mnesia_or_abort(),
  1905. mnesia_monitor:get_env(dir);
  1906. Other ->
  1907. Other
  1908. end;
  1909. system_info2(use_dir) ->
  1910. case ?catch_val(use_dir) of
  1911. {'EXIT',_} ->
  1912. %% Ensure that we access the intended Mnesia
  1913. %% directory. This function may not be called
  1914. %% during startup since it will cause the
  1915. %% application_controller to get into deadlock
  1916. load_mnesia_or_abort(),
  1917. mnesia_monitor:use_dir();
  1918. Other ->
  1919. Other
  1920. end;
  1921. system_info2(schema_location) ->
  1922. case ?catch_val(schema_location) of
  1923. {'EXIT',_} ->
  1924. %% Ensure that we access the intended Mnesia
  1925. %% directory. This function may not be called
  1926. %% during startup since it will cause the
  1927. %% application_controller to get into deadlock
  1928. load_mnesia_or_abort(),
  1929. mnesia_monitor:get_env(schema_location);
  1930. Other ->
  1931. Other
  1932. end;
  1933. system_info2(fallback_activated) ->
  1934. case ?catch_val(fallback_activated) of
  1935. {'EXIT',_} ->
  1936. %% Ensure that we access the intended Mnesia
  1937. %% directory. This function may not be called
  1938. %% during startup since it will cause the
  1939. %% application_controller to get into deadlock
  1940. load_mnesia_or_abort(),
  1941. mnesia_bup:fallback_exists();
  1942. Other ->
  1943. Other
  1944. end;
  1945. system_info2(version) ->
  1946. case ?catch_val(version) of
  1947. {'EXIT', _} ->
  1948. Apps = application:loaded_applications(),
  1949. case lists:keysearch(?APPLICATION, 1, Apps) of
  1950. {value, {_Name, _Desc, Version}} ->
  1951. Version;
  1952. false ->
  1953. %% Ensure that it does not match
  1954. {mnesia_not_loaded, node(), now()}
  1955. end;
  1956. Version ->
  1957. Version
  1958. end;
  1959. system_info2(access_module) -> mnesia_monitor:get_env(access_module);
  1960. system_info2(auto_repair) -> mnesia_monitor:get_env(auto_repair);
  1961. system_info2(is_running) -> mnesia_lib:is_running();
  1962. system_info2(backup_module) -> mnesia_monitor:get_env(backup_module);
  1963. system_info2(event_module) -> mnesia_monitor:get_env(event_module);
  1964. system_info2(debug) -> mnesia_monitor:get_env(debug);
  1965. system_info2(dump_log_load_regulation) -> mnesia_monitor:get_env(dump_log_load_regulation);
  1966. system_info2(dump_log_write_threshold) -> mnesia_monitor:get_env(dump_log_write_threshold);
  1967. system_info2(dump_log_time_threshold) -> mnesia_monitor:get_env(dump_log_time_threshold);
  1968. system_info2(dump_log_update_in_place) ->
  1969. mnesia_monitor:get_env(dump_log_update_in_place);
  1970. system_info2(max_wait_for_decision) -> mnesia_monitor:get_env(max_wait_for_decision);
  1971. system_info2(embedded_mnemosyne) -> mnesia_monitor:get_env(embedded_mnemosyne);
  1972. system_info2(ignore_fallback_at_startup) -> mnesia_monitor:get_env(ignore_fallback_at_startup);
  1973. system_info2(fallback_error_function) -> mnesia_monitor:get_env(fallback_error_function);
  1974. system_info2(log_version) -> mnesia_log:version();
  1975. system_info2(protocol_version) -> mnesia_monitor:protocol_version();
  1976. system_info2(schema_version) -> mnesia_schema:version(); %backward compatibility
  1977. system_info2(tables) -> val({schema, tables});
  1978. system_info2(local_tables) -> val({schema, local_tables});
  1979. system_info2(master_node_tables) -> mnesia_recover:get_master_node_tables();
  1980. system_info2(subscribers) -> mnesia_subscr:subscribers();
  1981. system_info2(checkpoints) -> mnesia_checkpoint:checkpoints();
  1982. system_info2(held_locks) -> mnesia_locker:get_held_locks();
  1983. system_info2(lock_queue) -> mnesia_locker:get_lock_queue();
  1984. system_info2(transactions) -> mnesia_tm:get_transactions();
  1985. system_info2(transaction_failures) -> mnesia_lib:read_counter(trans_failures);
  1986. system_info2(transaction_commits) -> mnesia_lib:read_counter(trans_commits);
  1987. system_info2(transaction_restarts) -> mnesia_lib:read_counter(trans_restarts);
  1988. system_info2(transaction_log_writes) -> mnesia_dumper:get_log_writes();
  1989. system_info2(Item) -> exit({badarg, Item}).
  1990. system_info_items(yes) ->
  1991. [
  1992. access_module,
  1993. auto_repair,
  1994. backup_module,
  1995. checkpoints,
  1996. db_nodes,
  1997. debug,
  1998. directory,
  1999. dump_log_load_regulation,
  2000. dump_log_time_threshold,
  2001. dump_log_update_in_place,
  2002. dump_log_write_threshold,
  2003. embedded_mnemosyne,
  2004. event_module,
  2005. extra_db_nodes,
  2006. fallback_activated,
  2007. held_locks,
  2008. ignore_fallback_at_startup,
  2009. fallback_error_function,
  2010. is_running,
  2011. local_tables,
  2012. lock_queue,
  2013. log_version,
  2014. master_node_tables,
  2015. max_wait_for_decision,
  2016. protocol_version,
  2017. running_db_nodes,
  2018. schema_location,
  2019. schema_version,
  2020. subscribers,
  2021. tables,
  2022. transaction_commits,
  2023. transaction_failures,
  2024. transaction_log_writes,
  2025. transaction_restarts,
  2026. transactions,
  2027. use_dir,
  2028. version
  2029. ];
  2030. system_info_items(no) ->
  2031. [
  2032. auto_repair,
  2033. backup_module,
  2034. db_nodes,
  2035. debug,
  2036. directory,
  2037. dump_log_load_regulation,
  2038. dump_log_time_threshold,
  2039. dump_log_update_in_place,
  2040. dump_log_write_threshold,
  2041. event_module,
  2042. extra_db_nodes,
  2043. ignore_fallback_at_startup,
  2044. fallback_error_function,
  2045. is_running,
  2046. log_version,
  2047. max_wait_for_decision,
  2048. protocol_version,
  2049. running_db_nodes,
  2050. schema_location,
  2051. schema_version,
  2052. use_dir,
  2053. version
  2054. ].
  2055. system_info() ->
  2056. IsRunning = mnesia_lib:is_running(),
  2057. case IsRunning of
  2058. yes ->
  2059. TmInfo = mnesia_tm:get_info(10000),
  2060. Held = system_info(held_locks),
  2061. Queued = system_info(lock_queue),
  2062. Pat = {'_', unclear, '_'},
  2063. Uncertain = ets:match_object(mnesia_decision, Pat),
  2064. display_system_info(Held, Queued, TmInfo, Uncertain);
  2065. _ ->
  2066. mini_info()
  2067. end,
  2068. IsRunning.
  2069. load_mnesia_or_abort() ->
  2070. case mnesia_lib:ensure_loaded(?APPLICATION) of
  2071. ok ->
  2072. ok;
  2073. {error, Reason} ->
  2074. abort(Reason)
  2075. end.
  2076. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2077. %% Database mgt
  2078. create_schema(Ns) ->
  2079. mnesia_bup:create_schema(Ns).
  2080. delete_schema(Ns) ->
  2081. mnesia_schema:delete_schema(Ns).
  2082. backup(Opaque) ->
  2083. mnesia_log:backup(Opaque).
  2084. backup(Opaque, Mod) ->
  2085. mnesia_log:backup(Opaque, Mod).
  2086. traverse_backup(S, T, Fun, Acc) ->
  2087. mnesia_bup:traverse_backup(S, T, Fun, Acc).
  2088. traverse_backup(S, SM, T, TM, F, A) ->
  2089. mnesia_bup:traverse_backup(S, SM, T, TM, F, A).
  2090. install_fallback(Opaque) ->
  2091. mnesia_bup:install_fallback(Opaque).
  2092. install_fallback(Opaque, Mod) ->
  2093. mnesia_bup:install_fallback(Opaque, Mod).
  2094. uninstall_fallback() ->
  2095. mnesia_bup:uninstall_fallback().
  2096. uninstall_fallback(Args) ->
  2097. mnesia_bup:uninstall_fallback(Args).
  2098. activate_checkpoint(Args) ->
  2099. mnesia_checkpoint:activate(Args).
  2100. deactivate_checkpoint(Name) ->
  2101. mnesia_checkpoint:deactivate(Name).
  2102. backup_checkpoint(Name, Opaque) ->
  2103. mnesia_log:backup_checkpoint(Name, Opaque).
  2104. backup_checkpoint(Name, Opaque, Mod) ->
  2105. mnesia_log:backup_checkpoint(Name, Opaque, Mod).
  2106. restore(Opaque, Args) ->
  2107. mnesia_schema:restore(Opaque, Args).
  2108. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2109. %% Table mgt
  2110. create_table(Arg) ->
  2111. mnesia_schema:create_table(Arg).
  2112. create_table(Name, Arg) when list(Arg) ->
  2113. mnesia_schema:create_table([{name, Name}| Arg]);
  2114. create_table(Name, Arg) ->
  2115. {aborted, badarg, Name, Arg}.
  2116. delete_table(Tab) ->
  2117. mnesia_schema:delete_table(Tab).
  2118. add_table_copy(Tab, N, S) ->
  2119. mnesia_schema:add_table_copy(Tab, N, S).
  2120. del_table_copy(Tab, N) ->
  2121. mnesia_schema:del_table_copy(Tab, N).
  2122. move_table_copy(Tab, From, To) ->
  2123. mnesia_schema:move_table(Tab, From, To).
  2124. add_table_index(Tab, Ix) ->
  2125. mnesia_schema:add_table_index(Tab, Ix).
  2126. del_table_index(Tab, Ix) ->
  2127. mnesia_schema:del_table_index(Tab, Ix).
  2128. transform_table(Tab, Fun, NewA) ->
  2129. case catch val({Tab, record_name}) of
  2130. {'EXIT', Reason} ->
  2131. mnesia:abort(Reason);
  2132. OldRN ->
  2133. mnesia_schema:transform_table(Tab, Fun, NewA, OldRN)
  2134. end.
  2135. transform_table(Tab, Fun, NewA, NewRN) ->
  2136. mnesia_schema:transform_table(Tab, Fun, NewA, NewRN).
  2137. change_table_copy_type(T, N, S) ->
  2138. mnesia_schema:change_table_copy_type(T, N, S).
  2139. clear_table(Tab) ->
  2140. mnesia_schema:clear_table(Tab).
  2141. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2142. %% Table mgt - user properties
  2143. read_table_property(Tab, PropKey) ->
  2144. val({Tab, user_property, PropKey}).
  2145. write_table_property(Tab, Prop) ->
  2146. mnesia_schema:write_table_property(Tab, Prop).
  2147. delete_table_property(Tab, PropKey) ->
  2148. mnesia_schema:delete_table_property(Tab, PropKey).
  2149. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2150. %% Table mgt - user properties
  2151. change_table_frag(Tab, FragProp) ->
  2152. mnesia_schema:change_table_frag(Tab, FragProp).
  2153. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2154. %% Table mgt - table load
  2155. %% Dump a ram table to disc
  2156. dump_tables(Tabs) ->
  2157. mnesia_schema:dump_tables(Tabs).
  2158. %% allow the user to wait for some tables to be loaded
  2159. wait_for_tables(Tabs, Timeout) ->
  2160. mnesia_controller:wait_for_tables(Tabs, Timeout).
  2161. force_load_table(Tab) ->
  2162. case mnesia_controller:force_load_table(Tab) of
  2163. ok -> yes; % Backwards compatibility
  2164. Other -> Other
  2165. end.
  2166. change_table_access_mode(T, Access) ->
  2167. mnesia_schema:change_table_access_mode(T, Access).
  2168. change_table_load_order(T, O) ->
  2169. mnesia_schema:change_table_load_order(T, O).
  2170. set_master_nodes(Nodes) when list(Nodes) ->
  2171. UseDir = system_info(use_dir),
  2172. IsRunning = system_info(is_running),
  2173. case IsRunning of
  2174. yes ->
  2175. CsPat = {{'_', cstruct}, '_'},
  2176. Cstructs0 = ?ets_match_object(mnesia_gvar, CsPat),
  2177. Cstructs = [Cs || {_, Cs} <- Cstructs0],
  2178. log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning);
  2179. _NotRunning ->
  2180. case UseDir of
  2181. true ->
  2182. mnesia_lib:lock_table(schema),
  2183. Res =
  2184. case mnesia_schema:read_cstructs_from_disc() of
  2185. {ok, Cstructs} ->
  2186. log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning);
  2187. {error, Reason} ->
  2188. {error, Reason}
  2189. end,
  2190. mnesia_lib:unlock_table(schema),
  2191. Res;
  2192. false ->
  2193. ok
  2194. end
  2195. end;
  2196. set_master_nodes(Nodes) ->
  2197. {error, {bad_type, Nodes}}.
  2198. log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning) ->
  2199. Fun = fun(Cs) ->
  2200. Copies = mnesia_lib:copy_holders(Cs),
  2201. Valid = mnesia_lib:intersect(Nodes, Copies),
  2202. {Cs#cstruct.name, Valid}
  2203. end,
  2204. Args = lists:map(Fun, Cstructs),
  2205. mnesia_recover:log_master_nodes(Args, UseDir, IsRunning).
  2206. set_master_nodes(Tab, Nodes) when list(Nodes) ->
  2207. UseDir = system_info(use_dir),
  2208. IsRunning = system_info(is_running),
  2209. case IsRunning of
  2210. yes ->
  2211. case ?catch_val({Tab, cstruct}) of
  2212. {'EXIT', _} ->
  2213. {error, {no_exists, Tab}};
  2214. Cs ->
  2215. case Nodes -- mnesia_lib:copy_holders(Cs) of
  2216. [] ->
  2217. Args = [{Tab , Nodes}],
  2218. mnesia_recover:log_master_nodes(Args, UseDir, IsRunning);
  2219. BadNodes ->
  2220. {error, {no_exists, Tab, BadNodes}}
  2221. end
  2222. end;
  2223. _NotRunning ->
  2224. case UseDir of
  2225. true ->
  2226. mnesia_lib:lock_table(schema),
  2227. Res =
  2228. case mnesia_schema:read_cstructs_from_disc() of
  2229. {ok, Cstructs} ->
  2230. case lists:keysearch(Tab, 2, Cstructs) of
  2231. {value, Cs} ->
  2232. case Nodes -- mnesia_lib:copy_holders(Cs) of
  2233. [] ->
  2234. Args = [{Tab , Nodes}],
  2235. mnesia_recover:log_master_nodes(Args, UseDir, IsRunning);
  2236. BadNodes ->
  2237. {error, {no_exists, Tab, BadNodes}}
  2238. end;
  2239. false ->
  2240. {error, {no_exists, Tab}}
  2241. end;
  2242. {error, Reason} ->
  2243. {error, Reason}
  2244. end,
  2245. mnesia_lib:unlock_table(schema),
  2246. Res;
  2247. false ->
  2248. ok
  2249. end
  2250. end;
  2251. set_master_nodes(Tab, Nodes) ->
  2252. {error, {bad_type, Tab, Nodes}}.
  2253. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2254. %% On-load fun
  2255. %%% This function is called by mnesia_loader. It can be shadowed by
  2256. %%% another access module. For backward compatibility and safety reasons,
  2257. %%% the function is optional in user-provided access modules
  2258. %%% (basically, if you don't know what you're doing, you shouldn't attempt
  2259. %%% to modify this function, since a crash will cause the mnesia node to
  2260. %%% dump core.)
  2261. onload_fun(Table, LoadReason) ->
  2262. Default = fun(_) -> ok end,
  2263. case system_info(access_module) of
  2264. ?MODULE ->
  2265. Default;
  2266. OtherMod ->
  2267. try OtherMod:onload_fun(Table, LoadReason)
  2268. catch
  2269. error:undef ->
  2270. Default;
  2271. error:Other ->
  2272. mnesia_lib:coredump({Other, [{OtherMod,
  2273. onload_fun,
  2274. [Table,LoadReason]},
  2275. erlang:get_stacktrace()]})
  2276. end
  2277. end.
  2278. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2279. %% Misc admin
  2280. dump_log() ->
  2281. mnesia_controller:sync_dump_log(user).
  2282. subscribe(What) ->
  2283. mnesia_subscr:subscribe(self(), What).
  2284. unsubscribe(What) ->
  2285. mnesia_subscr:unsubscribe(self(), What).
  2286. report_event(Event) ->
  2287. mnesia_lib:report_system_event({mnesia_user, Event}).
  2288. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2289. %% Snmp
  2290. snmp_open_table(Tab, Us) ->
  2291. mnesia_schema:add_snmp(Tab, Us).
  2292. snmp_close_table(Tab) ->
  2293. mnesia_schema:del_snmp(Tab).
  2294. snmp_get_row(Tab, RowIndex) when atom(Tab), Tab /= schema ->
  2295. dirty_rpc(Tab, mnesia_snmp_hook, get_row, [Tab, RowIndex]);
  2296. snmp_get_row(Tab, _RowIndex) ->
  2297. abort({bad_type, Tab}).
  2298. snmp_get_next_index(Tab, RowIndex) when atom(Tab), Tab /= schema ->
  2299. dirty_rpc(Tab, mnesia_snmp_hook, get_next_index, [Tab, RowIndex]);
  2300. snmp_get_next_index(Tab, _RowIndex) ->
  2301. abort({bad_type, Tab}).
  2302. snmp_get_mnesia_key(Tab, RowIndex) when atom(Tab), Tab /= schema ->
  2303. dirty_rpc(Tab, mnesia_snmp_hook, get_mnesia_key, [Tab, RowIndex]);
  2304. snmp_get_mnesia_key(Tab, _RowIndex) ->
  2305. abort({bad_type, Tab}).
  2306. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2307. %% Textfile access
  2308. load_textfile(F) ->
  2309. mnesia_text:load_textfile(F).
  2310. dump_to_textfile(F) ->
  2311. mnesia_text:dump_to_textfile(F).
  2312. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2313. %% QLC Handles
  2314. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2315. table(Tab) ->
  2316. table(Tab, []).
  2317. table(Tab,Opts) ->
  2318. {[Trav,Lock,NObjects],QlcOptions0} =
  2319. qlc_opts(Opts,[{traverse,select},{lock,read},{n_objects,100}]),
  2320. TF = case Trav of
  2321. {select,Ms} ->
  2322. fun() -> qlc_select(select(Tab,Ms,NObjects,Lock)) end;
  2323. select ->
  2324. fun(Ms) -> qlc_select(select(Tab,Ms,NObjects,Lock)) end;
  2325. _ ->
  2326. erlang:fault({badarg, {Trav,[Tab, Opts]}})
  2327. end,
  2328. Pre = fun(Arg) -> pre_qlc(Arg, Tab) end,
  2329. Post = fun() -> post_qlc(Tab) end,
  2330. Info = fun(Tag) -> qlc_info(Tab, Tag) end,
  2331. ParentFun = fun() ->
  2332. {mnesia_activity, mnesia:get_activity_id()}
  2333. end,
  2334. Lookup =
  2335. case Trav of
  2336. {select, _} -> [];
  2337. _ ->
  2338. LFun = fun(2, Keys) ->
  2339. Read = fun(Key) -> read(Tab,Key,Lock) end,
  2340. lists:flatmap(Read, Keys);
  2341. (Index,Keys) ->
  2342. IdxRead = fun(Key) -> index_read(Tab,Key,Index) end,
  2343. lists:flatmap(IdxRead, Keys)
  2344. end,
  2345. [{lookup_fun, LFun}]
  2346. end,
  2347. MFA = fun(Type) -> qlc_format(Type, Tab, NObjects, Lock, Opts) end,
  2348. QlcOptions = [{pre_fun, Pre}, {post_fun, Post},
  2349. {info_fun, Info}, {parent_fun, ParentFun},
  2350. {format_fun, MFA}|Lookup] ++ QlcOptions0,
  2351. qlc:table(TF, QlcOptions).
  2352. pre_qlc(Opts, Tab) ->
  2353. {_,Tid,_} =
  2354. case get(mnesia_activity_state) of
  2355. undefined ->
  2356. case lists:keysearch(parent_value, 1, Opts) of
  2357. {value, {parent_value,{mnesia_activity,undefined}}} ->
  2358. abort(no_transaction);
  2359. {value, {parent_value,{mnesia_activity,Aid}}} ->
  2360. {value,{stop_fun,Stop}} =
  2361. lists:keysearch(stop_fun,1,Opts),
  2362. put_activity_id(Aid,Stop),
  2363. Aid;
  2364. _ ->
  2365. abort(no_transaction)
  2366. end;
  2367. Else ->
  2368. Else
  2369. end,
  2370. case element(1,Tid) of
  2371. tid -> ok;
  2372. _ ->
  2373. case ?catch_val({Tab, setorbag}) of
  2374. ordered_set -> ok;
  2375. _ ->
  2376. dirty_rpc(Tab, mnesia_tm, fixtable, [Tab,true,self()]),
  2377. ok
  2378. end
  2379. end.
  2380. post_qlc(Tab) ->
  2381. case catch get(mnesia_activity_state) of
  2382. {_,#tid{},_} -> ok;
  2383. _ ->
  2384. case ?catch_val({Tab, setorbag}) of
  2385. ordered_set ->
  2386. ok;
  2387. _ ->
  2388. dirty_rpc(Tab, mnesia_tm, fixtable, [Tab,false,self()]),
  2389. ok
  2390. end
  2391. end.
  2392. qlc_select('$end_of_table') -> [];
  2393. qlc_select({Objects, Cont}) ->
  2394. Objects ++ fun() -> qlc_select(select(Cont)) end.
  2395. qlc_opts(Opts, Keys) when is_list(Opts) ->
  2396. qlc_opts(Opts, Keys, []);
  2397. qlc_opts(Option, Keys) ->
  2398. qlc_opts([Option], Keys, []).
  2399. qlc_opts(Opts, [{Key,Def}|Keys], Acc) ->
  2400. Opt = case lists:keysearch(Key,1, Opts) of
  2401. {value, {Key,Value}} ->
  2402. Value;
  2403. false ->
  2404. Def
  2405. end,
  2406. qlc_opts(lists:keydelete(Key,1,Opts),Keys,[Opt|Acc]);
  2407. qlc_opts(Opts,[],Acc) -> {lists:reverse(Acc),Opts}.
  2408. qlc_info(Tab, num_of_objects) ->
  2409. dirty_rpc(Tab, ?MODULE, raw_table_info, [Tab, size]);
  2410. qlc_info(_, keypos) -> 2;
  2411. qlc_info(_, is_unique_objects) -> true;
  2412. qlc_info(Tab, is_unique_keys) ->
  2413. case val({Tab, type}) of
  2414. set -> true;
  2415. ordered_set -> true;
  2416. _ -> false
  2417. end;
  2418. qlc_info(Tab, is_sorted_objects) ->
  2419. case val({Tab, type}) of
  2420. ordered_set ->
  2421. case ?catch_val({Tab, frag_hash}) of
  2422. {'EXIT', _} ->
  2423. ascending;
  2424. _ -> %% Fragmented tables are not ordered
  2425. no
  2426. end;
  2427. _ -> no
  2428. end;
  2429. qlc_info(Tab, indices) ->
  2430. val({Tab,index});
  2431. qlc_info(_Tab, _) ->
  2432. undefined.
  2433. qlc_format(all, Tab, NObjects, Lock, Opts) ->
  2434. {?MODULE, table, [Tab,[{n_objects, NObjects}, {lock,Lock}|Opts]]};
  2435. qlc_format({match_spec, Ms}, Tab, NObjects, Lock, Opts) ->
  2436. {?MODULE, table, [Tab,[{traverse,{select,Ms}},{n_objects, NObjects}, {lock,Lock}|Opts]]};
  2437. qlc_format({lookup, 2, Keys}, Tab, _, Lock, _) ->
  2438. io_lib:format("lists:flatmap(fun(V) -> "
  2439. "~w:read(~w, V, ~w) end, ~w)",
  2440. [?MODULE, Tab, Lock, Keys]);
  2441. qlc_format({lookup, Index,Keys}, Tab, _, _, _) ->
  2442. io_lib:format("lists:flatmap(fun(V) -> "
  2443. "~w:index_read(~w, V, ~w) end, ~w)",
  2444. [?MODULE, Tab, Index, Keys]).
  2445. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2446. do_fixtable(Tab, #tidstore{store=Store}) ->
  2447. do_fixtable(Tab,Store);
  2448. do_fixtable(Tab, Store) ->
  2449. case ?catch_val({Tab, setorbag}) of
  2450. ordered_set ->
  2451. ok;
  2452. _ ->
  2453. case ?ets_match_object(Store, {fixtable, {Tab, '_'}}) of
  2454. [] ->
  2455. Node = dirty_rpc(Tab, mnesia_tm, fixtable, [Tab,true,self()]),
  2456. ?ets_insert(Store, {fixtable, {Tab, Node}});
  2457. _ ->
  2458. ignore
  2459. end,
  2460. ok
  2461. end.
  2462. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2463. %% Mnemosyne exclusive
  2464. get_activity_id() ->
  2465. get(mnesia_activity_state).
  2466. put_activity_id(Activity) ->
  2467. mnesia_tm:put_activity_id(Activity).
  2468. put_activity_id(Activity,Fun) ->
  2469. mnesia_tm:put_activity_id(Activity,Fun).