PageRenderTime 40ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/rdbms/mnesia_patches/src/mnesia_schema.erl

https://github.com/bmizerany/jungerl
Erlang | 3174 lines | 2607 code | 308 blank | 259 comment | 118 complexity | bb0aa5fd29604a11c9ce9cd87b408ce4 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, AGPL-1.0
  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with this software. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% $Id$
  17. %%
  18. %% In this module we provide a number of explicit functions
  19. %% to maninpulate the schema. All these functions are called
  20. %% within a special schema transaction.
  21. %%
  22. %% We also have an init/1 function defined here, this func is
  23. %% used by mnesia:start() to initialize the entire schema.
  24. -module(mnesia_schema).
  25. -export([
  26. add_snmp/2,
  27. add_table_copy/3,
  28. add_table_index/2,
  29. arrange_restore/3,
  30. attr_tab_to_pos/2,
  31. attr_to_pos/2,
  32. change_table_copy_type/3,
  33. change_table_access_mode/2,
  34. change_table_load_order/2,
  35. change_table_frag/2,
  36. clear_table/1,
  37. create_table/1,
  38. cs2list/1,
  39. del_snmp/1,
  40. del_table_copy/2,
  41. del_table_index/2,
  42. delete_cstruct/2,
  43. delete_schema/1,
  44. delete_schema2/0,
  45. delete_table/1,
  46. delete_table_property/2,
  47. dump_tables/1,
  48. ensure_no_schema/1,
  49. get_create_list/1,
  50. get_initial_schema/2,
  51. get_table_properties/1,
  52. info/0,
  53. info/1,
  54. init/1,
  55. insert_cstruct/3,
  56. is_remote_member/1,
  57. list2cs/1,
  58. lock_schema/0,
  59. lock_del_table/4, % Spawned
  60. merge_schema/0,
  61. move_table/3,
  62. opt_create_dir/2,
  63. prepare_commit/3,
  64. purge_dir/2,
  65. purge_tmp_files/0,
  66. ram_delete_table/2,
  67. % ram_delete_table/3,
  68. read_cstructs_from_disc/0,
  69. read_nodes/0,
  70. remote_read_schema/0,
  71. restore/1,
  72. restore/2,
  73. restore/3,
  74. schema_coordinator/3,
  75. set_where_to_read/3,
  76. transform_table/4,
  77. undo_prepare_commit/2,
  78. unlock_schema/0,
  79. version/0,
  80. write_table_property/2
  81. ]).
  82. %% Exports for mnesia_frag
  83. -export([
  84. get_tid_ts_and_lock/2,
  85. make_create_table/1,
  86. ensure_active/1,
  87. pick/4,
  88. verify/3,
  89. incr_version/1,
  90. check_keys/3,
  91. check_duplicates/2,
  92. make_delete_table/2
  93. ]).
  94. %% Needed outside to be able to use/set table_properties
  95. %% from user (not supported)
  96. -export([schema_transaction/1,
  97. insert_schema_ops/2,
  98. do_create_table/1,
  99. do_delete_table/1,
  100. do_read_table_property/2,
  101. do_delete_table_property/2,
  102. do_write_table_property/2,
  103. do_read_table_info/1,
  104. prepare_restore/3, % run outside of a transaction, if side-effects
  105. do_restore/1]).
  106. %%% Exported to allow for some preliminary validation of table definitions
  107. -export([verify_cstruct/1]).
  108. -include("mnesia.hrl").
  109. -include_lib("kernel/include/file.hrl").
  110. -import(mnesia_lib, [set/2, del/2, verbose/2, dbg_out/2]).
  111. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  112. %% Here comes the init function which also resides in
  113. %% this module, it is called upon by the trans server
  114. %% at startup of the system
  115. %%
  116. %% We have a meta table which looks like
  117. %% {table, schema,
  118. %% {type, set},
  119. %% {disc_copies, all},
  120. %% {arity, 2}
  121. %% {attributes, [key, val]}
  122. %%
  123. %% This means that we have a series of {schema, Name, Cs} tuples
  124. %% in a table called schema !!
  125. init(IgnoreFallback) ->
  126. Res = read_schema(true, false, IgnoreFallback),
  127. {ok, Source, _CreateList} = exit_on_error(Res),
  128. verbose("Schema initiated from: ~p~n", [Source]),
  129. set({schema, tables}, []),
  130. set({schema, local_tables}, []),
  131. Tabs = set_schema(?ets_first(schema)),
  132. lists:foreach(fun(Tab) -> clear_whereabouts(Tab) end, Tabs),
  133. set({schema, where_to_read}, node()),
  134. set({schema, load_node}, node()),
  135. set({schema, load_reason}, initial),
  136. mnesia_controller:add_active_replica(schema, node()).
  137. exit_on_error({error, Reason}) ->
  138. exit(Reason);
  139. exit_on_error(GoodRes) ->
  140. GoodRes.
  141. val(Var) ->
  142. case ?catch_val(Var) of
  143. {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
  144. Value -> Value
  145. end.
  146. %% This function traverses all cstructs in the schema and
  147. %% sets all values in mnesia_gvar accordingly for each table/cstruct
  148. set_schema('$end_of_table') ->
  149. [];
  150. set_schema(Tab) ->
  151. do_set_schema(Tab),
  152. [Tab | set_schema(?ets_next(schema, Tab))].
  153. get_create_list(Tab) ->
  154. ?ets_lookup_element(schema, Tab, 3).
  155. do_set_schema(Tab) ->
  156. List = get_create_list(Tab),
  157. Cs = list2cs(List),
  158. do_set_schema(Tab, Cs).
  159. do_set_schema(Tab, Cs) ->
  160. Type = Cs#cstruct.type,
  161. set({Tab, setorbag}, Type),
  162. set({Tab, local_content}, Cs#cstruct.local_content),
  163. set({Tab, ram_copies}, Cs#cstruct.ram_copies),
  164. set({Tab, disc_copies}, Cs#cstruct.disc_copies),
  165. set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies),
  166. set({Tab, external_copies}, Cs#cstruct.external_copies),
  167. set({Tab, load_order}, Cs#cstruct.load_order),
  168. set({Tab, access_mode}, Cs#cstruct.access_mode),
  169. set({Tab, snmp}, Cs#cstruct.snmp),
  170. set({Tab, user_properties}, Cs#cstruct.user_properties),
  171. [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties],
  172. set({Tab, frag_properties}, Cs#cstruct.frag_properties),
  173. mnesia_frag:set_frag_hash(Tab, Cs#cstruct.frag_properties),
  174. set({Tab, attributes}, Cs#cstruct.attributes),
  175. Arity = length(Cs#cstruct.attributes) + 1,
  176. set({Tab, arity}, Arity),
  177. RecName = Cs#cstruct.record_name,
  178. set({Tab, record_name}, RecName),
  179. set({Tab, record_validation}, {RecName, Arity, Type}),
  180. set({Tab, wild_pattern}, wild(RecName, Arity)),
  181. set({Tab, index}, Cs#cstruct.index),
  182. %% create actual index tabs later
  183. set({Tab, cookie}, Cs#cstruct.cookie),
  184. set({Tab, version}, Cs#cstruct.version),
  185. set({Tab, cstruct}, Cs),
  186. Storage = mnesia_lib:schema_cs_to_storage_type(node(), Cs),
  187. set({Tab, storage_type}, Storage),
  188. mnesia_lib:add({schema, tables}, Tab),
  189. Ns = mnesia_lib:cs_to_nodes(Cs),
  190. case lists:member(node(), Ns) of
  191. true ->
  192. mnesia_lib:add({schema, local_tables}, Tab);
  193. false when Tab == schema ->
  194. mnesia_lib:add({schema, local_tables}, Tab);
  195. false ->
  196. ignore
  197. end.
  198. wild(RecName, Arity) ->
  199. Wp0 = list_to_tuple(lists:duplicate(Arity, '_')),
  200. setelement(1, Wp0, RecName).
  201. %% Temporarily read the local schema and return a list
  202. %% of all nodes mentioned in the schema.DAT file
  203. read_nodes() ->
  204. %% Ensure that we access the intended Mnesia
  205. %% directory. This function may not be called
  206. %% during startup since it will cause the
  207. %% application_controller to get into deadlock
  208. case mnesia_lib:ensure_loaded(?APPLICATION) of
  209. ok ->
  210. case read_schema(false, false) of
  211. {ok, _Source, CreateList} ->
  212. Cs = list2cs(CreateList),
  213. {ok, Cs#cstruct.disc_copies ++ Cs#cstruct.ram_copies};
  214. {error, Reason} ->
  215. {error, Reason}
  216. end;
  217. {error, Reason} ->
  218. {error, Reason}
  219. end.
  220. %% Returns Version from the tuple {Version,MasterNodes}
  221. version() ->
  222. case read_schema(false, false) of
  223. {ok, Source, CreateList} when Source /= default ->
  224. Cs = list2cs(CreateList),
  225. {Version, _Details} = Cs#cstruct.version,
  226. Version;
  227. _ ->
  228. case dir_exists(mnesia_lib:dir()) of
  229. true -> {1,0};
  230. false -> {0,0}
  231. end
  232. end.
  233. %% Calculate next table version from old cstruct
  234. incr_version(Cs) ->
  235. {{Major, Minor}, _} = Cs#cstruct.version,
  236. Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
  237. mnesia_lib:cs_to_nodes(Cs)),
  238. V =
  239. case Nodes -- val({Cs#cstruct.name, active_replicas}) of
  240. [] -> {Major + 1, 0}; % All replicas are active
  241. _ -> {Major, Minor + 1} % Some replicas are inactive
  242. end,
  243. Cs#cstruct{version = {V, {node(), now()}}}.
  244. %% Returns table name
  245. insert_cstruct(Tid, Cs, KeepWhereabouts) ->
  246. Tab = Cs#cstruct.name,
  247. TabDef = cs2list(Cs),
  248. Val = {schema, Tab, TabDef},
  249. mnesia_checkpoint:tm_retain(Tid, schema, Tab, write),
  250. mnesia_subscr:report_table_event(schema, Tid, Val, write),
  251. Active = val({Tab, active_replicas}),
  252. case KeepWhereabouts of
  253. true ->
  254. ignore;
  255. false when Active == [] ->
  256. clear_whereabouts(Tab);
  257. false ->
  258. %% Someone else has initiated table
  259. ignore
  260. end,
  261. set({Tab, cstruct}, Cs),
  262. ?ets_insert(schema, Val),
  263. do_set_schema(Tab, Cs),
  264. Val.
  265. clear_whereabouts(Tab) ->
  266. set({Tab, checkpoints}, []),
  267. set({Tab, subscribers}, []),
  268. set({Tab, where_to_read}, nowhere),
  269. set({Tab, active_replicas}, []),
  270. set({Tab, commit_work}, []),
  271. set({Tab, where_to_write}, []),
  272. set({Tab, where_to_commit}, []),
  273. set({Tab, load_by_force}, false),
  274. set({Tab, load_node}, unknown),
  275. set({Tab, load_reason}, unknown).
  276. %% Returns table name
  277. delete_cstruct(Tid, Cs) ->
  278. Tab = Cs#cstruct.name,
  279. TabDef = cs2list(Cs),
  280. Val = {schema, Tab, TabDef},
  281. mnesia_checkpoint:tm_retain(Tid, schema, Tab, delete),
  282. mnesia_subscr:report_table_event(schema, Tid, Val, delete),
  283. ?ets_match_delete(mnesia_gvar, {{Tab, '_'}, '_'}),
  284. ?ets_match_delete(mnesia_gvar, {{Tab, '_', '_'}, '_'}),
  285. del({schema, local_tables}, Tab),
  286. del({schema, tables}, Tab),
  287. ?ets_delete(schema, Tab),
  288. Val.
  289. %% Delete the Mnesia directory on all given nodes
  290. %% Requires that Mnesia is not running anywhere
  291. %% Returns ok | {error,Reason}
  292. delete_schema(Ns) when list(Ns), Ns /= [] ->
  293. RunningNs = mnesia_lib:running_nodes(Ns),
  294. Reason = "Cannot delete schema on all nodes",
  295. if
  296. RunningNs == [] ->
  297. case rpc:multicall(Ns, ?MODULE, delete_schema2, []) of
  298. {Replies, []} ->
  299. case [R || R <- Replies, R /= ok] of
  300. [] ->
  301. ok;
  302. BadReplies ->
  303. verbose("~s: ~p~n", [Reason, BadReplies]),
  304. {error, {"All nodes not running", BadReplies}}
  305. end;
  306. {_Replies, BadNs} ->
  307. verbose("~s: ~p~n", [Reason, BadNs]),
  308. {error, {"All nodes not running", BadNs}}
  309. end;
  310. true ->
  311. verbose("~s: ~p~n", [Reason, RunningNs]),
  312. {error, {"Mnesia is not stopped everywhere", RunningNs}}
  313. end;
  314. delete_schema(Ns) ->
  315. {error, {badarg, Ns}}.
  316. delete_schema2() ->
  317. %% Ensure that we access the intended Mnesia
  318. %% directory. This function may not be called
  319. %% during startup since it will cause the
  320. %% application_controller to get into deadlock
  321. case mnesia_lib:ensure_loaded(?APPLICATION) of
  322. ok ->
  323. case mnesia_lib:is_running() of
  324. no ->
  325. Dir = mnesia_lib:dir(),
  326. purge_dir(Dir, []),
  327. ok;
  328. _ ->
  329. {error, {"Mnesia still running", node()}}
  330. end;
  331. {error, Reason} ->
  332. {error, Reason}
  333. end.
  334. ensure_no_schema([H|T]) when atom(H) ->
  335. case rpc:call(H, ?MODULE, remote_read_schema, []) of
  336. {badrpc, Reason} ->
  337. {H, {"All nodes not running", H, Reason}};
  338. {ok,Source, _} when Source /= default ->
  339. {H, {already_exists, H}};
  340. _ ->
  341. ensure_no_schema(T)
  342. end;
  343. ensure_no_schema([H|_]) ->
  344. {error,{badarg, H}};
  345. ensure_no_schema([]) ->
  346. ok.
  347. remote_read_schema() ->
  348. %% Ensure that we access the intended Mnesia
  349. %% directory. This function may not be called
  350. %% during startup since it will cause the
  351. %% application_controller to get into deadlock
  352. case mnesia_lib:ensure_loaded(?APPLICATION) of
  353. ok ->
  354. case mnesia_monitor:get_env(schema_location) of
  355. opt_disc ->
  356. read_schema(false, true);
  357. _ ->
  358. read_schema(false, false)
  359. end;
  360. {error, Reason} ->
  361. {error, Reason}
  362. end.
  363. dir_exists(Dir) ->
  364. dir_exists(Dir, mnesia_monitor:use_dir()).
  365. dir_exists(Dir, true) ->
  366. case file:read_file_info(Dir) of
  367. {ok, _} -> true;
  368. _ -> false
  369. end;
  370. dir_exists(_Dir, false) ->
  371. false.
  372. opt_create_dir(UseDir, Dir) when UseDir == true->
  373. case dir_exists(Dir, UseDir) of
  374. true ->
  375. check_can_write(Dir);
  376. false ->
  377. case file:make_dir(Dir) of
  378. ok ->
  379. verbose("Create Directory ~p~n", [Dir]),
  380. ok;
  381. {error, Reason} ->
  382. verbose("Cannot create mnesia dir ~p~n", [Reason]),
  383. {error, {"Cannot create Mnesia dir", Dir, Reason}}
  384. end
  385. end;
  386. opt_create_dir(false, _) ->
  387. {error, {has_no_disc, node()}}.
  388. check_can_write(Dir) ->
  389. case file:read_file_info(Dir) of
  390. {ok, FI} when FI#file_info.type == directory,
  391. FI#file_info.access == read_write ->
  392. ok;
  393. {ok, _} ->
  394. {error, "Not allowed to write in Mnesia dir", Dir};
  395. _ ->
  396. {error, "Non existent Mnesia dir", Dir}
  397. end.
  398. lock_schema() ->
  399. mnesia_lib:lock_table(schema).
  400. unlock_schema() ->
  401. mnesia_lib:unlock_table(schema).
  402. read_schema(Keep, _UseDirAnyway) ->
  403. read_schema(Keep, false, false).
  404. %% The schema may be read for several reasons.
  405. %% If Mnesia is not already started the read intention
  406. %% we normally do not want the ets table named schema
  407. %% be left around.
  408. %% If Keep == true, the ets table schema is kept
  409. %% If Keep == false, the ets table schema is removed
  410. %%
  411. %% Returns {ok, Source, SchemaCstruct} or {error, Reason}
  412. %% Source may be: default | ram | disc | fallback
  413. read_schema(Keep, UseDirAnyway, IgnoreFallback) ->
  414. lock_schema(),
  415. Res =
  416. case mnesia:system_info(is_running) of
  417. yes ->
  418. {ok, ram, get_create_list(schema)};
  419. _IsRunning ->
  420. case mnesia_monitor:use_dir() of
  421. true ->
  422. read_disc_schema(Keep, IgnoreFallback);
  423. false when UseDirAnyway == true ->
  424. read_disc_schema(Keep, IgnoreFallback);
  425. false when Keep == true ->
  426. Args = [{keypos, 2}, public, named_table, set],
  427. mnesia_monitor:mktab(schema, Args),
  428. CreateList = get_initial_schema(ram_copies, []),
  429. ?ets_insert(schema,{schema, schema, CreateList}),
  430. {ok, default, CreateList};
  431. false when Keep == false ->
  432. CreateList = get_initial_schema(ram_copies, []),
  433. {ok, default, CreateList}
  434. end
  435. end,
  436. unlock_schema(),
  437. Res.
  438. read_disc_schema(Keep, IgnoreFallback) ->
  439. Running = mnesia:system_info(is_running),
  440. case mnesia_bup:fallback_exists() of
  441. true when IgnoreFallback == false, Running /= yes ->
  442. mnesia_bup:fallback_to_schema();
  443. _ ->
  444. %% If we're running, we read the schema file even
  445. %% if fallback exists
  446. Dat = mnesia_lib:tab2dat(schema),
  447. case mnesia_lib:exists(Dat) of
  448. true ->
  449. do_read_disc_schema(Dat, Keep);
  450. false ->
  451. Dmp = mnesia_lib:tab2dmp(schema),
  452. case mnesia_lib:exists(Dmp) of
  453. true ->
  454. %% May only happen when toggling of
  455. %% schema storage type has been
  456. %% interrupted
  457. do_read_disc_schema(Dmp, Keep);
  458. false ->
  459. {error, "No schema file exists"}
  460. end
  461. end
  462. end.
  463. do_read_disc_schema(Fname, Keep) ->
  464. T =
  465. case Keep of
  466. false ->
  467. Args = [{keypos, 2}, public, set],
  468. ?ets_new_table(schema, Args);
  469. true ->
  470. Args = [{keypos, 2}, public, named_table, set],
  471. mnesia_monitor:mktab(schema, Args)
  472. end,
  473. Repair = mnesia_monitor:get_env(auto_repair),
  474. Res = % BUGBUG Fixa till dcl!
  475. case mnesia_lib:dets_to_ets(schema, T, Fname, set, Repair, no) of
  476. loaded -> {ok, disc, ?ets_lookup_element(T, schema, 3)};
  477. Other -> {error, {"Cannot read schema", Fname, Other}}
  478. end,
  479. case Keep of
  480. true -> ignore;
  481. false -> ?ets_delete_table(T)
  482. end,
  483. Res.
  484. get_initial_schema(SchemaStorage, Nodes) ->
  485. Cs = #cstruct{name = schema,
  486. record_name = schema,
  487. attributes = [table, cstruct]},
  488. Cs2 =
  489. case SchemaStorage of
  490. ram_copies -> Cs#cstruct{ram_copies = Nodes};
  491. disc_copies -> Cs#cstruct{disc_copies = Nodes}
  492. end,
  493. cs2list(Cs2).
  494. read_cstructs_from_disc() ->
  495. %% Assumptions:
  496. %% - local schema lock in global
  497. %% - use_dir is true
  498. %% - Mnesia is not running
  499. %% - Ignore fallback
  500. Fname = mnesia_lib:tab2dat(schema),
  501. case mnesia_lib:exists(Fname) of
  502. true ->
  503. Args = [{file, Fname},
  504. {keypos, 2},
  505. {repair, mnesia_monitor:get_env(auto_repair)},
  506. {type, set}],
  507. case dets:open_file(make_ref(), Args) of
  508. {ok, Tab} ->
  509. Fun = fun({_, _, List}) ->
  510. {continue, list2cs(List)}
  511. end,
  512. Cstructs = dets:traverse(Tab, Fun),
  513. dets:close(Tab),
  514. {ok, Cstructs};
  515. {error, Reason} ->
  516. {error, Reason}
  517. end;
  518. false ->
  519. {error, "No schema file exists"}
  520. end.
  521. %% We run a very special type of transactions when we
  522. %% we want to manipulate the schema.
  523. get_tid_ts_and_lock(Tab, Intent) ->
  524. TidTs = get(mnesia_activity_state),
  525. case TidTs of
  526. {_Mod, Tid, Ts} when record(Ts, tidstore)->
  527. case is_schema_transaction() of
  528. true ->
  529. ok;
  530. false ->
  531. mnesia:abort(no_schema_transaction)
  532. end,
  533. Store = Ts#tidstore.store,
  534. case Intent of
  535. read -> mnesia_locker:rlock_table(Tid, Store, Tab);
  536. write -> mnesia_locker:wlock_table(Tid, Store, Tab);
  537. none -> ignore
  538. end,
  539. TidTs;
  540. _ ->
  541. mnesia:abort(no_transaction)
  542. end.
  543. schema_transaction(Fun) ->
  544. case get(mnesia_activity_state) of
  545. undefined ->
  546. Args = [self(), Fun, whereis(mnesia_controller)],
  547. Pid = spawn_link(?MODULE, schema_coordinator, Args),
  548. receive
  549. {transaction_done, Res, Pid} -> Res;
  550. {'EXIT', Pid, R} -> {aborted, {transaction_crashed, R}}
  551. end;
  552. _ ->
  553. {aborted, nested_transaction}
  554. end.
  555. is_schema_transaction() ->
  556. case process_info(self(), initial_call) of
  557. {_, {?MODULE, schema_coordinator,_}} ->
  558. true;
  559. _ ->
  560. false
  561. end.
  562. %% This process may dump the transaction log, and should
  563. %% therefore not be run in an application process
  564. %%
  565. schema_coordinator(Client, _Fun, undefined) ->
  566. Res = {aborted, {node_not_running, node()}},
  567. Client ! {transaction_done, Res, self()},
  568. unlink(Client);
  569. schema_coordinator(Client, Fun, Controller) when pid(Controller) ->
  570. %% Do not trap exit in order to automatically die
  571. %% when the controller dies
  572. link(Controller),
  573. unlink(Client),
  574. %% Fulfull the transaction even if the client dies
  575. Res = case catch mnesia:activity(transaction, Fun) of
  576. {'EXIT', BadRes} -> {aborted, BadRes};
  577. GoodRes -> {atomic, GoodRes}
  578. end,
  579. Client ! {transaction_done, Res, self()},
  580. unlink(Controller), % Avoids spurious exit message
  581. unlink(whereis(mnesia_tm)), % Avoids spurious exit message
  582. exit(normal).
  583. %% The make* rotines return a list of ops, this function
  584. %% inserts em all in the Store and maintains the local order
  585. %% of ops.
  586. insert_schema_ops({_Mod, _Tid, Ts}, SchemaIOps) ->
  587. do_insert_schema_ops(Ts#tidstore.store, SchemaIOps).
  588. do_insert_schema_ops(Store, [Head | Tail]) ->
  589. ?ets_insert(Store, Head),
  590. do_insert_schema_ops(Store, Tail);
  591. do_insert_schema_ops(_Store, []) ->
  592. ok.
  593. cs2list(Cs) when record(Cs, cstruct) ->
  594. Tags = record_info(fields, cstruct),
  595. rec2list(Tags, 2, Cs);
  596. cs2list(CreateList) when list(CreateList) ->
  597. CreateList.
  598. rec2list([Tag | Tags], Pos, Rec) ->
  599. Val = element(Pos, Rec),
  600. [{Tag, Val} | rec2list(Tags, Pos + 1, Rec)];
  601. rec2list([], _Pos, _Rec) ->
  602. [].
  603. list2cs(List) when list(List) ->
  604. Name = pick(unknown, name, List, must),
  605. Type = pick(Name, type, List, set),
  606. Rc0 = pick(Name, ram_copies, List, []),
  607. Dc = pick(Name, disc_copies, List, []),
  608. Doc = pick(Name, disc_only_copies, List, []),
  609. Ext = pick(Name, external_copies, List, []),
  610. ExtC = ext_nodes(Ext),
  611. Rc = case {Rc0, Dc, Doc, ExtC} of
  612. {[], [], [], []} -> [node()];
  613. _ -> Rc0
  614. end,
  615. LC = pick(Name, local_content, List, false),
  616. RecName = pick(Name, record_name, List, Name),
  617. Attrs = pick(Name, attributes, List, [key, val]),
  618. Snmp = pick(Name, snmp, List, []),
  619. LoadOrder = pick(Name, load_order, List, 0),
  620. AccessMode = pick(Name, access_mode, List, read_write),
  621. UserProps = pick(Name, user_properties, List, []),
  622. %% ..
  623. verify({alt, [nil, list]}, mnesia_lib:etype(UserProps),
  624. {bad_type, Name, {user_properties, UserProps}}),
  625. %% ..
  626. Cookie = pick(Name, cookie, List, ?unique_cookie),
  627. Version = pick(Name, version, List, {{2, 0}, []}),
  628. Ix = pick(Name, index, List, []),
  629. %% ..
  630. verify({alt, [nil, list]}, mnesia_lib:etype(Ix),
  631. {bad_type, Name, {index, [Ix]}}),
  632. %% ..
  633. Ix2 = [attr_to_pos(I, Attrs) || I <- Ix],
  634. Frag = pick(Name, frag_properties, List, []),
  635. %% ..
  636. verify({alt, [nil, list]}, mnesia_lib:etype(Frag),
  637. {badarg, Name, {frag_properties, Frag}}),
  638. Keys = check_keys(Name, List, record_info(fields, cstruct)),
  639. check_duplicates(Name, Keys),
  640. %% ..
  641. Cs = #cstruct{name = Name,
  642. ram_copies = Rc,
  643. disc_copies = Dc,
  644. disc_only_copies = Doc,
  645. external_copies = Ext,
  646. type = Type,
  647. index = Ix2,
  648. snmp = Snmp,
  649. load_order = LoadOrder,
  650. access_mode = AccessMode,
  651. local_content = LC,
  652. record_name = RecName,
  653. attributes = Attrs,
  654. user_properties = lists:sort(UserProps),
  655. frag_properties = lists:sort(Frag),
  656. cookie = Cookie,
  657. version = Version},
  658. verify_cstruct(Cs);
  659. list2cs(Other) ->
  660. mnesia:abort({badarg, Other}).
  661. pick(Tab, Key, List, Default) ->
  662. case lists:keysearch(Key, 1, List) of
  663. false when Default == must ->
  664. mnesia:abort({badarg, Tab, "Missing key", Key, List});
  665. false ->
  666. Default;
  667. {value, {Key, Value}} ->
  668. Value;
  669. {value, BadArg} ->
  670. mnesia:abort({bad_type, Tab, BadArg})
  671. end.
  672. %% Convert attribute name to integer if neccessary
  673. attr_tab_to_pos(_Tab, Pos) when integer(Pos) ->
  674. Pos;
  675. attr_tab_to_pos(Tab, Attr) ->
  676. attr_to_pos(Attr, val({Tab, attributes})).
  677. %% Convert attribute name to integer if neccessary
  678. attr_to_pos(Pos, _Attrs) when integer(Pos) ->
  679. Pos;
  680. attr_to_pos(Attr, Attrs) when atom(Attr) ->
  681. attr_to_pos(Attr, Attrs, 2);
  682. attr_to_pos(Attr, _) ->
  683. mnesia:abort({bad_type, Attr}).
  684. attr_to_pos(Attr, [Attr | _Attrs], Pos) ->
  685. Pos;
  686. attr_to_pos(Attr, [_ | Attrs], Pos) ->
  687. attr_to_pos(Attr, Attrs, Pos + 1);
  688. attr_to_pos(Attr, _, _) ->
  689. mnesia:abort({bad_type, Attr}).
  690. check_keys(Tab, [{Key, _Val} | Tail], Items) ->
  691. case lists:member(Key, Items) of
  692. true -> [Key | check_keys(Tab, Tail, Items)];
  693. false -> mnesia:abort({badarg, Tab, Key})
  694. end;
  695. check_keys(_, [], _) ->
  696. [];
  697. check_keys(Tab, Arg, _) ->
  698. mnesia:abort({badarg, Tab, Arg}).
  699. check_duplicates(Tab, Keys) ->
  700. case has_duplicates(Keys) of
  701. false -> ok;
  702. true -> mnesia:abort({badarg, Tab, "Duplicate keys", Keys})
  703. end.
  704. has_duplicates([H | T]) ->
  705. case lists:member(H, T) of
  706. true -> true;
  707. false -> has_duplicates(T)
  708. end;
  709. has_duplicates([]) ->
  710. false.
  711. %% This is the only place where we check the validity of data
  712. verify_cstruct(Cs) ->
  713. {#cstruct{} = NewCs, _} = verify_cstruct(Cs, _Visited = orddict:new()),
  714. NewCs.
  715. verify_cstruct(Cs, Visited) when record(Cs, cstruct) ->
  716. verify_nodes(Cs),
  717. Tab = Cs#cstruct.name,
  718. verify(atom, mnesia_lib:etype(Tab), {bad_type, Tab}),
  719. Type = Cs#cstruct.type,
  720. verify(true, lists:member(Type, [set, bag, ordered_set]),
  721. {bad_type, Tab, {type, Type}}),
  722. %% Currently ordered_set is not supported for disk_only_copies.
  723. if
  724. Type == ordered_set, Cs#cstruct.disc_only_copies /= [] ->
  725. mnesia:abort({bad_type, Tab, {not_supported, Type, disc_only_copies}});
  726. true ->
  727. ok
  728. end,
  729. RecName = Cs#cstruct.record_name,
  730. verify(atom, mnesia_lib:etype(RecName),
  731. {bad_type, Tab, {record_name, RecName}}),
  732. Attrs = Cs#cstruct.attributes,
  733. verify(list, mnesia_lib:etype(Attrs),
  734. {bad_type, Tab, {attributes, Attrs}}),
  735. Arity = length(Attrs) + 1,
  736. verify(true, Arity > 2, {bad_type, Tab, {attributes, Attrs}}),
  737. lists:foldl(fun(Attr,_Other) when Attr == snmp ->
  738. mnesia:abort({bad_type, Tab, {attributes, [Attr]}});
  739. (Attr,Other) ->
  740. verify(atom, mnesia_lib:etype(Attr),
  741. {bad_type, Tab, {attributes, [Attr]}}),
  742. verify(false, lists:member(Attr, Other),
  743. {combine_error, Tab, {attributes, [Attr | Other]}}),
  744. [Attr | Other]
  745. end,
  746. [],
  747. Attrs),
  748. Index = Cs#cstruct.index,
  749. verify({alt, [nil, list]}, mnesia_lib:etype(Index),
  750. {bad_type, Tab, {index, Index}}),
  751. IxFun =
  752. fun(Pos) ->
  753. verify(true, fun() ->
  754. if
  755. integer(Pos),
  756. Pos > 2,
  757. Pos =< Arity ->
  758. true;
  759. true -> false
  760. end
  761. end,
  762. {bad_type, Tab, {index, [Pos]}})
  763. end,
  764. lists:foreach(IxFun, Index),
  765. LC = Cs#cstruct.local_content,
  766. verify({alt, [true, false]}, LC,
  767. {bad_type, Tab, {local_content, LC}}),
  768. Access = Cs#cstruct.access_mode,
  769. verify({alt, [read_write, read_only]}, Access,
  770. {bad_type, Tab, {access_mode, Access}}),
  771. Snmp = Cs#cstruct.snmp,
  772. verify(true, mnesia_snmp_hook:check_ustruct(Snmp),
  773. {badarg, Tab, {snmp, Snmp}}),
  774. External = Cs#cstruct.external_copies,
  775. CheckProp = fun(Prop) when tuple(Prop), size(Prop) >= 1 -> ok;
  776. (Prop) -> mnesia:abort({bad_type, Tab, {user_properties, [Prop]}})
  777. end,
  778. lists:foreach(CheckProp, Cs#cstruct.user_properties),
  779. case Cs#cstruct.cookie of
  780. {{MegaSecs, Secs, MicroSecs}, _Node}
  781. when integer(MegaSecs), integer(Secs),
  782. integer(MicroSecs), atom(node) ->
  783. ok;
  784. Cookie ->
  785. mnesia:abort({bad_type, Tab, {cookie, Cookie}})
  786. end,
  787. case Cs#cstruct.version of
  788. {{Major, Minor}, _Detail}
  789. when integer(Major), integer(Minor) ->
  790. verify_external_copies(External, Cs, Visited);
  791. Version ->
  792. mnesia:abort({bad_type, Tab, {version, Version}})
  793. end.
  794. ext_nodes(Ext) ->
  795. %% this is checked to contain no duplicates in verify_nodes/1
  796. lists:concat([Ns || {_, Ns} <- Ext]).
  797. verify_external_copies(External, Cs, Visited) ->
  798. Tab = Cs#cstruct.name,
  799. NewVis = fun(Mod, CsX, V) ->
  800. orddict:update(Mod, fun({_OldCs,N}) ->
  801. {CsX, N+1}
  802. end, {CsX, 1}, V)
  803. end,
  804. DoVerify = fun(Mod, CsX, Vis) ->
  805. io:format("calling ~p:verify_cstruct(Cs)~n"
  806. "Visited = ~p~n", [Mod, Vis]),
  807. case Mod:verify_cstruct(CsX) of
  808. #cstruct{} = SameCs when SameCs == CsX ->
  809. {CsX, NewVis(Mod, CsX, Vis)};
  810. #cstruct{} = NewCs ->
  811. Vis1 = NewVis(Mod,NewCs, Vis),
  812. verify_cstruct(NewCs, Vis1)
  813. end
  814. end,
  815. lists:foldl(
  816. fun({Mod, Ns}, {CsX, VisitedX}) when is_atom(Mod), is_list(Ns) ->
  817. verify(true, fun() ->
  818. lists:all(fun(Node) when is_atom(Node) ->
  819. true;
  820. (_) -> false
  821. end, Ns)
  822. end, {bad_type, Tab, {external_copies, External}}),
  823. case orddict:find(Mod, VisitedX) of
  824. {ok, {Cs1, _}} when Cs1 == CsX ->
  825. %% have already checked
  826. {CsX, VisitedX};
  827. {ok, {Cs2, N}} when N < 17 -> % arbitrary loop ceiling
  828. DoVerify(Mod, Cs2, VisitedX);
  829. {ok, {_, N}} when N >= 17 ->
  830. mnesia:abort({loop_in_verify_external_copies,
  831. erlang:get_stacktrace()});
  832. error ->
  833. DoVerify(Mod, CsX, VisitedX)
  834. end
  835. end, {Cs, Visited}, External).
  836. verify_nodes(Cs) ->
  837. Tab = Cs#cstruct.name,
  838. Ram = Cs#cstruct.ram_copies,
  839. Disc = Cs#cstruct.disc_copies,
  840. DiscOnly = Cs#cstruct.disc_only_copies,
  841. External = Cs#cstruct.external_copies,
  842. LoadOrder = Cs#cstruct.load_order,
  843. verify({alt, [nil, list]}, mnesia_lib:etype(Ram),
  844. {bad_type, Tab, {ram_copies, Ram}}),
  845. verify({alt, [nil, list]}, mnesia_lib:etype(Disc),
  846. {bad_type, Tab, {disc_copies, Disc}}),
  847. case Tab of
  848. schema ->
  849. verify([], DiscOnly, {bad_type, Tab, {disc_only_copies, DiscOnly}});
  850. _ ->
  851. verify({alt, [nil, list]},
  852. mnesia_lib:etype(DiscOnly),
  853. {bad_type, Tab, {disc_only_copies, DiscOnly}})
  854. end,
  855. verify(integer, mnesia_lib:etype(LoadOrder),
  856. {bad_type, Tab, {load_order, LoadOrder}}),
  857. Nodes = Ram ++ Disc ++ DiscOnly ++ ext_nodes(External),
  858. verify(list, mnesia_lib:etype(Nodes),
  859. {combine_error, Tab,
  860. [{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []},
  861. {external_copies, []}]}),
  862. verify(false, has_duplicates(Nodes), {combine_error, Tab, Nodes}),
  863. AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end,
  864. lists:foreach(AtomCheck, Nodes).
  865. verify(Expected, Fun, Error) when function(Fun) ->
  866. do_verify(Expected, catch Fun(), Error);
  867. verify(Expected, Actual, Error) ->
  868. do_verify(Expected, Actual, Error).
  869. do_verify({alt, Values}, Value, Error) ->
  870. case lists:member(Value, Values) of
  871. true -> ok;
  872. false -> mnesia:abort(Error)
  873. end;
  874. do_verify(Value, Value, _) ->
  875. ok;
  876. do_verify(_Value, _, Error) ->
  877. mnesia:abort(Error).
  878. ensure_writable(Tab) ->
  879. case val({Tab, where_to_write}) of
  880. [] -> mnesia:abort({read_only, Tab});
  881. _ -> ok
  882. end.
  883. %% Ensure that all replicas on disk full nodes are active
  884. ensure_active(Cs) ->
  885. ensure_active(Cs, active_replicas).
  886. ensure_active(Cs, What) ->
  887. Tab = Cs#cstruct.name,
  888. case val({Tab, What}) of
  889. [] -> mnesia:abort({no_exists, Tab});
  890. _ -> ok
  891. end,
  892. Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
  893. mnesia_lib:cs_to_nodes(Cs)),
  894. W = {Tab, What},
  895. case Nodes -- val(W) of
  896. [] ->
  897. ok;
  898. Ns ->
  899. Expl = "All replicas on diskfull nodes are not active yet",
  900. case val({Tab, local_content}) of
  901. true ->
  902. case rpc:multicall(Ns, ?MODULE, is_remote_member, [W]) of
  903. {Replies, []} ->
  904. check_active(Replies, Expl, Tab);
  905. {_Replies, BadNs} ->
  906. mnesia:abort({not_active, Expl, Tab, BadNs})
  907. end;
  908. false ->
  909. mnesia:abort({not_active, Expl, Tab, Ns})
  910. end
  911. end.
  912. ensure_not_active(schema, Node) ->
  913. case lists:member(Node, val({schema, active_replicas})) of
  914. false ->
  915. ok;
  916. true ->
  917. Expl = "Mnesia is running",
  918. mnesia:abort({active, Expl, Node})
  919. end.
  920. is_remote_member(Key) ->
  921. IsActive = lists:member(node(), val(Key)),
  922. {IsActive, node()}.
  923. check_active([{true, _Node} | Replies], Expl, Tab) ->
  924. check_active(Replies, Expl, Tab);
  925. check_active([{false, Node} | _Replies], Expl, Tab) ->
  926. mnesia:abort({not_active, Expl, Tab, [Node]});
  927. check_active([{badrpc, Reason} | _Replies], Expl, Tab) ->
  928. mnesia:abort({not_active, Expl, Tab, Reason});
  929. check_active([], _Expl, _Tab) ->
  930. ok.
  931. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  932. %% Here's the real interface function to create a table
  933. create_table(TabDef) ->
  934. schema_transaction(fun() -> do_multi_create_table(TabDef) end).
  935. %% And the corresponding do routines ....
  936. do_multi_create_table(TabDef) ->
  937. get_tid_ts_and_lock(schema, write),
  938. ensure_writable(schema),
  939. Cs = list2cs(TabDef),
  940. case Cs#cstruct.frag_properties of
  941. [] ->
  942. do_create_table(Cs);
  943. _Props ->
  944. CsList = mnesia_frag:expand_cstruct(Cs),
  945. lists:foreach(fun do_create_table/1, CsList)
  946. end,
  947. ok.
  948. do_create_table(Cs) ->
  949. {_Mod, _Tid, Ts} = get_tid_ts_and_lock(schema, none),
  950. Store = Ts#tidstore.store,
  951. do_insert_schema_ops(Store, make_create_table(Cs)).
  952. make_create_table(Cs) ->
  953. Tab = Cs#cstruct.name,
  954. verify(false, check_if_exists(Tab), {already_exists, Tab}),
  955. unsafe_make_create_table(Cs).
  956. % unsafe_do_create_table(Cs) ->
  957. % {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
  958. % Store = Ts#tidstore.store,
  959. % do_insert_schema_ops(Store, unsafe_make_create_table(Cs)).
  960. unsafe_make_create_table(Cs0) ->
  961. {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
  962. Cs = verify_cstruct(Cs0),
  963. Tab = Cs#cstruct.name,
  964. %% Check that we have all disc replica nodes running
  965. DiscNodes = Cs#cstruct.disc_copies ++ Cs#cstruct.disc_only_copies,
  966. RunningNodes = val({current, db_nodes}),
  967. CheckDisc = fun(N) ->
  968. verify(true, lists:member(N, RunningNodes),
  969. {not_active, Tab, N})
  970. end,
  971. lists:foreach(CheckDisc, DiscNodes),
  972. Nodes = mnesia_lib:intersect(mnesia_lib:cs_to_nodes(Cs), RunningNodes),
  973. Store = Ts#tidstore.store,
  974. mnesia_locker:wlock_no_exist(Tid, Store, Tab, Nodes),
  975. [{op, create_table, cs2list(Cs)}].
  976. check_if_exists(Tab) ->
  977. TidTs = get_tid_ts_and_lock(schema, write),
  978. {_, _, Ts} = TidTs,
  979. Store = Ts#tidstore.store,
  980. ets:foldl(
  981. fun({op, create_table, [{name, T}|_]}, _Acc) when T==Tab ->
  982. true;
  983. ({op, delete_table, [{name,T}|_]}, _Acc) when T==Tab ->
  984. false;
  985. (_Other, Acc) ->
  986. Acc
  987. end, existed_before(Tab), Store).
  988. existed_before(Tab) ->
  989. ('EXIT' =/= element(1, ?catch_val({Tab,cstruct}))).
  990. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  991. %% Delete a table entirely on all nodes.
  992. delete_table(Tab) ->
  993. schema_transaction(fun() -> do_delete_table(Tab) end).
  994. do_delete_table(schema) ->
  995. mnesia:abort({bad_type, schema});
  996. do_delete_table(Tab) ->
  997. TidTs = get_tid_ts_and_lock(schema, write),
  998. ensure_writable(schema),
  999. insert_schema_ops(TidTs, make_delete_table(Tab, whole_table)).
  1000. make_delete_table(Tab, Mode) ->
  1001. case existed_before(Tab) of
  1002. false ->
  1003. %% Deleting a table that was created in this very
  1004. %% schema transaction. Delete all ops in the Store
  1005. %% that operate on this table. We cannot run a normal
  1006. %% delete operation, since that involves checking live
  1007. %% nodes etc.
  1008. TidTs = get_tid_ts_and_lock(schema, write),
  1009. {_, _, Ts} = TidTs,
  1010. Store = Ts#tidstore.store,
  1011. Deleted = ets:select_delete(
  1012. Store, [{{op,'$1',[{name,Tab}|'_']},
  1013. [{'or',
  1014. {'==','$1',create_table},
  1015. {'==','$1',delete_table}}], [true]}]),
  1016. ets:select_delete(
  1017. Store, [{{op,'$1',[{name,Tab}|'_'],'_'},
  1018. [{'or',
  1019. {'==','$1',write_table_property},
  1020. {'==','$1',delete_table_property}}],
  1021. [true]}]),
  1022. case Deleted of
  1023. 0 -> mnesia:abort({no_exists, Tab});
  1024. _ -> []
  1025. end;
  1026. true ->
  1027. case Mode of
  1028. whole_table ->
  1029. case val({Tab, frag_properties}) of
  1030. [] ->
  1031. [make_delete_table2(Tab)];
  1032. _Props ->
  1033. %% Check if it is a base table
  1034. mnesia_frag:lookup_frag_hash(Tab),
  1035. %% Check for foreigners
  1036. F = mnesia_frag:lookup_foreigners(Tab),
  1037. verify([], F, {combine_error,
  1038. Tab, "Too many foreigners", F}),
  1039. [make_delete_table2(T) ||
  1040. T <- mnesia_frag:frag_names(Tab)]
  1041. end;
  1042. single_frag ->
  1043. [make_delete_table2(Tab)]
  1044. end
  1045. end.
  1046. make_delete_table2(Tab) ->
  1047. get_tid_ts_and_lock(Tab, write),
  1048. Cs = val({Tab, cstruct}),
  1049. ensure_active(Cs),
  1050. ensure_writable(Tab),
  1051. {op, delete_table, cs2list(Cs)}.
  1052. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1053. %% Change fragmentation of a table
  1054. change_table_frag(Tab, Change) ->
  1055. schema_transaction(fun() -> do_change_table_frag(Tab, Change) end).
  1056. do_change_table_frag(Tab, Change) when atom(Tab), Tab /= schema ->
  1057. TidTs = get_tid_ts_and_lock(schema, write),
  1058. Ops = mnesia_frag:change_table_frag(Tab, Change),
  1059. [insert_schema_ops(TidTs, Op) || Op <- Ops],
  1060. ok;
  1061. do_change_table_frag(Tab, _Change) ->
  1062. mnesia:abort({bad_type, Tab}).
  1063. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1064. %% Clear a table
  1065. clear_table(Tab) ->
  1066. schema_transaction(fun() -> do_clear_table(Tab) end).
  1067. do_clear_table(schema) ->
  1068. mnesia:abort({bad_type, schema});
  1069. do_clear_table(Tab) ->
  1070. TidTs = get_tid_ts_and_lock(schema, write),
  1071. get_tid_ts_and_lock(Tab, write),
  1072. insert_schema_ops(TidTs, make_clear_table(Tab)).
  1073. make_clear_table(Tab) ->
  1074. ensure_writable(schema),
  1075. Cs = val({Tab, cstruct}),
  1076. ensure_active(Cs),
  1077. ensure_writable(Tab),
  1078. [{op, clear_table, cs2list(Cs)}].
  1079. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1080. add_table_copy(Tab, Node, Storage) ->
  1081. schema_transaction(fun() -> do_add_table_copy(Tab, Node, Storage) end).
  1082. do_add_table_copy(Tab, Node, Storage) when atom(Tab), atom(Node) ->
  1083. TidTs = get_tid_ts_and_lock(schema, write),
  1084. insert_schema_ops(TidTs, make_add_table_copy(Tab, Node, Storage));
  1085. do_add_table_copy(Tab,Node,_) ->
  1086. mnesia:abort({badarg, Tab, Node}).
  1087. make_add_table_copy(Tab, Node, Storage) ->
  1088. ensure_writable(schema),
  1089. Cs = incr_version(val({Tab, cstruct})),
  1090. Ns = mnesia_lib:cs_to_nodes(Cs),
  1091. verify(false, lists:member(Node, Ns), {already_exists, Tab, Node}),
  1092. Cs2 = verify_cstruct(new_cs(Cs, Node, Storage, add)),
  1093. %%% verify_cstruct(Cs2),
  1094. %% Check storage and if node is running
  1095. IsRunning = lists:member(Node, val({current, db_nodes})),
  1096. if
  1097. Storage == unknown ->
  1098. mnesia:abort({badarg, Tab, Storage});
  1099. Tab == schema ->
  1100. if
  1101. Storage /= ram_copies ->
  1102. mnesia:abort({badarg, Tab, Storage});
  1103. IsRunning == true ->
  1104. mnesia:abort({already_exists, Tab, Node});
  1105. true ->
  1106. ignore
  1107. end;
  1108. Storage == ram_copies ->
  1109. ignore;
  1110. IsRunning == true ->
  1111. ignore;
  1112. IsRunning == false ->
  1113. mnesia:abort({not_active, schema, Node})
  1114. end,
  1115. [{op, add_table_copy, Storage, Node, cs2list(Cs2)}].
  1116. del_table_copy(Tab, Node) ->
  1117. schema_transaction(fun() -> do_del_table_copy(Tab, Node) end).
  1118. do_del_table_copy(Tab, Node) when atom(Node) ->
  1119. TidTs = get_tid_ts_and_lock(schema, write),
  1120. %% get_tid_ts_and_lock(Tab, write),
  1121. insert_schema_ops(TidTs, make_del_table_copy(Tab, Node));
  1122. do_del_table_copy(Tab, Node) ->
  1123. mnesia:abort({badarg, Tab, Node}).
  1124. make_del_table_copy(Tab, Node) ->
  1125. ensure_writable(schema),
  1126. Cs = incr_version(val({Tab, cstruct})),
  1127. Storage = mnesia_lib:schema_cs_to_storage_type(Node, Cs),
  1128. Cs2 = new_cs(Cs, Node, Storage, del),
  1129. case mnesia_lib:cs_to_nodes(Cs2) of
  1130. [] when Tab == schema ->
  1131. mnesia:abort({combine_error, Tab, "Last replica"});
  1132. [] ->
  1133. ensure_active(Cs),
  1134. dbg_out("Last replica deleted in table ~p~n", [Tab]),
  1135. make_delete_table(Tab, whole_table);
  1136. _ when Tab == schema ->
  1137. ensure_active(Cs2),
  1138. ensure_not_active(Tab, Node),
  1139. Cs3 = verify_cstruct(Cs2),
  1140. Ops = remove_node_from_tabs(val({schema, tables}), Node),
  1141. [{op, del_table_copy, ram_copies, Node, cs2list(Cs3)} | Ops];
  1142. _ ->
  1143. ensure_active(Cs),
  1144. Cs3 = verify_cstruct(Cs2),
  1145. [{op, del_table_copy, Storage, Node, cs2list(Cs3)}]
  1146. end.
  1147. remove_node_from_tabs([], _Node) ->
  1148. [];
  1149. remove_node_from_tabs([schema|Rest], Node) ->
  1150. remove_node_from_tabs(Rest, Node);
  1151. remove_node_from_tabs([Tab|Rest], Node) ->
  1152. {Cs, IsFragModified} =
  1153. mnesia_frag:remove_node(Node, incr_version(val({Tab, cstruct}))),
  1154. case mnesia_lib:schema_cs_to_storage_type(Node, Cs) of
  1155. unknown ->
  1156. case IsFragModified of
  1157. true ->
  1158. [{op, change_table_frag, {del_node, Node}, cs2list(Cs)} |
  1159. remove_node_from_tabs(Rest, Node)];
  1160. false ->
  1161. remove_node_from_tabs(Rest, Node)
  1162. end;
  1163. Storage ->
  1164. Cs2 = new_cs(Cs, Node, Storage, del),
  1165. case mnesia_lib:cs_to_nodes(Cs2) of
  1166. [] ->
  1167. [{op, delete_table, cs2list(Cs)} |
  1168. remove_node_from_tabs(Rest, Node)];
  1169. _Ns ->
  1170. Cs3 = verify_cstruct(Cs2),
  1171. [{op, del_table_copy, ram_copies, Node, cs2list(Cs3)}|
  1172. remove_node_from_tabs(Rest, Node)]
  1173. end
  1174. end.
  1175. %% TODO: how to handle external copies here?
  1176. new_cs(Cs, Node, ram_copies, add) ->
  1177. Cs#cstruct{ram_copies = opt_add(Node, Cs#cstruct.ram_copies)};
  1178. new_cs(Cs, Node, disc_copies, add) ->
  1179. Cs#cstruct{disc_copies = opt_add(Node, Cs#cstruct.disc_copies)};
  1180. new_cs(Cs, Node, disc_only_copies, add) ->
  1181. Cs#cstruct{disc_only_copies = opt_add(Node, Cs#cstruct.disc_only_copies)};
  1182. new_cs(Cs, Node, ram_copies, del) ->
  1183. Cs#cstruct{ram_copies = lists:delete(Node , Cs#cstruct.ram_copies)};
  1184. new_cs(Cs, Node, disc_copies, del) ->
  1185. Cs#cstruct{disc_copies = lists:delete(Node , Cs#cstruct.disc_copies)};
  1186. new_cs(Cs, Node, disc_only_copies, del) ->
  1187. Cs#cstruct{disc_only_copies =
  1188. lists:delete(Node , Cs#cstruct.disc_only_copies)};
  1189. new_cs(Cs, _Node, Storage, _Op) ->
  1190. mnesia:abort({badarg, Cs#cstruct.name, Storage}).
  1191. opt_add(N, L) -> [N | lists:delete(N, L)].
  1192. move_table(Tab, FromNode, ToNode) ->
  1193. schema_transaction(fun() -> do_move_table(Tab, FromNode, ToNode) end).
  1194. do_move_table(schema, _FromNode, _ToNode) ->
  1195. mnesia:abort({bad_type, schema});
  1196. do_move_table(Tab, FromNode, ToNode) when atom(FromNode), atom(ToNode) ->
  1197. TidTs = get_tid_ts_and_lock(schema, write),
  1198. insert_schema_ops(TidTs, make_move_table(Tab, FromNode, ToNode));
  1199. do_move_table(Tab, FromNode, ToNode) ->
  1200. mnesia:abort({badarg, Tab, FromNode, ToNode}).
  1201. make_move_table(Tab, FromNode, ToNode) ->
  1202. ensure_writable(schema),
  1203. Cs = incr_version(val({Tab, cstruct})),
  1204. Ns = mnesia_lib:cs_to_nodes(Cs),
  1205. verify(false, lists:member(ToNode, Ns), {already_exists, Tab, ToNode}),
  1206. verify(true, lists:member(FromNode, val({Tab, where_to_write})),
  1207. {not_active, Tab, FromNode}),
  1208. verify(false, val({Tab,local_content}),
  1209. {"Cannot move table with local content", Tab}),
  1210. ensure_active(Cs),
  1211. Running = val({current, db_nodes}),
  1212. Storage = mnesia_lib:schema_cs_to_storage_type(FromNode, Cs),
  1213. verify(true, lists:member(ToNode, Running), {not_active, schema, ToNode}),
  1214. Cs2 = new_cs(Cs, ToNode, Storage, add),
  1215. Cs3 = verify_cstruct(new_cs(Cs2, FromNode, Storage, del)),
  1216. %%% verify_cstruct(Cs3),
  1217. [{op, add_table_copy, Storage, ToNode, cs2list(Cs2)},
  1218. {op, sync_trans},
  1219. {op, del_table_copy, Storage, FromNode, cs2list(Cs3)}].
  1220. %% end of functions to add and delete nodes to tables
  1221. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1222. %%
  1223. change_table_copy_type(Tab, Node, ToS) ->
  1224. schema_transaction(fun() -> do_change_table_copy_type(Tab, Node, ToS) end).
  1225. do_change_table_copy_type(Tab, Node, ToS) when atom(Node) ->
  1226. TidTs = get_tid_ts_and_lock(schema, write),
  1227. get_tid_ts_and_lock(Tab, write), % ensure global sync
  1228. %% get_tid_ts_and_lock(Tab, read),
  1229. insert_schema_ops(TidTs, make_change_table_copy_type(Tab, Node, ToS));
  1230. do_change_table_copy_type(Tab, Node, _ToS) ->
  1231. mnesia:abort({badarg, Tab, Node}).
  1232. make_change_table_copy_type(Tab, Node, unknown) ->
  1233. make_del_table_copy(Tab, Node);
  1234. make_change_table_copy_type(Tab, Node, ToS) ->
  1235. ensure_writable(schema),
  1236. Cs = incr_version(val({Tab, cstruct})),
  1237. FromS = mnesia_lib:storage_type_at_node(Node, Tab),
  1238. case compare_storage_type(false, FromS, ToS) of
  1239. {same, _} ->
  1240. mnesia:abort({already_exists, Tab, Node, ToS});
  1241. {diff, _} ->
  1242. ignore;
  1243. incompatible ->
  1244. ensure_active(Cs)
  1245. end,
  1246. Cs2 = new_cs(Cs, Node, FromS, del),
  1247. Cs3 = verify_cstruct(new_cs(Cs2, Node, ToS, add)),
  1248. %%% verify_cstruct(Cs3),
  1249. if
  1250. FromS == unknown ->
  1251. make_add_table_copy(Tab, Node, ToS);
  1252. true ->
  1253. ignore
  1254. end,
  1255. [{op, change_table_copy_type, Node, FromS, ToS, cs2list(Cs3)}].
  1256. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1257. %% change index functions ....
  1258. %% Pos is allready added by 1 in both of these functions
  1259. add_table_index(Tab, Pos) ->
  1260. schema_transaction(fun() -> do_add_table_index(Tab, Pos) end).
  1261. do_add_table_index(schema, _Attr) ->
  1262. mnesia:abort({bad_type, schema});
  1263. do_add_table_index(Tab, Attr) ->
  1264. TidTs = get_tid_ts_and_lock(schema, write),
  1265. get_tid_ts_and_lock(Tab, read),
  1266. Pos = attr_tab_to_pos(Tab, Attr),
  1267. insert_schema_ops(TidTs, make_add_table_index(Tab, Pos)).
  1268. make_add_table_index(Tab, Pos) ->
  1269. ensure_writable(schema),
  1270. Cs = incr_version(val({Tab, cstruct})),
  1271. ensure_active(Cs),
  1272. Ix = Cs#cstruct.index,
  1273. verify(false, lists:member(Pos, Ix), {already_exists, Tab, Pos}),
  1274. Ix2 = lists:sort([Pos | Ix]),
  1275. Cs2 = verify_cstruct(Cs#cstruct{index = Ix2}),
  1276. %%% verify_cstruct(Cs2),
  1277. [{op, add_index, Pos, cs2list(Cs2)}].
  1278. del_table_index(Tab, Pos) ->
  1279. schema_transaction(fun() -> do_del_table_index(Tab, Pos) end).
  1280. do_del_table_index(schema, _Attr) ->
  1281. mnesia:abort({bad_type, schema});
  1282. do_del_table_index(Tab, Attr) ->
  1283. TidTs = get_tid_ts_and_lock(schema, write),
  1284. get_tid_ts_and_lock(Tab, read),
  1285. Pos = attr_tab_to_pos(Tab, Attr),
  1286. insert_schema_ops(TidTs, make_del_table_index(Tab, Pos)).
  1287. make_del_table_index(Tab, Pos) ->
  1288. ensure_writable(schema),
  1289. Cs = incr_version(val({Tab, cstruct})),
  1290. ensure_active(Cs),
  1291. Ix = Cs#cstruct.index,
  1292. verify(true, lists:member(Pos, Ix), {no_exists, Tab, Pos}),
  1293. Cs2 = verify_cstruct(Cs#cstruct{index = lists:delete(Pos, Ix)}),
  1294. %%% verify_cstruct(Cs2),
  1295. [{op, del_index, Pos, cs2list(Cs2)}].
  1296. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1297. add_snmp(Tab, Ustruct) ->
  1298. schema_transaction(fun() -> do_add_snmp(Tab, Ustruct) end).
  1299. do_add_snmp(schema, _Ustruct) ->
  1300. mnesia:abort({bad_type, schema});
  1301. do_add_snmp(Tab, Ustruct) ->
  1302. TidTs = get_tid_ts_and_lock(schema, write),
  1303. get_tid_ts_and_lock(Tab, read),
  1304. insert_schema_ops(TidTs, make_add_snmp(Tab, Ustruct)).
  1305. make_add_snmp(Tab, Ustruct) ->
  1306. ensure_writable(schema),
  1307. Cs = incr_version(val({Tab, cstruct})),
  1308. ensure_active(Cs),
  1309. verify([], Cs#cstruct.snmp, {already_exists, Tab, snmp}),
  1310. Error = {badarg, Tab, snmp, Ustruct},
  1311. verify(true, mnesia_snmp_hook:check_ustruct(Ustruct), Error),
  1312. Cs2 = verify_cstruct(Cs#cstruct{snmp = Ustruct}),
  1313. %%% verify_cstruct(Cs2),
  1314. [{op, add_snmp, Ustruct, cs2list(Cs2)}].
  1315. del_snmp(Tab) ->
  1316. schema_transaction(fun() -> do_del_snmp(Tab) end).
  1317. do_del_snmp(schema) ->
  1318. mnesia:abort({bad_type, schema});
  1319. do_del_snmp(Tab) ->
  1320. TidTs = get_tid_ts_and_lock(schema, write),
  1321. get_tid_ts_and_lock(Tab, read),
  1322. insert_schema_ops(TidTs, make_del_snmp(Tab)).
  1323. make_del_snmp(Tab) ->
  1324. ensure_writable(schema),
  1325. Cs = incr_version(val({Tab, cstruct})),
  1326. ensure_active(Cs),
  1327. Cs2 = verify_cstruct(Cs#cstruct{snmp = []}),
  1328. %%% verify_cstruct(Cs2),
  1329. [{op, del_snmp, cs2list(Cs2)}].
  1330. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1331. %%
  1332. transform_table(Tab, Fun, NewAttrs, NewRecName)
  1333. when function(Fun), list(NewAttrs), atom(NewRecName) ->
  1334. schema_transaction(fun() -> do_transform_table(Tab, Fun, NewAttrs, NewRecName) end);
  1335. transform_table(Tab, ignore, NewAttrs, NewRecName)
  1336. when list(NewAttrs), atom(NewRecName) ->
  1337. schema_transaction(fun() -> do_transform_table(Tab, ignore, NewAttrs, NewRecName) end);
  1338. transform_table(Tab, Fun, NewAttrs, NewRecName) ->
  1339. {aborted,{bad_type, Tab, Fun, NewAttrs, NewRecName}}.
  1340. do_transform_table(schema, _Fun, _NewAttrs, _NewRecName) ->
  1341. mnesia:abort({bad_type, schema});
  1342. do_transform_table(Tab, Fun, NewAttrs, NewRecName) ->
  1343. TidTs = get_tid_ts_and_lock(schema, write),
  1344. get_tid_ts_and_lock(Tab, write),
  1345. insert_schema_ops(TidTs, make_transform(Tab, Fun, NewAttrs, NewRecName)).
  1346. make_transform(Tab, Fun, NewAttrs, NewRecName) ->
  1347. ensure_writable(schema),
  1348. Cs = incr_version(val({Tab, cstruct})),
  1349. ensure_active(Cs),
  1350. ensure_writable(Tab),
  1351. case mnesia_lib:val({Tab, index}) of
  1352. [] ->
  1353. Cs2 = verify_cstruct(
  1354. Cs#cstruct{attributes = NewAttrs,
  1355. record_name = NewRecName}),
  1356. %%% verify_cstruct(Cs2),
  1357. [{op, transform, Fun, cs2list(Cs2)}];
  1358. PosList ->
  1359. DelIdx = fun(Pos, Ncs) ->
  1360. Ix = Ncs#cstruct.index,
  1361. Ncs1 = Ncs#cstruct{index = lists:delete(Pos, Ix)},
  1362. Op = {op, del_index, Pos, cs2list(Ncs1)},
  1363. {Op, Ncs1}
  1364. end,
  1365. AddIdx = fun(Pos, Ncs) ->
  1366. Ix = Ncs#cstruct.index,
  1367. Ix2 = lists:sort([Pos | Ix]),
  1368. Ncs1 = Ncs#cstruct{index = Ix2},
  1369. Op = {op, add_index, Pos, cs2list(Ncs1)},
  1370. {Op, Ncs1}
  1371. end,
  1372. {DelOps, Cs1} = lists:mapfoldl(DelIdx, Cs, PosList),
  1373. Cs2 = Cs1#cstruct{attributes = NewAttrs, record_name = NewRecName},
  1374. {AddOps, Cs3} = lists:mapfoldl(AddIdx, Cs2, PosList),
  1375. Cs4 = verify_cstruct(Cs3),
  1376. lists:flatten([DelOps, {op, transform, Fun, cs2list(Cs4)}, AddOps])
  1377. end.
  1378. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1379. %%
  1380. change_table_access_mode(Tab, Mode) ->
  1381. schema_transaction(fun() -> do_change_table_access_mode(Tab, Mode) end).
  1382. do_change_table_access_mode(Tab, Mode) ->
  1383. {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
  1384. Store = Ts#tidstore.store,
  1385. mnesia_locker:wlock_no_exist(Tid, Store, schema, val({schema, active_replicas})),
  1386. mnesia_locker:wlock_no_exist(Tid, Store, Tab, val({Tab, active_replicas})),
  1387. do_insert_schema_ops(Store, make_change_table_access_mode(Tab, Mode)).
  1388. make_change_table_access_mode(Tab, Mode) ->
  1389. ensure_writable(schema),
  1390. Cs = incr_version(val({Tab, cstruct})),
  1391. ensure_active(Cs),
  1392. OldMode = Cs#cstruct.access_mode,
  1393. verify(false, OldMode == Mode, {already_exists, Tab, Mode}),
  1394. Cs2 = verify_cstruct(Cs#cstruct{access_mode = Mode}),
  1395. %%% verify_cstruct(Cs2),
  1396. [{op, change_table_access_mode, cs2list(Cs2), OldMode, Mode}].
  1397. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1398. change_table_load_order(Tab, LoadOrder) ->
  1399. schema_transaction(fun() -> do_change_table_load_order(Tab, LoadOrder) end).
  1400. do_change_table_load_order(schema, _LoadOrder) ->
  1401. mnesia:abort({bad_type, schema});
  1402. do_change_table_load_order(Tab, LoadOrder) ->
  1403. TidTs = get_tid_ts_and_lock(schema, write),
  1404. get_tid_ts_and_lock(Tab, none),
  1405. insert_schema_ops(TidTs, make_change_table_load_order(Tab, LoadOrder)).
  1406. make_change_table_load_order(Tab, LoadOrder) ->
  1407. ensure_writable(schema),
  1408. Cs = incr_version(val({Tab, cstruct})),
  1409. ensure_active(Cs),
  1410. OldLoadOrder = Cs#cstruct.load_order,
  1411. Cs2 = verify_cstruct(Cs#cstruct{load_order = LoadOrder}),
  1412. %%% verify_cstruct(Cs2),
  1413. [{op, change_table_load_order, cs2list(Cs2), OldLoadOrder, LoadOrder}].
  1414. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1415. write_table_property(Tab, Prop) when tuple(Prop), size(Prop) >= 1 ->
  1416. schema_transaction(fun() -> do_write_table_property(Tab, Prop) end);
  1417. write_table_property(Tab, Prop) ->
  1418. {aborted, {bad_type, Tab, Prop}}.
  1419. do_write_table_property(Tab, Prop) ->
  1420. TidTs = get_tid_ts_and_lock(schema, write),
  1421. {_, _, Ts} = TidTs,
  1422. Store = Ts#tidstore.store,
  1423. case change_prop_in_existing_op(Tab, Prop, write_property, Store) of
  1424. true ->
  1425. dbg_out("change_prop_in_existing_op"
  1426. "(~p,~p,write_property,Store) -> true~n",
  1427. [Tab,Prop]),
  1428. %% we have merged the table prop into the create_table op
  1429. ok;
  1430. false ->
  1431. dbg_out("change_prop_in_existing_op"
  1432. "(~p,~p,write_property,Store) -> false~n",
  1433. [Tab,Prop]),
  1434. %% this must be an existing table
  1435. get_tid_ts_and_lock(Tab, none),
  1436. insert_schema_ops(TidTs, make_write_table_properties(Tab, [Prop]))
  1437. end.
  1438. make_write_table_properties(Tab, Props) ->
  1439. ensure_writable(schema),
  1440. Cs = incr_version(val({Tab, cstruct})),
  1441. ensure_active(Cs),
  1442. make_write_table_properties(Tab, Props, Cs).
  1443. make_write_table_properties(Tab, [Prop | Props], Cs) ->
  1444. OldProps = Cs#cstruct.user_properties,
  1445. PropKey = element(1, Prop),
  1446. DelProps = lists:keydelete(PropKey, 1, OldProps),
  1447. MergedProps = lists:merge(DelProps, [Prop]),
  1448. Cs2 = verify_cstruct(Cs#cstruct{user_properties = MergedProps}),
  1449. %%% verify_cstruct(Cs2),
  1450. [{op, write_property, cs2list(Cs2), Prop} |
  1451. make_write_table_properties(Tab, Props, Cs2)];
  1452. make_write_table_properties(_Tab, [], _Cs) ->
  1453. [].
  1454. change_prop_in_existing_op(Tab, Prop, How, Store) ->
  1455. Ops = ets:match_object(Store, '_'),
  1456. case update_existing_op(Ops, Tab, Prop, How, []) of
  1457. {true, Ops1} ->
  1458. ets:match_delete(Store, '_'),
  1459. [ets:insert(Store, Op) || Op <- Ops1],
  1460. true;
  1461. false ->
  1462. false
  1463. end.
  1464. update_existing_op([{op, Op, L = [{name,Tab}|_], _OldProp}|Ops],
  1465. Tab, Prop, How, Acc) when Op == write_property;
  1466. Op == delete_property ->
  1467. %% Apparently, mnesia_dumper doesn't care about OldProp here -- just L,
  1468. %% so we will throw away OldProp (not that it matters...) and insert Prop.
  1469. %% as element 3.
  1470. L1 = insert_prop(Prop, L, How),
  1471. NewOp = {op, How, L1, Prop},
  1472. {true, lists:reverse(Acc) ++ [NewOp|Ops]};
  1473. update_existing_op([Op = {op, create_table, L}|Ops], Tab, Prop, How, Acc) ->
  1474. case lists:keysearch(name, 1, L) of
  1475. {value, {_, Tab}} ->
  1476. %% Tab is being created here -- insert Prop into L
  1477. L1 = insert_prop(Prop, L, How),
  1478. {true, lists:reverse(Acc) ++ [{op, create_table, L1}|Ops]};
  1479. _ ->
  1480. update_existing_op(Ops, Tab, Prop, How, [Op|Acc])
  1481. end;
  1482. update_existing_op([Op|Ops], Tab, Prop, How, Acc) ->
  1483. update_existing_op(Ops, Tab, Prop, How, [Op|Acc]);
  1484. update_existing_op([], _, _, _, _) ->
  1485. false.
  1486. do_read_table_property(Tab, Key) ->
  1487. Props = find_props(do_read_table_info(Tab)),
  1488. case lists:keysearch(Key, 1, Props) of
  1489. false ->
  1490. mnesia:abort({no_exists,Tab,Key});
  1491. {value, {_, Prop}} ->
  1492. Prop
  1493. end.
  1494. do_read_table_info(Tab) ->
  1495. TidTs = get_tid_ts_and_lock(schema, read),
  1496. {_, _, Ts} = TidTs,
  1497. Store = Ts#tidstore.store,
  1498. {InStore, Info} =
  1499. ets:foldl(
  1500. fun({op, create_table, [{name, T}|Opts]}, _Acc)
  1501. when T==Tab ->
  1502. {true, Opts};
  1503. ({op, Op, [{name,T}|Opts], _Prop}, _Acc)
  1504. when T==Tab, Op==write_property; Op==delete_property ->
  1505. {true, Opts};
  1506. ({op, delete_table, [{name,T}|_]}, _Acc)
  1507. when T==Tab ->
  1508. {true, []};
  1509. (_Other, Acc) ->
  1510. Acc
  1511. end, {false, []}, Store),
  1512. case InStore of
  1513. true ->
  1514. case Info of
  1515. [] ->
  1516. mnesia:abort({no_exists, {Tab,cstruct}});
  1517. I ->
  1518. I
  1519. end;
  1520. false ->
  1521. cs2list(val({Tab, cstruct}))
  1522. end.
  1523. %% perhaps a misnomer. How could also be delete_property... never mind.
  1524. %% Returns the modified L.
  1525. insert_prop(Prop, L, How) ->
  1526. Prev = find_props(L),
  1527. MergedProps = merge_with_previous(How, Prop, Prev),
  1528. replace_props(L, MergedProps).
  1529. find_props([{user_properties, P}|_]) -> P;
  1530. find_props([_H|T]) -> find_props(T).
  1531. %% we shouldn't reach []
  1532. replace_props([{user_properties, _}|T], P) -> [{user_properties, P}|T];
  1533. replace_props([H|T], P) -> [H|replace_props(T, P)].
  1534. %% again, we shouldn't reach []
  1535. merge_with_previous(write_property, Prop, Prev) ->
  1536. Key = element(1, Prop),
  1537. Prev1 = lists:keydelete(Key, 1, Prev),
  1538. lists:sort([Prop|Prev1]);
  1539. merge_with_previous(delete_property, PropKey, Prev) ->
  1540. lists:keydelete(PropKey, 1, Prev).
  1541. delete_table_property(Tab, PropKey) ->
  1542. schema_transaction(fun() -> do_delete_table_property(Tab, PropKey) end).
  1543. do_delete_table_property(Tab, PropKey) ->
  1544. TidTs = get_tid_ts_and_lock(schema, write),
  1545. {_, _, Ts} = TidTs,
  1546. Store = Ts#tidstore.store,
  1547. case change_prop_in_existing_op(Tab, PropKey, delete_property, Store) of
  1548. true ->
  1549. dbg_out("change_prop_in_existing_op"
  1550. "(~p,~p,delete_property,Store) -> true~n",
  1551. [Tab,PropKey]),
  1552. %% we have merged the table prop into the create_table op
  1553. ok;
  1554. false ->
  1555. dbg_out("change_prop_in_existing_op"
  1556. "(~p,~p,delete_property,Store) -> false~n",
  1557. [Tab,PropKey]),
  1558. %% this must be an existing table
  1559. get_tid_ts_and_lock(Tab, none),
  1560. insert_schema_ops(TidTs,
  1561. make_delete_table_properties(Tab, [PropKey]))
  1562. end.
  1563. make_delete_table_properties(Tab, PropKeys) ->
  1564. ensure_writable(schema),
  1565. Cs = incr_version(val({Tab, cstruct})),
  1566. ensure_active(Cs),
  1567. make_delete_table_properties(Tab, PropKeys, Cs).
  1568. make_delete_table_properties(Tab, [PropKey | PropKeys], Cs) ->
  1569. OldProps = Cs#cstruct.user_properties,
  1570. Props = lists:keydelete(PropKey, 1, OldProps),
  1571. Cs2 = verify_cstruct(Cs#cstruct{user_properties = Props}),
  1572. %%% verify_cstruct(Cs2),
  1573. [{op, delete_property, cs2list(Cs2), PropKey} |
  1574. make_delete_table_properties(Tab, PropKeys, Cs2)];
  1575. make_delete_table_properties(_Tab, [], _Cs) ->
  1576. [].
  1577. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1578. %% Ensure that the transaction can be committed even
  1579. %% if the node crashes and Mnesia is restarted
  1580. prepare_commit(Tid, Commit, WaitFor) ->
  1581. case Commit#commit.schema_ops of
  1582. [] ->
  1583. {false, Commit, optional};
  1584. OrigOps ->
  1585. {Modified, Ops, DumperMode} =
  1586. prepare_ops(Tid, OrigOps, WaitFor, false, [], optional),
  1587. InitBy = schema_prepare,
  1588. GoodRes = {Modified,
  1589. Commit#commit{schema_ops = lists:reverse(Ops)},
  1590. DumperMode},
  1591. case DumperMode of
  1592. optional ->
  1593. dbg_out("Transaction log dump skipped (~p): ~w~n",
  1594. [DumperMode, InitBy]);
  1595. mandatory ->
  1596. case mnesia_controller:sync_dump_log(InitBy) of
  1597. dumped ->
  1598. GoodRes;
  1599. {error, Reason} ->
  1600. mnesia:abort(Reason)
  1601. end
  1602. end,
  1603. case Ops of
  1604. [] ->
  1605. ignore;
  1606. _ ->
  1607. %% We need to grab a dumper lock here, the log may not
  1608. %% be dumped by others, during the schema commit phase.
  1609. mnesia_controller:wait_for_schema_commit_lock()
  1610. end,
  1611. GoodRes
  1612. end.
  1613. prepare_ops(Tid, [Op | Ops], WaitFor, Changed, Acc, DumperMode) ->
  1614. case prepare_op(Tid, Op, WaitFor) of
  1615. {true, mandatory} ->
  1616. prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], mandatory);
  1617. {true, optional} ->
  1618. prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], DumperMode);
  1619. {true, Ops2, mandatory} ->
  1620. prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, mandatory);
  1621. {true, Ops2, optional} ->
  1622. prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, DumperMode);
  1623. {false, mandatory} ->
  1624. prepare_ops(Tid, Ops, WaitFor, true, Acc, mandatory);
  1625. {false, optional} ->
  1626. prepare_ops(Tid, Ops, WaitFor, true, Acc, DumperMode)
  1627. end;
  1628. prepare_ops(_Tid, [], _WaitFor, Changed, Acc, DumperMode) ->
  1629. {Changed, Acc, DumperMode}.
  1630. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1631. %% Prepare for commit
  1632. %% returns true if Op should be included, i.e. unmodified
  1633. %% {true, Operation} if NewRecs should be included, i.e. modified
  1634. %% false if Op should NOT be included, i.e. modified
  1635. %%
  1636. prepare_op(_Tid, {op, rec, unknown, Rec}, _WaitFor) ->
  1637. {{Tab, Key}, Items, _Op} = Rec,
  1638. case val({Tab, storage_type}) of
  1639. unknown ->
  1640. {false, optional};
  1641. Storage ->
  1642. mnesia_tm:prepare_snmp(Tab, Key, Items), % May exit
  1643. {true, [{op, rec, Storage, Rec}], optional}
  1644. end;
  1645. prepare_op(_Tid, {op, announce_im_running, Node, SchemaDef, Running, RemoteRunning}, _WaitFor) ->
  1646. SchemaCs = list2cs(SchemaDef),
  1647. if
  1648. Node == node() -> %% Announce has already run on local node
  1649. ignore; %% from do_merge_schema
  1650. true ->
  1651. NewNodes = mnesia_lib:uniq(Running++RemoteRunning) -- val({current,db_nodes}),
  1652. mnesia_lib:set(prepare_op, {announce_im_running,NewNodes}),
  1653. announce_im_running(NewNodes, SchemaCs)
  1654. end,
  1655. {false, optional};
  1656. prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) ->
  1657. CoordPid ! {sync_trans, self()},
  1658. receive
  1659. {sync_trans, CoordPid} ->
  1660. {false, optional};
  1661. Else ->
  1662. mnesia_lib:verbose("sync_op terminated due to ~p~n", [Else]),
  1663. mnesia:abort(Else)
  1664. end;
  1665. prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) ->
  1666. case receive_sync(Nodes, []) of
  1667. {abort, Reason} ->
  1668. mnesia_lib:verbose("sync_op terminated due to ~p~n", [Reason]),
  1669. mnesia:abort(Reason);
  1670. Pids ->
  1671. [Pid ! {sync_trans, self()} || Pid <- Pids],
  1672. {false, optional}
  1673. end;
  1674. prepare_op(Tid, {op, create_table, TabDef}, _WaitFor) ->
  1675. Cs = list2cs(TabDef),
  1676. Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
  1677. UseDir = mnesia_monitor:use_dir(),
  1678. Tab = Cs#cstruct.name,
  1679. case Storage of
  1680. disc_copies when UseDir == false ->
  1681. UseDirReason = {bad_type, Tab, Storage, node()},
  1682. mnesia:abort(UseDirReason);
  1683. disc_only_copies when UseDir == false ->
  1684. UseDirReason = {bad_type, Tab, Storage, node()},
  1685. mnesia:abort(UseDirReason);
  1686. ram_copies ->
  1687. mnesia_lib:set({Tab, create_table},true),
  1688. create_ram_table(Tab, Cs#cstruct.type),
  1689. insert_cstruct(Tid, Cs, false),
  1690. {true, optional};
  1691. disc_copies ->
  1692. mnesia_lib:set({Tab, create_table},true),
  1693. create_ram_table(Tab, Cs#cstruct.type),
  1694. create_disc_table(Tab),
  1695. insert_cstruct(Tid, Cs, false),
  1696. {true, optional};
  1697. disc_only_copies ->
  1698. mnesia_lib:set({Tab, create_table},true),
  1699. create_disc_only_table(Tab,Cs#cstruct.type),
  1700. insert_cstruct(Tid, Cs, false),
  1701. {true, optional};
  1702. {external_copies, Mod} ->
  1703. mnesia_lib:set({Tab, create_table},true),
  1704. Mod:create_external_table(Cs),
  1705. insert_cstruct(Tid, Cs, false),
  1706. {true, optional};
  1707. unknown -> %% No replica on this node
  1708. mnesia_lib:set({Tab, create_table},true),
  1709. insert_cstruct(Tid, Cs, false),
  1710. {true, optional}
  1711. end;
  1712. prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}, _WaitFor) ->
  1713. Cs = list2cs(TabDef),
  1714. Tab = Cs#cstruct.name,
  1715. if
  1716. Tab == schema ->
  1717. {true, optional};
  1718. Node == node() ->
  1719. case mnesia_lib:val({schema, storage_type}) of
  1720. ram_copies when Storage /= ram_copies ->
  1721. %% TODO: should we consider external copies here?
  1722. Error = {combine_error, Tab, "has no disc", Node},
  1723. mnesia:abort(Error);
  1724. _ ->
  1725. ok
  1726. end,
  1727. %% Tables are created by mnesia_loader get_network code
  1728. insert_cstruct(Tid, Cs, true),
  1729. case mnesia_controller:get_network_copy(Tab, Cs) of
  1730. {loaded, ok} ->
  1731. {true, optional};
  1732. {not_loaded, ErrReason} ->
  1733. Reason = {system_limit, Tab, {Node, ErrReason}},
  1734. mnesia:abort(Reason)
  1735. end;
  1736. Node /= node() ->
  1737. %% Verify that ram table not has been dumped to disc
  1738. if
  1739. Storage /= ram_copies ->
  1740. case mnesia_lib:schema_cs_to_storage_type(node(), Cs) of
  1741. ram_copies ->
  1742. Dat = mnesia_lib:tab2dcd(Tab),
  1743. case mnesia_lib:exists(Dat) of
  1744. true ->
  1745. mnesia:abort({combine_error, Tab, Storage,
  1746. "Table dumped to disc", node()});
  1747. false ->
  1748. ok
  1749. end;
  1750. _ ->
  1751. ok
  1752. end;
  1753. true ->
  1754. ok
  1755. end,
  1756. insert_cstruct(Tid, Cs, true),
  1757. {true, optional}
  1758. end;
  1759. prepare_op(Tid, {op, del_table_copy, _Storage, Node, TabDef}, _WaitFor) ->
  1760. Cs = list2cs(TabDef),
  1761. Tab = Cs#cstruct.name,
  1762. if
  1763. %% Schema table lock is always required to run a schema op.
  1764. %% No need to look it.
  1765. node(Tid#tid.pid) == node(), Tab /= schema ->
  1766. Pid = spawn_link(?MODULE, lock_del_table, [Tab, Node, Cs, self()]),
  1767. receive
  1768. {Pid, updated} ->
  1769. {true, optional};
  1770. {Pid, FailReason} ->
  1771. mnesia:abort(FailReason);
  1772. {'EXIT', Pid, Reason} ->
  1773. mnesia:abort(Reason)
  1774. end;
  1775. true ->
  1776. {true, optional}
  1777. end;
  1778. %%% TODO: consider external copies
  1779. prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
  1780. when N == node() ->
  1781. Cs = list2cs(TabDef),
  1782. Tab = Cs#cstruct.name,
  1783. NotActive = mnesia_lib:not_active_here(Tab),
  1784. if
  1785. NotActive == true ->
  1786. mnesia:abort({not_active, Tab, node()});
  1787. Tab == schema ->
  1788. case {FromS, ToS} of
  1789. {ram_copies, disc_copies} ->
  1790. case mnesia:system_info(schema_location) of
  1791. opt_disc ->
  1792. ignore;
  1793. _ ->
  1794. mnesia:abort({combine_error, Tab, node(),
  1795. "schema_location must be opt_disc"})
  1796. end,
  1797. Dir = mnesia_lib:dir(),
  1798. case opt_create_dir(true, Dir) of
  1799. ok ->
  1800. purge_dir(Dir, []),
  1801. mnesia_log:purge_all_logs(),
  1802. set(use_dir, true),
  1803. mnesia_log:init(),
  1804. Ns = val({current, db_nodes}), %mnesia_lib:running_nodes(),
  1805. F = fun(U) -> mnesia_recover:log_mnesia_up(U) end,
  1806. lists:foreach(F, Ns),
  1807. mnesia_dumper:raw_named_dump_table(Tab, dmp),
  1808. mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS);
  1809. {error, Reason} ->
  1810. mnesia:abort(Reason)
  1811. end;
  1812. {disc_copies, ram_copies} ->
  1813. Ltabs = val({schema, local_tables}) -- [schema],
  1814. Dtabs = [L || L <- Ltabs,
  1815. val({L, storage_type}) /= ram_copies],
  1816. verify([], Dtabs, {"Disc resident tables", Dtabs, N});
  1817. _ ->
  1818. mnesia:abort({combine_error, Tab, ToS})
  1819. end;
  1820. FromS == ram_copies ->
  1821. case mnesia_monitor:use_dir() of
  1822. true ->
  1823. Dat = mnesia_lib:tab2dcd(Tab),
  1824. case mnesia_lib:exists(Dat) of
  1825. true ->
  1826. mnesia:abort({combine_error, Tab, node(),
  1827. "Table dump exists"});
  1828. false ->
  1829. case ToS of
  1830. disc_copies ->
  1831. mnesia_log:ets2dcd(Tab, dmp);
  1832. disc_only_copies ->
  1833. mnesia_dumper:raw_named_dump_table(Tab, dmp)
  1834. end,
  1835. mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS)
  1836. end;
  1837. false ->
  1838. mnesia:abort({has_no_disc, node()})
  1839. end;
  1840. FromS == disc_copies, ToS == disc_only_copies ->
  1841. mnesia_dumper:raw_named_dump_table(Tab, dmp);
  1842. FromS == disc_only_copies ->
  1843. Type = Cs#cstruct.type,
  1844. create_ram_table(Tab, Type),
  1845. Datname = mnesia_lib:tab2dat(Tab),
  1846. Repair = mnesia_monitor:get_env(auto_repair),
  1847. case mnesia_lib:dets_to_ets(Tab, Tab, Datname, Type, Repair, no) of
  1848. loaded -> ok;
  1849. Reason ->
  1850. Err = "Failed to copy disc data to ram",
  1851. mnesia:abort({system_limit, Tab, {Err,Reason}})
  1852. end;
  1853. true ->
  1854. ignore
  1855. end,
  1856. {true, mandatory};
  1857. prepare_op(_Tid, {op, change_table_copy_type, N, _FromS, _ToS, _TabDef}, _WaitFor)
  1858. when N /= node() ->
  1859. {true, mandatory};
  1860. prepare_op(_Tid, {op, delete_table, _TabDef}, _WaitFor) ->
  1861. {true, mandatory};
  1862. prepare_op(_Tid, {op, dump_table, unknown, TabDef}, _WaitFor) ->
  1863. Cs = list2cs(TabDef),
  1864. Tab = Cs#cstruct.name,
  1865. case lists:member(node(), Cs#cstruct.ram_copies) of
  1866. true ->
  1867. case mnesia_monitor:use_dir() of
  1868. true ->
  1869. mnesia_log:ets2dcd(Tab, dmp),
  1870. Size = mnesia:table_info(Tab, size),
  1871. {true, [{op, dump_table, Size, TabDef}], optional};
  1872. false ->
  1873. mnesia:abort({has_no_disc, node()})
  1874. end;
  1875. false ->
  1876. {false, optional}
  1877. end;
  1878. prepare_op(_Tid, {op, add_snmp, Ustruct, TabDef}, _WaitFor) ->
  1879. Cs = list2cs(TabDef),
  1880. case mnesia_lib:cs_to_storage_type(node(), Cs) of
  1881. unknown ->
  1882. {true, optional};
  1883. Storage ->
  1884. Tab = Cs#cstruct.name,
  1885. Stab = mnesia_snmp_hook:create_table(Ustruct, Tab, Storage),
  1886. mnesia_lib:set({Tab, {index, snmp}}, Stab),
  1887. {true, optional}
  1888. end;
  1889. prepare_op(_Tid, {op, transform, ignore, _TabDef}, _WaitFor) ->
  1890. {true, mandatory}; %% Apply schema changes only.
  1891. prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) ->
  1892. Cs = list2cs(TabDef),
  1893. case mnesia_lib:cs_to_storage_type(node(), Cs) of
  1894. unknown ->
  1895. {true, mandatory};
  1896. Storage ->
  1897. Tab = Cs#cstruct.name,
  1898. RecName = Cs#cstruct.record_name,
  1899. Type = Cs#cstruct.type,
  1900. NewArity = length(Cs#cstruct.attributes) + 1,
  1901. mnesia_lib:db_fixtable(Storage, Tab, true),
  1902. Key = mnesia_lib:db_first(Tab),
  1903. Op = {op, transform, Fun, TabDef},
  1904. case catch transform_objs(Fun, Tab, RecName,
  1905. Key, NewArity, Storage, Type, [Op]) of
  1906. {'EXIT', Reason} ->
  1907. mnesia_lib:db_fixtable(Storage, Tab, false),
  1908. exit({"Bad transform function", Tab, Fun, node(), Reason});
  1909. Objs ->
  1910. mnesia_lib:db_fixtable(Storage, Tab, false),
  1911. {true, Objs, mandatory}
  1912. end
  1913. end;
  1914. prepare_op(_Tid, {op, merge_schema, TabDef}, _WaitFor) ->
  1915. Cs = list2cs(TabDef),
  1916. case verify_merge(Cs) of
  1917. ok ->
  1918. {true, optional};
  1919. Error ->
  1920. verbose("Merge_Schema ~p failed on ~p: ~p~n", [_Tid,node(),Error]),
  1921. mnesia:abort({bad_commit, Error})
  1922. end;
  1923. prepare_op(_Tid, _Op, _WaitFor) ->
  1924. {true, optional}.
  1925. create_ram_table(Tab, Type) ->
  1926. Args = [{keypos, 2}, public, named_table, Type],
  1927. case mnesia_monitor:unsafe_mktab(Tab, Args) of
  1928. Tab ->
  1929. ok;
  1930. {error,Reason} ->
  1931. Err = "Failed to create ets table",
  1932. mnesia:abort({system_limit, Tab, {Err,Reason}})
  1933. end.
  1934. create_disc_table(Tab) ->
  1935. File = mnesia_lib:tab2dcd(Tab),
  1936. file:delete(File),
  1937. FArg = [{file, File}, {name, {mnesia,create}},
  1938. {repair, false}, {mode, read_write}],
  1939. case mnesia_monitor:open_log(FArg) of
  1940. {ok,Log} ->
  1941. mnesia_monitor:unsafe_close_log(Log),
  1942. ok;
  1943. {error,Reason} ->
  1944. Err = "Failed to create disc table",
  1945. mnesia:abort({system_limit, Tab, {Err,Reason}})
  1946. end.
  1947. create_disc_only_table(Tab,Type) ->
  1948. File = mnesia_lib:tab2dat(Tab),
  1949. file:delete(File),
  1950. Args = [{file, mnesia_lib:tab2dat(Tab)},
  1951. {type, mnesia_lib:disk_type(Tab, Type)},
  1952. {keypos, 2},
  1953. {repair, mnesia_monitor:get_env(auto_repair)}],
  1954. case mnesia_monitor:unsafe_open_dets(Tab, Args) of
  1955. {ok, _} ->
  1956. ok;
  1957. {error,Reason} ->
  1958. Err = "Failed to create disc table",
  1959. mnesia:abort({system_limit, Tab, {Err,Reason}})
  1960. end.
  1961. receive_sync([], Pids) ->
  1962. Pids;
  1963. receive_sync(Nodes, Pids) ->
  1964. receive
  1965. {sync_trans, Pid} ->
  1966. Node = node(Pid),
  1967. receive_sync(lists:delete(Node, Nodes), [Pid | Pids]);
  1968. Else ->
  1969. {abort, Else}
  1970. end.
  1971. lock_del_table(Tab, Node, Cs, Father) ->
  1972. Ns = val({schema, active_replicas}),
  1973. Lock = fun() ->
  1974. mnesia:write_lock_table(Tab),
  1975. {Res, []} = rpc:multicall(Ns, ?MODULE, set_where_to_read, [Tab, Node, Cs]),
  1976. Filter = fun(ok) ->
  1977. false;
  1978. ({badrpc, {'EXIT', {undef, _}}}) ->
  1979. %% This will be the case we talks with elder nodes
  1980. %% than 3.8.2, they will set where_to_read without
  1981. %% getting a lock.
  1982. false;
  1983. (_) ->
  1984. true
  1985. end,
  1986. [] = lists:filter(Filter, Res),
  1987. ok
  1988. end,
  1989. case mnesia:transaction(Lock) of
  1990. {atomic, ok} ->
  1991. Father ! {self(), updated};
  1992. {aborted, R} ->
  1993. Father ! {self(), R}
  1994. end,
  1995. unlink(Father),
  1996. exit(normal).
  1997. set_where_to_read(Tab, Node, Cs) ->
  1998. case mnesia_lib:val({Tab, where_to_read}) of
  1999. Node ->
  2000. case Cs#cstruct.local_content of
  2001. true ->
  2002. ok;
  2003. false ->
  2004. mnesia_lib:set_remote_where_to_read(Tab, [Node]),
  2005. ok
  2006. end;
  2007. _ ->
  2008. ok
  2009. end.
  2010. %% Build up the list in reverse order.
  2011. transform_objs(_Fun, _Tab, _RT, '$end_of_table', _NewArity, _Storage, _Type, Acc) ->
  2012. Acc;
  2013. transform_objs(Fun, Tab, RecName, Key, A, Storage, Type, Acc) ->
  2014. Objs = mnesia_lib:db_get(Tab, Key),
  2015. NextKey = mnesia_lib:db_next_key(Tab, Key),
  2016. Oid = {Tab, Key},
  2017. NewObjs = {Ws, Ds} = transform_obj(Tab, RecName, Key, Fun, Objs, A, Type, [], []),
  2018. if
  2019. NewObjs == {[], []} ->
  2020. transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, Acc);
  2021. Type == bag ->
  2022. transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
  2023. [{op, rec, Storage, {Oid, Ws, write}},
  2024. {op, rec, Storage, {Oid, [Oid], delete}} | Acc]);
  2025. Ds == [] ->
  2026. %% Type is set or ordered_set, no need to delete the record first
  2027. transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
  2028. [{op, rec, Storage, {Oid, Ws, write}} | Acc]);
  2029. Ws == [] ->
  2030. transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
  2031. [{op, rec, Storage, {Oid, Ds, write}} | Acc]);
  2032. true ->
  2033. transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
  2034. [{op, rec, Storage, {Oid, Ws, write}},
  2035. {op, rec, Storage, {Oid, Ds, delete}} | Acc])
  2036. end.
  2037. transform_obj(Tab, RecName, Key, Fun, [Obj|Rest], NewArity, Type, Ws, Ds) ->
  2038. NewObj = Fun(Obj),
  2039. if
  2040. size(NewObj) /= NewArity ->
  2041. exit({"Bad arity", Obj, NewObj});
  2042. NewObj == Obj ->
  2043. transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, Type, Ws, Ds);
  2044. RecName == element(1, NewObj), Key == element(2, NewObj) ->
  2045. transform_obj(Tab, RecName, Key, Fun, Rest, NewArity,
  2046. Type, [NewObj | Ws], Ds);
  2047. NewObj == delete ->
  2048. case Type of
  2049. bag -> %% Just don't write that object
  2050. transform_obj(Tab, RecName, Key, Fun, Rest,
  2051. NewArity, Type, Ws, Ds);
  2052. _ ->
  2053. transform_obj(Tab, RecName, Key, Fun, Rest, NewArity,
  2054. Type, Ws, [NewObj | Ds])
  2055. end;
  2056. true ->
  2057. exit({"Bad key or Record Name", Obj, NewObj})
  2058. end;
  2059. transform_obj(_Tab, _RecName, _Key, _Fun, [], _NewArity, _Type, Ws, Ds) ->
  2060. {lists:reverse(Ws), lists:reverse(Ds)}.
  2061. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2062. %% Undo prepare of commit
  2063. undo_prepare_commit(Tid, Commit) ->
  2064. case Commit#commit.schema_ops of
  2065. [] ->
  2066. ignore;
  2067. Ops ->
  2068. %% Catch to allow failure mnesia_controller may not be started
  2069. catch mnesia_controller:release_schema_commit_lock(),
  2070. undo_prepare_ops(Tid, Ops)
  2071. end,
  2072. Commit.
  2073. %% Undo in reverse order
  2074. undo_prepare_ops(Tid, [Op | Ops]) ->
  2075. case element(1, Op) of
  2076. TheOp when TheOp /= op, TheOp /= restore_op ->
  2077. undo_prepare_ops(Tid, Ops);
  2078. _ ->
  2079. undo_prepare_ops(Tid, Ops),
  2080. undo_prepare_op(Tid, Op)
  2081. end;
  2082. undo_prepare_ops(_Tid, []) ->
  2083. [].
  2084. undo_prepare_op(_Tid, {op, announce_im_running, _Node, _, _Running, _RemoteRunning}) ->
  2085. case ?catch_val(prepare_op) of
  2086. {announce_im_running, New} ->
  2087. unannounce_im_running(New);
  2088. _Else ->
  2089. ok
  2090. end;
  2091. undo_prepare_op(_Tid, {op, sync_trans}) ->
  2092. ok;
  2093. undo_prepare_op(Tid, {op, create_table, TabDef}) ->
  2094. Cs = list2cs(TabDef),
  2095. Tab = Cs#cstruct.name,
  2096. mnesia_lib:unset({Tab, create_table}),
  2097. delete_cstruct(Tid, Cs),
  2098. case mnesia_lib:cs_to_storage_type(node(), Cs) of
  2099. unknown ->
  2100. ok;
  2101. ram_copies ->
  2102. ram_delete_table(Tab, ram_copies);
  2103. disc_copies ->
  2104. ram_delete_table(Tab, disc_copies),
  2105. DcdFile = mnesia_lib:tab2dcd(Tab),
  2106. %% disc_delete_table(Tab, Storage),
  2107. file:delete(DcdFile);
  2108. disc_only_copies ->
  2109. mnesia_monitor:unsafe_close_dets(Tab),
  2110. Dat = mnesia_lib:tab2dat(Tab),
  2111. %% disc_delete_table(Tab, Storage),
  2112. file:delete(Dat);
  2113. {external_copies, Mod} ->
  2114. Mod:delete_external_table(Cs)
  2115. end;
  2116. undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) ->
  2117. Cs = list2cs(TabDef),
  2118. Tab = Cs#cstruct.name,
  2119. if
  2120. Tab == schema ->
  2121. true; % Nothing to prepare
  2122. Node == node() ->
  2123. mnesia_checkpoint:tm_del_copy(Tab, Node),
  2124. mnesia_controller:unannounce_add_table_copy(Tab, Node),
  2125. if
  2126. Storage == disc_only_copies; Tab == schema ->
  2127. mnesia_monitor:close_dets(Tab),
  2128. file:delete(mnesia_lib:tab2dat(Tab));
  2129. true ->
  2130. file:delete(mnesia_lib:tab2dcd(Tab))
  2131. end,
  2132. ram_delete_table(Tab, Storage),
  2133. Cs2 = new_cs(Cs, Node, Storage, del),
  2134. insert_cstruct(Tid, Cs2, true); % Don't care about the version
  2135. Node /= node() ->
  2136. mnesia_controller:unannounce_add_table_copy(Tab, Node),
  2137. Cs2 = new_cs(Cs, Node, Storage, del),
  2138. insert_cstruct(Tid, Cs2, true) % Don't care about the version
  2139. end;
  2140. undo_prepare_op(_Tid, {op, del_table_copy, _, Node, TabDef})
  2141. when Node == node() ->
  2142. Cs = list2cs(TabDef),
  2143. Tab = Cs#cstruct.name,
  2144. mnesia_lib:set({Tab, where_to_read}, Node);
  2145. undo_prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef})
  2146. when N == node() ->
  2147. Cs = list2cs(TabDef),
  2148. Tab = Cs#cstruct.name,
  2149. mnesia_checkpoint:tm_change_table_copy_type(Tab, ToS, FromS),
  2150. Dmp = mnesia_lib:tab2dmp(Tab),
  2151. case {FromS, ToS} of
  2152. {ram_copies, disc_copies} when Tab == schema ->
  2153. file:delete(Dmp),
  2154. mnesia_log:purge_some_logs(),
  2155. set(use_dir, false);
  2156. {ram_copies, disc_copies} ->
  2157. file:delete(Dmp);
  2158. {ram_copies, disc_only_copies} ->
  2159. file:delete(Dmp);
  2160. {disc_only_copies, _} ->
  2161. ram_delete_table(Tab, ram_copies);
  2162. _ ->
  2163. ignore
  2164. end;
  2165. undo_prepare_op(_Tid, {op, dump_table, _Size, TabDef}) ->
  2166. Cs = list2cs(TabDef),
  2167. case lists:member(node(), Cs#cstruct.ram_copies) of
  2168. true ->
  2169. Tab = Cs#cstruct.name,
  2170. Dmp = mnesia_lib:tab2dmp(Tab),
  2171. file:delete(Dmp);
  2172. false ->
  2173. ignore
  2174. end;
  2175. undo_prepare_op(_Tid, {op, add_snmp, _Ustruct, TabDef}) ->
  2176. Cs = list2cs(TabDef),
  2177. case mnesia_lib:cs_to_storage_type(node(), Cs) of
  2178. unknown ->
  2179. true;
  2180. _Storage ->
  2181. Tab = Cs#cstruct.name,
  2182. case ?catch_val({Tab, {index, snmp}}) of
  2183. {'EXIT',_} ->
  2184. ignore;
  2185. Stab ->
  2186. mnesia_snmp_hook:delete_table(Tab, Stab),
  2187. mnesia_lib:unset({Tab, {index, snmp}})
  2188. end
  2189. end;
  2190. undo_prepare_op(_Tid, _Op) ->
  2191. ignore.
  2192. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2193. ram_delete_table(Tab, Storage) ->
  2194. case Storage of
  2195. unknown ->
  2196. ignore;
  2197. disc_only_copies ->
  2198. ignore;
  2199. _Else ->
  2200. %% delete possible index files and data .....
  2201. %% Got to catch this since if no info has been set in the
  2202. %% mnesia_gvar it will crash
  2203. catch mnesia_index:del_transient(Tab, Storage),
  2204. case ?catch_val({Tab, {index, snmp}}) of
  2205. {'EXIT', _} ->
  2206. ignore;
  2207. Etab ->
  2208. catch mnesia_snmp_hook:delete_table(Tab, Etab)
  2209. end,
  2210. catch ?ets_delete_table(Tab)
  2211. end.
  2212. purge_dir(Dir, KeepFiles) ->
  2213. Suffixes = known_suffixes(),
  2214. purge_dir(Dir, KeepFiles, Suffixes).
  2215. purge_dir(Dir, KeepFiles, Suffixes) ->
  2216. case dir_exists(Dir) of
  2217. true ->
  2218. {ok, AllFiles} = file:list_dir(Dir),
  2219. purge_known_files(AllFiles, KeepFiles, Dir, Suffixes);
  2220. false ->
  2221. ok
  2222. end.
  2223. purge_tmp_files() ->
  2224. case mnesia_monitor:use_dir() of
  2225. true ->
  2226. Dir = mnesia_lib:dir(),
  2227. KeepFiles = [],
  2228. Exists = mnesia_lib:exists(mnesia_lib:tab2dat(schema)),
  2229. case Exists of
  2230. true ->
  2231. Suffixes = tmp_suffixes(),
  2232. purge_dir(Dir, KeepFiles, Suffixes);
  2233. false ->
  2234. %% Interrupted change of storage type
  2235. %% for schema table
  2236. Suffixes = known_suffixes(),
  2237. purge_dir(Dir, KeepFiles, Suffixes),
  2238. mnesia_lib:set(use_dir, false)
  2239. end;
  2240. false ->
  2241. ok
  2242. end.
  2243. purge_known_files([File | Tail], KeepFiles, Dir, Suffixes) ->
  2244. case lists:member(File, KeepFiles) of
  2245. true ->
  2246. ignore;
  2247. false ->
  2248. case has_known_suffix(File, Suffixes, false) of
  2249. false ->
  2250. ignore;
  2251. true ->
  2252. AbsFile = filename:join([Dir, File]),
  2253. file:delete(AbsFile)
  2254. end
  2255. end,
  2256. purge_known_files(Tail, KeepFiles, Dir, Suffixes);
  2257. purge_known_files([], _KeepFiles, _Dir, _Suffixes) ->
  2258. ok.
  2259. has_known_suffix(_File, _Suffixes, true) ->
  2260. true;
  2261. has_known_suffix(File, [Suffix | Tail], false) ->
  2262. has_known_suffix(File, Tail, lists:suffix(Suffix, File));
  2263. has_known_suffix(_File, [], Bool) ->
  2264. Bool.
  2265. known_suffixes() -> real_suffixes() ++ tmp_suffixes().
  2266. real_suffixes() -> [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"].
  2267. tmp_suffixes() -> [".TMP", ".BUPTMP", ".RET", ".DMP"].
  2268. info() ->
  2269. Tabs = lists:sort(val({schema, tables})),
  2270. lists:foreach(fun(T) -> info(T) end, Tabs),
  2271. ok.
  2272. info(Tab) ->
  2273. Props = get_table_properties(Tab),
  2274. io:format("-- Properties for ~w table --- ~n",[Tab]),
  2275. info2(Tab, Props).
  2276. info2(Tab, [{cstruct, _V} | Tail]) -> % Ignore cstruct
  2277. info2(Tab, Tail);
  2278. info2(Tab, [{frag_hash, _V} | Tail]) -> % Ignore frag_hash
  2279. info2(Tab, Tail);
  2280. info2(Tab, [{P, V} | Tail]) ->
  2281. io:format("~-20w -> ~p~n",[P,V]),
  2282. info2(Tab, Tail);
  2283. info2(_, []) ->
  2284. io:format("~n", []).
  2285. get_table_properties(Tab) ->
  2286. case catch mnesia_lib:db_match_object(ram_copies,
  2287. mnesia_gvar, {{Tab, '_'}, '_'}) of
  2288. {'EXIT', _} ->
  2289. mnesia:abort({no_exists, Tab, all});
  2290. RawGvar ->
  2291. case [{Item, Val} || {{_Tab, Item}, Val} <- RawGvar] of
  2292. [] ->
  2293. [];
  2294. Gvar ->
  2295. Size = {size, mnesia:table_info(Tab, size)},
  2296. Memory = {memory, mnesia:table_info(Tab, memory)},
  2297. Master = {master_nodes, mnesia:table_info(Tab, master_nodes)},
  2298. lists:sort([Size, Memory, Master | Gvar])
  2299. end
  2300. end.
  2301. %%%%%%%%%%% RESTORE %%%%%%%%%%%
  2302. -record(r, {iter = schema,
  2303. module,
  2304. table_options = [],
  2305. default_op = clear_tables,
  2306. tables = [],
  2307. opaque,
  2308. insert_op = error_fun,
  2309. recs = error_recs
  2310. }).
  2311. restore(Opaque) ->
  2312. restore(Opaque, [], mnesia_monitor:get_env(backup_module)).
  2313. restore(Opaque, Args) when list(Args) ->
  2314. restore(Opaque, Args, mnesia_monitor:get_env(backup_module));
  2315. restore(_Opaque, BadArg) ->
  2316. {aborted, {badarg, BadArg}}.
  2317. restore(Opaque, Args, Module) when list(Args), atom(Module) ->
  2318. InitR = #r{opaque = Opaque, module = Module},
  2319. case catch lists:foldl(fun check_restore_arg/2, InitR, Args) of
  2320. R when record(R, r) ->
  2321. case mnesia_bup:read_schema(Module, Opaque) of
  2322. {error, Reason} ->
  2323. io:format("mnesia_bup:read_schema() failed: ~p.~n",
  2324. [Reason]),
  2325. {aborted, Reason};
  2326. BupSchema ->
  2327. io:format("got BupSchema~n",[]),
  2328. schema_transaction(fun() -> do_restore(R, BupSchema) end)
  2329. end;
  2330. {'EXIT', Reason} ->
  2331. {aborted, Reason}
  2332. end;
  2333. restore(_Opaque, Args, Module) ->
  2334. {aborted, {badarg, Args, Module}}.
  2335. prepare_restore(Opaque, Args, Module) when is_list(Args), is_atom(Module) ->
  2336. io:format("~p: prepare_restore() called~n", [?LINE]),
  2337. InitR = #r{opaque = Opaque, module = Module},
  2338. case catch lists:foldl(fun check_restore_arg/2, InitR, Args) of
  2339. R when record(R, r) ->
  2340. case mnesia_bup:read_schema(Module, Opaque) of
  2341. {error, Reason} ->
  2342. exit(Reason);
  2343. BupSchema ->
  2344. io:format("~p: read_schema() -> ~p~n", [?LINE,BupSchema]),
  2345. {R, BupSchema}
  2346. end;
  2347. {'EXIT', Reason} ->
  2348. io:format("~p: got {'EXIT',~p} - ~p~n", [?LINE, Reason,
  2349. erlang:get_stacktrace()]),
  2350. {aborted, Reason}
  2351. end;
  2352. prepare_restore(_Opaque, Args, Module) ->
  2353. erlang:error({badarg, Args, Module}).
  2354. check_restore_arg({module, Mod}, R) when atom(Mod) ->
  2355. R#r{module = Mod};
  2356. check_restore_arg({clear_tables, List}, R) when list(List) ->
  2357. case lists:member(schema, List) of
  2358. false ->
  2359. TableList = [{Tab, clear_tables} || Tab <- List],
  2360. R#r{table_options = R#r.table_options ++ TableList};
  2361. true ->
  2362. exit({badarg, {clear_tables, schema}})
  2363. end;
  2364. check_restore_arg({recreate_tables, List}, R) when list(List) ->
  2365. case lists:member(schema, List) of
  2366. false ->
  2367. TableList = [{Tab, recreate_tables} || Tab <- List],
  2368. R#r{table_options = R#r.table_options ++ TableList};
  2369. true ->
  2370. exit({badarg, {recreate_tables, schema}})
  2371. end;
  2372. check_restore_arg({keep_tables, List}, R) when list(List) ->
  2373. TableList = [{Tab, keep_tables} || Tab <- List],
  2374. R#r{table_options = R#r.table_options ++ TableList};
  2375. check_restore_arg({skip_tables, List}, R) when list(List) ->
  2376. TableList = [{Tab, skip_tables} || Tab <- List],
  2377. R#r{table_options = R#r.table_options ++ TableList};
  2378. check_restore_arg({default_op, Op}, R) ->
  2379. case Op of
  2380. clear_tables -> ok;
  2381. recreate_tables -> ok;
  2382. keep_tables -> ok;
  2383. skip_tables -> ok;
  2384. Else ->
  2385. exit({badarg, {bad_default_op, Else}})
  2386. end,
  2387. R#r{default_op = Op};
  2388. check_restore_arg(BadArg,_) ->
  2389. exit({badarg, BadArg}).
  2390. do_restore({R, BupSchema}) ->
  2391. io:format("~p: do_restore/1 called.~n", [?LINE]),
  2392. do_restore(R, BupSchema).
  2393. do_restore(R, BupSchema) ->
  2394. TidTs = get_tid_ts_and_lock(schema, write),
  2395. R2 = restore_schema(BupSchema, R),
  2396. io:format("~p: schema restored~n", [?LINE]),
  2397. insert_schema_ops(TidTs, [{restore_op, R2}]),
  2398. io:format("~p: insert_schema_ops() succeeded~n", [?LINE]),
  2399. [element(1, TabStruct) || TabStruct <- R2#r.tables].
  2400. arrange_restore(R, Fun, Recs) ->
  2401. R2 = R#r{insert_op = Fun, recs = Recs},
  2402. io:format("R2 = ~p, ***tables = ~p ***~n", [R2, R2#r.tables]),
  2403. case mnesia_bup:iterate(R#r.module, fun restore_items/4, R#r.opaque, R2) of
  2404. {ok, R3} ->
  2405. io:format("R3 = ~p~n", [R3]),
  2406. R3#r.recs;
  2407. {error, Reason} -> mnesia:abort(Reason);
  2408. Reason -> mnesia:abort(Reason)
  2409. end.
  2410. restore_items([Rec | Recs], Header, Schema, R) ->
  2411. Tab = element(1, Rec),
  2412. case lists:keysearch(Tab, 1, R#r.tables) of
  2413. {value, {Tab, Where, Snmp, RecName}} ->
  2414. io:format("Rec=~p for Tab=~p, Op=~p~n",
  2415. [Rec, Tab, R#r.insert_op]),
  2416. {Rest, NRecs} =
  2417. restore_tab_items([Rec | Recs], Tab, RecName, Where, Snmp,
  2418. R#r.recs, R#r.insert_op),
  2419. restore_items(Rest, Header, Schema, R#r{recs = NRecs});
  2420. false ->
  2421. io:format("skipping Rec = ~p~n", [Rec]),
  2422. Rest = skip_tab_items(Recs, Tab),
  2423. restore_items(Rest, Header, Schema, R)
  2424. end;
  2425. restore_items([], _Header, _Schema, R) ->
  2426. R.
  2427. restore_func(Tab, R) ->
  2428. case lists:keysearch(Tab, 1, R#r.table_options) of
  2429. {value, {Tab, OP}} ->
  2430. OP;
  2431. false ->
  2432. R#r.default_op
  2433. end.
  2434. where_to_commit(Tab, CsList) ->
  2435. Ram = [{N, ram_copies} || N <- pick(Tab, ram_copies, CsList, [])],
  2436. Disc = [{N, disc_copies} || N <- pick(Tab, disc_copies, CsList, [])],
  2437. DiscO = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])],
  2438. Ext = lists:foldl(
  2439. fun({Mod,Ns},Acc) ->
  2440. lists:foldl(
  2441. fun(N,Acc1) ->
  2442. [{N,{external_copies,Mod}}|Acc1]
  2443. end, Acc, Ns)
  2444. end, [], pick(Tab, external_copies, CsList, [])),
  2445. Ram ++ Disc ++ DiscO ++ Ext.
  2446. %% Changes of the Meta info of schema itself is not allowed
  2447. restore_schema([{schema, schema, _List} | Schema], R) ->
  2448. restore_schema(Schema, R);
  2449. restore_schema([{schema, Tab, List} | Schema], R) ->
  2450. case restore_func(Tab, R) of
  2451. clear_tables ->
  2452. do_clear_table(Tab),
  2453. Where = val({Tab, where_to_commit}),
  2454. Snmp = val({Tab, snmp}),
  2455. RecName = val({Tab, record_name}),
  2456. R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
  2457. restore_schema(Schema, R2);
  2458. recreate_tables ->
  2459. case ?catch_val({Tab, cstruct}) of
  2460. {'EXIT', _} ->
  2461. TidTs = {_Mod, Tid, Ts} = get(mnesia_activity_state),
  2462. RunningNodes = val({current, db_nodes}),
  2463. Nodes = mnesia_lib:intersect(mnesia_lib:cs_to_nodes(list2cs(List)),
  2464. RunningNodes),
  2465. mnesia_locker:wlock_no_exist(Tid, Ts#tidstore.store, Tab, Nodes),
  2466. TidTs;
  2467. _ ->
  2468. TidTs = get_tid_ts_and_lock(Tab, write)
  2469. end,
  2470. NC = {cookie, ?unique_cookie},
  2471. List2 = lists:keyreplace(cookie, 1, List, NC),
  2472. Where = where_to_commit(Tab, List2),
  2473. Snmp = pick(Tab, snmp, List2, []),
  2474. RecName = pick(Tab, record_name, List2, Tab),
  2475. insert_schema_ops(TidTs, [{op, restore_recreate, List2}]),
  2476. R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
  2477. restore_schema(Schema, R2);
  2478. keep_tables ->
  2479. get_tid_ts_and_lock(Tab, write),
  2480. Where = val({Tab, where_to_commit}),
  2481. Snmp = val({Tab, snmp}),
  2482. RecName = val({Tab, record_name}),
  2483. R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
  2484. restore_schema(Schema, R2);
  2485. skip_tables ->
  2486. restore_schema(Schema, R)
  2487. end;
  2488. restore_schema([{schema, Tab} | Schema], R) ->
  2489. do_delete_table(Tab),
  2490. Tabs = lists:delete(Tab,R#r.tables),
  2491. restore_schema(Schema, R#r{tables = Tabs});
  2492. restore_schema([], R) ->
  2493. R.
  2494. restore_tab_items([Rec | Rest], Tab, RecName, Where, Snmp, Recs, Op)
  2495. when element(1, Rec) == Tab ->
  2496. NewRecs = Op(Rec, Recs, RecName, Where, Snmp),
  2497. restore_tab_items(Rest, Tab, RecName, Where, Snmp, NewRecs, Op);
  2498. restore_tab_items(Rest, _Tab, _RecName, _Where, _Snmp, Recs, _Op) ->
  2499. {Rest, Recs}.
  2500. skip_tab_items([Rec| Rest], Tab)
  2501. when element(1, Rec) == Tab ->
  2502. skip_tab_items(Rest, Tab);
  2503. skip_tab_items(Recs, _) ->
  2504. Recs.
  2505. %%%%%%%%% Dump tables %%%%%%%%%%%%%
  2506. dump_tables(Tabs) when list(Tabs) ->
  2507. schema_transaction(fun() -> do_dump_tables(Tabs) end);
  2508. dump_tables(Tabs) ->
  2509. {aborted, {bad_type, Tabs}}.
  2510. do_dump_tables(Tabs) ->
  2511. TidTs = get_tid_ts_and_lock(schema, write),
  2512. insert_schema_ops(TidTs, make_dump_tables(Tabs)).
  2513. make_dump_tables([schema | _Tabs]) ->
  2514. mnesia:abort({bad_type, schema});
  2515. make_dump_tables([Tab | Tabs]) ->
  2516. get_tid_ts_and_lock(Tab, read),
  2517. TabDef = get_create_list(Tab),
  2518. DiscResident = val({Tab, disc_copies}) ++ val({Tab, disc_only_copies}),
  2519. verify([], DiscResident,
  2520. {"Only allowed on ram_copies", Tab, DiscResident}),
  2521. [{op, dump_table, unknown, TabDef} | make_dump_tables(Tabs)];
  2522. make_dump_tables([]) ->
  2523. [].
  2524. %% Merge the local schema with the schema on other nodes
  2525. merge_schema() ->
  2526. schema_transaction(fun() -> do_merge_schema() end).
  2527. do_merge_schema() ->
  2528. {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
  2529. Connected = val(recover_nodes),
  2530. Running = val({current, db_nodes}),
  2531. Store = Ts#tidstore.store,
  2532. %% Verify that all nodes are locked that might not be the
  2533. %% case, if this trans where queued when new nodes where added.
  2534. case Running -- ets:lookup_element(Store, nodes, 2) of
  2535. [] -> ok; %% All known nodes are locked
  2536. Miss -> %% Abort! We don't want the sideeffects below to be executed
  2537. mnesia:abort({bad_commit, {missing_lock, Miss}})
  2538. end,
  2539. case Connected -- Running of
  2540. [Node | _] ->
  2541. %% Time for a schema merging party!
  2542. mnesia_locker:wlock_no_exist(Tid, Store, schema, [Node]),
  2543. case rpc:call(Node, mnesia_controller, get_cstructs, []) of
  2544. {cstructs, Cstructs, RemoteRunning1} ->
  2545. LockedAlready = Running ++ [Node],
  2546. {New, Old} = mnesia_recover:connect_nodes(RemoteRunning1),
  2547. RemoteRunning = mnesia_lib:intersect(New ++ Old, RemoteRunning1),
  2548. if
  2549. RemoteRunning /= RemoteRunning1 ->
  2550. mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n",
  2551. [node(), RemoteRunning1 -- RemoteRunning]);
  2552. true -> ok
  2553. end,
  2554. NeedsLock = RemoteRunning -- LockedAlready,
  2555. mnesia_locker:wlock_no_exist(Tid, Store, schema, NeedsLock),
  2556. {value, SchemaCs} =
  2557. lists:keysearch(schema, #cstruct.name, Cstructs),
  2558. %% Announce that Node is running
  2559. A = [{op, announce_im_running, node(),
  2560. cs2list(SchemaCs), Running, RemoteRunning}],
  2561. do_insert_schema_ops(Store, A),
  2562. %% Introduce remote tables to local node
  2563. do_insert_schema_ops(Store, make_merge_schema(Node, Cstructs)),
  2564. %% Introduce local tables to remote nodes
  2565. Tabs = val({schema, tables}),
  2566. Ops = [{op, merge_schema, get_create_list(T)}
  2567. || T <- Tabs,
  2568. not lists:keymember(T, #cstruct.name, Cstructs)],
  2569. do_insert_schema_ops(Store, Ops),
  2570. %% Ensure that the txn will be committed on all nodes
  2571. NewNodes = RemoteRunning -- Running,
  2572. mnesia_lib:set(prepare_op, {announce_im_running,NewNodes}),
  2573. announce_im_running(NewNodes, SchemaCs),
  2574. {merged, Running, RemoteRunning};
  2575. {error, Reason} ->
  2576. {"Cannot get cstructs", Node, Reason};
  2577. {badrpc, Reason} ->
  2578. {"Cannot get cstructs", Node, {badrpc, Reason}}
  2579. end;
  2580. [] ->
  2581. %% No more nodes to merge schema with
  2582. not_merged
  2583. end.
  2584. make_merge_schema(Node, [Cs | Cstructs]) ->
  2585. Ops = do_make_merge_schema(Node, Cs),
  2586. Ops ++ make_merge_schema(Node, Cstructs);
  2587. make_merge_schema(_Node, []) ->
  2588. [].
  2589. %% Merge definitions of schema table
  2590. do_make_merge_schema(Node, RemoteCs)
  2591. when RemoteCs#cstruct.name == schema ->
  2592. Cs = val({schema, cstruct}),
  2593. Masters = mnesia_recover:get_master_nodes(schema),
  2594. HasRemoteMaster = lists:member(Node, Masters),
  2595. HasLocalMaster = lists:member(node(), Masters),
  2596. Force = HasLocalMaster or HasRemoteMaster,
  2597. %% What is the storage types opinions?
  2598. StCsLocal = mnesia_lib:cs_to_storage_type(node(), Cs),
  2599. StRcsLocal = mnesia_lib:cs_to_storage_type(node(), RemoteCs),
  2600. StCsRemote = mnesia_lib:cs_to_storage_type(Node, Cs),
  2601. StRcsRemote = mnesia_lib:cs_to_storage_type(Node, RemoteCs),
  2602. if
  2603. Cs#cstruct.cookie == RemoteCs#cstruct.cookie,
  2604. Cs#cstruct.version == RemoteCs#cstruct.version ->
  2605. %% Great, we have the same cookie and version
  2606. %% and do not need to merge cstructs
  2607. [];
  2608. Cs#cstruct.cookie /= RemoteCs#cstruct.cookie,
  2609. Cs#cstruct.disc_copies /= [],
  2610. RemoteCs#cstruct.disc_copies /= [] ->
  2611. %% Both cstructs involves disc nodes
  2612. %% and we cannot merge them
  2613. if
  2614. HasLocalMaster == true,
  2615. HasRemoteMaster == false ->
  2616. %% Choose local cstruct,
  2617. %% since it's the master
  2618. [{op, merge_schema, cs2list(Cs)}];
  2619. HasRemoteMaster == true,
  2620. HasLocalMaster == false ->
  2621. %% Choose remote cstruct,
  2622. %% since it's the master
  2623. [{op, merge_schema, cs2list(RemoteCs)}];
  2624. true ->
  2625. Str = io_lib:format("Incompatible schema cookies. "
  2626. "Please, restart from old backup."
  2627. "~w = ~w, ~w = ~w~n",
  2628. [Node, cs2list(RemoteCs), node(), cs2list(Cs)]),
  2629. throw(Str)
  2630. end;
  2631. StCsLocal /= StRcsLocal, StRcsLocal /= unknown ->
  2632. Str = io_lib:format("Incompatible schema storage types (local). "
  2633. "on ~w storage ~w, on ~w storage ~w~n",
  2634. [node(), StCsLocal, Node, StRcsLocal]),
  2635. throw(Str);
  2636. StCsRemote /= StRcsRemote, StCsRemote /= unknown ->
  2637. Str = io_lib:format("Incompatible schema storage types (remote). "
  2638. "on ~w cs ~w, on ~w rcs ~w~n",
  2639. [node(), cs2list(Cs), Node, cs2list(RemoteCs)]),
  2640. throw(Str);
  2641. Cs#cstruct.disc_copies /= [] ->
  2642. %% Choose local cstruct,
  2643. %% since it involves disc nodes
  2644. MergedCs = merge_cstructs(Cs, RemoteCs, Force),
  2645. [{op, merge_schema, cs2list(MergedCs)}];
  2646. RemoteCs#cstruct.disc_copies /= [] ->
  2647. %% Choose remote cstruct,
  2648. %% since it involves disc nodes
  2649. MergedCs = merge_cstructs(RemoteCs, Cs, Force),
  2650. [{op, merge_schema, cs2list(MergedCs)}];
  2651. Cs > RemoteCs ->
  2652. %% Choose remote cstruct
  2653. MergedCs = merge_cstructs(RemoteCs, Cs, Force),
  2654. [{op, merge_schema, cs2list(MergedCs)}];
  2655. true ->
  2656. %% Choose local cstruct
  2657. MergedCs = merge_cstructs(Cs, RemoteCs, Force),
  2658. [{op, merge_schema, cs2list(MergedCs)}]
  2659. end;
  2660. %% Merge definitions of normal table
  2661. do_make_merge_schema(Node, RemoteCs) ->
  2662. Tab = RemoteCs#cstruct.name,
  2663. Masters = mnesia_recover:get_master_nodes(schema),
  2664. HasRemoteMaster = lists:member(Node, Masters),
  2665. HasLocalMaster = lists:member(node(), Masters),
  2666. Force = HasLocalMaster or HasRemoteMaster,
  2667. case ?catch_val({Tab, cstruct}) of
  2668. {'EXIT', _} ->
  2669. %% A completely new table, created while Node was down
  2670. [{op, merge_schema, cs2list(RemoteCs)}];
  2671. Cs when Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
  2672. if
  2673. Cs#cstruct.version == RemoteCs#cstruct.version ->
  2674. %% We have exactly the same version of the
  2675. %% table def
  2676. [];
  2677. Cs#cstruct.version > RemoteCs#cstruct.version ->
  2678. %% Oops, we have different versions
  2679. %% of the table def, lets merge them.
  2680. %% The only changes that may have occurred
  2681. %% is that new replicas may have been added.
  2682. MergedCs = merge_cstructs(Cs, RemoteCs, Force),
  2683. [{op, merge_schema, cs2list(MergedCs)}];
  2684. Cs#cstruct.version < RemoteCs#cstruct.version ->
  2685. %% Oops, we have different versions
  2686. %% of the table def, lets merge them
  2687. MergedCs = merge_cstructs(RemoteCs, Cs, Force),
  2688. [{op, merge_schema, cs2list(MergedCs)}]
  2689. end;
  2690. Cs ->
  2691. %% Different cookies, not possible to merge
  2692. if
  2693. HasLocalMaster == true,
  2694. HasRemoteMaster == false ->
  2695. %% Choose local cstruct,
  2696. %% since it's the master
  2697. [{op, merge_schema, cs2list(Cs)}];
  2698. HasRemoteMaster == true,
  2699. HasLocalMaster == false ->
  2700. %% Choose remote cstruct,
  2701. %% since it's the master
  2702. [{op, merge_schema, cs2list(RemoteCs)}];
  2703. true ->
  2704. Str = io_lib:format("Bad cookie in table definition"
  2705. " ~w: ~w = ~w, ~w = ~w~n",
  2706. [Tab, node(), Cs, Node, RemoteCs]),
  2707. throw(Str)
  2708. end
  2709. end.
  2710. %% Change of table definitions (cstructs) requires all replicas
  2711. %% of the table to be active. New replicas, db_nodes and tables
  2712. %% may however be added even if some replica is inactive. These
  2713. %% invariants must be enforced in order to allow merge of cstructs.
  2714. %%
  2715. %% Returns a new cstruct or issues a fatal error
  2716. merge_cstructs(Cs0, RemoteCs, Force) ->
  2717. Cs = verify_cstruct(Cs0),
  2718. case catch do_merge_cstructs(Cs, RemoteCs, Force) of
  2719. {'EXIT', {aborted, _Reason}} when Force == true ->
  2720. Cs;
  2721. {'EXIT', Reason} ->
  2722. exit(Reason);
  2723. MergedCs when record(MergedCs, cstruct) ->
  2724. MergedCs;
  2725. Other ->
  2726. throw(Other)
  2727. end.
  2728. do_merge_cstructs(Cs, RemoteCs0, Force) ->
  2729. RemoteCs = verify_cstruct(RemoteCs0),
  2730. Ns = mnesia_lib:uniq(mnesia_lib:cs_to_nodes(Cs) ++
  2731. mnesia_lib:cs_to_nodes(RemoteCs)),
  2732. {AnythingNew, MergedCs} =
  2733. merge_storage_type(Ns, false, Cs, RemoteCs, Force),
  2734. MergedCs2 = merge_versions(AnythingNew, MergedCs, RemoteCs, Force),
  2735. verify_cstruct(MergedCs2).
  2736. merge_storage_type([N | Ns], AnythingNew, Cs, RemoteCs, Force) ->
  2737. Local = mnesia_lib:cs_to_storage_type(N, Cs),
  2738. Remote = mnesia_lib:cs_to_storage_type(N, RemoteCs),
  2739. case compare_storage_type(true, Local, Remote) of
  2740. {same, _Storage} ->
  2741. merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
  2742. {diff, Storage} ->
  2743. Cs2 = change_storage_type(N, Storage, Cs),
  2744. merge_storage_type(Ns, true, Cs2, RemoteCs, Force);
  2745. incompatible when Force == true ->
  2746. merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
  2747. Other ->
  2748. Str = io_lib:format("Cannot merge storage type for node ~w "
  2749. "in cstruct ~w with remote cstruct ~w (~w)~n",
  2750. [N, Cs, RemoteCs, Other]),
  2751. throw(Str)
  2752. end;
  2753. merge_storage_type([], AnythingNew, MergedCs, _RemoteCs, _Force) ->
  2754. {AnythingNew, MergedCs}.
  2755. compare_storage_type(_Retry, Any, Any) ->
  2756. {same, Any};
  2757. compare_storage_type(_Retry, unknown, Any) ->
  2758. {diff, Any};
  2759. compare_storage_type(_Retry, ram_copies, disc_copies) ->
  2760. {diff, disc_copies};
  2761. compare_storage_type(_Retry, disc_copies, disc_only_copies) ->
  2762. {diff, disc_only_copies};
  2763. compare_storage_type(true, One, Another) ->
  2764. compare_storage_type(false, Another, One);
  2765. compare_storage_type(false, _One, _Another) ->
  2766. incompatible.
  2767. change_storage_type(N, ram_copies, Cs) ->
  2768. Nodes = [N | Cs#cstruct.ram_copies],
  2769. Cs#cstruct{ram_copies = mnesia_lib:uniq(Nodes)};
  2770. change_storage_type(N, disc_copies, Cs) ->
  2771. Nodes = [N | Cs#cstruct.disc_copies],
  2772. Cs#cstruct{disc_copies = mnesia_lib:uniq(Nodes)};
  2773. change_storage_type(N, disc_only_copies, Cs) ->
  2774. Nodes = [N | Cs#cstruct.disc_only_copies],
  2775. Cs#cstruct{disc_only_copies = mnesia_lib:uniq(Nodes)}.
  2776. %% BUGBUG: Verify match of frag info; equalit demanded for all but add_node
  2777. merge_versions(AnythingNew, Cs, RemoteCs, Force) ->
  2778. if
  2779. Cs#cstruct.name == schema ->
  2780. ok;
  2781. Cs#cstruct.name /= schema,
  2782. Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
  2783. ok;
  2784. Force == true ->
  2785. ok;
  2786. true ->
  2787. Str = io_lib:format("Bad cookies. Cannot merge definitions of "
  2788. "table ~w. Local = ~w, Remote = ~w~n",
  2789. [Cs#cstruct.name, Cs, RemoteCs]),
  2790. throw(Str)
  2791. end,
  2792. if
  2793. Cs#cstruct.name == RemoteCs#cstruct.name,
  2794. Cs#cstruct.type == RemoteCs#cstruct.type,
  2795. Cs#cstruct.local_content == RemoteCs#cstruct.local_content,
  2796. Cs#cstruct.attributes == RemoteCs#cstruct.attributes,
  2797. Cs#cstruct.index == RemoteCs#cstruct.index,
  2798. Cs#cstruct.snmp == RemoteCs#cstruct.snmp,
  2799. Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode,
  2800. Cs#cstruct.load_order == RemoteCs#cstruct.load_order,
  2801. Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties ->
  2802. do_merge_versions(AnythingNew, Cs, RemoteCs);
  2803. Force == true ->
  2804. do_merge_versions(AnythingNew, Cs, RemoteCs);
  2805. true ->
  2806. Str1 = io_lib:format("Cannot merge definitions of "
  2807. "table ~w. Local = ~w, Remote = ~w~n",
  2808. [Cs#cstruct.name, Cs, RemoteCs]),
  2809. throw(Str1)
  2810. end.
  2811. do_merge_versions(AnythingNew, MergedCs, RemoteCs) ->
  2812. {{Major1, Minor1}, _Detail1} = MergedCs#cstruct.version,
  2813. {{Major2, Minor2}, _Detail2} = RemoteCs#cstruct.version,
  2814. if
  2815. AnythingNew == false ->
  2816. MergedCs;
  2817. MergedCs#cstruct.version == RemoteCs#cstruct.version ->
  2818. V = {{Major1, Minor1}, dummy},
  2819. incr_version(MergedCs#cstruct{version = V});
  2820. Major1 == Major2 ->
  2821. Minor = lists:max([Minor1, Minor2]),
  2822. V = {{Major1, Minor}, dummy},
  2823. incr_version(MergedCs#cstruct{version = V});
  2824. Major1 /= Major2 ->
  2825. Major = lists:max([Major1, Major2]),
  2826. V = {{Major, 0}, dummy},
  2827. incr_version(MergedCs#cstruct{version = V})
  2828. end.
  2829. %% Verify the basics
  2830. verify_merge(RemoteCs) ->
  2831. Tab = RemoteCs#cstruct.name,
  2832. Masters = mnesia_recover:get_master_nodes(schema),
  2833. HasRemoteMaster = Masters /= [],
  2834. case ?catch_val({Tab, cstruct}) of
  2835. {'EXIT', _} ->
  2836. ok;
  2837. Cs ->
  2838. StCsLocal = mnesia_lib:cs_to_storage_type(node(), Cs),
  2839. StRcsLocal = mnesia_lib:cs_to_storage_type(node(), RemoteCs),
  2840. if
  2841. StCsLocal == StRcsLocal -> ok;
  2842. StCsLocal == unknown -> ok;
  2843. (StRcsLocal == unknown), (HasRemoteMaster == false) ->
  2844. {merge_error, Cs, RemoteCs};
  2845. %% Trust the merger
  2846. true -> ok
  2847. end
  2848. end.
  2849. announce_im_running([N | Ns], SchemaCs) ->
  2850. {L1, L2} = mnesia_recover:connect_nodes([N]),
  2851. case lists:member(N, L1) or lists:member(N, L2) of
  2852. true ->
  2853. mnesia_lib:add({current, db_nodes}, N),
  2854. mnesia_controller:add_active_replica(schema, N, SchemaCs);
  2855. false ->
  2856. ignore
  2857. end,
  2858. announce_im_running(Ns, SchemaCs);
  2859. announce_im_running([], _) ->
  2860. [].
  2861. unannounce_im_running([N | Ns]) ->
  2862. mnesia_lib:del({current, db_nodes}, N),
  2863. mnesia_controller:del_active_replica(schema, N),
  2864. unannounce_im_running(Ns);
  2865. unannounce_im_running([]) ->
  2866. ok.