PageRenderTime 52ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://github.com/gebi/jungerl
Erlang | 3174 lines | 2607 code | 308 blank | 259 comment | 118 complexity | bb0aa5fd29604a11c9ce9cd87b408ce4 MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause

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

  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with this software. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% $Id$
  17. %%
  18. %% 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() -> d

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