1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%% This module exports the public interface of the Mnesia DBMS engine
23
24-module(mnesia).
25%-behaviour(mnesia_access).
26
27-export([
28	 %% Start, stop and debugging
29	 start/0, start/1, stop/0,           % Not for public use
30	 set_debug_level/1, lkill/0, kill/0, % Not for public use
31	 ms/0,
32	 change_config/2,
33
34	 %% Activity mgt
35	 abort/1, transaction/1, transaction/2, transaction/3,
36	 sync_transaction/1, sync_transaction/2, sync_transaction/3,
37	 async_dirty/1, async_dirty/2, sync_dirty/1, sync_dirty/2, ets/1, ets/2,
38	 activity/2, activity/3, activity/4, % Not for public use
39	 is_transaction/0,
40
41	 %% Access within an activity - Lock acquisition
42	 lock/2, lock/4,
43	 lock_table/2,
44	 read_lock_table/1,
45	 write_lock_table/1,
46
47	 %% Access within an activity - Updates
48	 write/1, s_write/1, write/3, write/5,
49	 delete/1, s_delete/1, delete/3, delete/5,
50	 delete_object/1, s_delete_object/1, delete_object/3, delete_object/5,
51
52	 %% Access within an activity - Reads
53	 read/1, read/2, wread/1, read/3, read/5,
54	 match_object/1, match_object/3, match_object/5,
55	 select/1,select/2,select/3,select/4,select/5,select/6,
56	 all_keys/1, all_keys/4,
57	 index_match_object/2, index_match_object/4, index_match_object/6,
58	 index_read/3, index_read/6,
59	 first/1, next/2, last/1, prev/2,
60	 first/3, next/4, last/3, prev/4,
61
62	 %% Iterators within an activity
63	 foldl/3, foldl/4, foldr/3, foldr/4,
64
65	 %% Dirty access regardless of activities - Updates
66	 dirty_write/1, dirty_write/2,
67	 dirty_delete/1, dirty_delete/2,
68	 dirty_delete_object/1, dirty_delete_object/2,
69	 dirty_update_counter/2, dirty_update_counter/3,
70
71	 %% Dirty access regardless of activities - Read
72	 dirty_read/1, dirty_read/2,
73	 dirty_select/2,
74	 dirty_match_object/1, dirty_match_object/2, dirty_all_keys/1,
75	 dirty_index_match_object/2, dirty_index_match_object/3,
76	 dirty_index_read/3, dirty_slot/2,
77	 dirty_first/1, dirty_next/2, dirty_last/1, dirty_prev/2,
78
79	 %% Info
80	 table_info/2, table_info/4, schema/0, schema/1,
81	 error_description/1, info/0, system_info/1,
82	 system_info/0,                      % Not for public use
83
84	 %% Database mgt
85	 create_schema/1, create_schema/2, delete_schema/1,
86         add_backend_type/2,
87	 backup/1, backup/2, traverse_backup/4, traverse_backup/6,
88	 install_fallback/1, install_fallback/2,
89	 uninstall_fallback/0, uninstall_fallback/1,
90	 activate_checkpoint/1, deactivate_checkpoint/1,
91	 backup_checkpoint/2, backup_checkpoint/3, restore/2,
92
93	 %% Table mgt
94	 create_table/1, create_table/2, delete_table/1,
95	 add_table_copy/3, del_table_copy/2, move_table_copy/3,
96	 add_table_index/2, del_table_index/2,
97	 transform_table/3, transform_table/4,
98	 change_table_copy_type/3, change_table_majority/2,
99	 read_table_property/2, write_table_property/2, delete_table_property/2,
100	 change_table_frag/2,
101	 clear_table/1, clear_table/4,
102
103	 %% Table load
104	 dump_tables/1, wait_for_tables/2, force_load_table/1,
105	 change_table_access_mode/2, change_table_load_order/2,
106	 set_master_nodes/1, set_master_nodes/2,
107
108	 %% Misc admin
109	 dump_log/0, sync_log/0,
110	 subscribe/1, unsubscribe/1, report_event/1,
111
112	 %% Snmp
113	 snmp_open_table/2, snmp_close_table/1,
114	 snmp_get_row/2, snmp_get_next_index/2, snmp_get_mnesia_key/2,
115
116	 %% Textfile access
117	 load_textfile/1, dump_to_textfile/1,
118
119	 %% QLC functions
120	 table/1, table/2,
121
122	 %% Mnemosyne exclusive
123	 get_activity_id/0, put_activity_id/1, % Not for public use
124
125	 %% Mnesia internal functions
126	 dirty_rpc/4,                          % Not for public use
127	 has_var/1, fun_select/7, fun_select/10, select_cont/3, dirty_sel_init/5,
128	 foldl/6, foldr/6,
129
130	 %% Module internal callback functions
131	 raw_table_info/2,                      % Not for public use
132	 remote_dirty_match_object/2,           % Not for public use
133	 remote_dirty_select/2                  % Not for public use
134	]).
135
136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137
138-include("mnesia.hrl").
139-import(mnesia_lib, [verbose/2]).
140
141-type create_option() ::
142        {'access_mode', 'read_write' | 'read_only'} |
143        {'attributes', [atom()]} |
144        {'disc_copies', [node()]} |
145        {'disc_only_copies', [node()]} |
146        {'index', [index_attr()]} |
147        {'load_order', non_neg_integer()} |
148        {'majority', boolean()} |
149        {'ram_copies', [node()]} |
150        {'record_name', atom()} |
151        {'snmp', SnmpStruct::term()} |
152        {'storage_properties', [{Backend::module(), [BackendProp::_]}]} |
153        {'type', 'set' | 'ordered_set' | 'bag'} |
154        {'local_content', boolean()} |
155        {'user_properties', proplists:proplist()}.
156
157-type t_result(Res) :: {'atomic', Res} | {'aborted', Reason::term()}.
158-type result() :: 'ok' | {'error', Reason::term()}.
159-type activity() :: 'ets' | 'async_dirty' | 'sync_dirty' | 'transaction' | 'sync_transaction' |
160                    {'transaction', Retries::non_neg_integer()} |
161                    {'sync_transaction', Retries::non_neg_integer()}.
162-type table() :: atom().
163-type storage_type() :: 'ram_copies' | 'disc_copies' | 'disc_only_copies'.
164-type index_attr() :: atom() | non_neg_integer() | {atom()}.
165-type write_locks() :: 'write' | 'sticky_write'.
166-type read_locks() :: 'read'.
167-type lock_kind() :: write_locks() | read_locks().
168-type select_continuation() :: term().
169-type snmp_struct() :: [{atom(), snmp_type() | tuple_of(snmp_type())}].
170-type snmp_type() :: 'fix_string' | 'string' | 'integer'.
171-type tuple_of(_T) :: tuple().
172-type config_key() :: 'extra_db_nodes' | 'dc_dump_limit'.
173-type config_value() :: [node()] | number().
174-type config_result() :: {'ok', config_value()} | {'error', term()}.
175-type debug_level() :: 'none' | 'verbose' | 'debug' | 'trace'.
176
177-define(DEFAULT_ACCESS, ?MODULE).
178
179%% Select
180-define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]).
181-define(PATTERN_TO_BINDINGS_MATCH_SPEC(Pat), [{Pat,[],['$$']}]).
182
183%% Local function in order to avoid external function call
184val(Var) ->
185    case ?catch_val_and_stack(Var) of
186	{'EXIT', Stacktrace} -> mnesia_lib:other_val(Var, Stacktrace);
187	Value -> Value
188    end.
189
190is_dollar_digits(Var) ->
191    case atom_to_list(Var) of
192	[$$ | Digs] ->
193	    is_digits(Digs);
194	_ ->
195	    false
196    end.
197
198is_digits([Dig | Tail]) ->
199    if
200	$0 =< Dig, Dig =< $9 ->
201	    is_digits(Tail);
202	true ->
203	    false
204    end;
205is_digits([]) ->
206    true.
207
208has_var(X) when is_atom(X) ->
209    if
210	X == '_' ->
211	    true;
212	is_atom(X) ->
213	    is_dollar_digits(X);
214	true  ->
215	    false
216    end;
217has_var(X) when is_tuple(X) ->
218    e_has_var(X, tuple_size(X));
219has_var([H|T]) ->
220    case has_var(H) of
221	false -> has_var(T);
222	Other -> Other
223    end;
224has_var(_) -> false.
225
226e_has_var(_, 0) -> false;
227e_has_var(X, Pos) ->
228    case has_var(element(Pos, X))of
229	false -> e_has_var(X, Pos-1);
230	Other -> Other
231    end.
232
233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
234%% Start and stop
235-spec start() -> result().
236start() ->
237    start([]).
238
239start_() ->
240    {Time , Res} =  timer:tc(application, start, [?APPLICATION, temporary]),
241
242    Secs = Time div 1000000,
243    case Res of
244	ok ->
245	    verbose("Mnesia started, ~p seconds~n",[ Secs]),
246	    ok;
247	{error, {already_started, mnesia}} ->
248	    verbose("Mnesia already started, ~p seconds~n",[ Secs]),
249	    ok;
250	{error, R} ->
251	    verbose("Mnesia failed to start, ~p seconds: ~p~n",[ Secs, R]),
252	    {error, R}
253    end.
254
255-spec start([{Option::atom(), Value::_}]) -> result().
256start(ExtraEnv) when is_list(ExtraEnv) ->
257    case mnesia_lib:ensure_loaded(?APPLICATION) of
258	ok ->
259	    patched_start(ExtraEnv);
260	Error ->
261	    Error
262    end;
263start(ExtraEnv) ->
264    {error, {badarg, ExtraEnv}}.
265
266patched_start([{Env, Val} | Tail]) when is_atom(Env) ->
267    case mnesia_monitor:patch_env(Env, Val) of
268	{error, Reason} ->
269	    {error, Reason};
270	_NewVal ->
271	    patched_start(Tail)
272    end;
273patched_start([Head | _]) ->
274    {error, {bad_type, Head}};
275patched_start([]) ->
276    start_().
277
278-spec stop() -> 'stopped' | {'error', term()}.
279stop() ->
280    case application:stop(?APPLICATION) of
281	ok -> stopped;
282	{error, {not_started, ?APPLICATION}} -> stopped;
283	Other -> Other
284    end.
285
286-spec change_config(Config, Value) -> config_result() when
287      Config :: config_key(), Value :: config_value().
288change_config(extra_db_nodes, Ns) when is_list(Ns) ->
289    mnesia_controller:connect_nodes(Ns);
290change_config(dc_dump_limit, N) when is_number(N), N > 0 ->
291    case mnesia_lib:is_running() of
292	yes ->
293	    mnesia_lib:set(dc_dump_limit, N),
294	    {ok, N};
295	_ ->
296	    {error, {not_started, ?APPLICATION}}
297    end;
298change_config(BadKey, _BadVal) ->
299    {error, {badarg, BadKey}}.
300
301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
302%% Debugging
303
304
305-spec set_debug_level(Level  :: debug_level()) ->
306          OldLevel :: debug_level().
307
308set_debug_level(Level) ->
309    mnesia_subscr:set_debug_level(Level).
310
311lkill() ->
312    mnesia_sup:kill().
313
314kill() ->
315    rpc:multicall(mnesia_sup, kill, []).
316
317ms() ->
318    [
319     mnesia,
320     mnesia_app,
321     mnesia_backup,
322     mnesia_bup,
323     mnesia_checkpoint,
324     mnesia_checkpoint_sup,
325     mnesia_controller,
326     mnesia_dumper,
327     mnesia_loader,
328     mnesia_frag,
329     mnesia_frag_hash,
330     mnesia_index,
331     mnesia_kernel_sup,
332     mnesia_late_loader,
333     mnesia_lib,
334     mnesia_log,
335     mnesia_registry,
336     mnesia_schema,
337     mnesia_snmp_hook,
338     mnesia_snmp_sup,
339     mnesia_subscr,
340     mnesia_sup,
341     mnesia_text,
342     mnesia_tm,
343     mnesia_recover,
344     mnesia_locker,
345
346     %% Keep these last in the list, so
347     %% mnesia_sup kills these last
348     mnesia_ext_sup,
349     mnesia_monitor,
350     mnesia_event
351    ].
352
353
354%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
355%% Activity mgt
356
357-spec abort(_) -> no_return().
358abort(Reason = {aborted, _}) ->
359    exit(Reason);
360abort(Reason) ->
361    exit({aborted, Reason}).
362
363-spec is_transaction() -> boolean().
364is_transaction() ->
365    case get(mnesia_activity_state) of
366	{_, Tid, _Ts} when element(1,Tid) == tid ->
367	    true;
368	_ ->
369	    false
370    end.
371
372-spec transaction(Fun) -> t_result(Res) when
373      Fun :: fun(() -> Res).
374transaction(Fun) ->
375    transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, async).
376
377-spec transaction(Fun, Retries) -> t_result(Res) when
378      Fun :: fun(() -> Res),
379      Retries :: non_neg_integer() | 'infinity';
380                 (Fun, Args::[Arg::_]) -> t_result(Res) when
381      Fun :: fun((...) -> Res).
382transaction(Fun, Retries) when is_integer(Retries), Retries >= 0 ->
383    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
384transaction(Fun, Retries) when Retries == infinity ->
385    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
386transaction(Fun, Args) ->
387    transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, async).
388
389-spec transaction(Fun, [Arg::_], Retries) -> t_result(Res) when
390      Fun :: fun((...) -> Res),
391      Retries :: non_neg_integer() | 'infinity'.
392transaction(Fun, Args, Retries) ->
393    transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, async).
394
395-spec sync_transaction(Fun) -> t_result(Res) when
396      Fun :: fun(() -> Res).
397sync_transaction(Fun) ->
398    transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, sync).
399
400-spec sync_transaction(Fun, Retries) -> t_result(Res) when
401      Fun :: fun(() -> Res) | fun((...) -> Res),
402      Retries :: non_neg_integer() | 'infinity';
403                      (Fun, Args :: [Arg::_]) -> t_result(Res) when
404      Fun :: fun((...) -> Res).
405sync_transaction(Fun, Retries) when is_integer(Retries), Retries >= 0 ->
406    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
407sync_transaction(Fun, Retries) when Retries == infinity ->
408    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
409sync_transaction(Fun, Args) ->
410    transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, sync).
411
412-spec sync_transaction(Fun, [Arg::_], Retries) -> t_result(Res) when
413      Fun :: fun((...) -> Res),
414      Retries :: non_neg_integer() | 'infinity'.
415sync_transaction(Fun, Args, Retries) ->
416    transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, sync).
417
418transaction(State, Fun, Args, Retries, Mod, Kind)
419  when is_function(Fun), is_list(Args), Retries == infinity, is_atom(Mod) ->
420    mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
421transaction(State, Fun, Args, Retries, Mod, Kind)
422  when is_function(Fun), is_list(Args), is_integer(Retries), Retries >= 0, is_atom(Mod) ->
423    mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
424transaction(_State, Fun, Args, Retries, Mod, _Kind) ->
425    {aborted, {badarg, Fun, Args, Retries, Mod}}.
426
427non_transaction(State, Fun, Args, ActivityKind, Mod)
428  when is_function(Fun), is_list(Args), is_atom(Mod) ->
429    mnesia_tm:non_transaction(State, Fun, Args, ActivityKind, Mod);
430non_transaction(_State, Fun, Args, _ActivityKind, _Mod) ->
431    {aborted, {badarg, Fun, Args}}.
432
433-spec async_dirty(Fun) -> Res | no_return() when
434      Fun :: fun(() -> Res).
435async_dirty(Fun) ->
436    async_dirty(Fun, []).
437
438-spec async_dirty(Fun, [Arg::_]) -> Res | no_return() when
439      Fun :: fun((...) -> Res).
440async_dirty(Fun, Args) ->
441    non_transaction(get(mnesia_activity_state), Fun, Args, async_dirty, ?DEFAULT_ACCESS).
442
443-spec sync_dirty(Fun) -> Res | no_return() when
444      Fun :: fun(() -> Res).
445sync_dirty(Fun) ->
446    sync_dirty(Fun, []).
447
448-spec sync_dirty(Fun, [Arg::_]) -> Res | no_return() when
449      Fun :: fun((...) -> Res).
450sync_dirty(Fun, Args) ->
451    non_transaction(get(mnesia_activity_state), Fun, Args, sync_dirty, ?DEFAULT_ACCESS).
452
453-spec ets(Fun) -> Res | no_return() when
454      Fun :: fun(() -> Res).
455ets(Fun) ->
456    ets(Fun, []).
457
458-spec ets(Fun, [Arg::_]) -> Res | no_return() when
459      Fun :: fun((...) -> Res).
460ets(Fun, Args) ->
461    non_transaction(get(mnesia_activity_state), Fun, Args, ets, ?DEFAULT_ACCESS).
462
463-spec activity(Kind, Fun) -> t_result(Res) | Res when
464      Kind :: activity(),
465      Fun  :: fun(() -> Res).
466activity(Kind, Fun) ->
467    activity(Kind, Fun, []).
468
469-spec activity(Kind, Fun, [Arg::_]) -> t_result(Res) | Res when
470      Kind :: activity(),
471      Fun  :: fun((...) -> Res);
472              (Kind, Fun, Mod) -> t_result(Res) | Res when
473      Kind :: activity(),
474      Fun  :: fun(() -> Res),
475      Mod  :: atom().
476
477activity(Kind, Fun, Args) when is_list(Args) ->
478    activity(Kind, Fun, Args, mnesia_monitor:get_env(access_module));
479activity(Kind, Fun, Mod) ->
480    activity(Kind, Fun, [], Mod).
481
482-spec activity(Kind, Fun, [Arg::_], Mod) -> t_result(Res) | Res when
483      Kind :: activity(),
484      Fun  :: fun((...) -> Res),
485      Mod  :: atom().
486
487activity(Kind, Fun, Args, Mod) ->
488    State = get(mnesia_activity_state),
489    case Kind of
490	ets ->                    non_transaction(State, Fun, Args, Kind, Mod);
491	async_dirty ->            non_transaction(State, Fun, Args, Kind, Mod);
492	sync_dirty ->             non_transaction(State, Fun, Args, Kind, Mod);
493	transaction ->            wrap_trans(State, Fun, Args, infinity, Mod, async);
494	{transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, async);
495	sync_transaction ->            wrap_trans(State, Fun, Args, infinity, Mod, sync);
496	{sync_transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, sync);
497	_ ->                      {aborted, {bad_type, Kind}}
498    end.
499
500wrap_trans(State, Fun, Args, Retries, Mod, Kind) ->
501    case transaction(State, Fun, Args, Retries, Mod, Kind) of
502	{atomic, GoodRes} -> GoodRes;
503	BadRes -> exit(BadRes)
504    end.
505
506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507%% Access within an activity - lock acquisition
508
509%% Grab a lock on an item in the global lock table
510%% Item may be any term. Lock may be write or read.
511%% write lock is set on all the given nodes
512%% read lock is only set on the first node
513%% Nodes may either be a list of nodes or one node as an atom
514%% Mnesia on all Nodes must be connected to each other, but
515%% it is not neccessary that they are up and running.
516-spec lock(LockItem, LockKind) -> list() | tuple() | no_return() when
517      LockItem :: {'record', table(), Key::term()} |
518                  {'table',  table()} |
519                  {'global', Key::term(), MnesiaNodes::[node()]},
520      LockKind :: lock_kind() | 'load'.
521lock(LockItem, LockKind) ->
522    case get(mnesia_activity_state) of
523	{?DEFAULT_ACCESS, Tid, Ts} ->
524	    lock(Tid, Ts, LockItem, LockKind);
525	{Mod, Tid, Ts} ->
526	    Mod:lock(Tid, Ts, LockItem, LockKind);
527	_ ->
528	    abort(no_transaction)
529    end.
530
531-spec lock_table(Tab::table(), LockKind) -> [MnesiaNode] | no_return() when
532      MnesiaNode :: node(),
533      LockKind :: lock_kind() | 'load'.
534lock_table(Tab, LockKind) ->
535    lock({table, Tab}, LockKind).
536
537lock(Tid, Ts, LockItem, LockKind) ->
538    case element(1, Tid) of
539	tid ->
540	    case LockItem of
541		{record, Tab, Key} ->
542		    lock_record(Tid, Ts, Tab, Key, LockKind);
543		{table, Tab} ->
544		    lock_table(Tid, Ts, Tab, LockKind);
545		{global, GlobalKey, Nodes} ->
546		    global_lock(Tid, Ts, GlobalKey, LockKind, Nodes);
547		_ ->
548		    abort({bad_type, LockItem})
549	    end;
550	_Protocol ->
551	    []
552    end.
553
554%% Grab a read lock on a whole table
555-spec read_lock_table(Tab::table()) -> 'ok'.
556read_lock_table(Tab) ->
557    lock({table, Tab}, read),
558    ok.
559
560%% Grab a write lock on a whole table
561-spec write_lock_table(Tab::table()) -> 'ok'.
562write_lock_table(Tab) ->
563    lock({table, Tab}, write),
564    ok.
565
566lock_record(Tid, Ts, Tab, Key, LockKind) when is_atom(Tab) ->
567    Store = Ts#tidstore.store,
568    Oid =  {Tab, Key},
569    case LockKind of
570	read ->
571	    mnesia_locker:rlock(Tid, Store, Oid);
572	write ->
573	    mnesia_locker:wlock(Tid, Store, Oid);
574	sticky_write ->
575	    mnesia_locker:sticky_wlock(Tid, Store, Oid);
576	none ->
577	    [];
578	_ ->
579	    abort({bad_type, Tab, LockKind})
580    end;
581lock_record(_Tid, _Ts, Tab, _Key, _LockKind) ->
582    abort({bad_type, Tab}).
583
584lock_table(Tid, Ts, Tab, LockKind) when is_atom(Tab) ->
585    Store = Ts#tidstore.store,
586    case LockKind of
587	read ->
588	    mnesia_locker:rlock_table(Tid, Store, Tab);
589	write ->
590	    mnesia_locker:wlock_table(Tid, Store, Tab);
591	load ->
592	    mnesia_locker:load_lock_table(Tid, Store, Tab);
593	sticky_write ->
594	    mnesia_locker:sticky_wlock_table(Tid, Store, Tab);
595	none ->
596	    [];
597	_ ->
598	    abort({bad_type, Tab, LockKind})
599    end;
600lock_table(_Tid, _Ts, Tab, _LockKind) ->
601    abort({bad_type, Tab}).
602
603global_lock(Tid, Ts, Item, Kind, Nodes) when is_list(Nodes) ->
604    case element(1, Tid) of
605	tid ->
606	    Store = Ts#tidstore.store,
607	    GoodNs = good_global_nodes(Nodes),
608	    if
609		Kind /= read, Kind /= write ->
610		    abort({bad_type, Kind});
611		true ->
612		    mnesia_locker:global_lock(Tid, Store, Item, Kind, GoodNs)
613	    end;
614	_Protocol ->
615	    []
616    end;
617global_lock(_Tid, _Ts, _Item, _Kind, Nodes) ->
618    abort({bad_type, Nodes}).
619
620good_global_nodes(Nodes) ->
621    Recover = [node() | val(recover_nodes)],
622    mnesia_lib:intersect(Nodes, Recover).
623
624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625%% Access within an activity - updates
626-spec write(Record::tuple()) -> 'ok'.
627write(Val) when is_tuple(Val), tuple_size(Val) > 2 ->
628    Tab = element(1, Val),
629    write(Tab, Val, write);
630write(Val) ->
631    abort({bad_type, Val}).
632
633-spec s_write(Record::tuple()) -> 'ok'.
634s_write(Val) when is_tuple(Val), tuple_size(Val) > 2 ->
635    Tab = element(1, Val),
636    write(Tab, Val, sticky_write).
637
638-spec write(Tab::table(), Record::tuple(), LockKind::write_locks()) -> 'ok'.
639write(Tab, Val, LockKind) ->
640    case get(mnesia_activity_state) of
641	{?DEFAULT_ACCESS, Tid, Ts} ->
642	    write(Tid, Ts, Tab, Val, LockKind);
643	{Mod, Tid, Ts} ->
644	    Mod:write(Tid, Ts, Tab, Val, LockKind);
645	_ ->
646	    abort(no_transaction)
647    end.
648
649write(Tid, Ts, Tab, Val, LockKind)
650  when is_atom(Tab), Tab /= schema, is_tuple(Val), tuple_size(Val) > 2 ->
651    case element(1, Tid) of
652	ets ->
653	    ?ets_insert(Tab, Val),
654	    ok;
655	tid ->
656	    Store = Ts#tidstore.store,
657	    Oid = {Tab, element(2, Val)},
658	    case LockKind of
659		write ->
660		    mnesia_locker:wlock(Tid, Store, Oid);
661		sticky_write ->
662		    mnesia_locker:sticky_wlock(Tid, Store, Oid);
663		_ ->
664		    abort({bad_type, Tab, LockKind})
665	    end,
666	    write_to_store(Tab, Store, Oid, Val);
667	Protocol ->
668	    do_dirty_write(Protocol, Tab, Val)
669    end;
670write(_Tid, _Ts, Tab, Val, LockKind) ->
671    abort({bad_type, Tab, Val, LockKind}).
672
673write_to_store(Tab, Store, Oid, Val) ->
674    {_, _, Type} = mnesia_lib:validate_record(Tab, Val),
675    Oid = {Tab, element(2, Val)},
676    case Type of
677	bag ->
678	    ?ets_insert(Store, {Oid, Val, write});
679	_  ->
680	    ?ets_delete(Store, Oid),
681	    ?ets_insert(Store, {Oid, Val, write})
682    end,
683    ok.
684
685-spec delete({Tab::table(), Key::_}) -> 'ok'.
686delete({Tab, Key}) ->
687    delete(Tab, Key, write);
688delete(Oid) ->
689    abort({bad_type, Oid}).
690
691-spec s_delete({Tab::table(), Key::_}) -> 'ok'.
692s_delete({Tab, Key}) ->
693    delete(Tab, Key, sticky_write);
694s_delete(Oid) ->
695    abort({bad_type, Oid}).
696
697-spec delete(Tab::table(), Key::_, LockKind::write_locks()) -> 'ok'.
698delete(Tab, Key, LockKind) ->
699    case get(mnesia_activity_state) of
700	{?DEFAULT_ACCESS, Tid, Ts} ->
701	    delete(Tid, Ts, Tab, Key, LockKind);
702	{Mod, Tid, Ts} ->
703	    Mod:delete(Tid, Ts, Tab, Key, LockKind);
704	_ ->
705	    abort(no_transaction)
706    end.
707
708delete(Tid, Ts, Tab, Key, LockKind)
709  when is_atom(Tab), Tab /= schema ->
710      case element(1, Tid) of
711	  ets ->
712	      ?ets_delete(Tab, Key),
713	      ok;
714	  tid ->
715	      Store = Ts#tidstore.store,
716	      Oid = {Tab, Key},
717	      case LockKind of
718		  write ->
719		      mnesia_locker:wlock(Tid, Store, Oid);
720		  sticky_write ->
721		      mnesia_locker:sticky_wlock(Tid, Store, Oid);
722		  _ ->
723		      abort({bad_type, Tab, LockKind})
724	      end,
725	      ?ets_delete(Store, Oid),
726	      ?ets_insert(Store, {Oid, Oid, delete}),
727	      ok;
728	Protocol ->
729	      do_dirty_delete(Protocol, Tab, Key)
730    end;
731delete(_Tid, _Ts, Tab, _Key, _LockKind) ->
732    abort({bad_type, Tab}).
733
734-spec delete_object(Rec::tuple()) -> 'ok'.
735delete_object(Val) when is_tuple(Val), tuple_size(Val) > 2 ->
736    Tab = element(1, Val),
737    delete_object(Tab, Val, write);
738delete_object(Val) ->
739    abort({bad_type, Val}).
740
741-spec s_delete_object(Rec::tuple()) -> 'ok'.
742s_delete_object(Val) when is_tuple(Val), tuple_size(Val) > 2 ->
743    Tab = element(1, Val),
744    delete_object(Tab, Val, sticky_write);
745s_delete_object(Val) ->
746    abort({bad_type, Val}).
747
748-spec delete_object(Tab::table(), Rec::tuple(), LockKind::write_locks()) -> 'ok'.
749delete_object(Tab, Val, LockKind) ->
750    case get(mnesia_activity_state) of
751	{?DEFAULT_ACCESS, Tid, Ts} ->
752	    delete_object(Tid, Ts, Tab, Val, LockKind);
753	{Mod, Tid, Ts} ->
754	    Mod:delete_object(Tid, Ts, Tab, Val, LockKind);
755	_ ->
756	    abort(no_transaction)
757    end.
758
759delete_object(Tid, Ts, Tab, Val, LockKind)
760  when is_atom(Tab), Tab /= schema, is_tuple(Val), tuple_size(Val) > 2 ->
761    case has_var(Val) of
762	false ->
763	    do_delete_object(Tid, Ts, Tab, Val, LockKind);
764	true ->
765	    abort({bad_type, Tab, Val})
766    end;
767delete_object(_Tid, _Ts, Tab, _Key, _LockKind) ->
768    abort({bad_type, Tab}).
769
770do_delete_object(Tid, Ts, Tab, Val, LockKind) ->
771      case element(1, Tid) of
772	  ets ->
773	      ?ets_match_delete(Tab, Val),
774	      ok;
775	  tid ->
776	      Store = Ts#tidstore.store,
777	      Oid = {Tab, element(2, Val)},
778	      case LockKind of
779		  write ->
780		      mnesia_locker:wlock(Tid, Store, Oid);
781		  sticky_write ->
782		      mnesia_locker:sticky_wlock(Tid, Store, Oid);
783		  _ ->
784		      abort({bad_type, Tab, LockKind})
785	      end,
786	      case val({Tab, setorbag}) of
787		  bag ->
788		      ?ets_match_delete(Store, {Oid, Val, '_'}),
789		      ?ets_insert(Store, {Oid, Val, delete_object});
790		  _ ->
791		      case ?ets_match_object(Store, {Oid, '_', write}) ++
792                          ?ets_match_object(Store, {Oid, '_', delete}) of
793			      [] ->
794			          ?ets_match_delete(Store, {Oid, Val, '_'}),
795			          ?ets_insert(Store, {Oid, Val, delete_object});
796			      Ops  ->
797			          case lists:member({Oid, Val, write}, Ops) of
798			              true ->
799			                  ?ets_delete(Store, Oid),
800			                  ?ets_insert(Store, {Oid, Oid, delete});
801			              false -> ok
802			          end
803		      end
804	      end,
805	      ok;
806	Protocol ->
807	      do_dirty_delete_object(Protocol, Tab, Val)
808    end.
809
810%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
811%% Access within an activity - read
812
813-spec read(Tab::table(), Key::_) -> [tuple()].
814read(Tab, Key) ->
815    read(Tab, Key, read).
816
817-spec read({Tab::table(), Key::_}) -> [tuple()].
818read({Tab, Key}) ->
819    read(Tab, Key, read);
820read(Oid) ->
821    abort({bad_type, Oid}).
822
823-spec wread({Tab::table(), Key::_}) -> [tuple()].
824wread({Tab, Key}) ->
825    read(Tab, Key, write);
826wread(Oid) ->
827    abort({bad_type, Oid}).
828
829-spec read(Tab::table(), Key::_, LockKind::lock_kind()) -> [tuple()].
830read(Tab, Key, LockKind) ->
831    case get(mnesia_activity_state) of
832	{?DEFAULT_ACCESS, Tid, Ts} ->
833	    read(Tid, Ts, Tab, Key, LockKind);
834	{Mod, Tid, Ts} ->
835	    Mod:read(Tid, Ts, Tab, Key, LockKind);
836	_ ->
837	    abort(no_transaction)
838    end.
839
840read(Tid, Ts, Tab, Key, LockKind)
841  when is_atom(Tab), Tab /= schema ->
842    case element(1, Tid) of
843	ets ->
844	    ?ets_lookup(Tab, Key);
845	tid ->
846	    Store = Ts#tidstore.store,
847	    Oid = {Tab, Key},
848	    ObjsFun =
849                fun() ->
850                        case LockKind of
851                            read ->
852                                mnesia_locker:rlock(Tid, Store, Oid);
853                            write ->
854                                mnesia_locker:rwlock(Tid, Store, Oid);
855                            sticky_write ->
856                                mnesia_locker:sticky_rwlock(Tid, Store, Oid);
857                            _ ->
858                                abort({bad_type, Tab, LockKind})
859                        end
860                end,
861	    add_written(?ets_lookup(Store, Oid), Tab, ObjsFun, LockKind);
862	_Protocol ->
863	    dirty_read(Tab, Key)
864    end;
865read(_Tid, _Ts, Tab, _Key, _LockKind) ->
866    abort({bad_type, Tab}).
867
868-spec first(Tab::table()) -> Key::term().
869first(Tab) ->
870    case get(mnesia_activity_state) of
871	{?DEFAULT_ACCESS, Tid, Ts} ->
872	    first(Tid, Ts, Tab);
873	{Mod, Tid, Ts} ->
874	    Mod:first(Tid, Ts, Tab);
875	_ ->
876	    abort(no_transaction)
877    end.
878
879first(Tid, Ts, Tab)
880  when is_atom(Tab), Tab /= schema ->
881    case element(1, Tid) of
882	ets ->
883	    ?ets_first(Tab);
884	tid ->
885	    lock_table(Tid, Ts, Tab, read),
886	    do_fixtable(Tab,Ts),
887	    Key = dirty_first(Tab),
888	    stored_keys(Tab,Key,'$end_of_table',Ts,next,
889			val({Tab, setorbag}));
890	_Protocol ->
891	    dirty_first(Tab)
892    end;
893first(_Tid, _Ts,Tab) ->
894    abort({bad_type, Tab}).
895
896-spec last(Tab::table()) -> Key::term().
897last(Tab) ->
898    case get(mnesia_activity_state) of
899	{?DEFAULT_ACCESS, Tid, Ts} ->
900	    last(Tid, Ts, Tab);
901	{Mod, Tid, Ts} ->
902	    Mod:last(Tid, Ts, Tab);
903	_ ->
904	    abort(no_transaction)
905    end.
906
907last(Tid, Ts, Tab)
908  when is_atom(Tab), Tab /= schema ->
909    case element(1, Tid) of
910	ets ->
911	    ?ets_last(Tab);
912	tid ->
913	    lock_table(Tid, Ts, Tab, read),
914	    do_fixtable(Tab,Ts),
915	    Key = dirty_last(Tab),
916	    stored_keys(Tab,Key,'$end_of_table',Ts,prev,
917			val({Tab, setorbag}));
918	_Protocol ->
919	    dirty_last(Tab)
920    end;
921last(_Tid, _Ts,Tab) ->
922    abort({bad_type, Tab}).
923
924-spec next(Tab::table(), Key::term()) -> NextKey::term().
925next(Tab,Key) ->
926    case get(mnesia_activity_state) of
927	{?DEFAULT_ACCESS,Tid,Ts} ->
928	    next(Tid,Ts,Tab,Key);
929	{Mod,Tid,Ts} ->
930	    Mod:next(Tid,Ts,Tab,Key);
931	_ ->
932	    abort(no_transaction)
933    end.
934next(Tid,Ts,Tab,Key)
935  when is_atom(Tab), Tab /= schema ->
936    case element(1, Tid) of
937	ets ->
938	    ?ets_next(Tab,Key);
939	tid ->
940	    lock_table(Tid, Ts, Tab, read),
941	    do_fixtable(Tab,Ts),
942	    New = ?CATCH(dirty_next(Tab,Key)),
943	    stored_keys(Tab,New,Key,Ts,next,
944			val({Tab, setorbag}));
945	_Protocol ->
946	    dirty_next(Tab,Key)
947    end;
948next(_Tid, _Ts,Tab,_) ->
949    abort({bad_type, Tab}).
950
951-spec prev(Tab::table(), Key::term()) -> PrevKey::term().
952prev(Tab,Key) ->
953    case get(mnesia_activity_state) of
954	{?DEFAULT_ACCESS,Tid,Ts} ->
955	    prev(Tid,Ts,Tab,Key);
956	{Mod,Tid,Ts} ->
957	    Mod:prev(Tid,Ts,Tab,Key);
958	_ ->
959	    abort(no_transaction)
960    end.
961prev(Tid,Ts,Tab,Key)
962  when is_atom(Tab), Tab /= schema ->
963    case element(1, Tid) of
964	ets ->
965	    ?ets_prev(Tab,Key);
966	tid ->
967	    lock_table(Tid, Ts, Tab, read),
968	    do_fixtable(Tab,Ts),
969	    New = ?CATCH(dirty_prev(Tab,Key)),
970	    stored_keys(Tab,New,Key,Ts,prev,
971			val({Tab, setorbag}));
972	_Protocol ->
973	    dirty_prev(Tab,Key)
974    end;
975prev(_Tid, _Ts,Tab,_) ->
976    abort({bad_type, Tab}).
977
978%% Compensate for transaction written and/or deleted records
979stored_keys(Tab,'$end_of_table',Prev,Ts,Op,Type) ->
980    case ts_keys(Ts#tidstore.store,Tab,Op,Type,[]) of
981	[] -> '$end_of_table';
982	Keys when Type == ordered_set->
983	    get_ordered_tskey(Prev,Keys,Op);
984	Keys ->
985	    get_next_tskey(Prev,Keys,Tab)
986    end;
987stored_keys(Tab,{'EXIT',{aborted,R={badarg,[Tab,Key]}}},
988	    Key,#tidstore{store=Store},Op,Type) ->
989    %% Had to match on error, ouch..
990    case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
991	[] ->  abort(R);
992	Ops ->
993	    case lists:last(Ops) of
994		[delete] -> abort(R);
995		_ ->
996		    case ts_keys(Store,Tab,Op,Type,[]) of
997			[] -> '$end_of_table';
998			Keys -> get_next_tskey(Key,Keys,Tab)
999		    end
1000	    end
1001    end;
1002stored_keys(_,{'EXIT',{aborted,R}},_,_,_,_) ->
1003    abort(R);
1004stored_keys(Tab,Key,Prev,#tidstore{store=Store},Op,ordered_set) ->
1005    case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
1006	[] ->
1007	    Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
1008	    get_ordered_tskey(Prev,Keys,Op);
1009 	Ops ->
1010	    case lists:last(Ops) of
1011		[delete] ->
1012	 	    mnesia:Op(Tab,Key);
1013		_ ->
1014		    Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
1015		    get_ordered_tskey(Prev,Keys,Op)
1016	    end
1017    end;
1018stored_keys(Tab,Key,_,#tidstore{store=Store},Op,_) ->
1019    case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
1020	[] ->  Key;
1021 	Ops ->
1022	    case lists:last(Ops) of
1023		[delete] -> mnesia:Op(Tab,Key);
1024		_ ->      Key
1025	    end
1026    end.
1027
1028get_ordered_tskey('$end_of_table', [First|_],_) ->    First;
1029get_ordered_tskey(Prev, [First|_], next) when Prev < First -> First;
1030get_ordered_tskey(Prev, [First|_], prev) when Prev > First -> First;
1031get_ordered_tskey(Prev, [_|R],Op) ->  get_ordered_tskey(Prev,R,Op);
1032get_ordered_tskey(_, [],_) ->    '$end_of_table'.
1033
1034get_next_tskey(Key,Keys,Tab) ->
1035    Next =
1036	if Key == '$end_of_table' -> hd(Keys);
1037	   true ->
1038		case lists:dropwhile(fun(A) -> A /= Key end, Keys) of
1039		    [] -> hd(Keys); %% First stored key
1040		    [Key] -> '$end_of_table';
1041		    [Key,Next2|_] -> Next2
1042		end
1043	end,
1044    case Next of
1045	'$end_of_table' -> '$end_of_table';
1046	_ -> %% Really slow anybody got another solution??
1047	    case dirty_read(Tab, Next) of
1048		[] -> Next;
1049		_ ->
1050		    %% Updated value we already returned this key
1051		    get_next_tskey(Next,Keys,Tab)
1052	    end
1053    end.
1054
1055ts_keys(Store, Tab, Op, Type, Def) ->
1056    All = ?ets_match(Store, {{Tab,'$1'},'_','$2'}),
1057    Keys = ts_keys_1(All, Def),
1058    if
1059	Type == ordered_set, Op == prev ->
1060	    lists:reverse(lists:sort(Keys));
1061	Type == ordered_set ->
1062	    lists:sort(Keys);
1063	Op == next ->
1064	    lists:reverse(Keys);
1065	true ->
1066	    Keys
1067    end.
1068
1069ts_keys_1([[Key, write]|R], []) ->
1070    ts_keys_1(R, [Key]);
1071ts_keys_1([[Key, write]|R], Acc=[Key|_]) ->
1072    ts_keys_1(R, Acc);
1073ts_keys_1([[Key, write]|R], Acc) ->
1074    ts_keys_1(R, [Key|Acc]);
1075ts_keys_1([[Key, delete]|R], [Key|Acc]) ->
1076    ts_keys_1(R, Acc);
1077ts_keys_1([_|R], Acc) ->
1078    ts_keys_1(R, Acc);
1079ts_keys_1([], Acc) ->
1080    Acc.
1081
1082
1083%%%%%%%%%%%%%%%%%%%%%
1084%% Iterators
1085
1086-spec foldl(Fun, Acc0, Tab::table()) -> Acc when
1087      Fun::fun((Record::tuple(), Acc0) -> Acc).
1088foldl(Fun, Acc, Tab) ->
1089    foldl(Fun, Acc, Tab, read).
1090
1091foldl(Fun, Acc, Tab, LockKind) when is_function(Fun) ->
1092    case get(mnesia_activity_state) of
1093	{?DEFAULT_ACCESS, Tid, Ts} ->
1094	    foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
1095	{Mod, Tid, Ts} ->
1096	    Mod:foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
1097	_ ->
1098	    abort(no_transaction)
1099    end.
1100
1101foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
1102    {Type, Prev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
1103    Res = ?CATCH(do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)),
1104    close_iteration(Res, Tab).
1105
1106do_foldl(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
1107    lists:foldl(fun(Key, Acc) ->
1108			lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
1109		end, RAcc, Stored);
1110do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
1111    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1112    {_, Tid, Ts} = get(mnesia_activity_state),
1113    do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, Stored);
1114do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
1115    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
1116    {_, Tid, Ts} = get(mnesia_activity_state),
1117    do_foldl(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
1118do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
1119    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1120    {_, Tid, Ts} = get(mnesia_activity_state),
1121    do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
1122do_foldl(A, O, Tab, Key, Fun, Acc, Type, Stored) ->  %% Type is set or bag
1123    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1124    NewStored = ordsets:del_element(Key, Stored),
1125    {_, Tid, Ts} = get(mnesia_activity_state),
1126    do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, Type, NewStored).
1127
1128-spec foldr(Fun, Acc0, Tab::table()) -> Acc when
1129      Fun::fun((Record::tuple(), Acc0) -> Acc).
1130foldr(Fun, Acc, Tab) ->
1131    foldr(Fun, Acc, Tab, read).
1132foldr(Fun, Acc, Tab, LockKind) when is_function(Fun) ->
1133    case get(mnesia_activity_state) of
1134	{?DEFAULT_ACCESS, Tid, Ts} ->
1135	    foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
1136	{Mod, Tid, Ts} ->
1137	    Mod:foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
1138	_ ->
1139	    abort(no_transaction)
1140    end.
1141
1142foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
1143    {Type, TempPrev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
1144    Prev =
1145	if
1146	    Type == ordered_set ->
1147		lists:reverse(TempPrev);
1148	    true ->      %% Order doesn't matter for set and bag
1149		TempPrev %% Keep the order so we can use ordsets:del_element
1150	end,
1151    Res = ?CATCH(do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)),
1152    close_iteration(Res, Tab).
1153
1154do_foldr(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
1155    lists:foldl(fun(Key, Acc) ->
1156			lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
1157		end, RAcc, Stored);
1158do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
1159    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1160    {_, Tid, Ts} = get(mnesia_activity_state),
1161    do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, Stored);
1162do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
1163    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
1164    {_, Tid, Ts} = get(mnesia_activity_state),
1165    do_foldr(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
1166do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
1167    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1168    {_, Tid, Ts} = get(mnesia_activity_state),
1169    do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
1170do_foldr(A, O, Tab, Key, Fun, Acc, Type, Stored) ->  %% Type is set or bag
1171    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1172    NewStored = ordsets:del_element(Key, Stored),
1173    {_, Tid, Ts} = get(mnesia_activity_state),
1174    do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, Type, NewStored).
1175
1176init_iteration(ActivityId, Opaque, Tab, LockKind) ->
1177    lock(ActivityId, Opaque, {table, Tab}, LockKind),
1178    Type = val({Tab, setorbag}),
1179    Previous = add_previous(ActivityId, Opaque, Type, Tab),
1180    St = val({Tab, storage_type}),
1181    if
1182	St == unknown ->
1183	    ignore;
1184	true ->
1185	    mnesia_lib:db_fixtable(St, Tab, true)
1186    end,
1187    {Type, Previous}.
1188
1189close_iteration(Res, Tab) ->
1190    case val({Tab, storage_type}) of
1191	unknown ->
1192	    ignore;
1193	St ->
1194	    mnesia_lib:db_fixtable(St, Tab, false)
1195    end,
1196    case Res of
1197	{'EXIT', {aborted, What}} ->
1198	   abort(What);
1199	{'EXIT', What} ->
1200	    abort(What);
1201	_ ->
1202	    Res
1203    end.
1204
1205add_previous(_ActivityId, non_transaction, _Type, _Tab) ->
1206    [];
1207add_previous(_Tid, Ts, _Type, Tab) ->
1208    Previous = ?ets_match(Ts#tidstore.store, {{Tab, '$1'}, '_', write}),
1209    lists:sort(lists:concat(Previous)).
1210
1211%% This routine fixes up the return value from read/1 so that
1212%% it is correct with respect to what this particular transaction
1213%% has already written, deleted .... etc
1214%% The actual read from the table is not done if not needed due to local
1215%% transaction context, and if so, no extra read lock is needed either.
1216
1217add_written([], _Tab, ObjsFun, _LockKind) ->
1218    ObjsFun();  % standard normal fast case
1219add_written(Written, Tab, ObjsFun, LockKind) ->
1220    case val({Tab, setorbag}) of
1221	bag ->
1222	    add_written_to_bag(Written, ObjsFun(), []);
1223        _ when LockKind == read;
1224               LockKind == write ->
1225	    add_written_to_set(Written, ObjsFun);
1226	_   ->
1227            %% Fall back to request new lock and read from source
1228	    add_written_to_set(Written, ObjsFun())
1229    end.
1230
1231add_written_to_set(Ws, ObjsOrFun) ->
1232    case lists:last(Ws) of
1233	{_, _, delete} -> [];
1234	{_, Val, write} -> [Val];
1235	{Oid, _, delete_object} ->
1236            %% May be several 'delete_object' in Ws; need to check if any
1237            %% deleted Val exists in source table; if not return whatever
1238            %% is/is not in the source table (ie as the Val is only deleted
1239            %% if matched at commit this needs to be reflected here)
1240            [Val || Val <- get_objs(ObjsOrFun),
1241                    not lists:member({Oid, Val, delete_object}, Ws)]
1242    end.
1243
1244get_objs(ObjsFun) when is_function(ObjsFun) -> ObjsFun();
1245get_objs(Objs) when is_list(Objs)           -> Objs.
1246
1247add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
1248    add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
1249add_written_to_bag([], Objs, Ack) ->
1250    Objs ++ lists:reverse(Ack); %% Oldest write first as in ets
1251add_written_to_bag([{_, _ , delete} | Tail], _Objs, _Ack) ->
1252    %% This transaction just deleted all objects
1253    %% with this key
1254    add_written_to_bag(Tail, [], []);
1255add_written_to_bag([{_, Val, delete_object} | Tail], Objs, Ack) ->
1256    add_written_to_bag(Tail, lists:delete(Val, Objs), lists:delete(Val, Ack)).
1257
1258-spec match_object(Pattern::tuple()) -> [Record::tuple()].
1259match_object(Pat) when is_tuple(Pat), tuple_size(Pat) > 2 ->
1260    Tab = element(1, Pat),
1261    match_object(Tab, Pat, read);
1262match_object(Pat) ->
1263    abort({bad_type, Pat}).
1264
1265-spec match_object(Tab,Pattern,LockKind) -> [Record] when
1266      Tab::table(),Pattern::tuple(),LockKind::lock_kind(),Record::tuple().
1267match_object(Tab, Pat, LockKind) ->
1268    case get(mnesia_activity_state) of
1269	{?DEFAULT_ACCESS, Tid, Ts} ->
1270	    match_object(Tid, Ts, Tab, Pat, LockKind);
1271	{Mod, Tid, Ts} ->
1272	    Mod:match_object(Tid, Ts, Tab, Pat, LockKind);
1273	_ ->
1274	    abort(no_transaction)
1275    end.
1276
1277match_object(Tid, Ts, Tab, Pat, LockKind)
1278  when is_atom(Tab), Tab /= schema, is_tuple(Pat), tuple_size(Pat) > 2 ->
1279    case element(1, Tid) of
1280	ets ->
1281	    mnesia_lib:db_match_object(ram_copies, Tab, Pat);
1282	tid ->
1283	    Key = element(2, Pat),
1284	    case has_var(Key) of
1285		false -> lock_record(Tid, Ts, Tab, Key, LockKind);
1286		true  -> lock_table(Tid, Ts, Tab, LockKind)
1287	    end,
1288	    Objs = dirty_match_object(Tab, Pat),
1289	    add_written_match(Ts#tidstore.store, Pat, Tab, Objs);
1290	_Protocol ->
1291	    dirty_match_object(Tab, Pat)
1292    end;
1293match_object(_Tid, _Ts, Tab, Pat, _LockKind) ->
1294    abort({bad_type, Tab, Pat}).
1295
1296add_written_index(Store, Pos, Tab, Key, Objs) when is_integer(Pos) ->
1297    Pat = setelement(Pos, val({Tab, wild_pattern}), Key),
1298    add_written_match(Store, Pat, Tab, Objs);
1299add_written_index(Store, Pos, Tab, Key, Objs) when is_tuple(Pos) ->
1300    IxF = mnesia_index:index_vals_f(val({Tab, storage_type}), Tab, Pos),
1301    Ops = find_ops(Store, Tab, '_'),
1302    add_ix_match(Ops, Objs, IxF, Key, val({Tab, setorbag})).
1303
1304add_written_match(S, Pat, Tab, Objs) ->
1305    Ops = find_ops(S, Tab, Pat),
1306    FixedRes = add_match(Ops, Objs, val({Tab, setorbag})),
1307    MS = ets:match_spec_compile([{Pat, [], ['$_']}]),
1308    ets:match_spec_run(FixedRes, MS).
1309
1310find_ops(S, Tab, Pat) ->
1311    GetWritten = [{{{Tab, '_'}, '_', write}, [], ['$_']},
1312		  {{{Tab, '_'}, '_', delete}, [], ['$_']},
1313		  {{{Tab, '_'}, Pat, delete_object}, [], ['$_']}],
1314    ets:select(S, GetWritten).
1315
1316add_match([], Objs, _Type) ->
1317    Objs;
1318add_match(Written, Objs, ordered_set) ->
1319    %% Must use keysort which is stable
1320    add_ordered_match(lists:keysort(1,Written), Objs, []);
1321add_match([{Oid, _, delete}|R], Objs, Type) ->
1322    add_match(R, deloid(Oid, Objs), Type);
1323add_match([{_Oid, Val, delete_object}|R], Objs, Type) ->
1324    add_match(R, lists:delete(Val, Objs), Type);
1325add_match([{_Oid, Val, write}|R], Objs, bag) ->
1326    add_match(R, [Val | lists:delete(Val, Objs)], bag);
1327add_match([{Oid, Val, write}|R], Objs, set) ->
1328    add_match(R, [Val | deloid(Oid,Objs)],set).
1329
1330add_ix_match([], Objs, _IxF, _Key, _Type) ->
1331    Objs;
1332add_ix_match(Written, Objs, IxF, Key, ordered_set) ->
1333    %% Must use keysort which is stable
1334    add_ordered_match(lists:keysort(1, ix_filter_ops(IxF, Key, Written)), Objs, []);
1335add_ix_match([{Oid, _, delete}|R], Objs, IxF, Key, Type) ->
1336    add_ix_match(R, deloid(Oid, Objs), IxF, Key, Type);
1337add_ix_match([{_Oid, Val, delete_object}|R], Objs, IxF, Key, Type) ->
1338    case ix_match(Val, IxF, Key) of
1339        true ->
1340            add_ix_match(R, lists:delete(Val, Objs), IxF, Key, Type);
1341        false ->
1342            add_ix_match(R, Objs, IxF, Key, Type)
1343    end;
1344add_ix_match([{_Oid, Val, write}|R], Objs, IxF, Key, bag) ->
1345    case ix_match(Val, IxF, Key) of
1346        true ->
1347            add_ix_match(R, [Val | lists:delete(Val, Objs)], IxF, Key, bag);
1348        false ->
1349            add_ix_match(R, Objs, IxF, Key, bag)
1350    end;
1351add_ix_match([{Oid, Val, write}|R], Objs, IxF, Key, set) ->
1352    case ix_match(Val, IxF, Key) of
1353        true ->
1354            add_ix_match(R, [Val | deloid(Oid,Objs)],IxF,Key,set);
1355        false ->
1356            add_ix_match(R, Objs, IxF, Key, set)
1357    end.
1358
1359ix_match(Val, IxF, Key) ->
1360    lists:member(Key, IxF(Val)).
1361
1362ix_filter_ops(IxF, Key, Ops) ->
1363    lists:filter(
1364      fun({_Oid, Obj, write}) ->
1365              ix_match(Obj, IxF, Key);
1366         (_) ->
1367              true
1368      end, Ops).
1369
1370%% For ordered_set only !!
1371add_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs], Acc)
1372  when Key > element(2, Obj) ->
1373    add_ordered_match(Written, Objs, [Obj|Acc]);
1374add_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_], Acc)
1375  when Key < element(2, Obj) ->
1376    add_ordered_match(Rest, [Val|Objs],Acc);
1377add_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
1378  when Key < element(2, Obj) ->
1379    add_ordered_match(Rest,Objs,Acc);
1380%% Greater than last object
1381add_ordered_match([{_, Val, write}|Rest], [], Acc) ->
1382    add_ordered_match(Rest, [Val], Acc);
1383add_ordered_match([_|Rest], [], Acc) ->
1384    add_ordered_match(Rest, [], Acc);
1385%% Keys are equal from here
1386add_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
1387    add_ordered_match(Rest, [Val|Objs], Acc);
1388add_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
1389    add_ordered_match(Rest, Objs, Acc);
1390add_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
1391    add_ordered_match(Rest, Objs, Acc);
1392add_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
1393    add_ordered_match(Rest, Objs, Acc);
1394add_ordered_match([], Objs, Acc) ->
1395    lists:reverse(Acc, Objs).
1396
1397%% For select chunk
1398add_sel_match(Sorted, Objs, ordered_set) ->
1399    add_sel_ordered_match(Sorted, Objs, []);
1400add_sel_match(Written, Objs, Type) ->
1401    add_sel_match(Written, Objs, Type, []).
1402
1403add_sel_match([], Objs, _Type, Acc) ->
1404    {Objs,lists:reverse(Acc)};
1405add_sel_match([Op={Oid, _, delete}|R], Objs, Type, Acc) ->
1406    case deloid(Oid, Objs) of
1407	Objs ->
1408	    add_sel_match(R, Objs, Type, [Op|Acc]);
1409	NewObjs when Type == set ->
1410	    add_sel_match(R, NewObjs, Type, Acc);
1411	NewObjs ->  %% If bag we may get more in next chunk
1412	    add_sel_match(R, NewObjs, Type, [Op|Acc])
1413    end;
1414add_sel_match([Op = {_Oid, Val, delete_object}|R], Objs, Type, Acc) ->
1415    case lists:delete(Val, Objs) of
1416	Objs ->
1417	    add_sel_match(R, Objs, Type, [Op|Acc]);
1418	NewObjs when Type == set ->
1419	    add_sel_match(R, NewObjs, Type, Acc);
1420	NewObjs ->
1421	    add_sel_match(R, NewObjs, Type, [Op|Acc])
1422    end;
1423add_sel_match([Op={Oid={_,Key}, Val, write}|R], Objs, bag, Acc) ->
1424    case lists:keymember(Key, 2, Objs) of
1425	true ->
1426	    add_sel_match(R,[Val|lists:delete(Val,Objs)],bag,
1427			  [{Oid,Val,delete_object}|Acc]);
1428	false ->
1429	    add_sel_match(R,Objs,bag,[Op|Acc])
1430    end;
1431add_sel_match([Op={Oid, Val, write}|R], Objs, set, Acc) ->
1432    case deloid(Oid,Objs) of
1433	Objs ->
1434	    add_sel_match(R, Objs,set, [Op|Acc]);
1435	NewObjs ->
1436	    add_sel_match(R, [Val | NewObjs],set, Acc)
1437    end.
1438
1439%% For ordered_set only !!
1440add_sel_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs],Acc)
1441  when Key > element(2, Obj) ->
1442    add_sel_ordered_match(Written, Objs, [Obj|Acc]);
1443add_sel_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_],Acc)
1444  when Key < element(2, Obj) ->
1445    add_sel_ordered_match(Rest,[Val|Objs],Acc);
1446add_sel_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
1447  when Key < element(2, Obj) ->
1448    add_sel_ordered_match(Rest,Objs,Acc);
1449%% Greater than last object
1450add_sel_ordered_match(Ops1, [], Acc) ->
1451    {lists:reverse(Acc), Ops1};
1452%% Keys are equal from here
1453add_sel_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
1454    add_sel_ordered_match(Rest, [Val|Objs], Acc);
1455add_sel_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
1456    add_sel_ordered_match(Rest, Objs, Acc);
1457add_sel_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
1458    add_sel_ordered_match(Rest, Objs, Acc);
1459add_sel_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
1460    add_sel_ordered_match(Rest, Objs, Acc);
1461add_sel_ordered_match([], Objs, Acc) ->
1462    {lists:reverse(Acc, Objs),[]}.
1463
1464
1465deloid(_Oid, []) ->
1466    [];
1467deloid({Tab, Key}, [H | T]) when element(2, H) == Key ->
1468    deloid({Tab, Key}, T);
1469deloid(Oid, [H | T]) ->
1470    [H | deloid(Oid, T)].
1471
1472%%%%%%%%%%%%%%%%%%
1473% select
1474-spec select(Tab, Spec) -> [Match] when
1475      Tab::table(), Spec::ets:match_spec(), Match::term().
1476select(Tab, Pat) ->
1477    select(Tab, Pat, read).
1478-spec select(Tab, Spec, LockKind) -> [Match] when
1479      Tab::table(), Spec::ets:match_spec(),
1480      Match::term(),LockKind::lock_kind().
1481select(Tab, Pat, LockKind)
1482  when is_atom(Tab), Tab /= schema, is_list(Pat) ->
1483    case get(mnesia_activity_state) of
1484	{?DEFAULT_ACCESS, Tid, Ts} ->
1485	    select(Tid, Ts, Tab, Pat, LockKind);
1486	{Mod, Tid, Ts} ->
1487	    Mod:select(Tid, Ts, Tab, Pat, LockKind);
1488	_ ->
1489	    abort(no_transaction)
1490    end;
1491select(Tab, Pat, _Lock) ->
1492    abort({badarg, Tab, Pat}).
1493
1494select(Tid, Ts, Tab, Spec, LockKind) ->
1495    SelectFun = fun(FixedSpec) -> dirty_select(Tab, FixedSpec) end,
1496    fun_select(Tid, Ts, Tab, Spec, LockKind, Tab, SelectFun).
1497
1498fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, SelectFun) ->
1499    case element(1, Tid) of
1500	ets ->
1501	    mnesia_lib:db_select(ram_copies, Tab, Spec);
1502	tid ->
1503	    select_lock(Tid,Ts,LockKind,Spec,Tab),
1504	    Store = Ts#tidstore.store,
1505	    Written = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
1506	    case Written of
1507		[] ->
1508		    %% Nothing changed in the table during this transaction,
1509		    %% Simple case get results from [d]ets
1510		    SelectFun(Spec);
1511		_ ->
1512		    %% Hard (slow case) records added or deleted earlier
1513		    %% in the transaction, have to cope with that.
1514		    Type = val({Tab, setorbag}),
1515		    FixedSpec = get_record_pattern(Spec),
1516		    TabRecs = SelectFun(FixedSpec),
1517		    FixedRes = add_match(Written, TabRecs, Type),
1518		    CMS = ets:match_spec_compile(Spec),
1519		    ets:match_spec_run(FixedRes, CMS)
1520	    end;
1521	_Protocol ->
1522	    SelectFun(Spec)
1523    end.
1524
1525select_lock(Tid,Ts,LockKind,Spec,Tab) ->
1526    %% Avoid table lock if possible
1527    case Spec of
1528	[{HeadPat,_, _}] when is_tuple(HeadPat), tuple_size(HeadPat) > 2 ->
1529	    Key = element(2, HeadPat),
1530	    case has_var(Key) of
1531		false -> lock_record(Tid, Ts, Tab, Key, LockKind);
1532		true  -> lock_table(Tid, Ts, Tab, LockKind)
1533	    end;
1534	_ ->
1535	    lock_table(Tid, Ts, Tab, LockKind)
1536    end.
1537
1538%% Breakable Select
1539-spec select(Tab, Spec, N, LockKind) -> {[Match], Cont} | '$end_of_table' when
1540      Tab::table(), Spec::ets:match_spec(),
1541      Match::term(), N::non_neg_integer(),
1542      LockKind::lock_kind(),
1543      Cont::select_continuation().
1544select(Tab, Pat, NObjects, LockKind)
1545  when is_atom(Tab), Tab /= schema, is_list(Pat), is_integer(NObjects) ->
1546    case get(mnesia_activity_state) of
1547	{?DEFAULT_ACCESS, Tid, Ts} ->
1548	    select(Tid, Ts, Tab, Pat, NObjects, LockKind);
1549	{Mod, Tid, Ts} ->
1550	    Mod:select(Tid, Ts, Tab, Pat, NObjects, LockKind);
1551	_ ->
1552	    abort(no_transaction)
1553    end;
1554select(Tab, Pat, NObjects, _Lock) ->
1555    abort({badarg, Tab, Pat, NObjects}).
1556
1557select(Tid, Ts, Tab, Spec, NObjects, LockKind) ->
1558    Where = val({Tab,where_to_read}),
1559    Type = mnesia_lib:storage_type_at_node(Where,Tab),
1560    InitFun = fun(FixedSpec) -> dirty_sel_init(Where,Tab,FixedSpec,NObjects,Type) end,
1561    fun_select(Tid,Ts,Tab,Spec,LockKind,Tab,InitFun,NObjects,Where,Type).
1562
1563-record(mnesia_select, {tab,tid,node,storage,cont,written=[],spec,type,orig}).
1564
1565fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, Init, NObjects, Node, Storage) ->
1566    Def = #mnesia_select{tid=Tid,node=Node,storage=Storage,tab=Tab,orig=Spec},
1567    case element(1, Tid) of
1568	ets ->
1569	    select_state(mnesia_lib:db_select_init(ram_copies,Tab,Spec,NObjects),Def);
1570	tid ->
1571	    select_lock(Tid,Ts,LockKind,Spec,Tab),
1572	    Store = Ts#tidstore.store,
1573	    do_fixtable(Tab, Store),
1574
1575	    Written0 = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
1576	    case Written0 of
1577		[] ->
1578		    %% Nothing changed in the table during this transaction,
1579		    %% Simple case get results from [d]ets
1580		    select_state(Init(Spec),Def);
1581		_ ->
1582		    %% Hard (slow case) records added or deleted earlier
1583		    %% in the transaction, have to cope with that.
1584		    Type = val({Tab, setorbag}),
1585		    Written =
1586			if Type == ordered_set -> %% Sort stable
1587				lists:keysort(1,Written0);
1588			   true ->
1589				Written0
1590			end,
1591		    FixedSpec = get_record_pattern(Spec),
1592		    CMS = ets:match_spec_compile(Spec),
1593		    trans_select(Init(FixedSpec),
1594				 Def#mnesia_select{written=Written,spec=CMS,type=Type, orig=FixedSpec})
1595	    end;
1596	_Protocol ->
1597	    select_state(Init(Spec),Def)
1598    end.
1599
1600-spec select(Cont) -> {[Match], Cont} | '$end_of_table' when
1601      Match::term(),
1602      Cont::select_continuation().
1603select(Cont) ->
1604    case get(mnesia_activity_state) of
1605	{?DEFAULT_ACCESS, Tid, Ts} ->
1606	    select_cont(Tid,Ts,Cont);
1607	{Mod, Tid, Ts} ->
1608	    Mod:select_cont(Tid,Ts,Cont);
1609	_ ->
1610	    abort(no_transaction)
1611    end.
1612
1613select_cont(_Tid,_Ts,'$end_of_table') ->
1614    '$end_of_table';
1615select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid,cont=Cont, orig=Ms})
1616  when element(1,Tid) == ets ->
1617    case Cont of
1618	'$end_of_table' -> '$end_of_table';
1619	_ -> select_state(mnesia_lib:db_select_cont(ram_copies,Cont,Ms),State)
1620    end;
1621select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) ->
1622    select_state(dirty_sel_cont(State),State);
1623select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid})  ->
1624    trans_select(dirty_sel_cont(State), State);
1625select_cont(Tid2,_,#mnesia_select{tid=_Tid1})
1626  when element(1,Tid2) == tid ->  % Mismatching tids
1627    abort(wrong_transaction);
1628select_cont(Tid,Ts,State=#mnesia_select{}) ->
1629    % Repair mismatching tids in non-transactional contexts
1630    RepairedState = State#mnesia_select{tid = Tid, written = [],
1631                                        spec = undefined, type = undefined},
1632    select_cont(Tid,Ts,RepairedState);
1633select_cont(_,_,Cont) ->
1634    abort({badarg, Cont}).
1635
1636trans_select('$end_of_table', #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
1637    Written = add_match(Written0, [], Type),
1638    {ets:match_spec_run(Written, CMS), '$end_of_table'};
1639trans_select({TabRecs,Cont}, State = #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
1640    {FixedRes,Written} = add_sel_match(Written0, TabRecs, Type),
1641    select_state({ets:match_spec_run(FixedRes, CMS),Cont},
1642		 State#mnesia_select{written=Written}).
1643
1644select_state({Matches, Cont}, MS) ->
1645    {Matches, MS#mnesia_select{cont=Cont}};
1646select_state('$end_of_table',_) -> '$end_of_table'.
1647
1648get_record_pattern([]) ->    [];
1649get_record_pattern([{M,C,_B}|R]) ->
1650    [{M,C,['$_']} | get_record_pattern(R)].
1651
1652-spec all_keys(Tab::table()) -> [Key::term()].
1653all_keys(Tab) ->
1654    case get(mnesia_activity_state) of
1655	{?DEFAULT_ACCESS, Tid, Ts} ->
1656	    all_keys(Tid, Ts, Tab, read);
1657	{Mod, Tid, Ts} ->
1658	    Mod:all_keys(Tid, Ts, Tab, read);
1659	_ ->
1660	    abort(no_transaction)
1661    end.
1662
1663all_keys(Tid, Ts, Tab, LockKind)
1664  when is_atom(Tab), Tab /= schema ->
1665    Pat0 = val({Tab, wild_pattern}),
1666    Pat = setelement(2, Pat0, '$1'),
1667    Keys = select(Tid, Ts, Tab, [{Pat, [], ['$1']}], LockKind),
1668    case val({Tab, setorbag}) of
1669	bag ->
1670	    mnesia_lib:uniq(Keys);
1671	_ ->
1672	    Keys
1673    end;
1674all_keys(_Tid, _Ts, Tab, _LockKind) ->
1675    abort({bad_type, Tab}).
1676
1677-spec index_match_object(Pattern, Attr) -> [Record] when
1678      Pattern::tuple(), Attr::index_attr(), Record::tuple().
1679index_match_object(Pat, Attr) when is_tuple(Pat), tuple_size(Pat) > 2 ->
1680    Tab = element(1, Pat),
1681    index_match_object(Tab, Pat, Attr, read);
1682index_match_object(Pat, _Attr) ->
1683    abort({bad_type, Pat}).
1684
1685-spec index_match_object(Tab, Pattern, Attr, LockKind) -> [Record] when
1686      Tab::table(),
1687      Pattern::tuple(),
1688      Attr::index_attr(),
1689      LockKind::lock_kind(),
1690      Record::tuple().
1691index_match_object(Tab, Pat, Attr, LockKind) ->
1692    case get(mnesia_activity_state) of
1693	{?DEFAULT_ACCESS, Tid, Ts} ->
1694	    index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
1695	{Mod, Tid, Ts} ->
1696	    Mod:index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
1697	_ ->
1698	    abort(no_transaction)
1699    end.
1700
1701index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind)
1702  when is_atom(Tab), Tab /= schema, is_tuple(Pat), tuple_size(Pat) > 2 ->
1703    case element(1, Tid) of
1704	ets ->
1705	    dirty_index_match_object(Tab, Pat, Attr); % Should be optimized?
1706	tid ->
1707	    case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
1708                {_} ->
1709                    case LockKind of
1710                        read ->
1711			    Store = Ts#tidstore.store,
1712			    mnesia_locker:rlock_table(Tid, Store, Tab),
1713			    Objs = dirty_match_object(Tab, Pat),
1714			    add_written_match(Store, Pat, Tab, Objs);
1715                        _ ->
1716                            abort({bad_type, Tab, LockKind})
1717                    end;
1718		Pos when Pos =< tuple_size(Pat) ->
1719		    case LockKind of
1720			read ->
1721			    Store = Ts#tidstore.store,
1722			    mnesia_locker:rlock_table(Tid, Store, Tab),
1723			    Objs = dirty_index_match_object(Tab, Pat, Attr),
1724			    add_written_match(Store, Pat, Tab, Objs);
1725			_ ->
1726			    abort({bad_type, Tab, LockKind})
1727		    end;
1728		BadPos ->
1729		    abort({bad_type, Tab, BadPos})
1730	    end;
1731	_Protocol ->
1732	    dirty_index_match_object(Tab, Pat, Attr)
1733    end;
1734index_match_object(_Tid, _Ts, Tab, Pat, _Attr, _LockKind) ->
1735    abort({bad_type, Tab, Pat}).
1736
1737-spec index_read(Tab, Key, Attr) -> [Record] when
1738      Tab::table(),
1739      Key::term(),
1740      Attr::index_attr(),
1741      Record::tuple().
1742index_read(Tab, Key, Attr) ->
1743    case get(mnesia_activity_state) of
1744	{?DEFAULT_ACCESS, Tid, Ts} ->
1745	    index_read(Tid, Ts, Tab, Key, Attr, read);
1746	{Mod, Tid, Ts} ->
1747	    Mod:index_read(Tid, Ts, Tab, Key, Attr, read);
1748	_ ->
1749	    abort(no_transaction)
1750    end.
1751
1752index_read(Tid, Ts, Tab, Key, Attr, LockKind)
1753  when is_atom(Tab), Tab /= schema ->
1754    case element(1, Tid) of
1755	ets ->
1756	    dirty_index_read(Tab, Key, Attr); % Should be optimized?
1757	tid ->
1758	    Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
1759	    case LockKind of
1760		read ->
1761		    case has_var(Key) of
1762			false ->
1763			    Store = Ts#tidstore.store,
1764			    Objs = mnesia_index:read(Tid, Store, Tab, Key, Pos),
1765                            add_written_index(
1766                              Ts#tidstore.store, Pos, Tab, Key, Objs);
1767			true ->
1768			    abort({bad_type, Tab, Attr, Key})
1769		    end;
1770		_ ->
1771		    abort({bad_type, Tab, LockKind})
1772	    end;
1773	_Protocol ->
1774	    dirty_index_read(Tab, Key, Attr)
1775    end;
1776index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind) ->
1777    abort({bad_type, Tab}).
1778
1779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780%% Dirty access regardless of activities - updates
1781-spec dirty_write(Record::tuple()) -> 'ok'.
1782dirty_write(Val) when is_tuple(Val), tuple_size(Val) > 2  ->
1783    Tab = element(1, Val),
1784    dirty_write(Tab, Val);
1785dirty_write(Val) ->
1786    abort({bad_type, Val}).
1787
1788-spec dirty_write(Tab::table(), Record::tuple()) -> 'ok'.
1789dirty_write(Tab, Val) ->
1790    do_dirty_write(async_dirty, Tab, Val).
1791
1792do_dirty_write(SyncMode, Tab, Val)
1793  when is_atom(Tab), Tab /= schema, is_tuple(Val), tuple_size(Val) > 2 ->
1794    {_, _, _} = mnesia_lib:validate_record(Tab, Val),
1795    Oid = {Tab, element(2, Val)},
1796    mnesia_tm:dirty(SyncMode, {Oid, Val, write});
1797do_dirty_write(_SyncMode, Tab, Val) ->
1798    abort({bad_type, Tab, Val}).
1799
1800-spec dirty_delete({Tab::table(), Key::_}) -> 'ok'.
1801dirty_delete({Tab, Key}) ->
1802    dirty_delete(Tab, Key);
1803dirty_delete(Oid) ->
1804    abort({bad_type, Oid}).
1805
1806-spec dirty_delete(Tab::table(), Key::_) -> 'ok'.
1807dirty_delete(Tab, Key) ->
1808    do_dirty_delete(async_dirty, Tab, Key).
1809
1810do_dirty_delete(SyncMode, Tab, Key) when is_atom(Tab), Tab /= schema  ->
1811    Oid = {Tab, Key},
1812    mnesia_tm:dirty(SyncMode, {Oid, Oid, delete});
1813do_dirty_delete(_SyncMode, Tab, _Key) ->
1814    abort({bad_type, Tab}).
1815
1816-spec dirty_delete_object(Record::tuple()) -> 'ok'.
1817dirty_delete_object(Val) when is_tuple(Val), tuple_size(Val) > 2 ->
1818    Tab = element(1, Val),
1819    dirty_delete_object(Tab, Val);
1820dirty_delete_object(Val) ->
1821    abort({bad_type, Val}).
1822
1823-spec dirty_delete_object(Tab::table(), Record::tuple()) -> 'ok'.
1824dirty_delete_object(Tab, Val) ->
1825    do_dirty_delete_object(async_dirty, Tab, Val).
1826
1827do_dirty_delete_object(SyncMode, Tab, Val)
1828    when is_atom(Tab), Tab /= schema, is_tuple(Val), tuple_size(Val) > 2 ->
1829    Oid = {Tab, element(2, Val)},
1830    case has_var(Val) of
1831	false ->
1832	    mnesia_tm:dirty(SyncMode, {Oid, Val, delete_object});
1833	true ->
1834	    abort({bad_type, Tab, Val})
1835    end;
1836
1837do_dirty_delete_object(_SyncMode, Tab, Val) ->
1838    abort({bad_type, Tab, Val}).
1839
1840%% A Counter is an Oid being {CounterTab, CounterName}
1841-spec dirty_update_counter({Tab::table(), Key::_}, Incr::integer()) ->
1842                                  NewVal::integer().
1843dirty_update_counter({Tab, Key}, Incr) ->
1844    dirty_update_counter(Tab, Key, Incr);
1845dirty_update_counter(Counter, _Incr) ->
1846    abort({bad_type, Counter}).
1847
1848-spec dirty_update_counter(Tab::table(), Key::_, Incr::integer()) ->
1849                                  NewVal::integer().
1850dirty_update_counter(Tab, Key, Incr) ->
1851    do_dirty_update_counter(async_dirty, Tab, Key, Incr).
1852
1853do_dirty_update_counter(SyncMode, Tab, Key, Incr)
1854  when is_atom(Tab), Tab /= schema, is_integer(Incr) ->
1855    case mnesia_lib:validate_key(Tab, Key) of
1856	{RecName, 3, Type} when Type == set; Type == ordered_set ->
1857	    Oid = {Tab, Key},
1858	    mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
1859	_ ->
1860	    abort({combine_error, Tab, update_counter})
1861    end;
1862do_dirty_update_counter(_SyncMode, Tab, _Key, Incr) ->
1863    abort({bad_type, Tab, Incr}).
1864
1865%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1866%% Dirty access regardless of activities - read
1867
1868-spec dirty_read({Tab::table(), Key::_}) -> [tuple()].
1869dirty_read({Tab, Key}) ->
1870    dirty_read(Tab, Key);
1871dirty_read(Oid) ->
1872    abort({bad_type, Oid}).
1873
1874-spec dirty_read(Tab::table(), Key::_) -> [tuple()].
1875dirty_read(Tab, Key)
1876  when is_atom(Tab), Tab /= schema ->
1877    dirty_rpc(Tab, mnesia_lib, db_get, [Tab, Key]);
1878dirty_read(Tab, _Key) ->
1879    abort({bad_type, Tab}).
1880
1881-spec dirty_match_object(Pattern::tuple()) -> [Record::tuple()].
1882dirty_match_object(Pat) when is_tuple(Pat), tuple_size(Pat) > 2 ->
1883    Tab = element(1, Pat),
1884    dirty_match_object(Tab, Pat);
1885dirty_match_object(Pat) ->
1886    abort({bad_type, Pat}).
1887
1888-spec dirty_match_object(Tab,Pattern) -> [Record] when
1889      Tab::table(), Pattern::tuple(), Record::tuple().
1890dirty_match_object(Tab, Pat)
1891  when is_atom(Tab), Tab /= schema, is_tuple(Pat), tuple_size(Pat) > 2 ->
1892    dirty_rpc(Tab, ?MODULE, remote_dirty_match_object, [Tab, Pat]);
1893dirty_match_object(Tab, Pat) ->
1894    abort({bad_type, Tab, Pat}).
1895
1896remote_dirty_match_object(Tab, Pat) ->
1897    Key = element(2, Pat),
1898    case has_var(Key) of
1899	false ->
1900	    mnesia_lib:db_match_object(Tab, Pat);
1901	true ->
1902            PosList = regular_indexes(Tab),
1903	    remote_dirty_match_object(Tab, Pat, PosList)
1904    end.
1905
1906remote_dirty_match_object(Tab, Pat, [Pos | Tail]) when Pos =< tuple_size(Pat) ->
1907    IxKey = element(Pos, Pat),
1908    case has_var(IxKey) of
1909	false ->
1910	    mnesia_index:dirty_match_object(Tab, Pat, Pos);
1911	true ->
1912	    remote_dirty_match_object(Tab, Pat, Tail)
1913    end;
1914remote_dirty_match_object(Tab, Pat, []) ->
1915    mnesia_lib:db_match_object(Tab, Pat);
1916remote_dirty_match_object(Tab, Pat, _PosList) ->
1917    abort({bad_type, Tab, Pat}).
1918
1919-spec dirty_select(Tab, Spec) -> [Match] when
1920      Tab::table(), Spec::ets:match_spec(), Match::term().
1921dirty_select(Tab, Spec) when is_atom(Tab), Tab /= schema, is_list(Spec) ->
1922    dirty_rpc(Tab, ?MODULE, remote_dirty_select, [Tab, Spec]);
1923dirty_select(Tab, Spec) ->
1924    abort({bad_type, Tab, Spec}).
1925
1926remote_dirty_select(Tab, Spec) ->
1927    case Spec of
1928	[{HeadPat, _, _}] when is_tuple(HeadPat), tuple_size(HeadPat) > 2 ->
1929	    Key = element(2, HeadPat),
1930	    case has_var(Key) of
1931		false ->
1932		    mnesia_lib:db_select(Tab, Spec);
1933		true  ->
1934		    PosList = regular_indexes(Tab),
1935		    remote_dirty_select(Tab, Spec, PosList)
1936	    end;
1937	_ ->
1938	    mnesia_lib:db_select(Tab, Spec)
1939    end.
1940
1941remote_dirty_select(Tab, [{HeadPat,_, _}] = Spec, [Pos | Tail])
1942  when is_tuple(HeadPat), tuple_size(HeadPat) > 2, Pos =< tuple_size(HeadPat) ->
1943    Key = element(Pos, HeadPat),
1944    case has_var(Key) of
1945	false ->
1946	    Recs = mnesia_index:dirty_select(Tab, HeadPat, Pos),
1947	    %% Returns the records without applying the match spec
1948	    %% The actual filtering is handled by the caller
1949	    CMS = ets:match_spec_compile(Spec),
1950	    case val({Tab, setorbag}) of
1951		ordered_set ->
1952		    ets:match_spec_run(lists:sort(Recs), CMS);
1953		_ ->
1954		    ets:match_spec_run(Recs, CMS)
1955	    end;
1956	true  ->
1957	    remote_dirty_select(Tab, Spec, Tail)
1958    end;
1959remote_dirty_select(Tab, Spec, _) ->
1960    mnesia_lib:db_select(Tab, Spec).
1961
1962dirty_sel_init(Node,Tab,Spec,NObjects,Type) ->
1963    do_dirty_rpc(Tab,Node,mnesia_lib,db_select_init,[Type,Tab,Spec,NObjects]).
1964
1965dirty_sel_cont(#mnesia_select{cont='$end_of_table'}) -> '$end_of_table';
1966dirty_sel_cont(#mnesia_select{node=Node,tab=Tab,storage=Type,cont=Cont,orig=Ms}) ->
1967    do_dirty_rpc(Tab,Node,mnesia_lib,db_select_cont,[Type,Cont,Ms]).
1968
1969-spec dirty_all_keys(Tab::table()) -> [Key::term()].
1970dirty_all_keys(Tab) when is_atom(Tab), Tab /= schema ->
1971    case ?catch_val({Tab, wild_pattern}) of
1972	{'EXIT', _} ->
1973	    abort({no_exists, Tab});
1974	Pat0 ->
1975	    Pat = setelement(2, Pat0, '$1'),
1976	    Keys = dirty_select(Tab, [{Pat, [], ['$1']}]),
1977	    case val({Tab, setorbag}) of
1978		bag -> mnesia_lib:uniq(Keys);
1979		_ -> Keys
1980	    end
1981    end;
1982dirty_all_keys(Tab) ->
1983    abort({bad_type, Tab}).
1984
1985-spec dirty_index_match_object(Pattern, Attr) -> [Record] when
1986      Pattern::tuple(), Attr::index_attr(), Record::tuple().
1987dirty_index_match_object(Pat, Attr) when is_tuple(Pat), tuple_size(Pat) > 2 ->
1988    Tab = element(1, Pat),
1989    dirty_index_match_object(Tab, Pat, Attr);
1990dirty_index_match_object(Pat, _Attr) ->
1991    abort({bad_type, Pat}).
1992
1993-spec dirty_index_match_object(Tab, Pattern, Attr) -> [Record] when
1994      Tab::table(),
1995      Pattern::tuple(),
1996      Attr::index_attr(),
1997      Record::tuple().
1998dirty_index_match_object(Tab, Pat, Attr)
1999  when is_atom(Tab), Tab /= schema, is_tuple(Pat), tuple_size(Pat) > 2 ->
2000    case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
2001        {_} ->
2002            dirty_match_object(Tab, Pat);
2003	Pos when Pos =< tuple_size(Pat) ->
2004	    case has_var(element(2, Pat)) of
2005		false ->
2006		    dirty_match_object(Tab, Pat);
2007		true ->
2008		    Elem = element(Pos, Pat),
2009		    case has_var(Elem) of
2010			false ->
2011			    dirty_rpc(Tab, mnesia_index, dirty_match_object,
2012				      [Tab, Pat, Pos]);
2013			true ->
2014			    abort({bad_type, Tab, Attr, Elem})
2015		    end
2016	    end;
2017	BadPos ->
2018	    abort({bad_type, Tab, BadPos})
2019    end;
2020dirty_index_match_object(Tab, Pat, _Attr) ->
2021    abort({bad_type, Tab, Pat}).
2022
2023-spec dirty_index_read(Tab, Key, Attr) -> [Record] when
2024      Tab::table(),
2025      Key::term(),
2026      Attr::index_attr(),
2027      Record::tuple().
2028dirty_index_read(Tab, Key, Attr) when is_atom(Tab), Tab /= schema ->
2029    Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
2030    case has_var(Key) of
2031	false ->
2032	    mnesia_index:dirty_read(Tab, Key, Pos);
2033	true ->
2034	    abort({bad_type, Tab, Attr, Key})
2035    end;
2036dirty_index_read(Tab, _Key, _Attr) ->
2037    abort({bad_type, Tab}).
2038
2039%% do not use only for backwards compatibility
2040dirty_slot(Tab, Slot) when is_atom(Tab), Tab /= schema, is_integer(Slot)  ->
2041    dirty_rpc(Tab, mnesia_lib, db_slot, [Tab, Slot]);
2042dirty_slot(Tab, Slot) ->
2043    abort({bad_type, Tab, Slot}).
2044
2045-spec dirty_first(Tab::table()) -> Key::term().
2046dirty_first(Tab) when is_atom(Tab), Tab /= schema ->
2047    dirty_rpc(Tab, mnesia_lib, db_first, [Tab]);
2048dirty_first(Tab) ->
2049    abort({bad_type, Tab}).
2050
2051-spec dirty_last(Tab::table()) -> Key::term().
2052dirty_last(Tab) when is_atom(Tab), Tab /= schema ->
2053    dirty_rpc(Tab, mnesia_lib, db_last, [Tab]);
2054dirty_last(Tab) ->
2055    abort({bad_type, Tab}).
2056
2057-spec dirty_next(Tab::table(), Key::_) -> NextKey::term().
2058dirty_next(Tab, Key) when is_atom(Tab), Tab /= schema ->
2059    dirty_rpc(Tab, mnesia_lib, db_next_key, [Tab, Key]);
2060dirty_next(Tab, _Key) ->
2061    abort({bad_type, Tab}).
2062
2063-spec dirty_prev(Tab::table(), Key::_) -> PrevKey::term().
2064dirty_prev(Tab, Key) when is_atom(Tab), Tab /= schema ->
2065    dirty_rpc(Tab, mnesia_lib, db_prev_key, [Tab, Key]);
2066dirty_prev(Tab, _Key) ->
2067    abort({bad_type, Tab}).
2068
2069
2070dirty_rpc(Tab, M, F, Args) ->
2071    Node = val({Tab, where_to_read}),
2072    do_dirty_rpc(Tab, Node, M, F, Args).
2073
2074do_dirty_rpc(_Tab, nowhere, _, _, Args) ->
2075    mnesia:abort({no_exists, Args});
2076do_dirty_rpc(_Tab, Local, M, F, Args) when Local =:= node() ->
2077    try apply(M,F,Args)
2078    catch
2079        throw:Res -> Res;
2080        _:_ -> mnesia:abort({badarg, Args})
2081    end;
2082do_dirty_rpc(Tab, Node, M, F, Args) ->
2083    case mnesia_rpc:call(Node, M, F, Args) of
2084	{badrpc, Reason} ->
2085	    timer:sleep(20), %% Do not be too eager, and can't use yield on SMP
2086	    %% Sync with mnesia_monitor
2087	    _ = try sys:get_status(mnesia_monitor) catch _:_ -> ok end,
2088	    case mnesia_controller:call({check_w2r, Node, Tab}) of % Sync
2089		NewNode when NewNode =:= Node ->
2090		    ErrorTag = mnesia_lib:dirty_rpc_error_tag(Reason),
2091		    mnesia:abort({ErrorTag, Args});
2092		NewNode ->
2093		    case get(mnesia_activity_state) of
2094			{_Mod, Tid, _Ts} when is_record(Tid, tid) ->
2095			    %% In order to perform a consistent
2096			    %% retry of a transaction we need
2097			    %% to acquire the lock on the NewNode.
2098			    %% In this context we do neither know
2099			    %% the kind or granularity of the lock.
2100			    %% --> Abort the transaction
2101			    mnesia:abort({node_not_running, Node});
2102			{error, {node_not_running, _}} ->
2103			    %% Mnesia is stopping
2104			    mnesia:abort({no_exists, Args});
2105			_ ->
2106			    %% Splendid! A dirty retry is safe
2107			    %% 'Node' probably went down now
2108			    %% Let mnesia_controller get broken link message first
2109			    do_dirty_rpc(Tab, NewNode, M, F, Args)
2110		    end
2111	    end;
2112	Other ->
2113	    Other
2114    end.
2115
2116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2117%% Info
2118
2119%% Info about one table
2120-spec table_info(Tab::table(), Item::term()) -> Info::term().
2121table_info(Tab, Item) ->
2122    case get(mnesia_activity_state) of
2123	undefined ->
2124	    any_table_info(Tab, Item);
2125	{?DEFAULT_ACCESS, _Tid, _Ts} ->
2126	    any_table_info(Tab, Item);
2127	{Mod, Tid, Ts} ->
2128	    Mod:table_info(Tid, Ts, Tab, Item);
2129	_ ->
2130	    abort(no_transaction)
2131    end.
2132
2133table_info(_Tid, _Ts, Tab, Item) ->
2134    any_table_info(Tab, Item).
2135
2136
2137any_table_info(Tab, Item) when is_atom(Tab) ->
2138    case Item of
2139	master_nodes ->
2140	    mnesia_recover:get_master_nodes(Tab);
2141%	checkpoints ->
2142%	    case ?catch_val({Tab, commit_work}) of
2143%		[{checkpoints, List} | _] -> List;
2144%		No_chk when is_list(No_chk) ->  [];
2145%		Else -> info_reply(Else, Tab, Item)
2146%	    end;
2147	size ->
2148	    raw_table_info(Tab, Item);
2149	memory ->
2150	    raw_table_info(Tab, Item);
2151	type ->
2152	    case ?catch_val({Tab, setorbag}) of
2153		{'EXIT', _} ->
2154		    abort({no_exists, Tab, Item});
2155		Val ->
2156		    Val
2157	    end;
2158	all ->
2159	    case mnesia_schema:get_table_properties(Tab) of
2160		[] ->
2161		    abort({no_exists, Tab, Item});
2162		Props ->
2163                    Rename = fun ({setorbag, Type}) -> {type, Type};
2164                                 (Prop) -> Prop
2165                             end,
2166                    lists:sort(lists:map(Rename, Props))
2167	    end;
2168	name ->
2169	    Tab;
2170	_ ->
2171	    case ?catch_val({Tab, Item}) of
2172		{'EXIT', _} ->
2173		    abort({no_exists, Tab, Item});
2174		Val ->
2175		    Val
2176	    end
2177    end;
2178any_table_info(Tab, _Item) ->
2179    abort({bad_type, Tab}).
2180
2181raw_table_info(Tab, Item) ->
2182    try
2183	case ?ets_lookup_element(mnesia_gvar, {Tab, storage_type}, 2) of
2184	    ram_copies ->
2185		info_reply(?ets_info(Tab, Item), Tab, Item);
2186	    disc_copies ->
2187		info_reply(?ets_info(Tab, Item), Tab, Item);
2188	    disc_only_copies ->
2189		info_reply(dets:info(Tab, Item), Tab, Item);
2190            {ext, Alias, Mod} ->
2191                info_reply(Mod:info(Alias, Tab, Item), Tab, Item);
2192	    unknown ->
2193		bad_info_reply(Tab, Item)
2194	end
2195    catch error:_ ->
2196	    bad_info_reply(Tab, Item)
2197    end.
2198
2199info_reply({error, _Reason}, Tab, Item) ->
2200    bad_info_reply(Tab, Item);
2201info_reply(Val, _Tab, _Item) ->
2202    Val.
2203
2204bad_info_reply(_Tab, size) -> 0;
2205bad_info_reply(_Tab, memory) -> 0;
2206bad_info_reply(Tab, Item) -> abort({no_exists, Tab, Item}).
2207
2208%% Raw info about all tables
2209-spec schema() -> 'ok'.
2210schema() ->
2211    mnesia_schema:info().
2212
2213%% Raw info about one tables
2214-spec schema(Tab::table()) -> 'ok'.
2215schema(Tab) ->
2216    mnesia_schema:info(Tab).
2217
2218-spec error_description(Error::term()) -> string().
2219error_description(Err) ->
2220    mnesia_lib:error_desc(Err).
2221
2222-spec info() -> 'ok'.
2223info() ->
2224    case mnesia_lib:is_running() of
2225	yes ->
2226	    TmInfo = mnesia_tm:get_info(10000),
2227	    Held = system_info(held_locks),
2228	    Queued = system_info(lock_queue),
2229
2230	    io:format("---> Processes holding locks <--- ~n", []),
2231	    lists:foreach(fun(L) -> io:format("Lock: ~p~n", [L]) end,
2232			  Held),
2233
2234	    io:format( "---> Processes waiting for locks <--- ~n", []),
2235	    lists:foreach(fun({Oid, Op, _Pid, Tid, OwnerTid}) ->
2236				  io:format("Tid ~p waits for ~p lock "
2237					    "on oid ~p owned by ~p ~n",
2238					    [Tid, Op, Oid, OwnerTid])
2239		  end, Queued),
2240	    mnesia_tm:display_info(group_leader(), TmInfo),
2241
2242	    Pat = {'_', unclear, '_'},
2243	    Uncertain = ets:match_object(mnesia_decision, Pat),
2244
2245	    io:format( "---> Uncertain transactions <--- ~n", []),
2246	    lists:foreach(fun({Tid, _, Nodes}) ->
2247				  io:format("Tid ~w waits for decision "
2248					    "from ~w~n",
2249					    [Tid, Nodes])
2250		  end, Uncertain),
2251
2252	    mnesia_controller:info(),
2253	    display_system_info(Held, Queued, TmInfo, Uncertain);
2254	_ ->
2255	    mini_info()
2256    end,
2257    ok.
2258
2259mini_info() ->
2260    io:format("===> System info in version ~p, debug level = ~p <===~n",
2261	      [system_info(version), system_info(debug)]),
2262    Not =
2263	case system_info(use_dir) of
2264	    true -> "";
2265	    false  -> "NOT "
2266	end,
2267
2268    io:format("~w. Directory ~p is ~sused.~n",
2269	      [system_info(schema_location), system_info(directory), Not]),
2270    io:format("use fallback at restart = ~w~n",
2271	      [system_info(fallback_activated)]),
2272    Running = system_info(running_db_nodes),
2273    io:format("running db nodes   = ~w~n", [Running]),
2274    All = mnesia_lib:all_nodes(),
2275    io:format("stopped db nodes   = ~w ~n", [All -- Running]).
2276
2277display_system_info(Held, Queued, TmInfo, Uncertain) ->
2278    mini_info(),
2279    display_tab_info(),
2280    S = fun(Items) -> [system_info(I) || I <- Items] end,
2281
2282    io:format("~w transactions committed, ~w aborted, "
2283	      "~w restarted, ~w logged to disc~n",
2284	      S([transaction_commits, transaction_failures,
2285		transaction_restarts, transaction_log_writes])),
2286
2287    {Active, Pending} =
2288	case TmInfo of
2289	    {timeout, _} -> {infinity, infinity};
2290	    {info, P, A} -> {length(A), length(P)}
2291	end,
2292    io:format("~w held locks, ~w in queue; "
2293	      "~w local transactions, ~w remote~n",
2294	      [length(Held), length(Queued), Active, Pending]),
2295
2296    Ufold = fun({_, _, Ns}, {C, Old}) ->
2297		    New = [N || N <- Ns, not lists:member(N, Old)],
2298		    {C + 1, New ++ Old}
2299	    end,
2300    {Ucount, Unodes} = lists:foldl(Ufold, {0, []}, Uncertain),
2301    io:format("~w transactions waits for other nodes: ~p~n",
2302	      [Ucount, Unodes]).
2303
2304display_tab_info() ->
2305    MasterTabs = mnesia_recover:get_master_node_tables(),
2306    io:format("master node tables = ~p~n", [lists:sort(MasterTabs)]),
2307
2308    case get_backend_types() of
2309	[] -> ok;
2310	Ts -> list_backend_types(Ts, "backend types      = ")
2311    end,
2312
2313    case get_index_plugins() of
2314	[] -> ok;
2315	Ps -> list_index_plugins(Ps, "index plugins      = ")
2316    end,
2317
2318    Tabs = system_info(tables),
2319
2320    {Unknown, Ram, Disc, DiscOnly, Ext} =
2321	lists:foldl(fun storage_count/2, {[], [], [], [], []}, Tabs),
2322
2323    io:format("remote             = ~p~n", [lists:sort(Unknown)]),
2324    io:format("ram_copies         = ~p~n", [lists:sort(Ram)]),
2325    io:format("disc_copies        = ~p~n", [lists:sort(Disc)]),
2326    io:format("disc_only_copies   = ~p~n", [lists:sort(DiscOnly)]),
2327    [io:format("~-19s= ~p~n", [atom_to_list(A), Ts]) || {A,Ts} <- Ext],
2328
2329    Rfoldl = fun(T, Acc) ->
2330		     Rpat =
2331			 case val({T, access_mode}) of
2332			     read_only ->
2333				 lists:sort([{A, read_only} || A <- val({T, active_replicas})]);
2334			     read_write ->
2335				 [fix_wtc(W) || W <- table_info(T, where_to_commit)]
2336			 end,
2337		     case lists:keysearch(Rpat, 1, Acc) of
2338			 {value, {_Rpat, Rtabs}} ->
2339			     lists:keyreplace(Rpat, 1, Acc, {Rpat, [T | Rtabs]});
2340			 false ->
2341			     [{Rpat, [T]} | Acc]
2342		     end
2343	     end,
2344    Repl = lists:foldl(Rfoldl, [], Tabs),
2345    Rdisp = fun({Rpat, Rtabs}) -> io:format("~p = ~p~n", [Rpat, Rtabs]) end,
2346    lists:foreach(Rdisp, lists:sort(Repl)).
2347
2348-spec get_backend_types() -> [BackendType::term()].
2349get_backend_types() ->
2350    case ?catch_val({schema, user_property, mnesia_backend_types}) of
2351	{'EXIT', _} ->
2352	    [];
2353	{mnesia_backend_types, Ts} ->
2354	    lists:sort(Ts)
2355    end.
2356
2357-spec get_index_plugins() -> [IndexPlugins::term()].
2358get_index_plugins() ->
2359    case ?catch_val({schema, user_property, mnesia_index_plugins}) of
2360	{'EXIT', _} ->
2361	    [];
2362	{mnesia_index_plugins, Ps} ->
2363	    lists:sort(Ps)
2364    end.
2365
2366
2367list_backend_types([{A,M} | T] = Ts, Legend) ->
2368    Indent = [$\s || _ <- Legend],
2369    W = integer_to_list(
2370	  lists:foldl(fun({Alias,_}, Wa) ->
2371			      erlang:max(Wa, length(atom_to_list(Alias)))
2372		      end, 0, Ts)),
2373    io:fwrite(Legend ++ "~-" ++ W ++ "s - ~s~n",
2374	      [atom_to_list(A), atom_to_list(M)]),
2375    [io:fwrite(Indent ++ "~-" ++ W ++ "s - ~s~n",
2376	       [atom_to_list(A1), atom_to_list(M1)])
2377     || {A1,M1} <- T].
2378
2379list_index_plugins([{N,M,F} | T] = Ps, Legend) ->
2380    Indent = [$\s || _ <- Legend],
2381    W = integer_to_list(
2382	  lists:foldl(fun({N1,_,_}, Wa) ->
2383			      erlang:max(Wa, length(pp_ix_name(N1)))
2384		      end, 0, Ps)),
2385    io:fwrite(Legend ++ "~-" ++ W ++ "s - ~s:~ts~n",
2386	      [pp_ix_name(N), atom_to_list(M), atom_to_list(F)]),
2387    [io:fwrite(Indent ++ "~-" ++ W ++ "s - ~s:~ts~n",
2388	       [pp_ix_name(N1), atom_to_list(M1), atom_to_list(F1)])
2389     || {N1,M1,F1} <- T].
2390
2391pp_ix_name(N) ->
2392    lists:flatten(io_lib:fwrite("~w", [N])).
2393
2394fix_wtc({N, {ext,A,_}}) -> {N, A};
2395fix_wtc({N,A}) when is_atom(A) -> {N, A}.
2396
2397storage_count(T, {U, R, D, DO, Ext}) ->
2398    case table_info(T, storage_type) of
2399	unknown -> {[T | U], R, D, DO, Ext};
2400	ram_copies -> {U, [T | R], D, DO, Ext};
2401	disc_copies -> {U, R, [T | D], DO, Ext};
2402	disc_only_copies -> {U, R, D, [T | DO], Ext};
2403        {ext, A, _} -> {U, R, D, DO, orddict:append(A, T, Ext)}
2404    end.
2405
2406-spec system_info(Iterm::term()) -> Info::term().
2407system_info(Item) ->
2408    try system_info2(Item)
2409    catch _:Error -> abort(Error)
2410    end.
2411
2412system_info2(all) ->
2413    Items = system_info_items(mnesia_lib:is_running()),
2414    [{I, system_info(I)} || I <- Items];
2415
2416system_info2(db_nodes) ->
2417    DiscNs = ?catch_val({schema, disc_copies}),
2418    RamNs = ?catch_val({schema, ram_copies}),
2419    ExtNs = ?catch_val({schema, external_copies}),
2420    if
2421	is_list(DiscNs), is_list(RamNs), is_list(ExtNs) ->
2422	    DiscNs ++ RamNs ++ ExtNs;
2423	true ->
2424	    case mnesia_schema:read_nodes() of
2425		{ok, Nodes} -> Nodes;
2426		{error,Reason} -> exit(Reason)
2427	    end
2428    end;
2429system_info2(running_db_nodes) ->
2430    case ?catch_val({current, db_nodes}) of
2431	{'EXIT',_} ->
2432	    %% Ensure that we access the intended Mnesia
2433	    %% directory. This function may not be called
2434	    %% during startup since it will cause the
2435	    %% application_controller to get into deadlock
2436	    load_mnesia_or_abort(),
2437	    mnesia_lib:running_nodes();
2438	Other ->
2439	    Other
2440    end;
2441
2442system_info2(extra_db_nodes) ->
2443    case ?catch_val(extra_db_nodes) of
2444	{'EXIT',_} ->
2445	    %% Ensure that we access the intended Mnesia
2446	    %% directory. This function may not be called
2447	    %% during startup since it will cause the
2448	    %% application_controller to get into deadlock
2449	    load_mnesia_or_abort(),
2450	    mnesia_monitor:get_env(extra_db_nodes);
2451	Other ->
2452	    Other
2453    end;
2454
2455system_info2(directory) ->
2456    case ?catch_val(directory) of
2457	{'EXIT',_} ->
2458	    %% Ensure that we access the intended Mnesia
2459	    %% directory. This function may not be called
2460	    %% during startup since it will cause the
2461	    %% application_controller to get into deadlock
2462	    load_mnesia_or_abort(),
2463	    mnesia_monitor:get_env(dir);
2464	Other ->
2465	    Other
2466    end;
2467
2468system_info2(use_dir) ->
2469    case ?catch_val(use_dir) of
2470	{'EXIT',_} ->
2471	    %% Ensure that we access the intended Mnesia
2472	    %% directory. This function may not be called
2473	    %% during startup since it will cause the
2474	    %% application_controller to get into deadlock
2475	    load_mnesia_or_abort(),
2476	    mnesia_monitor:use_dir();
2477	Other ->
2478	    Other
2479    end;
2480
2481system_info2(schema_location) ->
2482    case ?catch_val(schema_location) of
2483	{'EXIT',_} ->
2484	    %% Ensure that we access the intended Mnesia
2485	    %% directory. This function may not be called
2486	    %% during startup since it will cause the
2487	    %% application_controller to get into deadlock
2488	    load_mnesia_or_abort(),
2489	    mnesia_monitor:get_env(schema_location);
2490	Other ->
2491	    Other
2492    end;
2493
2494system_info2(fallback_activated) ->
2495    case ?catch_val(fallback_activated) of
2496	{'EXIT',_} ->
2497	    %% Ensure that we access the intended Mnesia
2498	    %% directory. This function may not be called
2499	    %% during startup since it will cause the
2500	    %% application_controller to get into deadlock
2501	    load_mnesia_or_abort(),
2502	    mnesia_bup:fallback_exists();
2503	Other ->
2504	    Other
2505    end;
2506
2507system_info2(version) ->
2508    case ?catch_val(version) of
2509	{'EXIT', _} ->
2510	    Apps = application:loaded_applications(),
2511	    case lists:keysearch(?APPLICATION, 1, Apps) of
2512		{value, {_Name, _Desc, Version}} ->
2513		    Version;
2514		false ->
2515		    %% Ensure that it does not match
2516		    {mnesia_not_loaded, node(), erlang:timestamp()}
2517	    end;
2518	Version ->
2519	    Version
2520    end;
2521
2522system_info2(access_module) -> mnesia_monitor:get_env(access_module);
2523system_info2(auto_repair) -> mnesia_monitor:get_env(auto_repair);
2524system_info2(is_running) -> mnesia_lib:is_running();
2525system_info2(backup_module) -> mnesia_monitor:get_env(backup_module);
2526system_info2(backend_types) -> mnesia_schema:backend_types();
2527system_info2(event_module) -> mnesia_monitor:get_env(event_module);
2528system_info2(debug) -> mnesia_monitor:get_env(debug);
2529system_info2(dump_log_load_regulation) -> mnesia_monitor:get_env(dump_log_load_regulation);
2530system_info2(dump_log_write_threshold) -> mnesia_monitor:get_env(dump_log_write_threshold);
2531system_info2(dump_log_time_threshold) -> mnesia_monitor:get_env(dump_log_time_threshold);
2532system_info2(dump_log_update_in_place) ->
2533    mnesia_monitor:get_env(dump_log_update_in_place);
2534system_info2(max_wait_for_decision) -> mnesia_monitor:get_env(max_wait_for_decision);
2535system_info2(ignore_fallback_at_startup) -> mnesia_monitor:get_env(ignore_fallback_at_startup);
2536system_info2(fallback_error_function) ->  mnesia_monitor:get_env(fallback_error_function);
2537system_info2(log_version) -> mnesia_log:version();
2538system_info2(protocol_version) -> mnesia_monitor:protocol_version();
2539system_info2(schema_version) -> mnesia_schema:version(); %backward compatibility
2540system_info2(tables) -> val({schema, tables});
2541system_info2(local_tables) -> val({schema, local_tables});
2542system_info2(master_node_tables) -> mnesia_recover:get_master_node_tables();
2543system_info2(subscribers) -> mnesia_subscr:subscribers();
2544system_info2(checkpoints) -> mnesia_checkpoint:checkpoints();
2545system_info2(held_locks) -> mnesia_locker:get_held_locks();
2546system_info2(lock_queue) -> mnesia_locker:get_lock_queue();
2547system_info2(transactions) -> mnesia_tm:get_transactions();
2548system_info2(transaction_failures) -> mnesia_lib:read_counter(trans_failures);
2549system_info2(transaction_commits) -> mnesia_lib:read_counter(trans_commits);
2550system_info2(transaction_restarts) -> mnesia_lib:read_counter(trans_restarts);
2551system_info2(transaction_log_writes) -> mnesia_dumper:get_log_writes();
2552system_info2(core_dir) ->  mnesia_monitor:get_env(core_dir);
2553system_info2(no_table_loaders) ->  mnesia_monitor:get_env(no_table_loaders);
2554system_info2(dc_dump_limit) ->  mnesia_monitor:get_env(dc_dump_limit);
2555system_info2(send_compressed) -> mnesia_monitor:get_env(send_compressed);
2556
2557system_info2(Item) -> exit({badarg, Item}).
2558
2559system_info_items(yes) ->
2560    [
2561     access_module,
2562     auto_repair,
2563     backend_types,
2564     backup_module,
2565     checkpoints,
2566     db_nodes,
2567     debug,
2568     directory,
2569     dump_log_load_regulation,
2570     dump_log_time_threshold,
2571     dump_log_update_in_place,
2572     dump_log_write_threshold,
2573     event_module,
2574     extra_db_nodes,
2575     fallback_activated,
2576     held_locks,
2577     ignore_fallback_at_startup,
2578     fallback_error_function,
2579     is_running,
2580     local_tables,
2581     lock_queue,
2582     log_version,
2583     master_node_tables,
2584     max_wait_for_decision,
2585     protocol_version,
2586     running_db_nodes,
2587     schema_location,
2588     schema_version,
2589     subscribers,
2590     tables,
2591     transaction_commits,
2592     transaction_failures,
2593     transaction_log_writes,
2594     transaction_restarts,
2595     transactions,
2596     use_dir,
2597     core_dir,
2598     no_table_loaders,
2599     dc_dump_limit,
2600     send_compressed,
2601     version
2602    ];
2603system_info_items(no) ->
2604    [
2605     auto_repair,
2606     backup_module,
2607     db_nodes,
2608     debug,
2609     directory,
2610     dump_log_load_regulation,
2611     dump_log_time_threshold,
2612     dump_log_update_in_place,
2613     dump_log_write_threshold,
2614     event_module,
2615     extra_db_nodes,
2616     ignore_fallback_at_startup,
2617     fallback_error_function,
2618     is_running,
2619     log_version,
2620     max_wait_for_decision,
2621     protocol_version,
2622     running_db_nodes,
2623     schema_location,
2624     schema_version,
2625     use_dir,
2626     core_dir,
2627     version
2628    ].
2629
2630system_info() ->
2631    IsRunning = mnesia_lib:is_running(),
2632    case IsRunning of
2633	yes ->
2634	    TmInfo = mnesia_tm:get_info(10000),
2635	    Held = system_info(held_locks),
2636	    Queued = system_info(lock_queue),
2637	    Pat = {'_', unclear, '_'},
2638	    Uncertain = ets:match_object(mnesia_decision, Pat),
2639	    display_system_info(Held, Queued, TmInfo, Uncertain);
2640	_ ->
2641	    mini_info()
2642    end,
2643    IsRunning.
2644
2645load_mnesia_or_abort() ->
2646    case mnesia_lib:ensure_loaded(?APPLICATION) of
2647	ok ->
2648	    ok;
2649	{error, Reason} ->
2650	    abort(Reason)
2651    end.
2652
2653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2654%% Database mgt
2655
2656-spec create_schema(Ns::[node()]) -> result().
2657create_schema(Ns) ->
2658    create_schema(Ns, []).
2659
2660-spec create_schema(Ns::[node()], [Prop]) -> result() when
2661      Prop :: BackendType | IndexPlugin,
2662      BackendType :: {'backend_types', [{Name::atom(), Module::module()}]},
2663      IndexPlugin :: {'index_plugins', [{{Name::atom()}, Module::module(), Function::atom()}]}.
2664create_schema(Ns, Properties) ->
2665    mnesia_bup:create_schema(Ns, Properties).
2666
2667-spec delete_schema(Ns::[node()]) -> result().
2668delete_schema(Ns) ->
2669    mnesia_schema:delete_schema(Ns).
2670
2671-spec add_backend_type(Name::atom(), Module::module()) -> t_result('ok').
2672add_backend_type(Alias, Module) ->
2673    mnesia_schema:add_backend_type(Alias, Module).
2674
2675-spec backup(Dest::term()) -> result().
2676backup(Opaque) ->
2677    mnesia_log:backup(Opaque).
2678
2679-spec backup(Dest::term(), Mod::module()) ->
2680          result().
2681backup(Opaque, Mod) ->
2682    mnesia_log:backup(Opaque, Mod).
2683
2684-spec traverse_backup(Src::term(), Dest::term(), Fun, Acc) ->
2685                             {'ok', Acc} | {'error', Reason::term()} when
2686      Fun :: fun((Items, Acc) -> {Items,Acc}).
2687traverse_backup(S, T, Fun, Acc) ->
2688    mnesia_bup:traverse_backup(S, T, Fun, Acc).
2689
2690-spec traverse_backup(Src::term(), SrcMod::module(),
2691                      Dest::term(), DestMod::module(),
2692                      Fun, Acc) ->
2693                             {'ok', Acc} | {'error', Reason::term()} when
2694      Fun :: fun((Items, Acc) -> {Items,Acc}).
2695traverse_backup(S, SM, T, TM, F, A) ->
2696    mnesia_bup:traverse_backup(S, SM, T, TM, F, A).
2697
2698-spec install_fallback(Src::term()) -> result().
2699install_fallback(Opaque) ->
2700    mnesia_bup:install_fallback(Opaque).
2701
2702-spec install_fallback(Src::term(), Mod::module()|[Opt]) ->
2703          result() when
2704      Opt :: Module | Scope | Dir,
2705      Module :: {'module', Mod::module()},
2706      Scope :: {'scope', 'global' | 'local'},
2707      Dir :: {'mnesia_dir', Dir::string()}.
2708install_fallback(Opaque, Mod) ->
2709    mnesia_bup:install_fallback(Opaque, Mod).
2710
2711-spec uninstall_fallback() -> result().
2712uninstall_fallback() ->
2713    mnesia_bup:uninstall_fallback().
2714
2715-spec uninstall_fallback(Args) -> result() when
2716      Args :: [{'mnesia_dir', Dir::string()}].
2717uninstall_fallback(Args) ->
2718    mnesia_bup:uninstall_fallback(Args).
2719
2720-spec activate_checkpoint([Arg]) -> {'ok', Name, [node()]} | {'error', Reason::term()} when
2721      Arg :: {'name', Name} | {'max', [table()]} | {'min', [table()]} |
2722             {'allow_remote', boolean()} | {'ram_overrides_dump', boolean()}.
2723activate_checkpoint(Args) ->
2724    mnesia_checkpoint:activate(Args).
2725
2726-spec deactivate_checkpoint(Name::_) -> result().
2727deactivate_checkpoint(Name) ->
2728    mnesia_checkpoint:deactivate(Name).
2729
2730-spec backup_checkpoint(Name, Dest) -> result() when
2731      Name :: term(), Dest :: term().
2732backup_checkpoint(Name, Opaque) ->
2733    mnesia_log:backup_checkpoint(Name, Opaque).
2734
2735-spec backup_checkpoint(Name, Dest, Mod) -> result() when
2736      Name :: term(), Dest :: term(), Mod :: module().
2737backup_checkpoint(Name, Opaque, Mod) ->
2738    mnesia_log:backup_checkpoint(Name, Opaque, Mod).
2739
2740-spec restore(Src::_, [Arg]) -> t_result([table()]) when
2741      Op  :: 'skip_tables' | 'clear_tables' | 'keep_tables' | 'restore_tables',
2742      Arg :: {'module', module()} | {Op, [table()]} | {'default_op', Op}.
2743restore(Opaque, Args) ->
2744    mnesia_schema:restore(Opaque, Args).
2745
2746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2747%% Table mgt
2748-spec create_table([Arg]) -> t_result('ok') when
2749      Arg :: {'name', table()} | create_option().
2750create_table(Arg) ->
2751    mnesia_schema:create_table(Arg).
2752
2753-spec create_table(Name::table(), [create_option()]) -> t_result('ok').
2754create_table(Name, Arg) when is_list(Arg) ->
2755    mnesia_schema:create_table([{name, Name}| Arg]);
2756create_table(Name, Arg) ->
2757    {aborted, {badarg, Name, Arg}}.
2758
2759-spec delete_table(Tab::table()) -> t_result('ok').
2760delete_table(Tab) ->
2761    mnesia_schema:delete_table(Tab).
2762
2763-spec add_table_copy(Tab, N, ST) -> t_result('ok') when
2764      Tab :: table(), N::node(), ST::storage_type().
2765add_table_copy(Tab, N, S) ->
2766    mnesia_schema:add_table_copy(Tab, N, S).
2767
2768-spec del_table_copy(Tab::table(), N::node()) -> t_result('ok').
2769del_table_copy(Tab, N) ->
2770    mnesia_schema:del_table_copy(Tab, N).
2771
2772-spec move_table_copy(Tab::table(), From::node(), To::node()) -> t_result('ok').
2773move_table_copy(Tab, From, To) ->
2774    mnesia_schema:move_table(Tab, From, To).
2775
2776-spec add_table_index(Tab, I) -> t_result('ok') when
2777      Tab :: table(), I :: index_attr().
2778add_table_index(Tab, Ix) ->
2779    mnesia_schema:add_table_index(Tab, Ix).
2780-spec del_table_index(Tab, I) -> t_result('ok') when
2781      Tab::table(), I::index_attr().
2782del_table_index(Tab, Ix) ->
2783    mnesia_schema:del_table_index(Tab, Ix).
2784
2785-spec transform_table(Tab::table(), Fun, [Attr]) -> t_result('ok') when
2786      Attr :: atom(),
2787      Fun:: fun((Record::tuple()) -> Transformed::tuple()) | ignore.
2788transform_table(Tab, Fun, NewA) ->
2789    try val({Tab, record_name}) of
2790	OldRN -> mnesia_schema:transform_table(Tab, Fun, NewA, OldRN)
2791    catch exit:Reason ->
2792	    mnesia:abort(Reason)
2793    end.
2794
2795-spec transform_table(Tab::table(), Fun, [Attr], RecName) -> t_result('ok') when
2796      RecName :: atom(),
2797      Attr :: atom(),
2798      Fun:: fun((Record::tuple()) -> Transformed::tuple()) | ignore.
2799transform_table(Tab, Fun, NewA, NewRN) ->
2800    mnesia_schema:transform_table(Tab, Fun, NewA, NewRN).
2801
2802-spec change_table_copy_type(Tab::table(), Node::node(), To::storage_type()) -> t_result('ok').
2803change_table_copy_type(T, N, S) ->
2804    mnesia_schema:change_table_copy_type(T, N, S).
2805
2806-spec clear_table(Tab::table()) -> t_result('ok').
2807clear_table(Tab) ->
2808    case get(mnesia_activity_state) of
2809	State = {Mod, Tid, _Ts} when element(1, Tid) =/= tid ->
2810	    transaction(State, fun() -> do_clear_table(Tab) end, [], infinity, Mod, sync);
2811	undefined ->
2812	    transaction(undefined, fun() -> do_clear_table(Tab) end, [], infinity, ?DEFAULT_ACCESS, sync);
2813	_ -> %% Not allowed for clear_table
2814	    mnesia:abort({aborted, nested_transaction})
2815    end.
2816
2817do_clear_table(Tab) ->
2818    case get(mnesia_activity_state) of
2819	{?DEFAULT_ACCESS, Tid, Ts}  ->
2820	    clear_table(Tid, Ts, Tab, '_');
2821	{Mod, Tid, Ts} ->
2822	    Mod:clear_table(Tid, Ts, Tab, '_');
2823	_ ->
2824	    abort(no_transaction)
2825    end.
2826
2827clear_table(Tid, Ts, Tab, Obj) when element(1, Tid) =:= tid ->
2828    Store = Ts#tidstore.store,
2829    mnesia_locker:wlock_table(Tid, Store, Tab),
2830    Oid = {Tab, '_'},
2831    ?ets_insert(Store, {Oid, Obj, clear_table}),
2832    ok.
2833
2834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835%% Table mgt - user properties
2836-spec read_table_property(Tab::table(), PropKey::term()) -> Res::tuple().
2837read_table_property(Tab, PropKey) ->
2838    val({Tab, user_property, PropKey}).
2839
2840-spec write_table_property(Tab::table(), Prop::tuple()) -> t_result('ok').
2841write_table_property(Tab, Prop) ->
2842    mnesia_schema:write_table_property(Tab, Prop).
2843
2844-spec delete_table_property(Tab::table(), PropKey::term()) -> t_result('ok').
2845delete_table_property(Tab, PropKey) ->
2846    mnesia_schema:delete_table_property(Tab, PropKey).
2847
2848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2849%% Table mgt - user properties
2850
2851-spec change_table_frag(Tab::table(), FP::term()) -> t_result('ok').
2852change_table_frag(Tab, FragProp) ->
2853    mnesia_schema:change_table_frag(Tab, FragProp).
2854
2855%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2856%% Table mgt - table load
2857
2858%% Dump a ram table to disc
2859-spec dump_tables([Tab::table()]) -> t_result('ok').
2860dump_tables(Tabs) ->
2861    mnesia_schema:dump_tables(Tabs).
2862
2863%% allow the user to wait for some tables to be loaded
2864-spec wait_for_tables([Tab::table()], TMO::timeout()) ->
2865      result() | {'timeout', [table()]}.
2866wait_for_tables(Tabs, Timeout) ->
2867    mnesia_controller:wait_for_tables(Tabs, Timeout).
2868
2869-spec force_load_table(Tab::table()) -> 'yes' | {'error', Reason::term()}.
2870force_load_table(Tab) ->
2871    case mnesia_controller:force_load_table(Tab) of
2872	ok -> yes; % Backwards compatibility
2873	Other -> Other
2874    end.
2875
2876-spec change_table_access_mode(Tab::table(), Mode) -> t_result('ok') when
2877      Mode :: 'read_only'|'read_write'.
2878change_table_access_mode(T, Access) ->
2879    mnesia_schema:change_table_access_mode(T, Access).
2880
2881-spec change_table_load_order(Tab::table(), Order) -> t_result('ok') when
2882      Order :: non_neg_integer().
2883change_table_load_order(T, O) ->
2884    mnesia_schema:change_table_load_order(T, O).
2885
2886-spec change_table_majority(Tab::table(), M::boolean()) -> t_result('ok').
2887change_table_majority(T, M) ->
2888    mnesia_schema:change_table_majority(T, M).
2889
2890-spec set_master_nodes(Ns::[node()]) -> result().
2891set_master_nodes(Nodes) when is_list(Nodes) ->
2892    UseDir = system_info(use_dir),
2893    IsRunning = system_info(is_running),
2894    case IsRunning of
2895	yes ->
2896	    CsPat = {{'_', cstruct}, '_'},
2897	    Cstructs0 = ?ets_match_object(mnesia_gvar, CsPat),
2898	    Cstructs = [Cs || {_, Cs} <- Cstructs0],
2899	    log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning);
2900	_NotRunning ->
2901	    case UseDir of
2902		true ->
2903		    mnesia_lib:lock_table(schema),
2904		    Res =
2905			case mnesia_schema:read_cstructs_from_disc() of
2906			    {ok, Cstructs} ->
2907				log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning);
2908			    {error, Reason} ->
2909				{error, Reason}
2910			end,
2911			mnesia_lib:unlock_table(schema),
2912		    Res;
2913		false ->
2914		    ok
2915	    end
2916    end;
2917set_master_nodes(Nodes) ->
2918    {error, {bad_type, Nodes}}.
2919
2920log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning) ->
2921    Fun = fun(Cs) ->
2922		  Copies = mnesia_lib:copy_holders(Cs),
2923		  Valid = mnesia_lib:intersect(Nodes, Copies),
2924		  {Cs#cstruct.name, Valid}
2925	  end,
2926    Args = lists:map(Fun, Cstructs),
2927    mnesia_recover:log_master_nodes(Args, UseDir, IsRunning).
2928
2929-spec set_master_nodes(Tab::table(), Ns::[node()]) -> result().
2930set_master_nodes(Tab, Nodes) when is_list(Nodes) ->
2931    UseDir = system_info(use_dir),
2932    IsRunning = system_info(is_running),
2933    case IsRunning of
2934	yes ->
2935	    case ?catch_val({Tab, cstruct}) of
2936		{'EXIT', _} ->
2937		    {error, {no_exists, Tab}};
2938		Cs ->
2939		    case Nodes -- mnesia_lib:copy_holders(Cs) of
2940			[] ->
2941			    Args = [{Tab , Nodes}],
2942			    mnesia_recover:log_master_nodes(Args, UseDir, IsRunning);
2943			BadNodes ->
2944			    {error, {no_exists, Tab,  BadNodes}}
2945		    end
2946	    end;
2947	_NotRunning ->
2948	    case UseDir of
2949		true ->
2950		    mnesia_lib:lock_table(schema),
2951		    Res =
2952			case mnesia_schema:read_cstructs_from_disc() of
2953			    {ok, Cstructs} ->
2954				case lists:keysearch(Tab, 2, Cstructs) of
2955				    {value, Cs} ->
2956					case Nodes -- mnesia_lib:copy_holders(Cs) of
2957					    [] ->
2958						Args = [{Tab , Nodes}],
2959						mnesia_recover:log_master_nodes(Args, UseDir, IsRunning);
2960					    BadNodes ->
2961						{error, {no_exists, Tab,  BadNodes}}
2962					end;
2963				    false ->
2964					{error, {no_exists, Tab}}
2965				end;
2966			    {error, Reason} ->
2967				{error, Reason}
2968			end,
2969		    mnesia_lib:unlock_table(schema),
2970		    Res;
2971		false ->
2972		    ok
2973	    end
2974    end;
2975set_master_nodes(Tab, Nodes) ->
2976    {error, {bad_type, Tab, Nodes}}.
2977
2978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2979%% Misc admin
2980-spec dump_log() -> 'dumped'.
2981dump_log() ->
2982    mnesia_controller:sync_dump_log(user).
2983
2984-spec sync_log() -> result().
2985sync_log() ->
2986    mnesia_monitor:sync_log(latest_log).
2987
2988-spec subscribe(What) -> {'ok', node()} | {'error', Reason::term()} when
2989      What :: 'system' | 'activity' | {'table', table(), 'simple' | 'detailed'}.
2990subscribe(What) ->
2991    mnesia_subscr:subscribe(self(), What).
2992
2993-spec unsubscribe(What) -> {'ok', node()} | {'error', Reason::term()} when
2994      What :: 'system' | 'activity' | {'table', table(), 'simple' | 'detailed'}.
2995unsubscribe(What) ->
2996    mnesia_subscr:unsubscribe(self(), What).
2997
2998-spec report_event(Event::_) -> 'ok'.
2999report_event(Event) ->
3000    mnesia_lib:report_system_event({mnesia_user, Event}).
3001
3002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3003%% Snmp
3004-spec snmp_open_table(Tab::table(), Snmp::snmp_struct()) -> 'ok'.
3005snmp_open_table(Tab, Us) ->
3006    mnesia_schema:add_snmp(Tab, Us).
3007
3008-spec snmp_close_table(Tab::table()) -> 'ok'.
3009snmp_close_table(Tab) ->
3010    mnesia_schema:del_snmp(Tab).
3011
3012-spec snmp_get_row(Tab::table(), [integer()]) -> {'ok', Row::tuple()} | 'undefined'.
3013snmp_get_row(Tab, RowIndex) when is_atom(Tab), Tab /= schema, is_list(RowIndex) ->
3014    case get(mnesia_activity_state) of
3015 	{Mod, Tid, Ts=#tidstore{store=Store}} when element(1, Tid) =:= tid ->
3016	    case snmp_oid_to_mnesia_key(RowIndex, Tab) of
3017		unknown -> %% Arrg contains fix_string
3018		    Ops = find_ops(Store, Tab, val({Tab, wild_pattern})),
3019		    SnmpType = val({Tab,snmp}),
3020		    Fix = fun({{_,Key},Row,Op}, Res) ->
3021				  case mnesia_snmp_hook:key_to_oid(Tab,Key,SnmpType) of
3022				      RowIndex ->
3023					  case Op of
3024					      write -> {ok, Row};
3025					      _ ->
3026						  undefined
3027					  end;
3028				      _ ->
3029					  Res
3030				  end
3031			  end,
3032		    lists:foldl(Fix, undefined, Ops);
3033		Key ->
3034		    case Mod:read(Tid, Ts, Tab, Key, read) of
3035			[Row] ->
3036			    {ok, Row};
3037			_ ->
3038			    undefined
3039		    end
3040	    end;
3041	_ ->
3042 	    dirty_rpc(Tab, mnesia_snmp_hook, get_row, [Tab, RowIndex])
3043    end;
3044snmp_get_row(Tab, _RowIndex) ->
3045    abort({bad_type, Tab}).
3046
3047%%%%%%%%%%%%%
3048-spec snmp_get_next_index(Tab::table(), [integer()]) -> {'ok', [integer()]} | 'endOfTable'.
3049snmp_get_next_index(Tab, RowIndex) when is_atom(Tab), Tab /= schema, is_list(RowIndex) ->
3050    {Next,OrigKey} = dirty_rpc(Tab, mnesia_snmp_hook, get_next_index, [Tab, RowIndex]),
3051    case get(mnesia_activity_state) of
3052	{_Mod, Tid, #tidstore{store=Store}} when element(1, Tid) =:= tid ->
3053	    case OrigKey of
3054		undefined ->
3055		    snmp_order_keys(Store, Tab, RowIndex, []);
3056		_ ->
3057		    case ?ets_match(Store, {{Tab,OrigKey}, '_', '$1'}) of
3058			[] ->  snmp_order_keys(Store,Tab,RowIndex,[OrigKey]);
3059			Ops ->
3060			    case lists:last(Ops) of
3061				[delete] -> snmp_get_next_index(Tab, Next);
3062				_ -> snmp_order_keys(Store,Tab,RowIndex,[OrigKey])
3063			    end
3064		    end
3065	    end;
3066	_ ->
3067	    case Next of
3068		endOfTable -> endOfTable;
3069		_ -> {ok, Next}
3070	    end
3071    end;
3072snmp_get_next_index(Tab, _RowIndex) ->
3073    abort({bad_type, Tab}).
3074
3075snmp_order_keys(Store,Tab,RowIndex,Def) ->
3076    All = ?ets_match(Store, {{Tab,'$1'},'_','$2'}),
3077    SnmpType = val({Tab,snmp}),
3078    Keys0 = [mnesia_snmp_hook:key_to_oid(Tab,Key,SnmpType) ||
3079		Key <- ts_keys_1(All, Def)],
3080    Keys = lists:sort(Keys0),
3081    get_ordered_snmp_key(RowIndex,Keys).
3082
3083get_ordered_snmp_key(Prev, [First|_]) when Prev < First -> {ok, First};
3084get_ordered_snmp_key(Prev, [_|R]) ->
3085    get_ordered_snmp_key(Prev, R);
3086get_ordered_snmp_key(_, []) ->
3087    endOfTable.
3088
3089%%%%%%%%%%
3090-spec snmp_get_mnesia_key(Tab::table(), [integer()]) -> {'ok', Key::term()} | 'undefined'.
3091snmp_get_mnesia_key(Tab, RowIndex) when is_atom(Tab), Tab /= schema, is_list(RowIndex) ->
3092    case get(mnesia_activity_state) of
3093 	{_Mod, Tid, Ts} when element(1, Tid) =:= tid ->
3094	    Res = dirty_rpc(Tab,mnesia_snmp_hook,get_mnesia_key,[Tab,RowIndex]),
3095	    snmp_filter_key(Res, RowIndex, Tab, Ts#tidstore.store);
3096	_ ->
3097	    dirty_rpc(Tab, mnesia_snmp_hook, get_mnesia_key, [Tab, RowIndex])
3098    end;
3099snmp_get_mnesia_key(Tab, _RowIndex) ->
3100    abort({bad_type, Tab}).
3101
3102snmp_oid_to_mnesia_key(RowIndex, Tab) ->
3103    case mnesia_snmp_hook:oid_to_key(RowIndex, Tab) of
3104	unknown ->  %% Contains fix_string needs lookup
3105	    case dirty_rpc(Tab,mnesia_snmp_hook,get_mnesia_key,[Tab,RowIndex]) of
3106		{ok, MnesiaKey} -> MnesiaKey;
3107		undefined -> unknown
3108	    end;
3109	MnesiaKey ->
3110	    MnesiaKey
3111    end.
3112
3113snmp_filter_key(Res = {ok,Key}, _RowIndex, Tab, Store) ->
3114    case ?ets_lookup(Store, {Tab,Key}) of
3115	[] -> Res;
3116	Ops ->
3117	    case lists:last(Ops) of
3118		{_, _, write} -> Res;
3119		_ -> undefined
3120	    end
3121    end;
3122snmp_filter_key(undefined, RowIndex, Tab, Store) ->
3123    case mnesia_snmp_hook:oid_to_key(RowIndex, Tab) of
3124	unknown ->  %% Arrg contains fix_string
3125	    Ops = find_ops(Store, Tab, val({Tab, wild_pattern})),
3126	    SnmpType = val({Tab,snmp}),
3127	    Fix = fun({{_,Key},_,Op}, Res) ->
3128			  case mnesia_snmp_hook:key_to_oid(Tab,Key,SnmpType) of
3129			      RowIndex ->
3130				  case Op of
3131				      write -> {ok, Key};
3132				      _ ->
3133					  undefined
3134				  end;
3135			      _ ->
3136				  Res
3137			  end
3138		  end,
3139	    lists:foldl(Fix, undefined, Ops);
3140	Key ->
3141	    case ?ets_lookup(Store, {Tab,Key}) of
3142		[] ->
3143		    undefined;
3144		Ops ->
3145		    case lists:last(Ops) of
3146			{_, _, write} -> {ok, Key};
3147			_ -> undefined
3148		    end
3149	    end
3150    end.
3151
3152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3153%% Textfile access
3154-spec load_textfile(File::file:filename()) -> t_result('ok') | {'error', term()}.
3155load_textfile(F) ->
3156    mnesia_text:load_textfile(F).
3157
3158-spec dump_to_textfile(File :: file:filename()) -> result() | 'error'.
3159dump_to_textfile(F) ->
3160    mnesia_text:dump_to_textfile(F).
3161
3162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3163%% QLC Handles
3164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3165-spec table(Tab::table()) -> qlc:query_handle().
3166table(Tab) ->
3167    table(Tab, []).
3168
3169-spec table(Tab::table(), Options) -> qlc:query_handle() when
3170      Options   :: Option | [Option],
3171      Option    :: MnesiaOpt | QlcOption,
3172      MnesiaOpt :: {'traverse', SelectOp} | {'lock', lock_kind()} | {'n_objects', non_neg_integer()},
3173      SelectOp  ::  'select' | {'select', ets:match_spec()},
3174      QlcOption :: {'key_equality', '==' | '=:='}.
3175table(Tab,Opts) ->
3176    {[Trav,Lock,NObjects],QlcOptions0} =
3177	qlc_opts(Opts,[{traverse,select},{lock,read},{n_objects,100}]),
3178    TF = case Trav of
3179	     {select,Ms} ->
3180		 fun() -> qlc_select(select(Tab,Ms,NObjects,Lock)) end;
3181	     select ->
3182		 fun(Ms) -> qlc_select(select(Tab,Ms,NObjects,Lock)) end;
3183	     _ ->
3184		 erlang:error({badarg, {Trav,[Tab, Opts]}})
3185	 end,
3186    Pre  = fun(Arg) -> pre_qlc(Arg, Tab) end,
3187    Post = fun()  -> post_qlc(Tab) end,
3188    Info = fun(Tag) -> qlc_info(Tab, Tag) end,
3189    ParentFun = fun() ->
3190			{mnesia_activity, mnesia:get_activity_id()}
3191		end,
3192    Lookup =
3193	case Trav of
3194	    {select, _} -> [];
3195	    _ ->
3196		LFun = fun(2, Keys) ->
3197			       Read = fun(Key) -> read(Tab,Key,Lock) end,
3198			       lists:flatmap(Read, Keys);
3199			  (Index,Keys) ->
3200			       IdxRead = fun(Key) -> index_read(Tab,Key,Index) end,
3201			       lists:flatmap(IdxRead, Keys)
3202		       end,
3203		[{lookup_fun, LFun}]
3204	end,
3205    MFA  = fun(Type) -> qlc_format(Type, Tab, NObjects, Lock, Opts) end,
3206    QlcOptions = [{pre_fun, Pre}, {post_fun, Post},
3207		  {info_fun, Info}, {parent_fun, ParentFun},
3208		  {format_fun, MFA}|Lookup] ++ QlcOptions0,
3209    qlc:table(TF, QlcOptions).
3210
3211pre_qlc(Opts, Tab) ->
3212    {_,Tid,_} =
3213	case get(mnesia_activity_state) of
3214	    undefined ->
3215		case lists:keysearch(parent_value, 1, Opts) of
3216		    {value, {parent_value,{mnesia_activity,undefined}}} ->
3217			abort(no_transaction);
3218		    {value, {parent_value,{mnesia_activity,Aid}}} ->
3219			{value,{stop_fun,Stop}} =
3220			    lists:keysearch(stop_fun,1,Opts),
3221			put_activity_id(Aid,Stop),
3222			Aid;
3223		    _ ->
3224			abort(no_transaction)
3225		end;
3226	    Else ->
3227		Else
3228	end,
3229    case element(1,Tid) of
3230	tid -> ok;
3231	_ ->
3232	    case ?catch_val({Tab, setorbag}) of
3233		ordered_set ->   ok;
3234		_ ->
3235		    dirty_rpc(Tab, mnesia_tm, fixtable, [Tab,true,self()]),
3236		    ok
3237	    end
3238    end.
3239
3240post_qlc(Tab) ->
3241    case get(mnesia_activity_state) of
3242	{_,#tid{},_} -> ok;
3243	_ ->
3244	    case ?catch_val({Tab, setorbag}) of
3245		ordered_set ->
3246		    ok;
3247		_ ->
3248		    dirty_rpc(Tab, mnesia_tm, fixtable, [Tab,false,self()]),
3249		    ok
3250	    end
3251    end.
3252
3253qlc_select('$end_of_table') ->     [];
3254qlc_select({[], Cont}) -> qlc_select(select(Cont));
3255qlc_select({Objects, Cont}) ->
3256    Objects ++ fun() -> qlc_select(select(Cont)) end.
3257
3258qlc_opts(Opts, Keys) when is_list(Opts) ->
3259    qlc_opts(Opts, Keys, []);
3260qlc_opts(Option, Keys) ->
3261    qlc_opts([Option], Keys, []).
3262
3263qlc_opts(Opts, [{Key,Def}|Keys], Acc) ->
3264    Opt = case lists:keysearch(Key,1, Opts) of
3265	      {value, {Key,Value}} ->
3266		  Value;
3267	      false ->
3268		  Def
3269	  end,
3270    qlc_opts(lists:keydelete(Key,1,Opts),Keys,[Opt|Acc]);
3271qlc_opts(Opts,[],Acc) -> {lists:reverse(Acc),Opts}.
3272
3273qlc_info(Tab, num_of_objects) ->
3274    dirty_rpc(Tab, ?MODULE, raw_table_info, [Tab, size]);
3275qlc_info(_, keypos) ->    2;
3276qlc_info(_, is_unique_objects) ->    true;
3277qlc_info(Tab, is_unique_keys) ->
3278    case val({Tab, type}) of
3279	set -> true;
3280	ordered_set -> true;
3281	_ -> false
3282    end;
3283qlc_info(Tab, is_sorted_objects) ->
3284    case val({Tab, type}) of
3285	ordered_set ->
3286	    case ?catch_val({Tab, frag_hash}) of
3287		{'EXIT', _} ->
3288		    ascending;
3289		_ ->  %% Fragmented tables are not ordered
3290		    no
3291	    end;
3292	_ -> no
3293    end;
3294qlc_info(Tab, indices) ->
3295    val({Tab,index});
3296qlc_info(_Tab, _) ->
3297    undefined.
3298
3299qlc_format(all, Tab, NObjects, Lock, Opts) ->
3300    {?MODULE, table, [Tab,[{n_objects, NObjects}, {lock,Lock}|Opts]]};
3301qlc_format({match_spec, Ms}, Tab, NObjects, Lock, Opts) ->
3302    {?MODULE, table, [Tab,[{traverse,{select,Ms}},{n_objects, NObjects}, {lock,Lock}|Opts]]};
3303qlc_format({lookup, 2, Keys}, Tab, _, Lock, _) ->
3304    io_lib:format("lists:flatmap(fun(V) -> "
3305		  "~w:read(~w, V, ~w) end, ~w)",
3306		  [?MODULE, Tab, Lock, Keys]);
3307qlc_format({lookup, Index,Keys}, Tab, _, _, _) ->
3308    io_lib:format("lists:flatmap(fun(V) -> "
3309		  "~w:index_read(~w, V, ~w) end, ~w)",
3310		  [?MODULE, Tab, Index, Keys]).
3311
3312
3313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3314
3315do_fixtable(Tab, #tidstore{store=Store}) ->
3316    do_fixtable(Tab,Store);
3317do_fixtable(Tab, Store) ->
3318    case ?catch_val({Tab, setorbag}) of
3319	ordered_set ->
3320	    ok;
3321	_ ->
3322	    case ?ets_match_object(Store, {fixtable, {Tab, '_'}}) of
3323		[] ->
3324		    Node = dirty_rpc(Tab, mnesia_tm, fixtable, [Tab,true,self()]),
3325		    ?ets_insert(Store, {fixtable, {Tab, Node}});
3326		_ ->
3327		    ignore
3328	    end,
3329	    ok
3330    end.
3331
3332%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3333%% Mnemosyne exclusive
3334
3335get_activity_id() ->
3336    get(mnesia_activity_state).
3337
3338put_activity_id(Activity) ->
3339    mnesia_tm:put_activity_id(Activity).
3340put_activity_id(Activity,Fun) ->
3341    mnesia_tm:put_activity_id(Activity,Fun).
3342
3343regular_indexes(Tab) ->
3344    PosList = val({Tab, index}),
3345    [P || P <- PosList, is_integer(P)].
3346