PageRenderTime 14ms CodeModel.GetById 1315ms app.highlight 1927ms RepoModel.GetById 38ms app.codeStats 1ms

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

http://github.com/gebi/jungerl
Erlang | 2779 lines | 2285 code | 310 blank | 184 comment | 41 complexity | 74adda6f0a42b3a008e541753cbd3c19 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1%% ``The contents of this file are subject to the Erlang Public License,
   2%% Version 1.1, (the "License"); you may not use this file except in
   3%% compliance with the License. You should have received a copy of the
   4%% Erlang Public License along with this software. If not, it can be
   5%% retrieved via the world wide web at http://www.erlang.org/.
   6%% 
   7%% Software distributed under the License is distributed on an "AS IS"
   8%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   9%% the License for the specific language governing rights and limitations
  10%% under the License.
  11%% 
  12%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14%% AB. All Rights Reserved.''
  15%% 
  16%%     $Id$
  17%%
  18%% This module exports the public interface of the Mnesia DBMS engine
  19
  20-module(mnesia).
  21%-behaviour(mnesia_access).
  22
  23-export([
  24	 %% Start, stop and debugging
  25	 start/0, start/1, stop/0,           % Not for public use
  26	 set_debug_level/1, lkill/0, kill/0, % Not for public use
  27	 ms/0, nc/0, nc/1, ni/0, ni/1,       % Not for public use
  28	 change_config/2,
  29
  30	 %% Activity mgt
  31	 abort/1, transaction/1, transaction/2, transaction/3,
  32	 sync_transaction/1, sync_transaction/2, sync_transaction/3,
  33	 async_dirty/1, async_dirty/2, sync_dirty/1, sync_dirty/2, ets/1, ets/2,
  34	 activity/2, activity/3, activity/4, % Not for public use
  35
  36	 %% Access within an activity - Lock acquisition
  37	 lock/2, lock/4,
  38	 read_lock_table/1, 
  39	 write_lock_table/1,
  40
  41	 %% Access within an activity - Updates
  42	 write/1, s_write/1, write/3, write/5, 
  43	 delete/1, s_delete/1, delete/3, delete/5, 
  44	 delete_object/1, s_delete_object/1, delete_object/3, delete_object/5, 
  45	 
  46	 %% Access within an activity - Reads
  47	 read/1, wread/1, read/3, read/5,
  48	 match_object/1, match_object/3, match_object/5,
  49	 select/1,select/2,select/3,select/4,select/5,select/6,
  50	 all_keys/1, all_keys/4,
  51	 index_match_object/2, index_match_object/4, index_match_object/6,
  52	 index_read/3, index_read/6,
  53	 first/1, next/2, last/1, prev/2,
  54
  55	 %% Iterators within an activity 
  56	 foldl/3, foldl/4, foldr/3, foldr/4,
  57	 
  58	 %% Dirty access regardless of activities - Updates
  59	 dirty_write/1, dirty_write/2,
  60	 dirty_delete/1, dirty_delete/2,
  61	 dirty_delete_object/1, dirty_delete_object/2,
  62	 dirty_update_counter/2, dirty_update_counter/3,
  63
  64	 %% Dirty access regardless of activities - Read
  65	 dirty_read/1, dirty_read/2,
  66	 dirty_select/2,
  67	 dirty_match_object/1, dirty_match_object/2, dirty_all_keys/1,
  68	 dirty_index_match_object/2, dirty_index_match_object/3,
  69	 dirty_index_read/3, dirty_slot/2, 
  70	 dirty_first/1, dirty_next/2, dirty_last/1, dirty_prev/2, 
  71
  72	 %% Info
  73	 table_info/2, table_info/4, schema/0, schema/1,
  74	 error_description/1, info/0, system_info/1,
  75	 system_info/0,                      % Not for public use
  76
  77	 %% Database mgt
  78	 create_schema/1, delete_schema/1,
  79	 backup/1, backup/2, traverse_backup/4, traverse_backup/6,
  80	 install_fallback/1, install_fallback/2,
  81	 uninstall_fallback/0, uninstall_fallback/1,
  82	 activate_checkpoint/1, deactivate_checkpoint/1,
  83	 backup_checkpoint/2, backup_checkpoint/3, restore/2,
  84
  85	 %% Table mgt
  86	 create_table/1, create_table/2, delete_table/1,
  87	 add_table_copy/3, del_table_copy/2, move_table_copy/3,
  88	 add_table_index/2, del_table_index/2,
  89	 transform_table/3, transform_table/4,
  90	 change_table_copy_type/3,
  91	 read_table_property/2, write_table_property/2, delete_table_property/2,
  92	 change_table_frag/2,
  93	 clear_table/1,
  94
  95	 %% Table load
  96	 dump_tables/1, wait_for_tables/2, force_load_table/1,
  97	 change_table_access_mode/2, change_table_load_order/2,
  98	 set_master_nodes/1, set_master_nodes/2,
  99	 onload_fun/2,  % UW 2005-12-22
 100	 
 101	 %% Misc admin
 102	 dump_log/0, subscribe/1, unsubscribe/1, report_event/1,
 103
 104	 %% Snmp
 105	 snmp_open_table/2, snmp_close_table/1,
 106	 snmp_get_row/2, snmp_get_next_index/2, snmp_get_mnesia_key/2,
 107
 108	 %% Textfile access
 109	 load_textfile/1, dump_to_textfile/1,
 110	 
 111	 %% QLC functions
 112	 table/1, table/2,
 113
 114	 %% Mnemosyne exclusive
 115	 get_activity_id/0, put_activity_id/1, % Not for public use
 116
 117	 %% Mnesia internal functions
 118	 dirty_rpc/4,                          % Not for public use
 119	 has_var/1, fun_select/7, fun_select/10, select_cont/3, dirty_sel_init/5,
 120	 foldl/6, foldr/6,
 121
 122	 %% Module internal callback functions
 123	 raw_table_info/2,                      % Not for public use
 124	 remote_dirty_match_object/2,           % Not for public use
 125	 remote_dirty_select/2                  % Not for public use
 126	]).
 127
 128-export([begin_activity/4, end_activity/4]).
 129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 130
 131-include("mnesia.hrl").
 132-import(mnesia_lib, [verbose/2]).
 133
 134-define(DEFAULT_ACCESS, ?MODULE).
 135
 136%% Select 
 137-define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]).
 138-define(PATTERN_TO_BINDINGS_MATCH_SPEC(Pat), [{Pat,[],['$$']}]).
 139
 140
 141begin_activity(_Type, _Factor, _Tid, _Ts) ->
 142    ok.
 143
 144end_activity(_Result, _Type, _Tid, _Ts) ->
 145    ok.
 146   
 147%% Local function in order to avoid external function call
 148val(Var) ->
 149    case ?catch_val(Var) of
 150	{'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); 
 151	Value -> Value
 152    end.
 153
 154is_dollar_digits(Var) ->
 155    case atom_to_list(Var) of
 156	[$$ | Digs] -> 
 157	    is_digits(Digs);
 158	_ ->
 159	    false
 160    end.
 161
 162is_digits([Dig | Tail]) ->
 163    if
 164	$0 =< Dig, Dig =< $9 ->
 165	    is_digits(Tail);
 166	true ->
 167	    false
 168    end;
 169is_digits([]) ->
 170    true.
 171
 172has_var(X) when atom(X) -> 
 173    if 
 174	X == '_' -> 
 175	    true;
 176	atom(X) -> 
 177	    is_dollar_digits(X);
 178	true  -> 
 179	    false
 180    end;
 181has_var(X) when tuple(X) ->
 182    e_has_var(X, size(X));
 183has_var([H|T]) ->
 184    case has_var(H) of
 185	false -> has_var(T);
 186	Other -> Other
 187    end;
 188has_var(_) -> false.
 189
 190e_has_var(_, 0) -> false;
 191e_has_var(X, Pos) ->
 192    case has_var(element(Pos, X))of
 193	false -> e_has_var(X, Pos-1);
 194	Other -> Other
 195    end.
 196
 197%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 198%% Start and stop
 199
 200start() ->
 201    {Time , Res} =  timer:tc(application, start, [?APPLICATION, temporary]),
 202    
 203    Secs = Time div 1000000,
 204    case Res of 
 205	ok ->
 206	    verbose("Mnesia started, ~p seconds~n",[ Secs]),
 207	    ok;
 208	{error, {already_started, mnesia}} ->
 209	    verbose("Mnesia already started, ~p seconds~n",[ Secs]),
 210	    ok;
 211	{error, R} ->
 212	    verbose("Mnesia failed to start, ~p seconds: ~p~n",[ Secs, R]),
 213	    {error, R}
 214    end.
 215
 216start(ExtraEnv) when list(ExtraEnv) ->
 217    case mnesia_lib:ensure_loaded(?APPLICATION) of
 218	ok ->
 219	    patched_start(ExtraEnv);
 220	Error ->
 221	    Error
 222    end;
 223start(ExtraEnv) ->
 224    {error, {badarg, ExtraEnv}}.
 225
 226patched_start([{Env, Val} | Tail]) when atom(Env) ->
 227    case mnesia_monitor:patch_env(Env, Val) of
 228	{error, Reason} ->
 229	    {error, Reason};
 230	_NewVal ->
 231	    patched_start(Tail)
 232    end;
 233patched_start([Head | _]) ->
 234    {error, {bad_type, Head}};
 235patched_start([]) ->
 236    start().
 237
 238stop() ->
 239    case application:stop(?APPLICATION) of
 240	ok -> stopped;
 241	{error, {not_started, ?APPLICATION}} -> stopped;
 242	Other -> Other
 243    end.
 244
 245change_config(extra_db_nodes, Ns) when list(Ns) ->
 246    mnesia_controller:connect_nodes(Ns);
 247change_config(BadKey, _BadVal) ->
 248    {error, {badarg, BadKey}}.
 249     
 250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 251%% Debugging
 252
 253set_debug_level(Level) -> 
 254    mnesia_subscr:set_debug_level(Level).
 255
 256lkill() ->
 257    mnesia_sup:kill().
 258
 259kill() ->
 260    rpc:multicall(mnesia_sup, kill, []).
 261
 262ms() ->
 263    [
 264     mnesia,
 265     mnesia_backup,
 266     mnesia_bup,
 267     mnesia_checkpoint,
 268     mnesia_checkpoint_sup,
 269     mnesia_controller,
 270     mnesia_dumper,
 271     mnesia_loader,
 272     mnesia_frag, 
 273     mnesia_frag_hash, 
 274     mnesia_frag_old_hash, 
 275     mnesia_index,
 276     mnesia_kernel_sup,
 277     mnesia_late_loader,
 278     mnesia_lib,
 279     mnesia_log,
 280     mnesia_registry,
 281     mnesia_schema,
 282     mnesia_snmp_hook,
 283     mnesia_snmp_sup,
 284     mnesia_subscr,
 285     mnesia_sup,
 286     mnesia_text,
 287     mnesia_tm,
 288     mnesia_recover,
 289     mnesia_locker,
 290
 291     %% Keep these last in the list, so
 292     %% mnesia_sup kills these last
 293     mnesia_monitor, 
 294     mnesia_event
 295    ]. 
 296
 297nc() ->
 298    Mods = ms(),
 299    nc(Mods).
 300
 301nc(Mods) when list(Mods)->
 302    [Mod || Mod <- Mods, ok /= load(Mod, compile)].
 303
 304ni() -> 
 305    Mods = ms(),
 306    ni(Mods).
 307
 308ni(Mods) when list(Mods) ->
 309    [Mod || Mod <- Mods, ok /= load(Mod, interpret)].
 310
 311load(Mod, How) when atom(Mod) ->
 312    case try_load(Mod, How) of
 313	ok ->
 314	    ok;
 315	_ ->
 316	    mnesia_lib:show( "~n RETRY ~p FROM: ", [Mod]),
 317	    Abs = mod2abs(Mod),
 318	    load(Abs, How)
 319    end;
 320load(Abs, How) ->
 321    case try_load(Abs, How) of
 322	ok ->
 323	    ok;
 324	{error, Reason} ->
 325	    mnesia_lib:show( " *** ERROR *** ~p~n", [Reason]),
 326	    {error, Reason}
 327    end.
 328
 329try_load(Mod, How) ->
 330    mnesia_lib:show( " ~p ", [Mod]),
 331    Flags = [{d, debug}],
 332    case How of
 333	compile ->
 334	    case catch c:nc(Mod, Flags) of
 335		{ok, _} -> ok;
 336		Other -> {error, Other}
 337	    end;
 338	interpret ->
 339	    case catch int:ni(Mod, Flags) of
 340		{module, _} -> ok;
 341		Other -> {error, Other}
 342	    end
 343    end.
 344
 345mod2abs(Mod) ->
 346    ModString = atom_to_list(Mod),
 347    SubDir =
 348	case lists:suffix("test", ModString) of
 349	    true -> test;
 350	    false -> src
 351	end,
 352    filename:join([code:lib_dir(?APPLICATION), SubDir, ModString]).
 353
 354%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 355%% Activity mgt
 356
 357abort(Reason) -> 
 358    exit({aborted, Reason}).
 359
 360transaction(Fun) ->
 361    transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, async).
 362transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
 363    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
 364transaction(Fun, Retries) when Retries == infinity ->
 365    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
 366transaction(Fun, Args) ->
 367    transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, async).
 368transaction(Fun, Args, Retries) ->
 369    transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, async).
 370
 371sync_transaction(Fun) ->
 372    transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, sync).
 373sync_transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
 374    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
 375sync_transaction(Fun, Retries) when Retries == infinity ->
 376    transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
 377sync_transaction(Fun, Args) ->
 378    transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, sync).
 379sync_transaction(Fun, Args, Retries) ->
 380    transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, sync).
 381
 382
 383transaction(State, Fun, Args, Retries, Mod, Kind) 
 384  when function(Fun), list(Args), Retries == infinity, atom(Mod) ->
 385    mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
 386transaction(State, Fun, Args, Retries, Mod, Kind)
 387  when function(Fun), list(Args), integer(Retries), Retries >= 0, atom(Mod) ->
 388    mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
 389transaction(_State, Fun, Args, Retries, Mod, _Kind) ->
 390    {aborted, {badarg, Fun, Args, Retries, Mod}}.
 391
 392non_transaction(State, Fun, Args, ActivityKind, Mod) 
 393  when function(Fun), list(Args), atom(Mod) ->
 394    mnesia_tm:non_transaction(State, Fun, Args, ActivityKind, Mod);
 395non_transaction(_State, Fun, Args, _ActivityKind, _Mod) ->
 396    {aborted, {badarg, Fun, Args}}.
 397
 398async_dirty(Fun) ->
 399    async_dirty(Fun, []).
 400async_dirty(Fun, Args) ->
 401    non_transaction(get(mnesia_activity_state), Fun, Args, async_dirty, ?DEFAULT_ACCESS).
 402
 403sync_dirty(Fun) ->
 404    sync_dirty(Fun, []).
 405sync_dirty(Fun, Args) ->
 406    non_transaction(get(mnesia_activity_state), Fun, Args, sync_dirty, ?DEFAULT_ACCESS).
 407
 408ets(Fun) ->
 409    ets(Fun, []).
 410ets(Fun, Args) ->
 411    non_transaction(get(mnesia_activity_state), Fun, Args, ets, ?DEFAULT_ACCESS).
 412
 413activity(Kind, Fun) ->
 414    activity(Kind, Fun, []).
 415activity(Kind, Fun, Args) when list(Args) ->
 416    activity(Kind, Fun, Args, mnesia_monitor:get_env(access_module));
 417activity(Kind, Fun, Mod) ->
 418    activity(Kind, Fun, [], Mod).
 419
 420activity(Kind, Fun, Args, Mod) ->
 421    State = get(mnesia_activity_state),
 422    case Kind of
 423	ets ->                    non_transaction(State, Fun, Args, Kind, Mod);
 424	async_dirty ->            non_transaction(State, Fun, Args, Kind, Mod);
 425	sync_dirty ->             non_transaction(State, Fun, Args, Kind, Mod);
 426	transaction ->            wrap_trans(State, Fun, Args, infinity, Mod, async);
 427	{transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, async);
 428	sync_transaction ->            wrap_trans(State, Fun, Args, infinity, Mod, sync);
 429	{sync_transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, sync);
 430	_ ->                      {aborted, {bad_type, Kind}}
 431    end.
 432
 433wrap_trans(State, Fun, Args, Retries, Mod, Kind) ->
 434    case transaction(State, Fun, Args, Retries, Mod, Kind) of
 435	{atomic, GoodRes} -> GoodRes;
 436	BadRes -> exit(BadRes)
 437    end.
 438	     
 439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 440%% Access within an activity - lock acquisition
 441
 442%% Grab a lock on an item in the global lock table
 443%% Item may be any term. Lock may be write or read.
 444%% write lock is set on all the given nodes
 445%% read lock is only set on the first node
 446%% Nodes may either be a list of nodes or one node as an atom
 447%% Mnesia on all Nodes must be connected to each other, but
 448%% it is not neccessary that they are up and running.
 449
 450lock(LockItem, LockKind) ->
 451    case get(mnesia_activity_state) of
 452	{?DEFAULT_ACCESS, Tid, Ts} ->
 453	    lock(Tid, Ts, LockItem, LockKind);
 454	{Mod, Tid, Ts} ->
 455	    Mod:lock(Tid, Ts, LockItem, LockKind);
 456	_ ->
 457	    abort(no_transaction)
 458    end.
 459
 460lock(Tid, Ts, LockItem, LockKind) ->
 461    case element(1, Tid) of
 462	tid ->
 463	    case LockItem of
 464		{record, Tab, Key} ->
 465		    lock_record(Tid, Ts, Tab, Key, LockKind);
 466		{table, Tab} ->
 467		    lock_table(Tid, Ts, Tab, LockKind);
 468		{global, GlobalKey, Nodes} ->
 469		    global_lock(Tid, Ts, GlobalKey, LockKind, Nodes);
 470		_ ->
 471		    abort({bad_type, LockItem})
 472	    end;
 473	_Protocol ->
 474	    []
 475    end.
 476
 477%% Grab a read lock on a whole table
 478read_lock_table(Tab) ->
 479    lock({table, Tab}, read),
 480    ok.
 481
 482%% Grab a write lock on a whole table
 483write_lock_table(Tab) ->
 484    lock({table, Tab}, write),
 485    ok.
 486
 487lock_record(Tid, Ts, Tab, Key, LockKind) when atom(Tab) ->
 488    Store = Ts#tidstore.store,
 489    Oid =  {Tab, Key},
 490    case LockKind of
 491	read ->
 492	    mnesia_locker:rlock(Tid, Store, Oid);
 493	write ->
 494	    mnesia_locker:wlock(Tid, Store, Oid);
 495	sticky_write ->
 496	    mnesia_locker:sticky_wlock(Tid, Store, Oid);
 497	none ->
 498	    [];
 499	_ ->
 500	    abort({bad_type, Tab, LockKind})
 501    end;
 502lock_record(_Tid, _Ts, Tab, _Key, _LockKind) ->
 503    abort({bad_type, Tab}).
 504
 505lock_table(Tid, Ts, Tab, LockKind) when atom(Tab) ->
 506    Store = Ts#tidstore.store,
 507    case LockKind of
 508	read ->
 509	    mnesia_locker:rlock_table(Tid, Store, Tab);
 510	write ->
 511	    mnesia_locker:wlock_table(Tid, Store, Tab);
 512	sticky_write ->
 513	    mnesia_locker:sticky_wlock_table(Tid, Store, Tab);
 514	none ->
 515	    [];
 516	_ ->
 517	    abort({bad_type, Tab, LockKind})
 518    end;
 519lock_table(_Tid, _Ts, Tab, _LockKind) ->
 520    abort({bad_type, Tab}).
 521
 522global_lock(Tid, Ts, Item, Kind, Nodes) when list(Nodes) ->
 523    case element(1, Tid) of
 524	tid ->
 525	    Store = Ts#tidstore.store,
 526	    GoodNs = good_global_nodes(Nodes),
 527	    if
 528		Kind /= read, Kind /= write ->
 529		    abort({bad_type, Kind});
 530		true ->
 531		    mnesia_locker:global_lock(Tid, Store, Item, Kind, GoodNs)
 532	    end;
 533	_Protocol ->
 534	    []
 535    end;
 536global_lock(_Tid, _Ts, _Item, _Kind, Nodes) ->
 537    abort({bad_type, Nodes}).
 538
 539good_global_nodes(Nodes) ->
 540    Recover = [node() | val(recover_nodes)],
 541    mnesia_lib:intersect(Nodes, Recover).
 542
 543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 544%% Access within an activity - updates
 545
 546write(Val) when tuple(Val), size(Val) > 2 -> 
 547    Tab = element(1, Val),
 548    write(Tab, Val, write);
 549write(Val) ->
 550    abort({bad_type, Val}).
 551
 552s_write(Val) when tuple(Val), size(Val) > 2 -> 
 553    Tab = element(1, Val),
 554    write(Tab, Val, sticky_write).
 555
 556write(Tab, Val, LockKind) ->
 557    case get(mnesia_activity_state) of
 558	{?DEFAULT_ACCESS, Tid, Ts} ->
 559	    write(Tid, Ts, Tab, Val, LockKind);
 560	{Mod, Tid, Ts} ->
 561	    Mod:write(Tid, Ts, Tab, Val, LockKind);
 562	_ ->
 563	    abort(no_transaction)
 564    end.
 565
 566write(Tid, Ts, Tab, Val, LockKind)
 567  when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
 568    case element(1, Tid) of
 569	ets ->
 570	    ?ets_insert(Tab, Val),
 571	    ok;
 572	tid ->
 573	    Store = Ts#tidstore.store,
 574	    Oid = {Tab, element(2, Val)},
 575	    case LockKind of
 576		write ->
 577		    mnesia_locker:wlock(Tid, Store, Oid);
 578		sticky_write ->
 579		    mnesia_locker:sticky_wlock(Tid, Store, Oid);
 580		_ ->
 581		    abort({bad_type, Tab, LockKind})
 582	    end,
 583	    write_to_store(Tab, Store, Oid, Val);
 584	Protocol ->
 585	    do_dirty_write(Protocol, Tab, Val)
 586    end;
 587write(_Tid, _Ts, Tab, Val, LockKind) ->
 588    abort({bad_type, Tab, Val, LockKind}).
 589
 590write_to_store(Tab, Store, Oid, Val) ->
 591    case ?catch_val({Tab, record_validation}) of
 592	{RecName, Arity, Type}
 593	  when size(Val) == Arity, RecName == element(1, Val) ->
 594	    case Type of
 595		bag ->
 596		    ?ets_insert(Store, {Oid, Val, write});
 597		_  ->
 598		    ?ets_delete(Store, Oid),
 599		    ?ets_insert(Store, {Oid, Val, write})
 600	    end, 
 601	    ok;
 602	{'EXIT', _} ->
 603	    abort({no_exists, Tab});
 604	_ ->
 605	    abort({bad_type, Val})
 606    end.
 607
 608delete({Tab, Key}) ->
 609    delete(Tab, Key, write);
 610delete(Oid) ->
 611    abort({bad_type, Oid}).
 612
 613s_delete({Tab, Key}) ->
 614    delete(Tab, Key, sticky_write);
 615s_delete(Oid) ->
 616    abort({bad_type, Oid}).
 617
 618delete(Tab, Key, LockKind) ->
 619    case get(mnesia_activity_state) of
 620	{?DEFAULT_ACCESS, Tid, Ts} ->
 621	    delete(Tid, Ts, Tab, Key, LockKind);
 622	{Mod, Tid, Ts} ->
 623	    Mod:delete(Tid, Ts, Tab, Key, LockKind);
 624	_ ->
 625	    abort(no_transaction)
 626    end.
 627
 628delete(Tid, Ts, Tab, Key, LockKind)
 629  when atom(Tab), Tab /= schema ->
 630      case element(1, Tid) of
 631	  ets ->
 632	      ?ets_delete(Tab, Key),
 633	      ok;
 634	  tid ->
 635	      Store = Ts#tidstore.store,
 636	      Oid = {Tab, Key},
 637	      case LockKind of
 638		  write ->
 639		      mnesia_locker:wlock(Tid, Store, Oid);
 640		  sticky_write ->
 641		      mnesia_locker:sticky_wlock(Tid, Store, Oid);
 642		  _ ->
 643		      abort({bad_type, Tab, LockKind})
 644	      end,
 645	      ?ets_delete(Store, Oid),
 646	      ?ets_insert(Store, {Oid, Oid, delete}),
 647	      ok;
 648	Protocol ->
 649	      do_dirty_delete(Protocol, Tab, Key)
 650    end; 
 651delete(_Tid, _Ts, Tab, _Key, _LockKind) ->
 652    abort({bad_type, Tab}).
 653
 654delete_object(Val) when tuple(Val), size(Val) > 2 ->
 655    Tab = element(1, Val),
 656    delete_object(Tab, Val, write);
 657delete_object(Val) ->
 658    abort({bad_type, Val}).
 659
 660s_delete_object(Val) when tuple(Val), size(Val) > 2 ->
 661    Tab = element(1, Val),
 662    delete_object(Tab, Val, sticky_write);
 663s_delete_object(Val) ->
 664    abort({bad_type, Val}).
 665
 666delete_object(Tab, Val, LockKind) ->
 667    case get(mnesia_activity_state) of
 668	{?DEFAULT_ACCESS, Tid, Ts} ->
 669	    delete_object(Tid, Ts, Tab, Val, LockKind);
 670	{Mod, Tid, Ts} ->
 671	    Mod:delete_object(Tid, Ts, Tab, Val, LockKind);
 672	_ ->
 673	    abort(no_transaction)
 674    end.
 675
 676delete_object(Tid, Ts, Tab, Val, LockKind)
 677  when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
 678      case element(1, Tid) of
 679	  ets ->
 680	      ?ets_match_delete(Tab, Val),
 681	      ok;
 682	  tid ->
 683	      Store = Ts#tidstore.store,
 684	      Oid = {Tab, element(2, Val)},
 685	      case LockKind of
 686		  write ->
 687		      mnesia_locker:wlock(Tid, Store, Oid);
 688		  sticky_write ->
 689		      mnesia_locker:sticky_wlock(Tid, Store, Oid);
 690		  _ ->
 691		      abort({bad_type, Tab, LockKind})
 692	      end,
 693	      case val({Tab, setorbag}) of
 694		  bag -> 
 695		      ?ets_match_delete(Store, {Oid, Val, '_'}),
 696		      ?ets_insert(Store, {Oid, Val, delete_object});
 697		  _ ->
 698		      case ?ets_match_object(Store, {Oid, '_', write}) of
 699			  [] ->
 700			      ?ets_match_delete(Store, {Oid, Val, '_'}),
 701			      ?ets_insert(Store, {Oid, Val, delete_object});
 702			  _  ->
 703			      ?ets_delete(Store, Oid),
 704			      ?ets_insert(Store, {Oid, Oid, delete})
 705		      end
 706	      end,
 707	      ok;
 708	Protocol ->
 709	      do_dirty_delete_object(Protocol, Tab, Val)
 710    end;
 711delete_object(_Tid, _Ts, Tab, _Key, _LockKind) ->
 712    abort({bad_type, Tab}).
 713
 714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 715%% Access within an activity - read
 716
 717read({Tab, Key}) ->
 718    read(Tab, Key, read);
 719read(Oid) ->
 720    abort({bad_type, Oid}).
 721
 722wread({Tab, Key}) ->
 723    read(Tab, Key, write);
 724wread(Oid) ->
 725    abort({bad_type, Oid}).
 726
 727read(Tab, Key, LockKind) ->
 728    case get(mnesia_activity_state) of
 729	{?DEFAULT_ACCESS, Tid, Ts} ->
 730	    read(Tid, Ts, Tab, Key, LockKind);
 731	{Mod, Tid, Ts} ->
 732	    Mod:read(Tid, Ts, Tab, Key, LockKind);
 733	_ ->
 734	    abort(no_transaction)
 735    end.
 736
 737read(Tid, Ts, Tab, Key, LockKind)
 738  when atom(Tab), Tab /= schema ->
 739    case element(1, Tid) of
 740	ets ->
 741	    ?ets_lookup(Tab, Key);
 742	tid ->
 743	    Store = Ts#tidstore.store,
 744	    Oid = {Tab, Key},
 745	    Objs =
 746		case LockKind of
 747		    read ->
 748			mnesia_locker:rlock(Tid, Store, Oid);
 749		    write ->
 750			mnesia_locker:rwlock(Tid, Store, Oid);
 751		    sticky_write ->
 752			mnesia_locker:sticky_rwlock(Tid, Store, Oid);
 753		    _ ->
 754			abort({bad_type, Tab, LockKind})
 755		end,
 756	    add_written(?ets_lookup(Store, Oid), Tab, Objs);
 757	_Protocol ->
 758	    dirty_read(Tab, Key)
 759    end; 
 760read(_Tid, _Ts, Tab, _Key, _LockKind) ->
 761    abort({bad_type, Tab}).
 762
 763first(Tab) ->
 764    case get(mnesia_activity_state) of
 765	{?DEFAULT_ACCESS, Tid, Ts} ->
 766	    first(Tid, Ts, Tab);
 767	{Mod, Tid, Ts} ->
 768	    Mod:first(Tid, Ts, Tab);
 769	_ ->
 770	    abort(no_transaction)
 771    end.
 772    
 773first(Tid, Ts, Tab)
 774  when atom(Tab), Tab /= schema ->
 775    case element(1, Tid) of
 776	ets ->
 777	    ?ets_first(Tab);
 778	tid ->
 779	    lock_table(Tid, Ts, Tab, read),
 780	    do_fixtable(Tab,Ts),
 781	    Key = dirty_first(Tab),
 782	    stored_keys(Tab,Key,'$end_of_table',Ts,next,
 783			val({Tab, setorbag}));
 784	_Protocol ->
 785	    dirty_first(Tab)
 786    end;
 787first(_Tid, _Ts,Tab) ->
 788    abort({bad_type, Tab}).
 789
 790last(Tab) ->
 791    case get(mnesia_activity_state) of
 792	{?DEFAULT_ACCESS, Tid, Ts} ->
 793	    last(Tid, Ts, Tab);
 794	{Mod, Tid, Ts} ->
 795	    Mod:last(Tid, Ts, Tab);
 796	_ ->
 797	    abort(no_transaction)
 798    end.
 799
 800last(Tid, Ts, Tab)
 801  when atom(Tab), Tab /= schema ->
 802    case element(1, Tid) of
 803	ets ->
 804	    ?ets_last(Tab);
 805	tid ->
 806	    lock_table(Tid, Ts, Tab, read),
 807	    do_fixtable(Tab,Ts),
 808	    Key = dirty_last(Tab),
 809	    stored_keys(Tab,Key,'$end_of_table',Ts,prev,
 810			val({Tab, setorbag}));
 811	_Protocol ->
 812	    dirty_last(Tab)
 813    end;
 814last(_Tid, _Ts,Tab) ->
 815    abort({bad_type, Tab}).
 816
 817next(Tab,Key) ->
 818    case get(mnesia_activity_state) of
 819	{?DEFAULT_ACCESS,Tid,Ts} ->
 820	    next(Tid,Ts,Tab,Key);
 821	{Mod,Tid,Ts} ->
 822	    Mod:next(Tid,Ts,Tab,Key);
 823	_ ->
 824	    abort(no_transaction)
 825    end.
 826next(Tid,Ts,Tab,Key)
 827  when atom(Tab), Tab /= schema ->
 828    case element(1, Tid) of
 829	ets ->
 830	    ?ets_next(Tab,Key);
 831	tid ->
 832	    lock_table(Tid, Ts, Tab, read),
 833	    do_fixtable(Tab,Ts),
 834	    New = (catch dirty_next(Tab,Key)),
 835	    stored_keys(Tab,New,Key,Ts,next,
 836			val({Tab, setorbag}));
 837	_Protocol ->
 838	    dirty_next(Tab,Key)
 839    end;
 840next(_Tid, _Ts,Tab,_) ->
 841    abort({bad_type, Tab}).
 842
 843prev(Tab,Key) ->
 844    case get(mnesia_activity_state) of
 845	{?DEFAULT_ACCESS,Tid,Ts} ->
 846	    prev(Tid,Ts,Tab,Key);
 847	{Mod,Tid,Ts} ->
 848	    Mod:prev(Tid,Ts,Tab,Key);
 849	_ ->
 850	    abort(no_transaction)
 851    end.
 852prev(Tid,Ts,Tab,Key)
 853  when atom(Tab), Tab /= schema ->
 854    case element(1, Tid) of
 855	ets ->
 856	    ?ets_prev(Tab,Key);
 857	tid ->
 858	    lock_table(Tid, Ts, Tab, read),
 859	    do_fixtable(Tab,Ts),
 860	    New = (catch dirty_prev(Tab,Key)),
 861	    stored_keys(Tab,New,Key,Ts,prev,
 862			val({Tab, setorbag}));
 863	_Protocol ->
 864	    dirty_prev(Tab,Key)
 865    end;
 866prev(_Tid, _Ts,Tab,_) ->
 867    abort({bad_type, Tab}).
 868
 869%% Compensate for transaction written and/or deleted records
 870stored_keys(Tab,'$end_of_table',Prev,Ts,Op,Type) ->
 871    case ts_keys(Ts#tidstore.store,Tab,Op,Type,[]) of
 872	[] -> '$end_of_table';
 873	Keys when Type == ordered_set-> 
 874	    get_ordered_tskey(Prev,Keys,Op);
 875	Keys -> 
 876	    get_next_tskey(Prev,Keys,Tab)
 877    end;
 878stored_keys(Tab,{'EXIT',{aborted,R={badarg,[Tab,Key]}}},
 879	    Key,#tidstore{store=Store},Op,Type) ->
 880    %% Had to match on error, ouch..
 881    case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
 882	[] ->  abort(R);
 883	Ops ->
 884	    case lists:last(Ops) of
 885		[delete] -> abort(R);
 886		_ -> 
 887		    case ts_keys(Store,Tab,Op,Type,[]) of
 888			[] -> '$end_of_table';
 889			Keys -> get_next_tskey(Key,Keys,Tab)
 890		    end
 891	    end
 892    end;
 893stored_keys(_,{'EXIT',{aborted,R}},_,_,_,_) ->
 894    abort(R);
 895stored_keys(Tab,Key,Prev,#tidstore{store=Store},Op,ordered_set) ->
 896    case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
 897	[] -> 
 898	    Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
 899	    get_ordered_tskey(Prev,Keys,Op);
 900 	Ops ->
 901	    case lists:last(Ops) of
 902		[delete] ->
 903	 	    mnesia:Op(Tab,Key);
 904		_ -> 
 905		    Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
 906		    get_ordered_tskey(Prev,Keys,Op)
 907	    end
 908    end;
 909stored_keys(Tab,Key,_,#tidstore{store=Store},Op,_) ->
 910    case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
 911	[] ->  Key;
 912 	Ops ->
 913	    case lists:last(Ops) of
 914		[delete] -> mnesia:Op(Tab,Key);
 915		_ ->      Key
 916	    end
 917    end.
 918
 919get_ordered_tskey('$end_of_table', [First|_],_) ->    First;
 920get_ordered_tskey(Prev, [First|_], next) when Prev < First -> First;
 921get_ordered_tskey(Prev, [First|_], prev) when Prev > First -> First;
 922get_ordered_tskey(Prev, [_|R],Op) ->  get_ordered_tskey(Prev,R,Op);
 923get_ordered_tskey(_, [],_) ->    '$end_of_table'.
 924
 925get_next_tskey(_, [],_) -> '$end_of_table';
 926get_next_tskey(Key,Keys,Tab) ->
 927    Next = 
 928	if Key == '$end_of_table' -> hd(Keys);
 929	   true ->
 930		case lists:dropwhile(fun(A) -> A /= Key end, Keys) of
 931		    [] -> hd(Keys); %% First stored key
 932		    [Key] -> '$end_of_table';
 933		    [Key,Next2|_] -> Next2
 934		end
 935	end,
 936    case Next of
 937	'$end_of_table' -> '$end_of_table';
 938	_ -> %% Really slow anybody got another solution??
 939	    case dirty_read(Tab, Next) of
 940		[] -> Next;
 941		_ ->  
 942		    %% Updated value we already returned this key
 943		    get_next_tskey(Next,Keys,Tab)
 944	    end
 945    end.
 946
 947ts_keys(Store, Tab, Op, Type, Def) ->
 948    All = ?ets_match(Store, {{Tab,'$1'},'_','$2'}),
 949    Keys = ts_keys_1(All, Def),
 950    if 
 951	Type == ordered_set, Op == prev ->
 952	    lists:reverse(lists:sort(Keys));
 953	Type == ordered_set ->
 954	    lists:sort(Keys);
 955	Op == next ->
 956	    lists:reverse(Keys);
 957	true ->
 958	    Keys
 959    end.
 960
 961ts_keys_1([[Key, write]|R], []) ->
 962    ts_keys_1(R, [Key]);
 963ts_keys_1([[Key, write]|R], Acc=[Key|_]) ->
 964    ts_keys_1(R, Acc);
 965ts_keys_1([[Key, write]|R], Acc) ->
 966    ts_keys_1(R, [Key|Acc]);
 967ts_keys_1([[Key, delete]|R], [Key|Acc]) ->
 968    ts_keys_1(R, Acc);
 969ts_keys_1([_|R], Acc) ->
 970    ts_keys_1(R, Acc);
 971ts_keys_1([], Acc) ->
 972    Acc.
 973
 974
 975%%%%%%%%%%%%%%%%%%%%%
 976%% Iterators 
 977
 978foldl(Fun, Acc, Tab) ->
 979    foldl(Fun, Acc, Tab, read).
 980
 981foldl(Fun, Acc, Tab, LockKind) when function(Fun) ->
 982    case get(mnesia_activity_state) of
 983	{?DEFAULT_ACCESS, Tid, Ts} ->
 984	    foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
 985	{Mod, Tid, Ts} ->
 986	    Mod:foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
 987	_ ->
 988	    abort(no_transaction)
 989    end.
 990
 991foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
 992    {Type, Prev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
 993    Res = (catch do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)),
 994    close_iteration(Res, Tab).
 995
 996do_foldl(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
 997    lists:foldl(fun(Key, Acc) -> 
 998			lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
 999		end, RAcc, Stored);
1000do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
1001    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1002    {_, Tid, Ts} = get(mnesia_activity_state),
1003    do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, Stored);
1004do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
1005    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
1006    {_, Tid, Ts} = get(mnesia_activity_state),
1007    do_foldl(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
1008do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
1009    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1010    {_, Tid, Ts} = get(mnesia_activity_state),
1011    do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
1012do_foldl(A, O, Tab, Key, Fun, Acc, Type, Stored) ->  %% Type is set or bag 
1013    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1014    NewStored = ordsets:del_element(Key, Stored),
1015    {_, Tid, Ts} = get(mnesia_activity_state),
1016    do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, Type, NewStored).
1017
1018foldr(Fun, Acc, Tab) ->
1019    foldr(Fun, Acc, Tab, read).
1020foldr(Fun, Acc, Tab, LockKind) when function(Fun) ->
1021    case get(mnesia_activity_state) of
1022	{?DEFAULT_ACCESS, Tid, Ts} ->
1023	    foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
1024	{Mod, Tid, Ts} ->
1025	    Mod:foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
1026	_ ->
1027	    abort(no_transaction)
1028    end.
1029
1030foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
1031    {Type, TempPrev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
1032    Prev = 
1033	if 
1034	    Type == ordered_set ->
1035		lists:reverse(TempPrev);
1036	    true ->      %% Order doesn't matter for set and bag
1037		TempPrev %% Keep the order so we can use ordsets:del_element
1038	end,
1039    Res = (catch do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)),
1040    close_iteration(Res, Tab).
1041
1042do_foldr(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
1043    lists:foldl(fun(Key, Acc) -> 
1044			lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
1045		end, RAcc, Stored);
1046do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
1047    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1048    {_, Tid, Ts} = get(mnesia_activity_state),
1049    do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, Stored);
1050do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
1051    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
1052    {_, Tid, Ts} = get(mnesia_activity_state),
1053    do_foldr(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
1054do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
1055    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1056    {_, Tid, Ts} = get(mnesia_activity_state),
1057    do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
1058do_foldr(A, O, Tab, Key, Fun, Acc, Type, Stored) ->  %% Type is set or bag 
1059    NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
1060    NewStored = ordsets:del_element(Key, Stored),
1061    {_, Tid, Ts} = get(mnesia_activity_state),
1062    do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, Type, NewStored).
1063
1064init_iteration(ActivityId, Opaque, Tab, LockKind) ->
1065    lock(ActivityId, Opaque, {table, Tab}, LockKind),
1066    Type = val({Tab, setorbag}),    
1067    Previous = add_previous(ActivityId, Opaque, Type, Tab),
1068    St = val({Tab, storage_type}),
1069    if 
1070	St == unknown -> 
1071	    ignore;
1072	true ->
1073	    mnesia_lib:db_fixtable(St, Tab, true)
1074    end, 
1075    {Type, Previous}.
1076
1077close_iteration(Res, Tab) ->
1078    case val({Tab, storage_type}) of
1079	unknown -> 
1080	    ignore;
1081	St -> 
1082	    mnesia_lib:db_fixtable(St, Tab, false)
1083    end,
1084    case Res of 
1085	{'EXIT', {aborted, What}} ->
1086	   abort(What);
1087	{'EXIT', What} ->
1088	    abort(What);
1089	_ ->
1090	    Res
1091    end.
1092
1093add_previous(_ActivityId, non_transaction, _Type, _Tab) ->
1094    [];
1095add_previous(_Tid, Ts, _Type, Tab) ->
1096    Previous = ?ets_match(Ts#tidstore.store, {{Tab, '$1'}, '_', write}),
1097    lists:sort(lists:concat(Previous)).
1098
1099%% This routine fixes up the return value from read/1 so that
1100%% it is correct with respect to what this particular transaction
1101%% has already written, deleted .... etc
1102
1103add_written([], _Tab, Objs) -> 
1104    Objs;  % standard normal fast case
1105add_written(Written, Tab, Objs) ->
1106    case val({Tab, setorbag}) of
1107	bag ->
1108	    add_written_to_bag(Written, Objs, []);
1109	_   ->
1110	    add_written_to_set(Written)
1111    end.
1112
1113add_written_to_set(Ws) ->
1114    case lists:last(Ws) of
1115	{_, _, delete} -> [];
1116	{_, Val, write} -> [Val];
1117	{_, _, delete_object} -> []
1118    end.
1119
1120add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
1121    add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
1122add_written_to_bag([], Objs, Ack) -> 
1123    Objs ++ lists:reverse(Ack); %% Oldest write first as in ets
1124add_written_to_bag([{_, _ , delete} | Tail], _Objs, _Ack) ->
1125    %% This transaction just deleted all objects
1126    %% with this key
1127    add_written_to_bag(Tail, [], []);
1128add_written_to_bag([{_, Val, delete_object} | Tail], Objs, Ack) ->
1129    add_written_to_bag(Tail, lists:delete(Val, Objs), lists:delete(Val, Ack)).
1130
1131match_object(Pat) when tuple(Pat), size(Pat) > 2 ->
1132    Tab = element(1, Pat),
1133    match_object(Tab, Pat, read);
1134match_object(Pat) ->
1135    abort({bad_type, Pat}).
1136
1137match_object(Tab, Pat, LockKind) ->
1138    case get(mnesia_activity_state) of
1139	{?DEFAULT_ACCESS, Tid, Ts} ->
1140	    match_object(Tid, Ts, Tab, Pat, LockKind);
1141	{Mod, Tid, Ts} ->
1142	    Mod:match_object(Tid, Ts, Tab, Pat, LockKind);
1143	_ ->
1144	    abort(no_transaction)
1145    end.
1146
1147match_object(Tid, Ts, Tab, Pat, LockKind) 
1148  when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
1149    case element(1, Tid) of
1150	ets ->
1151	    mnesia_lib:db_match_object(ram_copies, Tab, Pat);
1152	tid ->
1153	    Key = element(2, Pat),
1154	    case has_var(Key) of
1155		false -> lock_record(Tid, Ts, Tab, Key, LockKind);
1156		true  -> lock_table(Tid, Ts, Tab, LockKind)
1157	    end,
1158	    Objs = dirty_match_object(Tab, Pat),
1159	    add_written_match(Ts#tidstore.store, Pat, Tab, Objs);
1160	_Protocol ->
1161	    dirty_match_object(Tab, Pat)
1162    end;
1163match_object(_Tid, _Ts, Tab, Pat, _LockKind) ->
1164    abort({bad_type, Tab, Pat}).
1165
1166add_written_match(S, Pat, Tab, Objs) ->
1167    Ops = find_ops(S, Tab, Pat),
1168    add_match(Ops, Objs, val({Tab, setorbag})).
1169
1170find_ops(S, Tab, Pat) ->
1171    GetWritten = [{{{Tab, '_'}, Pat, write}, [], ['$_']}, 
1172		  {{{Tab, '_'}, '_', delete}, [], ['$_']},
1173		  {{{Tab, '_'}, Pat, delete_object}, [], ['$_']}],
1174    ets:select(S, GetWritten).
1175    
1176add_match([], Objs, _Type) ->
1177    Objs;
1178add_match(Written, Objs, ordered_set) ->
1179    %% Must use keysort which is stable
1180    add_ordered_match(lists:keysort(1,Written), Objs, []);
1181add_match([{Oid, _, delete}|R], Objs, Type) ->
1182    add_match(R, deloid(Oid, Objs), Type);
1183add_match([{_Oid, Val, delete_object}|R], Objs, Type) ->
1184    add_match(R, lists:delete(Val, Objs), Type);
1185add_match([{_Oid, Val, write}|R], Objs, bag) ->
1186    add_match(R, [Val | lists:delete(Val, Objs)], bag);
1187add_match([{Oid, Val, write}|R], Objs, set) ->
1188    add_match(R, [Val | deloid(Oid,Objs)],set).
1189
1190%% For ordered_set only !!
1191add_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs], Acc) 
1192  when Key > element(2, Obj) ->
1193    add_ordered_match(Written, Objs, [Obj|Acc]);
1194add_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_], Acc) 
1195  when Key < element(2, Obj) ->
1196    add_ordered_match(Rest, [Val|Objs],Acc);
1197add_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc) 
1198  when Key < element(2, Obj) ->
1199    add_ordered_match(Rest,Objs,Acc);
1200%% Greater than last object
1201add_ordered_match([{_, Val, write}|Rest], [], Acc) ->
1202    add_ordered_match(Rest, [Val], Acc);
1203add_ordered_match([_|Rest], [], Acc) ->
1204    add_ordered_match(Rest, [], Acc);
1205%% Keys are equal from here 
1206add_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
1207    add_ordered_match(Rest, [Val|Objs], Acc);
1208add_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
1209    add_ordered_match(Rest, Objs, Acc);
1210add_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
1211    add_ordered_match(Rest, Objs, Acc);
1212add_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
1213    add_ordered_match(Rest, Objs, Acc);
1214add_ordered_match([], Objs, Acc) ->
1215    lists:reverse(Acc, Objs).
1216
1217%% For select chunk
1218add_sel_match(Sorted, Objs, ordered_set) ->
1219    add_sel_ordered_match(Sorted, Objs, []);
1220add_sel_match(Written, Objs, Type) ->
1221    add_sel_match(Written, Objs, Type, []).
1222
1223add_sel_match([], Objs, _Type, Acc) ->
1224    {Objs,lists:reverse(Acc)};
1225add_sel_match([Op={Oid, _, delete}|R], Objs, Type, Acc) ->
1226    case deloid(Oid, Objs) of
1227	Objs ->
1228	    add_sel_match(R, Objs, Type, [Op|Acc]);
1229	NewObjs when Type == set ->
1230	    add_sel_match(R, NewObjs, Type, Acc);
1231	NewObjs ->  %% If bag we may get more in next chunk
1232	    add_sel_match(R, NewObjs, Type, [Op|Acc])
1233    end;
1234add_sel_match([Op = {_Oid, Val, delete_object}|R], Objs, Type, Acc) ->
1235    case lists:delete(Val, Objs) of
1236	Objs -> 
1237	    add_sel_match(R, Objs, Type, [Op|Acc]);
1238	NewObjs when Type == set ->
1239	    add_sel_match(R, NewObjs, Type, Acc);
1240	NewObjs ->
1241	    add_sel_match(R, NewObjs, Type, [Op|Acc])
1242    end;
1243add_sel_match([Op={Oid={_,Key}, Val, write}|R], Objs, bag, Acc) ->
1244    case lists:keymember(Key, 2, Objs) of
1245	true ->
1246	    add_sel_match(R,[Val|lists:delete(Val,Objs)],bag,
1247			  [{Oid,Val,delete_object}|Acc]);
1248	false ->
1249	    add_sel_match(R,Objs,bag,[Op|Acc])
1250    end;
1251add_sel_match([Op={Oid, Val, write}|R], Objs, set, Acc) ->
1252    case deloid(Oid,Objs) of
1253	Objs -> 
1254	    add_sel_match(R, Objs,set, [Op|Acc]);
1255	NewObjs ->
1256	    add_sel_match(R, [Val | NewObjs],set, Acc)
1257    end.
1258
1259%% For ordered_set only !!
1260add_sel_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs],Acc) 
1261  when Key > element(2, Obj) ->
1262    add_sel_ordered_match(Written, Objs, [Obj|Acc]);
1263add_sel_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_],Acc) 
1264  when Key < element(2, Obj) ->
1265    add_sel_ordered_match(Rest,[Val|Objs],Acc);
1266add_sel_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc) 
1267  when Key < element(2, Obj) ->
1268    add_sel_ordered_match(Rest,Objs,Acc);
1269%% Greater than last object
1270add_sel_ordered_match(Ops1, [], Acc) ->
1271    {lists:reverse(Acc), Ops1};
1272%% Keys are equal from here 
1273add_sel_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
1274    add_sel_ordered_match(Rest, [Val|Objs], Acc);
1275add_sel_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
1276    add_sel_ordered_match(Rest, Objs, Acc);
1277add_sel_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
1278    add_sel_ordered_match(Rest, Objs, Acc);
1279add_sel_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
1280    add_sel_ordered_match(Rest, Objs, Acc);
1281add_sel_ordered_match([], Objs, Acc) ->
1282    {lists:reverse(Acc, Objs),[]}.
1283
1284
1285deloid(_Oid, []) ->
1286    [];
1287deloid({Tab, Key}, [H | T]) when element(2, H) == Key ->
1288    deloid({Tab, Key}, T);
1289deloid(Oid, [H | T]) ->
1290    [H | deloid(Oid, T)].
1291
1292%%%%%%%%%%%%%%%%%%
1293% select 
1294
1295select(Tab, Pat) ->
1296    select(Tab, Pat, read).
1297select(Tab, Pat, LockKind) 
1298  when atom(Tab), Tab /= schema, list(Pat) ->
1299    case get(mnesia_activity_state) of
1300	{?DEFAULT_ACCESS, Tid, Ts} ->
1301	    select(Tid, Ts, Tab, Pat, LockKind);
1302	{Mod, Tid, Ts} ->
1303	    Mod:select(Tid, Ts, Tab, Pat, LockKind);
1304	_ ->
1305	    abort(no_transaction)
1306    end;
1307select(Tab, Pat, _Lock) ->
1308    abort({badarg, Tab, Pat}).
1309
1310select(Tid, Ts, Tab, Spec, LockKind) ->
1311    SelectFun = fun(FixedSpec) -> dirty_select(Tab, FixedSpec) end,
1312    fun_select(Tid, Ts, Tab, Spec, LockKind, Tab, SelectFun).
1313
1314fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, SelectFun) ->
1315    case element(1, Tid) of
1316	ets ->
1317	    mnesia_lib:db_select(ram_copies, Tab, Spec);
1318	tid ->
1319	    select_lock(Tid,Ts,LockKind,Spec,Tab),
1320	    Store = Ts#tidstore.store,
1321	    Written = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
1322	    case Written of 
1323		[] ->  
1324		    %% Nothing changed in the table during this transaction,
1325		    %% Simple case get results from [d]ets
1326		    SelectFun(Spec);
1327		_ ->   
1328		    %% Hard (slow case) records added or deleted earlier 
1329		    %% in the transaction, have to cope with that.
1330		    Type = val({Tab, setorbag}),
1331		    FixedSpec = get_record_pattern(Spec),
1332		    TabRecs = SelectFun(FixedSpec),
1333		    FixedRes = add_match(Written, TabRecs, Type),
1334		    CMS = ets:match_spec_compile(Spec),
1335		    ets:match_spec_run(FixedRes, CMS)
1336	    end;
1337	_Protocol ->
1338	    SelectFun(Spec)
1339    end.
1340
1341select_lock(Tid,Ts,LockKind,Spec,Tab) ->
1342    %% Avoid table lock if possible
1343    case Spec of
1344	[{HeadPat,_, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
1345	    Key = element(2, HeadPat),
1346	    case has_var(Key) of
1347		false -> lock_record(Tid, Ts, Tab, Key, LockKind);
1348		true  -> lock_table(Tid, Ts, Tab, LockKind)
1349	    end;
1350	_ ->
1351	    lock_table(Tid, Ts, Tab, LockKind)
1352    end.
1353
1354%% Breakable Select
1355select(Tab, Pat, NObjects, LockKind) 
1356  when atom(Tab), Tab /= schema, list(Pat), number(NObjects) ->
1357    case get(mnesia_activity_state) of
1358	{?DEFAULT_ACCESS, Tid, Ts} ->
1359	    select(Tid, Ts, Tab, Pat, NObjects, LockKind);
1360	{Mod, Tid, Ts} ->
1361	    Mod:select(Tid, Ts, Tab, Pat, NObjects, LockKind);
1362	_ ->
1363	    abort(no_transaction)
1364    end;
1365select(Tab, Pat, NObjects, _Lock) ->
1366    abort({badarg, Tab, Pat, NObjects}).
1367
1368select(Tid, Ts, Tab, Spec, NObjects, LockKind) ->
1369    Where = val({Tab,where_to_read}),
1370    Type = mnesia_lib:storage_type_at_node(Where,Tab),
1371    InitFun = fun(FixedSpec) -> dirty_sel_init(Where,Tab,FixedSpec,NObjects,Type) end,
1372    fun_select(Tid,Ts,Tab,Spec,LockKind,Tab,InitFun,NObjects,Where,Type).
1373
1374-record(mnesia_select, {tab,tid,node,storage,cont,written=[],spec,type,orig}).
1375
1376fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, Init, NObjects, Node, Storage) ->
1377    Def = #mnesia_select{tid=Tid,node=Node,storage=Storage,tab=Tab,orig=Spec},
1378    case element(1, Tid) of
1379	ets ->
1380	    select_state(mnesia_lib:db_select_init(ram_copies,Tab,Spec,NObjects),Def);
1381	tid ->
1382	    select_lock(Tid,Ts,LockKind,Spec,Tab),
1383	    Store = Ts#tidstore.store,
1384	    do_fixtable(Tab, Store),
1385	    
1386	    Written0 = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),	    
1387	    case Written0 of 
1388		[] ->  
1389		    %% Nothing changed in the table during this transaction,
1390		    %% Simple case get results from [d]ets
1391		    select_state(Init(Spec),Def);
1392		_ ->   
1393		    %% Hard (slow case) records added or deleted earlier 
1394		    %% in the transaction, have to cope with that.
1395		    Type = val({Tab, setorbag}),
1396		    Written = 
1397			if Type == ordered_set -> %% Sort stable
1398				lists:keysort(1,Written0);
1399			   true -> 
1400				Written0
1401			end,
1402		    FixedSpec = get_record_pattern(Spec),
1403		    CMS = ets:match_spec_compile(Spec),
1404		    trans_select(Init(FixedSpec), 
1405				 Def#mnesia_select{written=Written,spec=CMS,type=Type})
1406	    end;
1407	_Protocol ->
1408	    select_state(Init(Spec),Def)
1409    end.
1410
1411select(Cont) ->
1412    case get(mnesia_activity_state) of
1413	{?DEFAULT_ACCESS, Tid, Ts} ->
1414	    select_cont(Tid,Ts,Cont);
1415	{Mod, Tid, Ts} ->
1416	    Mod:select_cont(Tid,Ts,Cont);
1417	_ ->
1418	    abort(no_transaction)
1419    end.
1420
1421select_cont(_Tid,_Ts,'$end_of_table') ->
1422    '$end_of_table';
1423select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid,cont=Cont, orig=Ms}) 
1424  when element(1,Tid) == ets ->
1425    case Cont of
1426	'$end_of_table' -> '$end_of_table';
1427	_ -> select_state(mnesia_lib:db_select_cont(ram_copies,Cont,Ms),State)
1428    end;
1429select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) ->
1430    select_state(dirty_sel_cont(State),State);
1431select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid})  ->
1432    trans_select(dirty_sel_cont(State), State);
1433select_cont(_Tid2,_,#mnesia_select{tid=_Tid1}) ->  % Missmatching tids
1434    abort(wrong_transaction);
1435select_cont(_,_,Cont) ->
1436    abort({badarg, Cont}).
1437
1438trans_select('$end_of_table', #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
1439    Written = add_match(Written0, [], Type),
1440    {ets:match_spec_run(Written, CMS), '$end_of_table'};
1441trans_select({TabRecs,Cont}, State = #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
1442    {FixedRes,Written} = add_sel_match(Written0, TabRecs, Type),
1443    select_state({ets:match_spec_run(FixedRes, CMS),Cont},
1444		 State#mnesia_select{written=Written}). 
1445
1446select_state({Matches, Cont}, MS) ->
1447    {Matches, MS#mnesia_select{cont=Cont}};
1448select_state('$end_of_table',_) -> '$end_of_table'.
1449
1450get_record_pattern([]) ->    [];
1451get_record_pattern([{M,C,_B}|R]) ->
1452    [{M,C,['$_']} | get_record_pattern(R)].
1453
1454all_keys(Tab) ->
1455    case get(mnesia_activity_state) of
1456	{?DEFAULT_ACCESS, Tid, Ts} ->
1457	    all_keys(Tid, Ts, Tab, read);
1458	{Mod, Tid, Ts} ->
1459	    Mod:all_keys(Tid, Ts, Tab, read);
1460	_ ->
1461	    abort(no_transaction)
1462    end. 
1463
1464all_keys(Tid, Ts, Tab, LockKind) 
1465  when atom(Tab), Tab /= schema ->
1466    Pat0 = val({Tab, wild_pattern}),
1467    Pat = setelement(2, Pat0, '$1'),
1468    Keys = select(Tid, Ts, Tab, [{Pat, [], ['$1']}], LockKind),
1469    case val({Tab, setorbag}) of
1470	bag ->
1471	    mnesia_lib:uniq(Keys);
1472	_ ->
1473	    Keys
1474    end;
1475all_keys(_Tid, _Ts, Tab, _LockKind) ->    
1476    abort({bad_type, Tab}).
1477
1478index_match_object(Pat, Attr) when tuple(Pat), size(Pat) > 2 ->
1479    Tab = element(1, Pat),
1480    index_match_object(Tab, Pat, Attr, read);
1481index_match_object(Pat, _Attr) ->
1482    abort({bad_type, Pat}).
1483
1484index_match_object(Tab, Pat, Attr, LockKind) ->
1485    case get(mnesia_activity_state) of
1486	{?DEFAULT_ACCESS, Tid, Ts} ->
1487	    index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
1488	{Mod, Tid, Ts} ->
1489	    Mod:index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
1490	_ ->
1491	    abort(no_transaction)
1492    end.
1493
1494index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind) 
1495  when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
1496    case element(1, Tid) of
1497	ets ->
1498	    dirty_index_match_object(Tab, Pat, Attr); % Should be optimized?
1499	tid ->
1500	    case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
1501		Pos when Pos =< size(Pat) ->
1502		    case LockKind of
1503			read ->
1504			    Store = Ts#tidstore.store,
1505			    mnesia_locker:rlock_table(Tid, Store, Tab),
1506			    Objs = dirty_index_match_object(Tab, Pat, Attr),
1507			    add_written_match(Store, Pat, Tab, Objs);
1508			_ ->
1509			    abort({bad_type, Tab, LockKind})
1510		    end;
1511		BadPos ->
1512		    abort({bad_type, Tab, BadPos})
1513	    end;
1514	_Protocol ->
1515	    dirty_index_match_object(Tab, Pat, Attr)
1516    end;
1517index_match_object(_Tid, _Ts, Tab, Pat, _Attr, _LockKind) ->
1518    abort({bad_type, Tab, Pat}).
1519
1520index_read(Tab, Key, Attr) ->
1521    case get(mnesia_activity_state) of
1522	{?DEFAULT_ACCESS, Tid, Ts} ->
1523	    index_read(Tid, Ts, Tab, Key, Attr, read);
1524	{Mod, Tid, Ts} ->
1525	    Mod:index_read(Tid, Ts, Tab, Key, Attr, read);
1526	_ ->
1527	    abort(no_transaction)
1528    end.
1529
1530index_read(Tid, Ts, Tab, Key, Attr, LockKind) 
1531  when atom(Tab), Tab /= schema ->
1532    case element(1, Tid) of
1533	ets ->
1534	    dirty_index_read(Tab, Key, Attr); % Should be optimized?
1535	tid ->
1536	    Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
1537	    case LockKind of
1538		read ->
1539		    case has_var(Key) of
1540			false ->
1541			    Store = Ts#tidstore.store,
1542			    Objs = mnesia_index:read(Tid, Store, Tab, Key, Pos),
1543			    Pat = setelement(Pos, val({Tab, wild_pattern}), Key),
1544			    add_written_match(Store, Pat, Tab, Objs);
1545			true ->
1546			    abort({bad_type, Tab, Attr, Key})
1547		    end;
1548		_ ->
1549		    abort({bad_type, Tab, LockKind})
1550	    end;
1551	_Protocol ->
1552	    dirty_index_read(Tab, Key, Attr)
1553    end;
1554index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind) ->
1555    abort({bad_type, Tab}).
1556
1557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1558%% Dirty access regardless of activities - updates
1559
1560dirty_write(Val) when tuple(Val), size(Val) > 2  ->
1561    Tab = element(1, Val),
1562    dirty_write(Tab, Val);
1563dirty_write(Val) ->
1564    abort({bad_type, Val}).
1565    
1566dirty_write(Tab, Val) ->
1567    do_dirty_write(async_dirty, Tab, Val).
1568
1569do_dirty_write(SyncMode, Tab, Val)
1570  when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
1571    case ?catch_val({Tab, record_validation}) of
1572	{RecName, Arity, _Type}
1573	when size(Val) == Arity, RecName == element(1, Val) ->
1574	    Oid = {Tab, element(2, Val)},
1575	    mnesia_tm:dirty(SyncMode, {Oid, Val, write});
1576	{'EXIT', _} ->
1577	    abort({no_exists, Tab});
1578	_ ->
1579	    abort({bad_type, Val})
1580    end;
1581do_dirty_write(_SyncMode, Tab, Val) ->
1582    abort({bad_type, Tab, Val}).
1583
1584dirty_delete({Tab, Key}) ->
1585    dirty_delete(Tab, Key);
1586dirty_delete(Oid) ->
1587    abort({bad_type, Oid}).
1588
1589dirty_delete(Tab, Key) ->
1590    do_dirty_delete(async_dirty, Tab, Key).
1591    
1592do_dirty_delete(SyncMode, Tab, Key) when atom(Tab), Tab /= schema  ->
1593    Oid = {Tab, Key},
1594    mnesia_tm:dirty(SyncMode, {Oid, Oid, delete});
1595do_dirty_delete(_SyncMode, Tab, _Key) ->
1596    abort({bad_type, Tab}).
1597
1598dirty_delete_object(Val) when tuple(Val), size(Val) > 2 ->
1599    Tab = element(1, Val),
1600    dirty_delete_object(Tab, Val);
1601dirty_delete_object(Val) ->
1602    abort({bad_type, Val}).
1603
1604dirty_delete_object(Tab, Val) ->
1605    do_dirty_delete_object(async_dirty, Tab, Val).
1606
1607do_dirty_delete_object(SyncMode, Tab, Val)
1608    when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
1609    Oid = {Tab, element(2, Val)},
1610    mnesia_tm:dirty(SyncMode, {Oid, Val, delete_object});
1611do_dirty_delete_object(_SyncMode, Tab, Val) ->
1612    abort({bad_type, Tab, Val}).
1613
1614%% A Counter is an Oid being {CounterTab, CounterName}
1615
1616dirty_update_counter({Tab, Key}, Incr) ->
1617    dirty_update_counter(Tab, Key, Incr);
1618dirty_update_counter(Counter, _Incr) ->
1619    abort({bad_type, Counter}).
1620
1621dirty_update_counter(Tab, Key, Incr) ->
1622    do_dirty_update_counter(async_dirty, Tab, Key, Incr).
1623    
1624do_dirty_update_counter(SyncMode, Tab, Key, Incr)
1625  when atom(Tab), Tab /= schema, integer(Incr) ->
1626    case ?catch_val({Tab, record_validation}) of
1627	{RecName, 3, set} ->
1628	    Oid = {Tab, Key},
1629	    mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
1630	_ ->
1631	    abort({combine_error, Tab, update_counter})
1632    end;
1633do_dirty_update_counter(_SyncMode, Tab, _Key, Incr) ->
1634    abort({bad_type, Tab, Incr}).
1635
1636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1637%% Dirty access regardless of activities - read
1638
1639dirty_read({Tab, Key}) ->
1640    dirty_read(Tab, Key);
1641dirty_read(Oid) ->
1642    abort({bad_type, Oid}).
1643
1644dirty_read(Tab, Key)
1645  when atom(Tab), Tab /= schema ->
1646%%    case catch ?ets_lookup(Tab, Key) of
1647%%        {'EXIT', _} ->
1648            %% Bad luck, we have to perform a real lookup
1649            dirty

Large files files are truncated, but you can click here to view the full file