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