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