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