/lib/rdbms/mnesia_patches/src/mnesia.erl
Erlang | 2779 lines | 2285 code | 310 blank | 184 comment | 41 complexity | 74adda6f0a42b3a008e541753cbd3c19 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
- %% ``The contents of this file are subject to the Erlang Public License,
- %% Version 1.1, (the "License"); you may not use this file except in
- %% compliance with the License. You should have received a copy of the
- %% Erlang Public License along with this software. If not, it can be
- %% retrieved via the world wide web at http://www.erlang.org/.
- %%
- %% Software distributed under the License is distributed on an "AS IS"
- %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- %% the License for the specific language governing rights and limitations
- %% under the License.
- %%
- %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
- %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
- %% AB. All Rights Reserved.''
- %%
- %% $Id$
- %%
- %% This module exports the public interface of the Mnesia DBMS engine
- -module(mnesia).
- %-behaviour(mnesia_access).
- -export([
- %% Start, stop and debugging
- start/0, start/1, stop/0, % Not for public use
- set_debug_level/1, lkill/0, kill/0, % Not for public use
- ms/0, nc/0, nc/1, ni/0, ni/1, % Not for public use
- change_config/2,
- %% Activity mgt
- abort/1, transaction/1, transaction/2, transaction/3,
- sync_transaction/1, sync_transaction/2, sync_transaction/3,
- async_dirty/1, async_dirty/2, sync_dirty/1, sync_dirty/2, ets/1, ets/2,
- activity/2, activity/3, activity/4, % Not for public use
- %% Access within an activity - Lock acquisition
- lock/2, lock/4,
- read_lock_table/1,
- write_lock_table/1,
- %% Access within an activity - Updates
- write/1, s_write/1, write/3, write/5,
- delete/1, s_delete/1, delete/3, delete/5,
- delete_object/1, s_delete_object/1, delete_object/3, delete_object/5,
-
- %% Access within an activity - Reads
- read/1, wread/1, read/3, read/5,
- match_object/1, match_object/3, match_object/5,
- select/1,select/2,select/3,select/4,select/5,select/6,
- all_keys/1, all_keys/4,
- index_match_object/2, index_match_object/4, index_match_object/6,
- index_read/3, index_read/6,
- first/1, next/2, last/1, prev/2,
- %% Iterators within an activity
- foldl/3, foldl/4, foldr/3, foldr/4,
-
- %% Dirty access regardless of activities - Updates
- dirty_write/1, dirty_write/2,
- dirty_delete/1, dirty_delete/2,
- dirty_delete_object/1, dirty_delete_object/2,
- dirty_update_counter/2, dirty_update_counter/3,
- %% Dirty access regardless of activities - Read
- dirty_read/1, dirty_read/2,
- dirty_select/2,
- dirty_match_object/1, dirty_match_object/2, dirty_all_keys/1,
- dirty_index_match_object/2, dirty_index_match_object/3,
- dirty_index_read/3, dirty_slot/2,
- dirty_first/1, dirty_next/2, dirty_last/1, dirty_prev/2,
- %% Info
- table_info/2, table_info/4, schema/0, schema/1,
- error_description/1, info/0, system_info/1,
- system_info/0, % Not for public use
- %% Database mgt
- create_schema/1, delete_schema/1,
- backup/1, backup/2, traverse_backup/4, traverse_backup/6,
- install_fallback/1, install_fallback/2,
- uninstall_fallback/0, uninstall_fallback/1,
- activate_checkpoint/1, deactivate_checkpoint/1,
- backup_checkpoint/2, backup_checkpoint/3, restore/2,
- %% Table mgt
- create_table/1, create_table/2, delete_table/1,
- add_table_copy/3, del_table_copy/2, move_table_copy/3,
- add_table_index/2, del_table_index/2,
- transform_table/3, transform_table/4,
- change_table_copy_type/3,
- read_table_property/2, write_table_property/2, delete_table_property/2,
- change_table_frag/2,
- clear_table/1,
- %% Table load
- dump_tables/1, wait_for_tables/2, force_load_table/1,
- change_table_access_mode/2, change_table_load_order/2,
- set_master_nodes/1, set_master_nodes/2,
- onload_fun/2, % UW 2005-12-22
-
- %% Misc admin
- dump_log/0, subscribe/1, unsubscribe/1, report_event/1,
- %% Snmp
- snmp_open_table/2, snmp_close_table/1,
- snmp_get_row/2, snmp_get_next_index/2, snmp_get_mnesia_key/2,
- %% Textfile access
- load_textfile/1, dump_to_textfile/1,
-
- %% QLC functions
- table/1, table/2,
- %% Mnemosyne exclusive
- get_activity_id/0, put_activity_id/1, % Not for public use
- %% Mnesia internal functions
- dirty_rpc/4, % Not for public use
- has_var/1, fun_select/7, fun_select/10, select_cont/3, dirty_sel_init/5,
- foldl/6, foldr/6,
- %% Module internal callback functions
- raw_table_info/2, % Not for public use
- remote_dirty_match_object/2, % Not for public use
- remote_dirty_select/2 % Not for public use
- ]).
- -export([begin_activity/4, end_activity/4]).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- -include("mnesia.hrl").
- -import(mnesia_lib, [verbose/2]).
- -define(DEFAULT_ACCESS, ?MODULE).
- %% Select
- -define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]).
- -define(PATTERN_TO_BINDINGS_MATCH_SPEC(Pat), [{Pat,[],['$$']}]).
- begin_activity(_Type, _Factor, _Tid, _Ts) ->
- ok.
- end_activity(_Result, _Type, _Tid, _Ts) ->
- ok.
-
- %% Local function in order to avoid external function call
- val(Var) ->
- case ?catch_val(Var) of
- {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
- Value -> Value
- end.
- is_dollar_digits(Var) ->
- case atom_to_list(Var) of
- [$$ | Digs] ->
- is_digits(Digs);
- _ ->
- false
- end.
- is_digits([Dig | Tail]) ->
- if
- $0 =< Dig, Dig =< $9 ->
- is_digits(Tail);
- true ->
- false
- end;
- is_digits([]) ->
- true.
- has_var(X) when atom(X) ->
- if
- X == '_' ->
- true;
- atom(X) ->
- is_dollar_digits(X);
- true ->
- false
- end;
- has_var(X) when tuple(X) ->
- e_has_var(X, size(X));
- has_var([H|T]) ->
- case has_var(H) of
- false -> has_var(T);
- Other -> Other
- end;
- has_var(_) -> false.
- e_has_var(_, 0) -> false;
- e_has_var(X, Pos) ->
- case has_var(element(Pos, X))of
- false -> e_has_var(X, Pos-1);
- Other -> Other
- end.
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Start and stop
- start() ->
- {Time , Res} = timer:tc(application, start, [?APPLICATION, temporary]),
-
- Secs = Time div 1000000,
- case Res of
- ok ->
- verbose("Mnesia started, ~p seconds~n",[ Secs]),
- ok;
- {error, {already_started, mnesia}} ->
- verbose("Mnesia already started, ~p seconds~n",[ Secs]),
- ok;
- {error, R} ->
- verbose("Mnesia failed to start, ~p seconds: ~p~n",[ Secs, R]),
- {error, R}
- end.
- start(ExtraEnv) when list(ExtraEnv) ->
- case mnesia_lib:ensure_loaded(?APPLICATION) of
- ok ->
- patched_start(ExtraEnv);
- Error ->
- Error
- end;
- start(ExtraEnv) ->
- {error, {badarg, ExtraEnv}}.
- patched_start([{Env, Val} | Tail]) when atom(Env) ->
- case mnesia_monitor:patch_env(Env, Val) of
- {error, Reason} ->
- {error, Reason};
- _NewVal ->
- patched_start(Tail)
- end;
- patched_start([Head | _]) ->
- {error, {bad_type, Head}};
- patched_start([]) ->
- start().
- stop() ->
- case application:stop(?APPLICATION) of
- ok -> stopped;
- {error, {not_started, ?APPLICATION}} -> stopped;
- Other -> Other
- end.
- change_config(extra_db_nodes, Ns) when list(Ns) ->
- mnesia_controller:connect_nodes(Ns);
- change_config(BadKey, _BadVal) ->
- {error, {badarg, BadKey}}.
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Debugging
- set_debug_level(Level) ->
- mnesia_subscr:set_debug_level(Level).
- lkill() ->
- mnesia_sup:kill().
- kill() ->
- rpc:multicall(mnesia_sup, kill, []).
- ms() ->
- [
- mnesia,
- mnesia_backup,
- mnesia_bup,
- mnesia_checkpoint,
- mnesia_checkpoint_sup,
- mnesia_controller,
- mnesia_dumper,
- mnesia_loader,
- mnesia_frag,
- mnesia_frag_hash,
- mnesia_frag_old_hash,
- mnesia_index,
- mnesia_kernel_sup,
- mnesia_late_loader,
- mnesia_lib,
- mnesia_log,
- mnesia_registry,
- mnesia_schema,
- mnesia_snmp_hook,
- mnesia_snmp_sup,
- mnesia_subscr,
- mnesia_sup,
- mnesia_text,
- mnesia_tm,
- mnesia_recover,
- mnesia_locker,
- %% Keep these last in the list, so
- %% mnesia_sup kills these last
- mnesia_monitor,
- mnesia_event
- ].
- nc() ->
- Mods = ms(),
- nc(Mods).
- nc(Mods) when list(Mods)->
- [Mod || Mod <- Mods, ok /= load(Mod, compile)].
- ni() ->
- Mods = ms(),
- ni(Mods).
- ni(Mods) when list(Mods) ->
- [Mod || Mod <- Mods, ok /= load(Mod, interpret)].
- load(Mod, How) when atom(Mod) ->
- case try_load(Mod, How) of
- ok ->
- ok;
- _ ->
- mnesia_lib:show( "~n RETRY ~p FROM: ", [Mod]),
- Abs = mod2abs(Mod),
- load(Abs, How)
- end;
- load(Abs, How) ->
- case try_load(Abs, How) of
- ok ->
- ok;
- {error, Reason} ->
- mnesia_lib:show( " *** ERROR *** ~p~n", [Reason]),
- {error, Reason}
- end.
- try_load(Mod, How) ->
- mnesia_lib:show( " ~p ", [Mod]),
- Flags = [{d, debug}],
- case How of
- compile ->
- case catch c:nc(Mod, Flags) of
- {ok, _} -> ok;
- Other -> {error, Other}
- end;
- interpret ->
- case catch int:ni(Mod, Flags) of
- {module, _} -> ok;
- Other -> {error, Other}
- end
- end.
- mod2abs(Mod) ->
- ModString = atom_to_list(Mod),
- SubDir =
- case lists:suffix("test", ModString) of
- true -> test;
- false -> src
- end,
- filename:join([code:lib_dir(?APPLICATION), SubDir, ModString]).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Activity mgt
- abort(Reason) ->
- exit({aborted, Reason}).
- transaction(Fun) ->
- transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, async).
- transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
- transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
- transaction(Fun, Retries) when Retries == infinity ->
- transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
- transaction(Fun, Args) ->
- transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, async).
- transaction(Fun, Args, Retries) ->
- transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, async).
- sync_transaction(Fun) ->
- transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, sync).
- sync_transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
- transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
- sync_transaction(Fun, Retries) when Retries == infinity ->
- transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
- sync_transaction(Fun, Args) ->
- transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, sync).
- sync_transaction(Fun, Args, Retries) ->
- transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, sync).
- transaction(State, Fun, Args, Retries, Mod, Kind)
- when function(Fun), list(Args), Retries == infinity, atom(Mod) ->
- mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
- transaction(State, Fun, Args, Retries, Mod, Kind)
- when function(Fun), list(Args), integer(Retries), Retries >= 0, atom(Mod) ->
- mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
- transaction(_State, Fun, Args, Retries, Mod, _Kind) ->
- {aborted, {badarg, Fun, Args, Retries, Mod}}.
- non_transaction(State, Fun, Args, ActivityKind, Mod)
- when function(Fun), list(Args), atom(Mod) ->
- mnesia_tm:non_transaction(State, Fun, Args, ActivityKind, Mod);
- non_transaction(_State, Fun, Args, _ActivityKind, _Mod) ->
- {aborted, {badarg, Fun, Args}}.
- async_dirty(Fun) ->
- async_dirty(Fun, []).
- async_dirty(Fun, Args) ->
- non_transaction(get(mnesia_activity_state), Fun, Args, async_dirty, ?DEFAULT_ACCESS).
- sync_dirty(Fun) ->
- sync_dirty(Fun, []).
- sync_dirty(Fun, Args) ->
- non_transaction(get(mnesia_activity_state), Fun, Args, sync_dirty, ?DEFAULT_ACCESS).
- ets(Fun) ->
- ets(Fun, []).
- ets(Fun, Args) ->
- non_transaction(get(mnesia_activity_state), Fun, Args, ets, ?DEFAULT_ACCESS).
- activity(Kind, Fun) ->
- activity(Kind, Fun, []).
- activity(Kind, Fun, Args) when list(Args) ->
- activity(Kind, Fun, Args, mnesia_monitor:get_env(access_module));
- activity(Kind, Fun, Mod) ->
- activity(Kind, Fun, [], Mod).
- activity(Kind, Fun, Args, Mod) ->
- State = get(mnesia_activity_state),
- case Kind of
- ets -> non_transaction(State, Fun, Args, Kind, Mod);
- async_dirty -> non_transaction(State, Fun, Args, Kind, Mod);
- sync_dirty -> non_transaction(State, Fun, Args, Kind, Mod);
- transaction -> wrap_trans(State, Fun, Args, infinity, Mod, async);
- {transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, async);
- sync_transaction -> wrap_trans(State, Fun, Args, infinity, Mod, sync);
- {sync_transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, sync);
- _ -> {aborted, {bad_type, Kind}}
- end.
- wrap_trans(State, Fun, Args, Retries, Mod, Kind) ->
- case transaction(State, Fun, Args, Retries, Mod, Kind) of
- {atomic, GoodRes} -> GoodRes;
- BadRes -> exit(BadRes)
- end.
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Access within an activity - lock acquisition
- %% Grab a lock on an item in the global lock table
- %% Item may be any term. Lock may be write or read.
- %% write lock is set on all the given nodes
- %% read lock is only set on the first node
- %% Nodes may either be a list of nodes or one node as an atom
- %% Mnesia on all Nodes must be connected to each other, but
- %% it is not neccessary that they are up and running.
- lock(LockItem, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- lock(Tid, Ts, LockItem, LockKind);
- {Mod, Tid, Ts} ->
- Mod:lock(Tid, Ts, LockItem, LockKind);
- _ ->
- abort(no_transaction)
- end.
- lock(Tid, Ts, LockItem, LockKind) ->
- case element(1, Tid) of
- tid ->
- case LockItem of
- {record, Tab, Key} ->
- lock_record(Tid, Ts, Tab, Key, LockKind);
- {table, Tab} ->
- lock_table(Tid, Ts, Tab, LockKind);
- {global, GlobalKey, Nodes} ->
- global_lock(Tid, Ts, GlobalKey, LockKind, Nodes);
- _ ->
- abort({bad_type, LockItem})
- end;
- _Protocol ->
- []
- end.
- %% Grab a read lock on a whole table
- read_lock_table(Tab) ->
- lock({table, Tab}, read),
- ok.
- %% Grab a write lock on a whole table
- write_lock_table(Tab) ->
- lock({table, Tab}, write),
- ok.
- lock_record(Tid, Ts, Tab, Key, LockKind) when atom(Tab) ->
- Store = Ts#tidstore.store,
- Oid = {Tab, Key},
- case LockKind of
- read ->
- mnesia_locker:rlock(Tid, Store, Oid);
- write ->
- mnesia_locker:wlock(Tid, Store, Oid);
- sticky_write ->
- mnesia_locker:sticky_wlock(Tid, Store, Oid);
- none ->
- [];
- _ ->
- abort({bad_type, Tab, LockKind})
- end;
- lock_record(_Tid, _Ts, Tab, _Key, _LockKind) ->
- abort({bad_type, Tab}).
- lock_table(Tid, Ts, Tab, LockKind) when atom(Tab) ->
- Store = Ts#tidstore.store,
- case LockKind of
- read ->
- mnesia_locker:rlock_table(Tid, Store, Tab);
- write ->
- mnesia_locker:wlock_table(Tid, Store, Tab);
- sticky_write ->
- mnesia_locker:sticky_wlock_table(Tid, Store, Tab);
- none ->
- [];
- _ ->
- abort({bad_type, Tab, LockKind})
- end;
- lock_table(_Tid, _Ts, Tab, _LockKind) ->
- abort({bad_type, Tab}).
- global_lock(Tid, Ts, Item, Kind, Nodes) when list(Nodes) ->
- case element(1, Tid) of
- tid ->
- Store = Ts#tidstore.store,
- GoodNs = good_global_nodes(Nodes),
- if
- Kind /= read, Kind /= write ->
- abort({bad_type, Kind});
- true ->
- mnesia_locker:global_lock(Tid, Store, Item, Kind, GoodNs)
- end;
- _Protocol ->
- []
- end;
- global_lock(_Tid, _Ts, _Item, _Kind, Nodes) ->
- abort({bad_type, Nodes}).
- good_global_nodes(Nodes) ->
- Recover = [node() | val(recover_nodes)],
- mnesia_lib:intersect(Nodes, Recover).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Access within an activity - updates
- write(Val) when tuple(Val), size(Val) > 2 ->
- Tab = element(1, Val),
- write(Tab, Val, write);
- write(Val) ->
- abort({bad_type, Val}).
- s_write(Val) when tuple(Val), size(Val) > 2 ->
- Tab = element(1, Val),
- write(Tab, Val, sticky_write).
- write(Tab, Val, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- write(Tid, Ts, Tab, Val, LockKind);
- {Mod, Tid, Ts} ->
- Mod:write(Tid, Ts, Tab, Val, LockKind);
- _ ->
- abort(no_transaction)
- end.
- write(Tid, Ts, Tab, Val, LockKind)
- when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
- case element(1, Tid) of
- ets ->
- ?ets_insert(Tab, Val),
- ok;
- tid ->
- Store = Ts#tidstore.store,
- Oid = {Tab, element(2, Val)},
- case LockKind of
- write ->
- mnesia_locker:wlock(Tid, Store, Oid);
- sticky_write ->
- mnesia_locker:sticky_wlock(Tid, Store, Oid);
- _ ->
- abort({bad_type, Tab, LockKind})
- end,
- write_to_store(Tab, Store, Oid, Val);
- Protocol ->
- do_dirty_write(Protocol, Tab, Val)
- end;
- write(_Tid, _Ts, Tab, Val, LockKind) ->
- abort({bad_type, Tab, Val, LockKind}).
- write_to_store(Tab, Store, Oid, Val) ->
- case ?catch_val({Tab, record_validation}) of
- {RecName, Arity, Type}
- when size(Val) == Arity, RecName == element(1, Val) ->
- case Type of
- bag ->
- ?ets_insert(Store, {Oid, Val, write});
- _ ->
- ?ets_delete(Store, Oid),
- ?ets_insert(Store, {Oid, Val, write})
- end,
- ok;
- {'EXIT', _} ->
- abort({no_exists, Tab});
- _ ->
- abort({bad_type, Val})
- end.
- delete({Tab, Key}) ->
- delete(Tab, Key, write);
- delete(Oid) ->
- abort({bad_type, Oid}).
- s_delete({Tab, Key}) ->
- delete(Tab, Key, sticky_write);
- s_delete(Oid) ->
- abort({bad_type, Oid}).
- delete(Tab, Key, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- delete(Tid, Ts, Tab, Key, LockKind);
- {Mod, Tid, Ts} ->
- Mod:delete(Tid, Ts, Tab, Key, LockKind);
- _ ->
- abort(no_transaction)
- end.
- delete(Tid, Ts, Tab, Key, LockKind)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- ?ets_delete(Tab, Key),
- ok;
- tid ->
- Store = Ts#tidstore.store,
- Oid = {Tab, Key},
- case LockKind of
- write ->
- mnesia_locker:wlock(Tid, Store, Oid);
- sticky_write ->
- mnesia_locker:sticky_wlock(Tid, Store, Oid);
- _ ->
- abort({bad_type, Tab, LockKind})
- end,
- ?ets_delete(Store, Oid),
- ?ets_insert(Store, {Oid, Oid, delete}),
- ok;
- Protocol ->
- do_dirty_delete(Protocol, Tab, Key)
- end;
- delete(_Tid, _Ts, Tab, _Key, _LockKind) ->
- abort({bad_type, Tab}).
- delete_object(Val) when tuple(Val), size(Val) > 2 ->
- Tab = element(1, Val),
- delete_object(Tab, Val, write);
- delete_object(Val) ->
- abort({bad_type, Val}).
- s_delete_object(Val) when tuple(Val), size(Val) > 2 ->
- Tab = element(1, Val),
- delete_object(Tab, Val, sticky_write);
- s_delete_object(Val) ->
- abort({bad_type, Val}).
- delete_object(Tab, Val, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- delete_object(Tid, Ts, Tab, Val, LockKind);
- {Mod, Tid, Ts} ->
- Mod:delete_object(Tid, Ts, Tab, Val, LockKind);
- _ ->
- abort(no_transaction)
- end.
- delete_object(Tid, Ts, Tab, Val, LockKind)
- when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
- case element(1, Tid) of
- ets ->
- ?ets_match_delete(Tab, Val),
- ok;
- tid ->
- Store = Ts#tidstore.store,
- Oid = {Tab, element(2, Val)},
- case LockKind of
- write ->
- mnesia_locker:wlock(Tid, Store, Oid);
- sticky_write ->
- mnesia_locker:sticky_wlock(Tid, Store, Oid);
- _ ->
- abort({bad_type, Tab, LockKind})
- end,
- case val({Tab, setorbag}) of
- bag ->
- ?ets_match_delete(Store, {Oid, Val, '_'}),
- ?ets_insert(Store, {Oid, Val, delete_object});
- _ ->
- case ?ets_match_object(Store, {Oid, '_', write}) of
- [] ->
- ?ets_match_delete(Store, {Oid, Val, '_'}),
- ?ets_insert(Store, {Oid, Val, delete_object});
- _ ->
- ?ets_delete(Store, Oid),
- ?ets_insert(Store, {Oid, Oid, delete})
- end
- end,
- ok;
- Protocol ->
- do_dirty_delete_object(Protocol, Tab, Val)
- end;
- delete_object(_Tid, _Ts, Tab, _Key, _LockKind) ->
- abort({bad_type, Tab}).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Access within an activity - read
- read({Tab, Key}) ->
- read(Tab, Key, read);
- read(Oid) ->
- abort({bad_type, Oid}).
- wread({Tab, Key}) ->
- read(Tab, Key, write);
- wread(Oid) ->
- abort({bad_type, Oid}).
- read(Tab, Key, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- read(Tid, Ts, Tab, Key, LockKind);
- {Mod, Tid, Ts} ->
- Mod:read(Tid, Ts, Tab, Key, LockKind);
- _ ->
- abort(no_transaction)
- end.
- read(Tid, Ts, Tab, Key, LockKind)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- ?ets_lookup(Tab, Key);
- tid ->
- Store = Ts#tidstore.store,
- Oid = {Tab, Key},
- Objs =
- case LockKind of
- read ->
- mnesia_locker:rlock(Tid, Store, Oid);
- write ->
- mnesia_locker:rwlock(Tid, Store, Oid);
- sticky_write ->
- mnesia_locker:sticky_rwlock(Tid, Store, Oid);
- _ ->
- abort({bad_type, Tab, LockKind})
- end,
- add_written(?ets_lookup(Store, Oid), Tab, Objs);
- _Protocol ->
- dirty_read(Tab, Key)
- end;
- read(_Tid, _Ts, Tab, _Key, _LockKind) ->
- abort({bad_type, Tab}).
- first(Tab) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- first(Tid, Ts, Tab);
- {Mod, Tid, Ts} ->
- Mod:first(Tid, Ts, Tab);
- _ ->
- abort(no_transaction)
- end.
-
- first(Tid, Ts, Tab)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- ?ets_first(Tab);
- tid ->
- lock_table(Tid, Ts, Tab, read),
- do_fixtable(Tab,Ts),
- Key = dirty_first(Tab),
- stored_keys(Tab,Key,'$end_of_table',Ts,next,
- val({Tab, setorbag}));
- _Protocol ->
- dirty_first(Tab)
- end;
- first(_Tid, _Ts,Tab) ->
- abort({bad_type, Tab}).
- last(Tab) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- last(Tid, Ts, Tab);
- {Mod, Tid, Ts} ->
- Mod:last(Tid, Ts, Tab);
- _ ->
- abort(no_transaction)
- end.
- last(Tid, Ts, Tab)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- ?ets_last(Tab);
- tid ->
- lock_table(Tid, Ts, Tab, read),
- do_fixtable(Tab,Ts),
- Key = dirty_last(Tab),
- stored_keys(Tab,Key,'$end_of_table',Ts,prev,
- val({Tab, setorbag}));
- _Protocol ->
- dirty_last(Tab)
- end;
- last(_Tid, _Ts,Tab) ->
- abort({bad_type, Tab}).
- next(Tab,Key) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS,Tid,Ts} ->
- next(Tid,Ts,Tab,Key);
- {Mod,Tid,Ts} ->
- Mod:next(Tid,Ts,Tab,Key);
- _ ->
- abort(no_transaction)
- end.
- next(Tid,Ts,Tab,Key)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- ?ets_next(Tab,Key);
- tid ->
- lock_table(Tid, Ts, Tab, read),
- do_fixtable(Tab,Ts),
- New = (catch dirty_next(Tab,Key)),
- stored_keys(Tab,New,Key,Ts,next,
- val({Tab, setorbag}));
- _Protocol ->
- dirty_next(Tab,Key)
- end;
- next(_Tid, _Ts,Tab,_) ->
- abort({bad_type, Tab}).
- prev(Tab,Key) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS,Tid,Ts} ->
- prev(Tid,Ts,Tab,Key);
- {Mod,Tid,Ts} ->
- Mod:prev(Tid,Ts,Tab,Key);
- _ ->
- abort(no_transaction)
- end.
- prev(Tid,Ts,Tab,Key)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- ?ets_prev(Tab,Key);
- tid ->
- lock_table(Tid, Ts, Tab, read),
- do_fixtable(Tab,Ts),
- New = (catch dirty_prev(Tab,Key)),
- stored_keys(Tab,New,Key,Ts,prev,
- val({Tab, setorbag}));
- _Protocol ->
- dirty_prev(Tab,Key)
- end;
- prev(_Tid, _Ts,Tab,_) ->
- abort({bad_type, Tab}).
- %% Compensate for transaction written and/or deleted records
- stored_keys(Tab,'$end_of_table',Prev,Ts,Op,Type) ->
- case ts_keys(Ts#tidstore.store,Tab,Op,Type,[]) of
- [] -> '$end_of_table';
- Keys when Type == ordered_set->
- get_ordered_tskey(Prev,Keys,Op);
- Keys ->
- get_next_tskey(Prev,Keys,Tab)
- end;
- stored_keys(Tab,{'EXIT',{aborted,R={badarg,[Tab,Key]}}},
- Key,#tidstore{store=Store},Op,Type) ->
- %% Had to match on error, ouch..
- case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
- [] -> abort(R);
- Ops ->
- case lists:last(Ops) of
- [delete] -> abort(R);
- _ ->
- case ts_keys(Store,Tab,Op,Type,[]) of
- [] -> '$end_of_table';
- Keys -> get_next_tskey(Key,Keys,Tab)
- end
- end
- end;
- stored_keys(_,{'EXIT',{aborted,R}},_,_,_,_) ->
- abort(R);
- stored_keys(Tab,Key,Prev,#tidstore{store=Store},Op,ordered_set) ->
- case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
- [] ->
- Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
- get_ordered_tskey(Prev,Keys,Op);
- Ops ->
- case lists:last(Ops) of
- [delete] ->
- mnesia:Op(Tab,Key);
- _ ->
- Keys = ts_keys(Store,Tab,Op,ordered_set,[Key]),
- get_ordered_tskey(Prev,Keys,Op)
- end
- end;
- stored_keys(Tab,Key,_,#tidstore{store=Store},Op,_) ->
- case ?ets_match(Store, {{Tab, Key}, '_', '$1'}) of
- [] -> Key;
- Ops ->
- case lists:last(Ops) of
- [delete] -> mnesia:Op(Tab,Key);
- _ -> Key
- end
- end.
- get_ordered_tskey('$end_of_table', [First|_],_) -> First;
- get_ordered_tskey(Prev, [First|_], next) when Prev < First -> First;
- get_ordered_tskey(Prev, [First|_], prev) when Prev > First -> First;
- get_ordered_tskey(Prev, [_|R],Op) -> get_ordered_tskey(Prev,R,Op);
- get_ordered_tskey(_, [],_) -> '$end_of_table'.
- get_next_tskey(_, [],_) -> '$end_of_table';
- get_next_tskey(Key,Keys,Tab) ->
- Next =
- if Key == '$end_of_table' -> hd(Keys);
- true ->
- case lists:dropwhile(fun(A) -> A /= Key end, Keys) of
- [] -> hd(Keys); %% First stored key
- [Key] -> '$end_of_table';
- [Key,Next2|_] -> Next2
- end
- end,
- case Next of
- '$end_of_table' -> '$end_of_table';
- _ -> %% Really slow anybody got another solution??
- case dirty_read(Tab, Next) of
- [] -> Next;
- _ ->
- %% Updated value we already returned this key
- get_next_tskey(Next,Keys,Tab)
- end
- end.
- ts_keys(Store, Tab, Op, Type, Def) ->
- All = ?ets_match(Store, {{Tab,'$1'},'_','$2'}),
- Keys = ts_keys_1(All, Def),
- if
- Type == ordered_set, Op == prev ->
- lists:reverse(lists:sort(Keys));
- Type == ordered_set ->
- lists:sort(Keys);
- Op == next ->
- lists:reverse(Keys);
- true ->
- Keys
- end.
- ts_keys_1([[Key, write]|R], []) ->
- ts_keys_1(R, [Key]);
- ts_keys_1([[Key, write]|R], Acc=[Key|_]) ->
- ts_keys_1(R, Acc);
- ts_keys_1([[Key, write]|R], Acc) ->
- ts_keys_1(R, [Key|Acc]);
- ts_keys_1([[Key, delete]|R], [Key|Acc]) ->
- ts_keys_1(R, Acc);
- ts_keys_1([_|R], Acc) ->
- ts_keys_1(R, Acc);
- ts_keys_1([], Acc) ->
- Acc.
- %%%%%%%%%%%%%%%%%%%%%
- %% Iterators
- foldl(Fun, Acc, Tab) ->
- foldl(Fun, Acc, Tab, read).
- foldl(Fun, Acc, Tab, LockKind) when function(Fun) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
- {Mod, Tid, Ts} ->
- Mod:foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
- _ ->
- abort(no_transaction)
- end.
- foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
- {Type, Prev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
- Res = (catch do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)),
- close_iteration(Res, Tab).
- do_foldl(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
- lists:foldl(fun(Key, Acc) ->
- lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
- end, RAcc, Stored);
- do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, Stored);
- do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldl(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
- do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
- do_foldl(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
- NewStored = ordsets:del_element(Key, Stored),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, Type, NewStored).
- foldr(Fun, Acc, Tab) ->
- foldr(Fun, Acc, Tab, read).
- foldr(Fun, Acc, Tab, LockKind) when function(Fun) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
- {Mod, Tid, Ts} ->
- Mod:foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
- _ ->
- abort(no_transaction)
- end.
- foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
- {Type, TempPrev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
- Prev =
- if
- Type == ordered_set ->
- lists:reverse(TempPrev);
- true -> %% Order doesn't matter for set and bag
- TempPrev %% Keep the order so we can use ordsets:del_element
- end,
- Res = (catch do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)),
- close_iteration(Res, Tab).
- do_foldr(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
- lists:foldl(fun(Key, Acc) ->
- lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
- end, RAcc, Stored);
- do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, Stored);
- do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldr(Tid, Ts, Tab, Key, Fun, NewAcc, ordered_set, Stored);
- do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
- do_foldr(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag
- NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
- NewStored = ordsets:del_element(Key, Stored),
- {_, Tid, Ts} = get(mnesia_activity_state),
- do_foldr(Tid, Ts, Tab, dirty_prev(Tab, Key), Fun, NewAcc, Type, NewStored).
- init_iteration(ActivityId, Opaque, Tab, LockKind) ->
- lock(ActivityId, Opaque, {table, Tab}, LockKind),
- Type = val({Tab, setorbag}),
- Previous = add_previous(ActivityId, Opaque, Type, Tab),
- St = val({Tab, storage_type}),
- if
- St == unknown ->
- ignore;
- true ->
- mnesia_lib:db_fixtable(St, Tab, true)
- end,
- {Type, Previous}.
- close_iteration(Res, Tab) ->
- case val({Tab, storage_type}) of
- unknown ->
- ignore;
- St ->
- mnesia_lib:db_fixtable(St, Tab, false)
- end,
- case Res of
- {'EXIT', {aborted, What}} ->
- abort(What);
- {'EXIT', What} ->
- abort(What);
- _ ->
- Res
- end.
- add_previous(_ActivityId, non_transaction, _Type, _Tab) ->
- [];
- add_previous(_Tid, Ts, _Type, Tab) ->
- Previous = ?ets_match(Ts#tidstore.store, {{Tab, '$1'}, '_', write}),
- lists:sort(lists:concat(Previous)).
- %% This routine fixes up the return value from read/1 so that
- %% it is correct with respect to what this particular transaction
- %% has already written, deleted .... etc
- add_written([], _Tab, Objs) ->
- Objs; % standard normal fast case
- add_written(Written, Tab, Objs) ->
- case val({Tab, setorbag}) of
- bag ->
- add_written_to_bag(Written, Objs, []);
- _ ->
- add_written_to_set(Written)
- end.
- add_written_to_set(Ws) ->
- case lists:last(Ws) of
- {_, _, delete} -> [];
- {_, Val, write} -> [Val];
- {_, _, delete_object} -> []
- end.
- add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
- add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
- add_written_to_bag([], Objs, Ack) ->
- Objs ++ lists:reverse(Ack); %% Oldest write first as in ets
- add_written_to_bag([{_, _ , delete} | Tail], _Objs, _Ack) ->
- %% This transaction just deleted all objects
- %% with this key
- add_written_to_bag(Tail, [], []);
- add_written_to_bag([{_, Val, delete_object} | Tail], Objs, Ack) ->
- add_written_to_bag(Tail, lists:delete(Val, Objs), lists:delete(Val, Ack)).
- match_object(Pat) when tuple(Pat), size(Pat) > 2 ->
- Tab = element(1, Pat),
- match_object(Tab, Pat, read);
- match_object(Pat) ->
- abort({bad_type, Pat}).
- match_object(Tab, Pat, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- match_object(Tid, Ts, Tab, Pat, LockKind);
- {Mod, Tid, Ts} ->
- Mod:match_object(Tid, Ts, Tab, Pat, LockKind);
- _ ->
- abort(no_transaction)
- end.
- match_object(Tid, Ts, Tab, Pat, LockKind)
- when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
- case element(1, Tid) of
- ets ->
- mnesia_lib:db_match_object(ram_copies, Tab, Pat);
- tid ->
- Key = element(2, Pat),
- case has_var(Key) of
- false -> lock_record(Tid, Ts, Tab, Key, LockKind);
- true -> lock_table(Tid, Ts, Tab, LockKind)
- end,
- Objs = dirty_match_object(Tab, Pat),
- add_written_match(Ts#tidstore.store, Pat, Tab, Objs);
- _Protocol ->
- dirty_match_object(Tab, Pat)
- end;
- match_object(_Tid, _Ts, Tab, Pat, _LockKind) ->
- abort({bad_type, Tab, Pat}).
- add_written_match(S, Pat, Tab, Objs) ->
- Ops = find_ops(S, Tab, Pat),
- add_match(Ops, Objs, val({Tab, setorbag})).
- find_ops(S, Tab, Pat) ->
- GetWritten = [{{{Tab, '_'}, Pat, write}, [], ['$_']},
- {{{Tab, '_'}, '_', delete}, [], ['$_']},
- {{{Tab, '_'}, Pat, delete_object}, [], ['$_']}],
- ets:select(S, GetWritten).
-
- add_match([], Objs, _Type) ->
- Objs;
- add_match(Written, Objs, ordered_set) ->
- %% Must use keysort which is stable
- add_ordered_match(lists:keysort(1,Written), Objs, []);
- add_match([{Oid, _, delete}|R], Objs, Type) ->
- add_match(R, deloid(Oid, Objs), Type);
- add_match([{_Oid, Val, delete_object}|R], Objs, Type) ->
- add_match(R, lists:delete(Val, Objs), Type);
- add_match([{_Oid, Val, write}|R], Objs, bag) ->
- add_match(R, [Val | lists:delete(Val, Objs)], bag);
- add_match([{Oid, Val, write}|R], Objs, set) ->
- add_match(R, [Val | deloid(Oid,Objs)],set).
- %% For ordered_set only !!
- add_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs], Acc)
- when Key > element(2, Obj) ->
- add_ordered_match(Written, Objs, [Obj|Acc]);
- add_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_], Acc)
- when Key < element(2, Obj) ->
- add_ordered_match(Rest, [Val|Objs],Acc);
- add_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
- when Key < element(2, Obj) ->
- add_ordered_match(Rest,Objs,Acc);
- %% Greater than last object
- add_ordered_match([{_, Val, write}|Rest], [], Acc) ->
- add_ordered_match(Rest, [Val], Acc);
- add_ordered_match([_|Rest], [], Acc) ->
- add_ordered_match(Rest, [], Acc);
- %% Keys are equal from here
- add_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
- add_ordered_match(Rest, [Val|Objs], Acc);
- add_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
- add_ordered_match(Rest, Objs, Acc);
- add_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
- add_ordered_match(Rest, Objs, Acc);
- add_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
- add_ordered_match(Rest, Objs, Acc);
- add_ordered_match([], Objs, Acc) ->
- lists:reverse(Acc, Objs).
- %% For select chunk
- add_sel_match(Sorted, Objs, ordered_set) ->
- add_sel_ordered_match(Sorted, Objs, []);
- add_sel_match(Written, Objs, Type) ->
- add_sel_match(Written, Objs, Type, []).
- add_sel_match([], Objs, _Type, Acc) ->
- {Objs,lists:reverse(Acc)};
- add_sel_match([Op={Oid, _, delete}|R], Objs, Type, Acc) ->
- case deloid(Oid, Objs) of
- Objs ->
- add_sel_match(R, Objs, Type, [Op|Acc]);
- NewObjs when Type == set ->
- add_sel_match(R, NewObjs, Type, Acc);
- NewObjs -> %% If bag we may get more in next chunk
- add_sel_match(R, NewObjs, Type, [Op|Acc])
- end;
- add_sel_match([Op = {_Oid, Val, delete_object}|R], Objs, Type, Acc) ->
- case lists:delete(Val, Objs) of
- Objs ->
- add_sel_match(R, Objs, Type, [Op|Acc]);
- NewObjs when Type == set ->
- add_sel_match(R, NewObjs, Type, Acc);
- NewObjs ->
- add_sel_match(R, NewObjs, Type, [Op|Acc])
- end;
- add_sel_match([Op={Oid={_,Key}, Val, write}|R], Objs, bag, Acc) ->
- case lists:keymember(Key, 2, Objs) of
- true ->
- add_sel_match(R,[Val|lists:delete(Val,Objs)],bag,
- [{Oid,Val,delete_object}|Acc]);
- false ->
- add_sel_match(R,Objs,bag,[Op|Acc])
- end;
- add_sel_match([Op={Oid, Val, write}|R], Objs, set, Acc) ->
- case deloid(Oid,Objs) of
- Objs ->
- add_sel_match(R, Objs,set, [Op|Acc]);
- NewObjs ->
- add_sel_match(R, [Val | NewObjs],set, Acc)
- end.
- %% For ordered_set only !!
- add_sel_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs],Acc)
- when Key > element(2, Obj) ->
- add_sel_ordered_match(Written, Objs, [Obj|Acc]);
- add_sel_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_],Acc)
- when Key < element(2, Obj) ->
- add_sel_ordered_match(Rest,[Val|Objs],Acc);
- add_sel_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
- when Key < element(2, Obj) ->
- add_sel_ordered_match(Rest,Objs,Acc);
- %% Greater than last object
- add_sel_ordered_match(Ops1, [], Acc) ->
- {lists:reverse(Acc), Ops1};
- %% Keys are equal from here
- add_sel_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
- add_sel_ordered_match(Rest, [Val|Objs], Acc);
- add_sel_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
- add_sel_ordered_match(Rest, Objs, Acc);
- add_sel_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
- add_sel_ordered_match(Rest, Objs, Acc);
- add_sel_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
- add_sel_ordered_match(Rest, Objs, Acc);
- add_sel_ordered_match([], Objs, Acc) ->
- {lists:reverse(Acc, Objs),[]}.
- deloid(_Oid, []) ->
- [];
- deloid({Tab, Key}, [H | T]) when element(2, H) == Key ->
- deloid({Tab, Key}, T);
- deloid(Oid, [H | T]) ->
- [H | deloid(Oid, T)].
- %%%%%%%%%%%%%%%%%%
- % select
- select(Tab, Pat) ->
- select(Tab, Pat, read).
- select(Tab, Pat, LockKind)
- when atom(Tab), Tab /= schema, list(Pat) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- select(Tid, Ts, Tab, Pat, LockKind);
- {Mod, Tid, Ts} ->
- Mod:select(Tid, Ts, Tab, Pat, LockKind);
- _ ->
- abort(no_transaction)
- end;
- select(Tab, Pat, _Lock) ->
- abort({badarg, Tab, Pat}).
- select(Tid, Ts, Tab, Spec, LockKind) ->
- SelectFun = fun(FixedSpec) -> dirty_select(Tab, FixedSpec) end,
- fun_select(Tid, Ts, Tab, Spec, LockKind, Tab, SelectFun).
- fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, SelectFun) ->
- case element(1, Tid) of
- ets ->
- mnesia_lib:db_select(ram_copies, Tab, Spec);
- tid ->
- select_lock(Tid,Ts,LockKind,Spec,Tab),
- Store = Ts#tidstore.store,
- Written = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
- case Written of
- [] ->
- %% Nothing changed in the table during this transaction,
- %% Simple case get results from [d]ets
- SelectFun(Spec);
- _ ->
- %% Hard (slow case) records added or deleted earlier
- %% in the transaction, have to cope with that.
- Type = val({Tab, setorbag}),
- FixedSpec = get_record_pattern(Spec),
- TabRecs = SelectFun(FixedSpec),
- FixedRes = add_match(Written, TabRecs, Type),
- CMS = ets:match_spec_compile(Spec),
- ets:match_spec_run(FixedRes, CMS)
- end;
- _Protocol ->
- SelectFun(Spec)
- end.
- select_lock(Tid,Ts,LockKind,Spec,Tab) ->
- %% Avoid table lock if possible
- case Spec of
- [{HeadPat,_, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
- Key = element(2, HeadPat),
- case has_var(Key) of
- false -> lock_record(Tid, Ts, Tab, Key, LockKind);
- true -> lock_table(Tid, Ts, Tab, LockKind)
- end;
- _ ->
- lock_table(Tid, Ts, Tab, LockKind)
- end.
- %% Breakable Select
- select(Tab, Pat, NObjects, LockKind)
- when atom(Tab), Tab /= schema, list(Pat), number(NObjects) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- select(Tid, Ts, Tab, Pat, NObjects, LockKind);
- {Mod, Tid, Ts} ->
- Mod:select(Tid, Ts, Tab, Pat, NObjects, LockKind);
- _ ->
- abort(no_transaction)
- end;
- select(Tab, Pat, NObjects, _Lock) ->
- abort({badarg, Tab, Pat, NObjects}).
- select(Tid, Ts, Tab, Spec, NObjects, LockKind) ->
- Where = val({Tab,where_to_read}),
- Type = mnesia_lib:storage_type_at_node(Where,Tab),
- InitFun = fun(FixedSpec) -> dirty_sel_init(Where,Tab,FixedSpec,NObjects,Type) end,
- fun_select(Tid,Ts,Tab,Spec,LockKind,Tab,InitFun,NObjects,Where,Type).
- -record(mnesia_select, {tab,tid,node,storage,cont,written=[],spec,type,orig}).
- fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, Init, NObjects, Node, Storage) ->
- Def = #mnesia_select{tid=Tid,node=Node,storage=Storage,tab=Tab,orig=Spec},
- case element(1, Tid) of
- ets ->
- select_state(mnesia_lib:db_select_init(ram_copies,Tab,Spec,NObjects),Def);
- tid ->
- select_lock(Tid,Ts,LockKind,Spec,Tab),
- Store = Ts#tidstore.store,
- do_fixtable(Tab, Store),
-
- Written0 = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
- case Written0 of
- [] ->
- %% Nothing changed in the table during this transaction,
- %% Simple case get results from [d]ets
- select_state(Init(Spec),Def);
- _ ->
- %% Hard (slow case) records added or deleted earlier
- %% in the transaction, have to cope with that.
- Type = val({Tab, setorbag}),
- Written =
- if Type == ordered_set -> %% Sort stable
- lists:keysort(1,Written0);
- true ->
- Written0
- end,
- FixedSpec = get_record_pattern(Spec),
- CMS = ets:match_spec_compile(Spec),
- trans_select(Init(FixedSpec),
- Def#mnesia_select{written=Written,spec=CMS,type=Type})
- end;
- _Protocol ->
- select_state(Init(Spec),Def)
- end.
- select(Cont) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- select_cont(Tid,Ts,Cont);
- {Mod, Tid, Ts} ->
- Mod:select_cont(Tid,Ts,Cont);
- _ ->
- abort(no_transaction)
- end.
- select_cont(_Tid,_Ts,'$end_of_table') ->
- '$end_of_table';
- select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid,cont=Cont, orig=Ms})
- when element(1,Tid) == ets ->
- case Cont of
- '$end_of_table' -> '$end_of_table';
- _ -> select_state(mnesia_lib:db_select_cont(ram_copies,Cont,Ms),State)
- end;
- select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) ->
- select_state(dirty_sel_cont(State),State);
- select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid}) ->
- trans_select(dirty_sel_cont(State), State);
- select_cont(_Tid2,_,#mnesia_select{tid=_Tid1}) -> % Missmatching tids
- abort(wrong_transaction);
- select_cont(_,_,Cont) ->
- abort({badarg, Cont}).
- trans_select('$end_of_table', #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
- Written = add_match(Written0, [], Type),
- {ets:match_spec_run(Written, CMS), '$end_of_table'};
- trans_select({TabRecs,Cont}, State = #mnesia_select{written=Written0,spec=CMS,type=Type}) ->
- {FixedRes,Written} = add_sel_match(Written0, TabRecs, Type),
- select_state({ets:match_spec_run(FixedRes, CMS),Cont},
- State#mnesia_select{written=Written}).
- select_state({Matches, Cont}, MS) ->
- {Matches, MS#mnesia_select{cont=Cont}};
- select_state('$end_of_table',_) -> '$end_of_table'.
- get_record_pattern([]) -> [];
- get_record_pattern([{M,C,_B}|R]) ->
- [{M,C,['$_']} | get_record_pattern(R)].
- all_keys(Tab) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- all_keys(Tid, Ts, Tab, read);
- {Mod, Tid, Ts} ->
- Mod:all_keys(Tid, Ts, Tab, read);
- _ ->
- abort(no_transaction)
- end.
- all_keys(Tid, Ts, Tab, LockKind)
- when atom(Tab), Tab /= schema ->
- Pat0 = val({Tab, wild_pattern}),
- Pat = setelement(2, Pat0, '$1'),
- Keys = select(Tid, Ts, Tab, [{Pat, [], ['$1']}], LockKind),
- case val({Tab, setorbag}) of
- bag ->
- mnesia_lib:uniq(Keys);
- _ ->
- Keys
- end;
- all_keys(_Tid, _Ts, Tab, _LockKind) ->
- abort({bad_type, Tab}).
- index_match_object(Pat, Attr) when tuple(Pat), size(Pat) > 2 ->
- Tab = element(1, Pat),
- index_match_object(Tab, Pat, Attr, read);
- index_match_object(Pat, _Attr) ->
- abort({bad_type, Pat}).
- index_match_object(Tab, Pat, Attr, LockKind) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
- {Mod, Tid, Ts} ->
- Mod:index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
- _ ->
- abort(no_transaction)
- end.
- index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind)
- when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
- case element(1, Tid) of
- ets ->
- dirty_index_match_object(Tab, Pat, Attr); % Should be optimized?
- tid ->
- case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
- Pos when Pos =< size(Pat) ->
- case LockKind of
- read ->
- Store = Ts#tidstore.store,
- mnesia_locker:rlock_table(Tid, Store, Tab),
- Objs = dirty_index_match_object(Tab, Pat, Attr),
- add_written_match(Store, Pat, Tab, Objs);
- _ ->
- abort({bad_type, Tab, LockKind})
- end;
- BadPos ->
- abort({bad_type, Tab, BadPos})
- end;
- _Protocol ->
- dirty_index_match_object(Tab, Pat, Attr)
- end;
- index_match_object(_Tid, _Ts, Tab, Pat, _Attr, _LockKind) ->
- abort({bad_type, Tab, Pat}).
- index_read(Tab, Key, Attr) ->
- case get(mnesia_activity_state) of
- {?DEFAULT_ACCESS, Tid, Ts} ->
- index_read(Tid, Ts, Tab, Key, Attr, read);
- {Mod, Tid, Ts} ->
- Mod:index_read(Tid, Ts, Tab, Key, Attr, read);
- _ ->
- abort(no_transaction)
- end.
- index_read(Tid, Ts, Tab, Key, Attr, LockKind)
- when atom(Tab), Tab /= schema ->
- case element(1, Tid) of
- ets ->
- dirty_index_read(Tab, Key, Attr); % Should be optimized?
- tid ->
- Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
- case LockKind of
- read ->
- case has_var(Key) of
- false ->
- Store = Ts#tidstore.store,
- Objs = mnesia_index:read(Tid, Store, Tab, Key, Pos),
- Pat = setelement(Pos, val({Tab, wild_pattern}), Key),
- add_written_match(Store, Pat, Tab, Objs);
- true ->
- abort({bad_type, Tab, Attr, Key})
- end;
- _ ->
- abort({bad_type, Tab, LockKind})
- end;
- _Protocol ->
- dirty_index_read(Tab, Key, Attr)
- end;
- index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind) ->
- abort({bad_type, Tab}).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Dirty access regardless of activities - updates
- dirty_write(Val) when tuple(Val), size(Val) > 2 ->
- Tab = element(1, Val),
- dirty_write(Tab, Val);
- dirty_write(Val) ->
- abort({bad_type, Val}).
-
- dirty_write(Tab, Val) ->
- do_dirty_write(async_dirty, Tab, Val).
- do_dirty_write(SyncMode, Tab, Val)
- when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
- case ?catch_val({Tab, record_validation}) of
- {RecName, Arity, _Type}
- when size(Val) == Arity, RecName == element(1, Val) ->
- Oid = {Tab, element(2, Val)},
- mnesia_tm:dirty(SyncMode, {Oid, Val, write});
- {'EXIT', _} ->
- abort({no_exists, Tab});
- _ ->
- abort({bad_type, Val})
- end;
- do_dirty_write(_SyncMode, Tab, Val) ->
- abort({bad_type, Tab, Val}).
- dirty_delete({Tab, Key}) ->
- dirty_delete(Tab, Key);
- dirty_delete(Oid) ->
- abort({bad_type, Oid}).
- dirty_delete(Tab, Key) ->
- do_dirty_delete(async_dirty, Tab, Key).
-
- do_dirty_delete(SyncMode, Tab, Key) when atom(Tab), Tab /= schema ->
- Oid = {Tab, Key},
- mnesia_tm:dirty(SyncMode, {Oid, Oid, delete});
- do_dirty_delete(_SyncMode, Tab, _Key) ->
- abort({bad_type, Tab}).
- dirty_delete_object(Val) when tuple(Val), size(Val) > 2 ->
- Tab = element(1, Val),
- dirty_delete_object(Tab, Val);
- dirty_delete_object(Val) ->
- abort({bad_type, Val}).
- dirty_delete_object(Tab, Val) ->
- do_dirty_delete_object(async_dirty, Tab, Val).
- do_dirty_delete_object(SyncMode, Tab, Val)
- when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
- Oid = {Tab, element(2, Val)},
- mnesia_tm:dirty(SyncMode, {Oid, Val, delete_object});
- do_dirty_delete_object(_SyncMode, Tab, Val) ->
- abort({bad_type, Tab, Val}).
- %% A Counter is an Oid being {CounterTab, CounterName}
- dirty_update_counter({Tab, Key}, Incr) ->
- dirty_update_counter(Tab, Key, Incr);
- dirty_update_counter(Counter, _Incr) ->
- abort({bad_type, Counter}).
- dirty_update_counter(Tab, Key, Incr) ->
- do_dirty_update_counter(async_dirty, Tab, Key, Incr).
-
- do_dirty_update_counter(SyncMode, Tab, Key, Incr)
- when atom(Tab), Tab /= schema, integer(Incr) ->
- case ?catch_val({Tab, record_validation}) of
- {RecName, 3, set} ->
- Oid = {Tab, Key},
- mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
- _ ->
- abort({combine_error, Tab, update_counter})
- end;
- do_dirty_update_counter(_SyncMode, Tab, _Key, Incr) ->
- abort({bad_type, Tab, Incr}).
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Dirty access regardless of activities - read
- dirty_read({Tab, Key}) ->
- dirty_read(Tab, Key);
- dirty_read(Oid) ->
- abort({bad_type, Oid}).
- dirty_read(Tab, Key)
- when atom(Tab), Tab /= schema ->
- %% case catch ?ets_lookup(Tab, Key) of
- %% {'EXIT', _} ->
- %% Bad luck, we have to perform a real lookup
- dirty…
Large files files are truncated, but you can click here to view the full file