PageRenderTime 66ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rdbms/src/rdbms_index.erl

https://github.com/bmizerany/jungerl
Erlang | 1289 lines | 1029 code | 125 blank | 135 comment | 22 complexity | a367adc838fb04f12ba9f6ce71332696 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. %% Purpose: Handles index functionality in mnesia
  19. -module(rdbms_index).
  20. %%% Called by rdbms
  21. -export([read/7,
  22. w_read/7,
  23. index_values/4]).
  24. -export([update_index/7,
  25. index_record/3
  26. %%% pos_index/2
  27. %%% dirty_read/4,
  28. %%% dirty_read2/3,
  29. %%% dirty_read3/4,
  30. %%% filter_found_set/3
  31. ]).
  32. -export([valid_index/2,
  33. valid_index/3]).
  34. %%% Callback for mnesia table load hook
  35. -export([index_init_fun/2, index_init_fun/3]).
  36. -export([match_object/7,
  37. dirty_match_object/3]).
  38. -export([frag_index_read/7]). % used for index_read on fragmented tabs
  39. -export([dirty_read/4,
  40. dirty_index_first/2, dirty_index_first/3,
  41. dirty_index_relop/4,
  42. dirty_index_next/3,
  43. dirty_index_prev/3,
  44. dirty_index_last/2]).
  45. -export([do_add_indexes/2,
  46. do_delete_indexes/1]).
  47. %%% called by rdbms_index_load
  48. -export([attr_pos/3,
  49. table_info/3]).
  50. %%% called by dirty_read/4 via rpc:call()
  51. -export([dirty_read2/3,
  52. dirty_read3/4]).
  53. -import(mnesia_lib, [verbose/2]).
  54. -import(rdbms_frag, [key_to_frag_name/2]).
  55. -import(proplists, [get_value/2]).
  56. %%%-include_lib("mnesia/src/mnesia.hrl").
  57. -include("mnesia.hrl").
  58. -import(rdbms_props, [table_property/2]).
  59. -include("rdbms.hrl").
  60. read(Tid, Ts, Tab, Key, {_Pos,_Tag}=Index, LockKind, VMod)
  61. when is_atom(Tab), Tab =/= schema ->
  62. #index{table_name = IxTab} = Ix = index_record(Tab, Index, VMod),
  63. verify_key(Tab, Index, Key, LockKind), % aborts if something fishy
  64. Oids =
  65. case Ix#index.type of
  66. ordered ->
  67. Pat = select_pattern(Ix, Key),
  68. rdbms:do_select(Tid, Ts, IxTab, Pat, LockKind, VMod);
  69. weighted ->
  70. %% we support weighted indexes here, but we throw away
  71. %% the weights
  72. Pat = select_pattern(Ix, Key),
  73. [O || {O,_} <-
  74. rdbms:do_select(
  75. Tid, Ts, IxTab, Pat, LockKind, VMod)];
  76. T when T==bag; T==set ->
  77. IxObjs = rdbms:read(Tid, Ts, IxTab, Key, LockKind, VMod),
  78. [Oid || #ix{oid = Oid} <- IxObjs]
  79. end,
  80. FoundSet = [rdbms:read(Tid,Ts,Tab,Oid,LockKind,VMod) || Oid <- Oids],
  81. case table_info(Tab, setorbag, VMod) of
  82. bag ->
  83. filter_found_set(FoundSet, Ix, Key);
  84. _ ->
  85. lists:append(FoundSet)
  86. end;
  87. read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind, _Vmod) ->
  88. mnesia:abort({bad_type, Tab}).
  89. w_read(Tid, Ts, Tab, Key, {_Pos,_Tag}=Index, LockKind, VMod)
  90. when is_atom(Tab), Tab =/= schema ->
  91. #index{table_name = IxTab} = Ix = index_record(Tab, Index, VMod),
  92. verify_key(Tab, Index, Key, LockKind), % aborts if something fishy
  93. Oids =
  94. case Ix#index.type of
  95. ordered ->
  96. %% introduce dummy weights
  97. Pat = select_pattern(Ix, Key),
  98. [{O,1} ||
  99. O <- rdbms:do_select(Tid, Ts, IxTab, Pat, LockKind, VMod)];
  100. weighted ->
  101. Pat = select_pattern(Ix, Key),
  102. rdbms:do_select(Tid, Ts, IxTab, Pat, LockKind, VMod);
  103. bag ->
  104. %% introduce dummy weights
  105. IxObjs = rdbms:read(Tid, Ts, IxTab, Key, LockKind, VMod),
  106. [{Oid, 1} || #ix{oid = Oid} <- IxObjs]
  107. end,
  108. FoundSet = [{rdbms:read(Tid,Ts,Tab,Oid,LockKind,VMod), Wt} ||
  109. {Oid, Wt} <- Oids],
  110. case table_info(Tab, setorbag, VMod) of
  111. bag ->
  112. filter_weighted_set(FoundSet, Ix, Key);
  113. _ ->
  114. lists:append(FoundSet)
  115. end;
  116. w_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind, _Vmod) ->
  117. mnesia:abort({bad_type, Tab}).
  118. table_indexes(Tab, rdbms_verify_jit) ->
  119. rdbms_verify_jit:indexes(Tab);
  120. table_indexes(Tab, {rdbms_verify, VRec}) ->
  121. rdbms_verify:indexes(Tab, VRec).
  122. access_module(Tab, rdbms_verify_jit) ->
  123. rdbms_verify_jit:module(Tab);
  124. access_module(Tab, {rdbms_verify, VRec}) ->
  125. rdbms_verify:module(Tab, VRec).
  126. table_info(Tab, Item, rdbms_verify_jit) ->
  127. rdbms_verify_jit:table_info(Tab, Item);
  128. table_info(Tab, Item, {rdbms_verify,VRec}) ->
  129. rdbms_verify:table_info(Tab, Item, VRec).
  130. update_index(Tid, Ts, Tab, Op, Val, LockKind, VMod) ->
  131. BaseTab = base_tab(Tab, VMod),
  132. update_indexes(table_indexes(BaseTab, VMod),
  133. Tid, Ts, Tab, Op, Val, LockKind, VMod).
  134. update_indexes([], _, _, _, _, _, _, _) -> ok;
  135. update_indexes([_|_] = Ixes, Tid, Ts, Tab, write, Val, LockKind, VMod) ->
  136. Oid = element(2, Val),
  137. case table_info(Tab, setorbag, VMod) of
  138. set ->
  139. AMod = access_module(Tab, VMod),
  140. OldVal = AMod:dirty_read(Tab, Oid),
  141. ix_write_set(Ixes, Oid, Val, OldVal, Tab, VMod,
  142. [VMod, Tid, Ts, LockKind]);
  143. bag ->
  144. ix_write_bag(Ixes, Oid, Val, Tab, VMod,
  145. [VMod, Tid, Ts, LockKind])
  146. end;
  147. update_indexes([_|_] = Ixes, Tid, Ts, Tab, delete, Key, LockKind, VMod) ->
  148. Old = mnesia:dirty_read(Tab, Key),
  149. update_index_delete(Ixes, VMod, Tid, Ts, Tab, Old, LockKind);
  150. update_indexes([_|_] = Ixes, Tid,Ts,Tab, delete_object, Val, LockKind, VMod) ->
  151. update_index_delete(Ixes, VMod, Tid, Ts, Tab, [Val], LockKind);
  152. update_indexes([_|_] = Ixes, Tid, Ts, Tab, fill, Val, LockKind, VMod) ->
  153. %% used when initializing indexes. Like 'write', but we don't read the
  154. %% previous value.
  155. Oid = element(2, Val),
  156. case table_info(Tab, setorbag, VMod) of
  157. set ->
  158. OldVal = [],
  159. ix_write_set(Ixes, Oid, Val, OldVal, Tab, VMod,
  160. [VMod, Tid, Ts, LockKind]);
  161. bag ->
  162. ix_write_bag(Ixes, Oid, Val, Tab, VMod,
  163. [VMod, Tid, Ts, LockKind])
  164. end.
  165. update_index_delete(Ixes, VMod, Tid, Ts, Tab, Objs, LockKind) ->
  166. lists:foreach(
  167. fun(#index{pos = IxPos, m_f = {M, F}, arg = Arg} = Ix) ->
  168. Pos = attr_pos(Tab, IxPos, VMod),
  169. lists:foreach(
  170. fun(Obj) ->
  171. Oid = element(2, Obj),
  172. IxValues = index_values(Obj, Pos, M, F, Arg),
  173. delete_ix_values(
  174. IxValues, Oid, Ix, [VMod, Tid, Ts, LockKind])
  175. end, Objs)
  176. end, Ixes).
  177. ix_write_set(Ixes, Oid, Val, OldVal, Tab, VMod, Mode) ->
  178. lists:foreach(
  179. fun(#index{pos = IxPos, m_f = {M, F}, arg = Arg} = Ix) ->
  180. Pos = attr_pos(Tab, IxPos, VMod),
  181. OldIxValues = ordsets:from_list(
  182. case OldVal of
  183. [Old] ->
  184. index_values(Old, Pos, M, F, Arg);
  185. [] ->
  186. []
  187. end),
  188. NewIxValues = ordsets:from_list(
  189. index_values(Val, Pos, M, F, Arg)),
  190. DelIxValues = OldIxValues -- NewIxValues,
  191. WriteIxValues = NewIxValues -- OldIxValues,
  192. delete_ix_values(DelIxValues, Oid, Ix, Mode),
  193. check_if_unique(Ix, WriteIxValues, Tab, Oid, VMod, Mode),
  194. insert_ix_values(WriteIxValues, Oid, Ix, Mode)
  195. end, Ixes).
  196. ix_write_bag(Ixes, Oid, Val, Tab, VMod, Mode) ->
  197. lists:foreach(
  198. fun(#index{pos = IxPos, m_f = {M,F}, arg = Arg} = Ix) ->
  199. Pos = attr_pos(Tab, IxPos, VMod),
  200. NewIxValues = index_values(Val, Pos, M, F, Arg),
  201. insert_ix_values(NewIxValues, Oid, Ix, Mode)
  202. end, Ixes).
  203. index_values(Tab, Ix, Obj, VMod) when is_integer(Ix) ->
  204. case attr_pos(Tab, Ix, VMod) of
  205. P when P > 1 ->
  206. [element(P, Obj)];
  207. _ ->
  208. mnesia:abort({invalid_index, {Tab, Ix}})
  209. end;
  210. index_values(Tab, {Pos0, Tag} = Ix, Obj, VMod) ->
  211. Ixes = table_indexes(Tab, VMod),
  212. Pos = attr_pos(Tab, Pos0, VMod),
  213. %% TODO: shouldn't we normalize the metadata, rather than
  214. %% expending runtime cycles de-abstracting it?
  215. case lists:dropwhile(
  216. fun(#index{pos = {_, T}}) when T =/= Tag ->
  217. true;
  218. (#index{pos = {IxPos, T}}) when T == Tag ->
  219. Pos =/= attr_pos(Tab, IxPos, VMod)
  220. end, Ixes) of
  221. [] ->
  222. mnesia:abort({invalid_index, {Tab, Ix}});
  223. [#index{m_f = {M,F}, arg = Arg}|_] ->
  224. index_values(Obj, Pos, M, F, Arg)
  225. end.
  226. index_values(Obj, 1, M, F, Arg) ->
  227. M:F(Obj, Arg);
  228. index_values(Obj, Pos, M, F, Arg) ->
  229. M:F(element(Pos, Obj), Arg).
  230. delete_ix_values(Vals, Oid, #index{table_name = IxTab,
  231. type = ordered}, Mode) ->
  232. case Mode of
  233. dirty ->
  234. lists:foreach(
  235. fun(Key) ->
  236. mnesia:dirty_delete(IxTab, {Key, Oid})
  237. end, Vals);
  238. [VMod, Tid, Ts, LockKind] ->
  239. AMod = access_module(IxTab, VMod),
  240. lists:foreach(
  241. fun(Key) ->
  242. AMod:delete(Tid, Ts, IxTab, {Key, Oid}, LockKind)
  243. end, Vals)
  244. end;
  245. delete_ix_values(Vals, Oid, #index{table_name = IxTab,
  246. type = weighted}, Mode) ->
  247. case Mode of
  248. dirty ->
  249. lists:foreach(
  250. fun({Key,W}) ->
  251. mnesia:dirty_delete(IxTab, {Key, W, Oid})
  252. end, Vals);
  253. [VMod, Tid, Ts, LockKind] ->
  254. AMod = access_module(IxTab, VMod),
  255. lists:foreach(
  256. fun({Key,W}) ->
  257. AMod:delete(Tid, Ts, IxTab, {Key, W, Oid}, LockKind)
  258. end, Vals)
  259. end;
  260. delete_ix_values(Vals, _Oid, #index{table_name = IxTab,
  261. type = set}, Mode) ->
  262. case Mode of
  263. dirty ->
  264. lists:foreach(
  265. fun(Key) ->
  266. mnesia:dirty_delete(IxTab, Key)
  267. end, Vals);
  268. [VMod, Tid, Ts, LockKind] ->
  269. AMod = access_module(IxTab, VMod),
  270. lists:foreach(
  271. fun(Key) ->
  272. AMod:delete(
  273. Tid, Ts, IxTab, Key, LockKind)
  274. end, Vals)
  275. end;
  276. delete_ix_values(Vals, Oid, #index{table_name = IxTab,
  277. type = bag}, Mode) ->
  278. case Mode of
  279. dirty ->
  280. lists:foreach(
  281. fun(Key) ->
  282. mnesia:dirty_delete_object(
  283. IxTab, #ix{key = Key, oid = Oid})
  284. end, Vals);
  285. [VMod, Tid, Ts, LockKind] ->
  286. AMod = access_module(IxTab, VMod),
  287. lists:foreach(
  288. fun(Key) ->
  289. AMod:delete_object(
  290. Tid, Ts, IxTab, #ix{key = Key, oid = Oid}, LockKind)
  291. end, Vals)
  292. end.
  293. check_if_unique(#index{options = Opts, type = Type} = Ix,
  294. NewIxValues, Tab, Oid, VMod, Mode) ->
  295. case (Type == set) orelse proplists:get_value(unique, Opts, false) of
  296. false ->
  297. ok;
  298. true ->
  299. IxTab = Ix#index.table_name,
  300. lists:foreach(
  301. fun(IxValue) ->
  302. Objs =
  303. case Mode of
  304. dirty ->
  305. mnesia:dirty_read({IxTab, IxValue});
  306. [VMod, Tid, Ts, LockKind] ->
  307. rdbms:read(
  308. Tid,Ts,IxTab,IxValue,LockKind,VMod)
  309. end,
  310. lists:foreach(
  311. fun(#ix{key = IxKey, oid = Id}) when Id =/= Oid ->
  312. mnesia:abort({unique_ix_violation,
  313. [Tab,Ix#index.pos,IxKey]});
  314. (#ix{oid = Id}) when Id == Oid ->
  315. ok
  316. end, Objs)
  317. end, NewIxValues)
  318. end.
  319. insert_ix_values(Vals, Oid, #index{table_name = IxTab,
  320. type = ordered}, Mode) ->
  321. case Mode of
  322. dirty ->
  323. lists:foreach(
  324. fun(Key) ->
  325. mnesia:do_dirty_write(
  326. async_dirty, IxTab, #ord_ix{key = {Key, Oid}})
  327. end, Vals);
  328. [VMod, Tid, Ts, LockKind] ->
  329. AMod = access_module(IxTab, VMod),
  330. lists:foreach(
  331. fun(Key) ->
  332. AMod:write(
  333. Tid, Ts, IxTab, #ord_ix{key = {Key, Oid}}, LockKind)
  334. end, Vals)
  335. end;
  336. insert_ix_values(Vals, Oid, #index{table_name = IxTab,
  337. type = weighted}, Mode) ->
  338. case Mode of
  339. dirty ->
  340. lists:foreach(
  341. fun({Key, Weight}) ->
  342. mnesia:do_dirty_write(
  343. async_dirty, IxTab, #ord_ix{key = {Key, Weight, Oid}})
  344. end, Vals);
  345. [VMod, Tid, Ts, LockKind] ->
  346. AMod = access_module(IxTab, VMod),
  347. lists:foreach(
  348. fun({Key, Weight}) ->
  349. AMod:write(
  350. Tid, Ts, IxTab, #ord_ix{key = {Key, Weight, Oid}},
  351. LockKind)
  352. end, Vals)
  353. end;
  354. insert_ix_values(Vals, Oid, #index{table_name = IxTab,
  355. type = Type}, Mode)
  356. when Type == bag; Type == set ->
  357. case Mode of
  358. dirty ->
  359. lists:foreach(
  360. fun(Key) ->
  361. mnesia:do_dirty_write(
  362. async_dirty, IxTab, #ix{key = Key, oid = Oid})
  363. end, Vals);
  364. [VMod, Tid, Ts, LockKind] ->
  365. AMod = access_module(IxTab, VMod),
  366. lists:foreach(
  367. fun(Key) ->
  368. AMod:write(
  369. Tid, Ts, IxTab, #ix{key = Key, oid = Oid}, LockKind)
  370. end, Vals)
  371. end.
  372. base_tab(Tab, VMod) ->
  373. case access_module(Tab, VMod) of
  374. mnesia_frag ->
  375. table_info(Tab, base_table, VMod);
  376. _ ->
  377. Tab
  378. end.
  379. select_pattern(#index{type = Type}, Key) ->
  380. KeyPat = if is_tuple(Key) ->
  381. {Key};
  382. true ->
  383. Key
  384. end,
  385. case Type of
  386. ordered ->
  387. MatchPat = #ord_ix{key = {'$1','$2'}, _ = '_'},
  388. [{MatchPat, [{'==', '$1', KeyPat}], ['$2']}];
  389. weighted ->
  390. MatchPat = #w_ix{key = {'$1','$2','$3'}, _ = '_'},
  391. [{MatchPat, [{'==', '$1', KeyPat}], [{{'$3', '$2'}}]}];
  392. T when T==set; T==bag ->
  393. MatchPat = #ix{key = '$1', oid = '$2'},
  394. [{MatchPat, [{'==', '$1', KeyPat}], ['$2']}]
  395. end.
  396. %% TODO Fix so that it works even for single attribute index callbacks
  397. filter_found_set(Set, #index{pos = 1, %% does this work at all?
  398. m_f = {M, F},
  399. arg = Arg}, Key) ->
  400. filter_found_set1(Set, M, F, Arg, Key, []);
  401. filter_found_set(Set, #index{pos = Pos,
  402. m_f = {M, F},
  403. arg = Arg}, Key) ->
  404. filter_found_set1(Set, Pos, M, F, Arg, Key, []).
  405. %% Index function operates on whole object
  406. filter_found_set1([[]|T], M, F, Arg, Key, Acc) ->
  407. filter_found_set1(T, M, F, Arg, Key, Acc);
  408. filter_found_set1([Set|T], M, F, Arg, Key, Acc) ->
  409. Acc2 =
  410. lists:foldr(
  411. fun(Obj, Acc1) ->
  412. Keys = M:F(Obj, Arg),
  413. case lists:member(Key, Keys) of
  414. true ->
  415. [Obj | Acc1];
  416. false ->
  417. Acc1
  418. end
  419. end, Acc, Set),
  420. filter_found_set1(T, M, F, Arg, Key, Acc2);
  421. filter_found_set1([], _, _, _, _, Acc) ->
  422. Acc.
  423. %% Index function operates on single attribute
  424. filter_found_set1([[]|T], Pos, M, F, Arg, Key, Acc) ->
  425. filter_found_set1(T, Pos, M, F, Arg, Key, Acc);
  426. filter_found_set1([Set|T], Pos, M, F, Arg, Key, Acc) ->
  427. Acc2 =
  428. lists:foldr(
  429. fun(Obj, Acc1) ->
  430. Keys = M:F(element(Pos, Obj), Arg),
  431. case lists:member(Key, Keys) of
  432. true ->
  433. [Obj | Acc1];
  434. false ->
  435. Acc1
  436. end
  437. end, Acc, Set),
  438. filter_found_set1(T, Pos, M, F, Arg, Key, Acc2);
  439. filter_found_set1([], _, _, _, _, _, Acc) ->
  440. Acc.
  441. %% TODO Fix so that it works even for single attribute index callbacks
  442. filter_weighted_set(Set, #index{pos = 1, %% does this work at all?
  443. m_f = {M, F},
  444. arg = Arg}, Key) ->
  445. filter_weighted_set1(Set, M, F, Arg, Key, []);
  446. filter_weighted_set(Set, #index{pos = Pos,
  447. m_f = {M, F},
  448. arg = Arg}, Key) ->
  449. filter_weighted_set1(Set, Pos, M, F, Arg, Key, []).
  450. %% Index function operates on whole object
  451. filter_weighted_set1([[]|T], M, F, Arg, Key, Acc) ->
  452. filter_weighted_set1(T, M, F, Arg, Key, Acc);
  453. filter_weighted_set1([Set|T], M, F, Arg, Key, Acc) ->
  454. Acc2 =
  455. lists:foldr(
  456. fun({Obj,Weight}, Acc1) ->
  457. Keys = M:F(Obj, Arg),
  458. case lists:member(Key, Keys) of
  459. true ->
  460. [{Obj, Weight} | Acc1];
  461. false ->
  462. Acc1
  463. end
  464. end, Acc, Set),
  465. filter_weighted_set1(T, M, F, Arg, Key, Acc2);
  466. filter_weighted_set1([], _, _, _, _, Acc) ->
  467. Acc.
  468. %% Index function operates on single attribute
  469. filter_weighted_set1([[]|T], Pos, M, F, Arg, Key, Acc) ->
  470. filter_weighted_set1(T, Pos, M, F, Arg, Key, Acc);
  471. filter_weighted_set1([Set|T], Pos, M, F, Arg, Key, Acc) ->
  472. Acc2 =
  473. lists:foldr(
  474. fun({Obj, Weight}, Acc1) ->
  475. Keys = M:F(element(Pos, Obj), Arg),
  476. case lists:member(Key, Keys) of
  477. true ->
  478. [{Obj, Weight} | Acc1];
  479. false ->
  480. Acc1
  481. end
  482. end, Acc, Set),
  483. filter_weighted_set1(T, Pos, M, F, Arg, Key, Acc2);
  484. filter_weighted_set1([], _, _, _, _, _, Acc) ->
  485. Acc.
  486. match_object(Tid, Ts, Tab, Pat, #index{pos = Pos,
  487. table_name = IxTab} = Index,
  488. LockKind, VMod) when is_integer(Pos) ->
  489. IxPat = element(Pos, Pat),
  490. SelectPat = select_pattern(Index, IxPat),
  491. Keys = mnesia:select(Tid, Ts, IxTab, SelectPat, read),
  492. TabType = table_info(Tab, type, VMod),
  493. AMod = access_module(Tab, VMod),
  494. Tmp = ets:new(tmp, [TabType]),
  495. lists:foreach(
  496. fun(Key) ->
  497. Objs = AMod:read(Tid, Ts, Tab, Key, LockKind),
  498. ets:insert(Tmp, Objs)
  499. end, Keys),
  500. Result = ets:match_object(Tmp, Pat),
  501. ets:delete(Tmp),
  502. Result.
  503. dirty_match_object(Tab, Pat, #index{pos = Pos,
  504. table_name = IxTab} = Index)
  505. when is_integer(Pos) ->
  506. %% Assume that we are on the node where the replica is
  507. IxPat = element(Pos, Pat),
  508. SelectPat = select_pattern(Index, IxPat),
  509. Keys = mnesia:dirty_select(IxTab, SelectPat),
  510. TabType = mnesia:table_info(Tab, type),
  511. Tmp = ets:new(tmp, [TabType]),
  512. lists:foreach(
  513. fun(Key) ->
  514. Objs = mnesia:dirty_read(Tab, Key),
  515. ets:insert(Tmp, Objs)
  516. end, Keys),
  517. Result = ets:match_object(Tmp, Pat),
  518. ets:delete(Tmp),
  519. Result.
  520. dirty_read(Tab, IxKey, Index, VMod) ->
  521. #index{table_name = IxTab} = Ix = index_record(Tab, Index, VMod),
  522. Oids = mnesia:dirty_rpc(IxTab, ?MODULE, dirty_read2,
  523. [Tab, Ix, IxKey]),
  524. mnesia:dirty_rpc(Tab, ?MODULE, dirty_read3,
  525. [Tab, Oids, Ix, IxKey]).
  526. dirty_read2(_Tab, #index{table_name = IxTab} = Ix, IxKey) ->
  527. Pat = select_pattern(Ix, IxKey),
  528. mnesia:dirty_select(IxTab, Pat).
  529. dirty_read3(Tab, Oids, #index{type = Type} = Ix, IxKey) ->
  530. %% TODO:
  531. %% While this function should work even with fragmented tables,
  532. %% it is far from optimal for it -- and violates sort order. FIX!!
  533. IsOrdered = (Type == ordered) or (Type == weighted),
  534. VMod = rdbms:default_verification_module(), % since this is done remotely
  535. Objs = r_keys(Oids, Tab, []),
  536. Objs1 =
  537. case table_info(Tab, setorbag, VMod) of
  538. bag ->
  539. %% Remove all tuples which don't include Ixkey
  540. %% FIXME: doesn't work with fun indicies
  541. %%% Pos = attr_pos(Tab, IxPos, VMod),
  542. %%% case Ix#index.m_f of
  543. %%% {?MODULE, pos_index} ->
  544. %%% mnesia_lib:key_search_all(
  545. %%% IxKey, Pos, Objs);
  546. %%% _ ->
  547. IxVal = index_value_fun(Tab, Ix, VMod),
  548. case Type of
  549. weighted ->
  550. [Obj || Obj <- Objs,
  551. lists:keymember(1, IxVal(Obj))];
  552. _ ->
  553. [Obj || Obj <- Objs,
  554. lists:member(IxKey, IxVal(Obj))]
  555. end;
  556. _ ->
  557. Objs
  558. end,
  559. if IsOrdered ->
  560. lists:reverse(Objs1);
  561. true ->
  562. Objs1
  563. end.
  564. r_keys([H|T],Tab,Ack) ->
  565. V = mnesia_lib:db_get(Tab, H),
  566. r_keys(T, Tab, V ++ Ack);
  567. r_keys([], _, Ack) ->
  568. Ack.
  569. dirty_index_first(Tab, #index{table_name = IxTab} = Index) ->
  570. mnesia:dirty_rpc(IxTab, ?MODULE, dirty_index_first, [first, Tab, Index]).
  571. dirty_index_last(Tab, #index{table_name = IxTab} = Index) ->
  572. mnesia:dirty_rpc(IxTab, ?MODULE, dirty_index_first, [last, Tab, Index]).
  573. dirty_index_first(FirstOrLast, Tab,
  574. #index{pos = Pos,
  575. table_name = IxTab,
  576. type = Type}) ->
  577. Storage = mnesia_lib:storage_type_at_node(node(), IxTab),
  578. Res = case Storage of
  579. ram_copies ->
  580. ets:FirstOrLast(IxTab);
  581. disc_only_copies ->
  582. dets:FirstOrLast(IxTab)
  583. end,
  584. case Res of
  585. '$end_of_table' = R ->
  586. R;
  587. IxKey ->
  588. case Type of
  589. weighted ->
  590. {Ix, _Weight, _FirstObjKey} = IxKey,
  591. mnesia:dirty_index_read(Tab, Ix, Pos);
  592. ordered ->
  593. {Ix, _FirstObjKey} = IxKey,
  594. mnesia:dirty_index_read(Tab, Ix, Pos);
  595. T when T==set; T==bag ->
  596. {IxKey, mnesia:dirty_index_read(Tab, IxKey, Pos)}
  597. end
  598. end.
  599. dirty_index_next(Tab, #index{table_name = IxTab} = Index, IxKey) ->
  600. mnesia:dirty_rpc(IxTab, ?MODULE, dirty_index_relop,
  601. [next, Tab, Index, IxKey]).
  602. dirty_index_prev(Tab, #index{table_name = IxTab} = Index, IxKey) ->
  603. mnesia:dirty_rpc(IxTab, ?MODULE, dirty_index_relop,
  604. [prev, Tab, Index, IxKey]).
  605. dirty_index_relop(
  606. Direction, Tab, #index{pos = Pos,
  607. table_name = Ixt,
  608. type = Type} = Index, IxKey) ->
  609. Storage = mnesia_lib:storage_type_at_node(node(), Ixt),
  610. IsOrdered = (Type == ordered) or (Type == weighted),
  611. Next =
  612. case {Storage, Direction, IsOrdered} of
  613. {ram_copies, prev, true} ->
  614. {Ix,_} = IxKey,
  615. Pat = select_pattern(Index, Ix),
  616. case ets:select(Ixt, Pat, 1) of
  617. '$end_of_table' = R1 ->
  618. R1;
  619. {[FirstObjKey], _Cont} ->
  620. ets:prev(Ixt, {Ix,FirstObjKey})
  621. end;
  622. {ram_copies,_,_} ->
  623. ets:Direction(Ixt, IxKey);
  624. {disc_only_copies,_,_} ->
  625. dets:Direction(Ixt, IxKey)
  626. end,
  627. case Next of
  628. '$end_of_table' = R2 ->
  629. R2;
  630. NewIxKey ->
  631. case IsOrdered of
  632. true ->
  633. {NewIx, _FirstObjKey} = NewIxKey,
  634. Objs = mnesia:dirty_index_read(Tab, NewIx, Pos),
  635. LastObj = lists:last(Objs),
  636. LastObjKey = element(2, LastObj),
  637. {{NewIx, LastObjKey}, Objs};
  638. false ->
  639. {NewIxKey, mnesia:dirty_index_read(Tab, NewIxKey, Pos)}
  640. end
  641. end.
  642. valid_index(Tab, Index) ->
  643. valid_index(Tab, Index, rdbms:fetch_verification_module()).
  644. valid_index(Tab, Index, VMod) ->
  645. case catch index_record(Tab, Index, VMod) of
  646. #index{} ->
  647. true;
  648. _ ->
  649. false
  650. end.
  651. %%% ============ index_read for fragmented tables
  652. frag_read(ActivityId, Opaque, Tab, Key, LockKind) ->
  653. Frag = key_to_frag_name(Tab, Key),
  654. mnesia:read(ActivityId, Opaque, Frag, Key, LockKind).
  655. frag_index_read(Tid, Ts, Tab, Key, Attr, LockKind, VMod)
  656. when atom(Tab), Tab /= schema ->
  657. Ix = index_record(Tab, Attr, VMod),
  658. IxTab = Ix#index.table_name,
  659. verify_key(Tab, Attr, Key, LockKind), % aborts if something fishy
  660. {Oids, Nodes} =
  661. case Ix#index.type of
  662. ordered ->
  663. Pat = select_pattern(Ix, Key),
  664. Found = mnesia_frag:select(Tid, Ts, IxTab, Pat, LockKind),
  665. lists:foldl(
  666. fun(K, {Os, Ns}) ->
  667. Frag = key_to_frag_name(Tab, K),
  668. Node = mnesia_lib:val({Frag, where_to_read}),
  669. {[{Node, Frag, K}|Os], sets:add_element(Node, Ns)}
  670. end, {[], sets:new()}, Found);
  671. weighted ->
  672. Pat = select_pattern(Ix, Key),
  673. Found = mnesia_frag:select(Tid, Ts, IxTab, Pat, LockKind),
  674. lists:foldl(
  675. fun({K,_}, {Os, Ns}) ->
  676. Frag = key_to_frag_name(Tab, K),
  677. Node = mnesia_lib:val({Frag, where_to_read}),
  678. {[{Node, Frag, K}|Os], sets:add_element(Node, Ns)}
  679. end, {[], sets:new()}, Found);
  680. T when T==set; T==bag ->
  681. IxObjs = frag_read(Tid, Ts, IxTab, Key, LockKind),
  682. lists:foldl(
  683. fun(#ix{oid = K}, {Os, Ns}) ->
  684. Frag = key_to_frag_name(Tab, K),
  685. Node = mnesia_lib:val({Frag, where_to_read}),
  686. {[{Node, Frag, K}|Os], sets:add_element(Node, Ns)}
  687. end, {[], sets:new()}, IxObjs)
  688. end,
  689. %% This can be improved: right now we send all keys to all nodes
  690. %% and let the receiving end figure out which objects to read.
  691. FoundSet =
  692. case rpc:multicall(
  693. sets:to_list(Nodes),
  694. lists, foldl,
  695. [fun({N, Frag, K}, Acc) when N == node() ->
  696. mnesia:read(Tid, Ts, Frag, K, read) ++ Acc;
  697. (_, Acc) ->
  698. Acc
  699. end, [], Oids]) of
  700. {Replies, []} ->
  701. Replies;
  702. {_Replies, BadNodes} ->
  703. mnesia:abort({badarg, BadNodes})
  704. end,
  705. case val({Tab, setorbag}) of
  706. bag ->
  707. filter_found_set(FoundSet, Ix, Key);
  708. _ ->
  709. lists:append(FoundSet)
  710. end;
  711. frag_index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind, _VMod) ->
  712. mnesia:abort({bad_type, Tab}).
  713. verify_key(Tab, Attr, Key, read) ->
  714. case mnesia:has_var(Key) of
  715. false ->
  716. ok;
  717. true ->
  718. mnesia:abort({bad_type, Tab, Attr, Key})
  719. end;
  720. verify_key(Tab, _Attr, _Key, LockKind) ->
  721. mnesia:abort({bad_type, Tab, LockKind}).
  722. %%% ============ end index_read for fragmented tables
  723. do_add_indexes(_Name, []) ->
  724. ok;
  725. do_add_indexes(TabName, [_|_] = Indexes) ->
  726. %% TODO: must make sure that index is brought up-to-date
  727. TabInfo = mnesia_schema:do_read_table_info(TabName),
  728. io:format("TabInfo = ~p~n", [TabInfo]),
  729. BaseCs = mnesia_schema:list2cs(maybe_add_name(TabName, TabInfo)),
  730. io:format("BaseCs = ~p~n", [BaseCs]),
  731. Attrs = BaseCs#cstruct.attributes,
  732. RecName = BaseCs#cstruct.record_name,
  733. IndexRecs =
  734. lists:map(
  735. fun({{P, Tag}, M, F, Info, Options} = Ix) ->
  736. P1 = if
  737. is_integer(P) ->
  738. P;
  739. P == RecName ->
  740. 1;
  741. is_atom(P) ->
  742. io:format("HERE!~n"
  743. "recname = ~p~n"
  744. "Attrs = ~p~n",
  745. [RecName, Attrs]),
  746. case pos(P, [RecName|Attrs]) of
  747. Pos when Pos == 0; Pos == 2 ->
  748. %% cannot put index on primary key(??)
  749. mnesia:abort({bad_type, {index, Ix}});
  750. Pos ->
  751. Pos
  752. end;
  753. true ->
  754. mnesia:abort({bad_type, {index, Ix}})
  755. end,
  756. #index{pos = {P1, Tag},
  757. m_f = {M, F},
  758. arg = Info,
  759. type = index_type(Options),
  760. tab_opts = index_tab_options(Options),
  761. table_name = index_tab_name(TabName, {P1, Tag}),
  762. options = index_other_options(Options)}
  763. end, Indexes),
  764. rdbms_props:do_set_property(TabName, indexes, IndexRecs),
  765. IxTabInfo =
  766. [{Ix#index.table_name,
  767. mnesia_schema:cs2list(index_table(TabName, Ix, BaseCs))} ||
  768. Ix <- IndexRecs],
  769. case parent_table_loaded(TabName) of
  770. false ->
  771. io:format("parent_table_loaded(~p)-> false.~n", [TabName]),
  772. do_create_index_tabs(IndexRecs, TabName, BaseCs);
  773. %%% fill_indexes(IndexRecs, TabName).
  774. true -> % table existed before this transaction
  775. io:format("parent_table_loaded(~p)-> true.~n", [TabName]),
  776. Prep = mnesia_schema:prepare_restore(
  777. {IndexRecs, IxTabInfo, TabName},
  778. [{default_op, recreate_tables}],
  779. rdbms_index_load),
  780. mnesia_schema:do_restore(Prep)
  781. end.
  782. parent_table_loaded(Tab) ->
  783. case mnesia_lib:val({Tab,where_to_read}) of
  784. nowhere -> false;
  785. _ ->
  786. true
  787. end.
  788. maybe_add_name(Name, [{name,Name}|_] = Info) ->
  789. Info;
  790. maybe_add_name(Name, [{K,_}|_] = Info) when K =/= Name ->
  791. [{name, Name}|Info].
  792. %%% fill_indexes(Ixs, Tab) ->
  793. %%% {_Mod, Tid, Ts} = get(mnesia_activity_state),
  794. %%% mnesia:write_lock_table(Tab),
  795. %%% VMod = rdbms:fetch_verification_module(),
  796. %%% case table_info(Tab, size, VMod) of
  797. %%% 0 ->
  798. %%% ok;
  799. %%% _N ->
  800. %%% io:format("Filling indexes from ~p (~p)~n", [Tab, Ixs]),
  801. %%% Fun = fun(Obj, _Acc) ->
  802. %%% update_indexes(
  803. %%% Ixs, Tid, Ts, Tab, fill, Obj, write, VMod),
  804. %%% ok
  805. %%% end,
  806. %%% rdbms:foldl(Tid, Ts, Fun, ok, Tab, write)
  807. %%% end.
  808. pos(X, List) ->
  809. pos(X, List, 1).
  810. pos(X, [X|_], P) ->
  811. P;
  812. pos(X, [_|T], P) ->
  813. pos(X, T, P+1);
  814. pos(_X, [], _) ->
  815. 0.
  816. index_tab_name(Tab, Pos) ->
  817. Prefix = "-RDBMS-NDX-",
  818. case Pos of
  819. N when is_integer(N) ->
  820. list_to_atom(lists:append([Prefix,
  821. atom_to_list(Tab), "-",
  822. integer_to_list(N)]));
  823. {N, Tag} when is_integer(N), is_atom(Tag) ->
  824. list_to_atom(lists:append([Prefix,
  825. atom_to_list(Tab), "-",
  826. integer_to_list(N), "-",
  827. atom_to_list(Tag)]))
  828. end.
  829. index_type(Opts) when is_list(Opts) ->
  830. case lists:keysearch(type, 1, Opts) of
  831. {value, {_, T}} ->
  832. case lists:member(T, [ordered, weighted, set, bag]) of
  833. true ->
  834. T;
  835. false ->
  836. mnesia:abort({bad_type, T})
  837. end;
  838. false ->
  839. bag
  840. end.
  841. %%% index_is_ordered(Opts) when is_list(Opts) ->
  842. %%% case lists:keysearch(type, 1, Opts) of
  843. %%% {value, {_, ordered_set}} ->
  844. %%% true;
  845. %%% _ ->
  846. %%% false
  847. %%% end.
  848. index_tab_options(Opts) when is_list(Opts) ->
  849. case lists:keysearch(tab_options, 1, Opts) of
  850. {value, {_, TabOpts}} ->
  851. TabOpts;
  852. false ->
  853. []
  854. end.
  855. index_other_options(Opts) ->
  856. lists:filter(
  857. fun({tab_options,_}) ->
  858. false;
  859. ({type, _}) ->
  860. false;
  861. ({unique,B}) when is_boolean(B) ->
  862. true;
  863. (Other) ->
  864. mnesia:abort({invalid_index_option, Other})
  865. end, Opts).
  866. do_create_index_tabs(IndexRecs, TabName, TabCs) ->
  867. Tabs = [index_table(TabName, Ix, TabCs) || Ix <- IndexRecs],
  868. lists:foreach(
  869. fun(IxTabCs) ->
  870. mnesia_schema:do_create_table(IxTabCs)
  871. end, Tabs).
  872. index_table(Tab, #index{pos = Pos,
  873. type = Type,
  874. tab_opts = TabOpts}, TabCs) ->
  875. %% inherit table's
  876. %% - replication scheme
  877. %% - local_content flag
  878. %% - load_order
  879. %% - frag_properties (?)
  880. {TType, RecName, Attrs} =
  881. case Type of
  882. set -> {set, ix, record_info(fields, ix)};
  883. bag -> {bag, ix, record_info(fields, ix)};
  884. ordered -> {ordered_set, ord_ix, record_info(fields, ord_ix)};
  885. weighted -> {ordered_set, w_ix, record_info(fields, w_ix)}
  886. end,
  887. IxTabName = index_tab_name(Tab, Pos),
  888. Cs0 =
  889. TabCs#cstruct{
  890. name = IxTabName,
  891. type = TType,
  892. load_order = TabCs#cstruct.load_order + 1, % load index bef main tab
  893. index = [],
  894. frag_properties = case TabCs#cstruct.frag_properties of
  895. [{base_table, Tab}|Rest] ->
  896. %% mnesia_frag adds base_table later
  897. lists:keydelete(hash_state, 1, Rest);
  898. [] ->
  899. []
  900. end,
  901. snmp = [],
  902. record_name = RecName,
  903. attributes = Attrs,
  904. %% local_content inherits the value of the parent tab
  905. %% (We should use the acl functionality here instead... TODO)
  906. user_properties = [{{tab, access_mode}, index},
  907. {{tab, index_properties}, [{parent, Tab}]}]},
  908. check_ix_tab_opts(Tab, TabOpts, Cs0).
  909. check_ix_tab_opts(Tab, Opts, Cs0) ->
  910. lists:foldl(
  911. fun({ram_copies, Ns}, Cs) ->
  912. Cs#cstruct{ram_copies = Ns};
  913. ({disc_copies, Ns}, Cs) ->
  914. Cs#cstruct{disc_copies = Ns};
  915. ({disc_only_copies, Ns}, Cs) ->
  916. Cs#cstruct{disc_only_copies = Ns};
  917. ({local_content, Bool}, Cs) ->
  918. Cs#cstruct{local_content = Bool};
  919. ({load_order, Order}, Cs) ->
  920. Cs#cstruct{load_order = Order};
  921. ({user_properties, Props}, Cs) ->
  922. Cs#cstruct{user_properties = Props};
  923. ({frag_properties, Props}, Cs) ->
  924. Cs#cstruct{frag_properties = Props};
  925. (Other, _Cs) ->
  926. mnesia:abort({bad_type, {index_option, Tab, Other}})
  927. end, Cs0, Opts).
  928. %%% End do_add_indexes
  929. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  930. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  931. %%% do_delete_indexes/1
  932. do_delete_indexes([]) ->
  933. ok;
  934. do_delete_indexes(Indexes) ->
  935. lists:foreach(
  936. fun(#index{table_name = IxTab}) ->
  937. %% TODO - How about fragmented indexes?
  938. mnesia_schema:do_delete_table(IxTab)
  939. end, Indexes).
  940. attr_pos(Tab, Index, VMod) ->
  941. case Index of
  942. I when is_integer(I) ->
  943. I;
  944. {I, _Tag} when is_integer(I) ->
  945. I;
  946. {Attr, _Tag} when is_atom(Attr) ->
  947. attr_tab_to_pos(Tab, Attr, VMod);
  948. #index{pos = IxPos} ->
  949. %% BW compat (???)
  950. attr_pos(Tab, IxPos, VMod)
  951. end.
  952. %% Convert attribute name to integer if neccessary
  953. attr_tab_to_pos(_Tab, Pos, _VMod) when integer(Pos) ->
  954. Pos;
  955. attr_tab_to_pos(Tab, Attr, VMod) ->
  956. attr_to_pos(Attr, table_info(Tab, attributes, VMod)).
  957. %% Convert attribute name to integer if neccessary
  958. attr_to_pos(Pos, _Attrs) when integer(Pos) ->
  959. Pos;
  960. attr_to_pos(Attr, Attrs) when atom(Attr) ->
  961. attr_to_pos(Attr, Attrs, 2);
  962. attr_to_pos(Attr, _) ->
  963. mnesia:abort({bad_type, Attr}).
  964. attr_to_pos(Attr, [Attr | _Attrs], Pos) ->
  965. Pos;
  966. attr_to_pos(Attr, [_ | Attrs], Pos) ->
  967. attr_to_pos(Attr, Attrs, Pos + 1);
  968. attr_to_pos(Attr, _, _) ->
  969. mnesia:abort({bad_type, Attr}).
  970. index_record(Tab0, Index, VMod) ->
  971. Tab = base_tab(Tab0, VMod),
  972. IxId = case Index of
  973. I when is_integer(I) ->
  974. I;
  975. Attr when is_atom(Attr) ->
  976. attr_tab_to_pos(Tab, Attr, VMod);
  977. {I, _Tag} when is_integer(I) ->
  978. Index;
  979. {Attr, Tag} when is_atom(Attr) ->
  980. P = attr_tab_to_pos(Tab, Attr, VMod),
  981. {P, Tag}
  982. end,
  983. Ixes = table_indexes(Tab, VMod),
  984. case lists:keysearch(IxId, #index.pos, Ixes) of
  985. {value, Ix} ->
  986. Ix;
  987. false ->
  988. mnesia:abort({bad_type,Tab,Index})
  989. end.
  990. %%% %% Default callback for old-style mnesia indexes.
  991. %%% %%
  992. %%% pos_index(Val, _Arg) ->
  993. %%% [Val].
  994. %% Called when table is actually loaded. Figures out which indexes to
  995. %% rebuild. Basically, local_content ram_copy indexes are always rebuilt,
  996. %% and normal ram_copies are rebuilt the first time only (if there is not
  997. %% already an active copy (replica) somewhere).
  998. index_init_fun(Tab, LoadReason) ->
  999. VMod = rdbms:fetch_verification_module(),
  1000. index_init_fun(Tab, mnesia_lib:val({Tab, cstruct}), LoadReason, VMod).
  1001. index_init_fun(Tab, Cs, LoadReason) ->
  1002. index_init_fun(Tab, Cs, LoadReason, rdbms:fetch_verification_module()).
  1003. index_init_fun(Tab, _Cs, LoadReason, VMod) ->
  1004. Indexes = table_indexes(Tab, VMod),
  1005. io:format("~p - Indexes(~p) = ~p, LoadReason=~p~n",
  1006. [self(), Tab,Indexes,LoadReason]),
  1007. FirstLoad = case LoadReason of
  1008. initial -> true;
  1009. local_only -> true;
  1010. local_master -> true;
  1011. _ ->
  1012. false
  1013. end,
  1014. try init_indexes(Tab, FirstLoad, Indexes)
  1015. catch
  1016. throw:requeue ->
  1017. requeue
  1018. end.
  1019. init_indexes(_Tab, FirstLoad, Indexes) ->
  1020. InitIndexes =
  1021. lists:foldl(
  1022. fun(#index{table_name = IxTab} = Ix, Acc) ->
  1023. Cs = mnesia_lib:val({IxTab, cstruct}),
  1024. io:format("Cs = ~p~n", [Cs]),
  1025. StorageType = mnesia_lib:cs_to_storage_type(node(), Cs),
  1026. case StorageType of
  1027. disc_only_copies ->
  1028. %% we should really check somewhere that
  1029. %% the index is consistent with the table.
  1030. %% For now, we just assume it is.
  1031. Acc;
  1032. disc_copies ->
  1033. %% we should really check somewhere that
  1034. %% the index is consistent with the table.
  1035. %% For now, we just assume it is.
  1036. Acc;
  1037. ram_copies ->
  1038. io:format("Ix = ~p~n"
  1039. "Dict = ~p~n"
  1040. "ProcI = ~p~n",
  1041. [Ix, get(), process_info(self())]),
  1042. case Cs#cstruct.local_content of
  1043. true ->
  1044. [Ix|Acc];
  1045. false ->
  1046. case FirstLoad of
  1047. true ->
  1048. io:format("Ix = ~p~n"
  1049. "Dict = ~p~n"
  1050. "ProcI = ~p~n",
  1051. [Ix, get(),
  1052. process_info(self())]),
  1053. [Ix|Acc];
  1054. false ->
  1055. %% Not local_content
  1056. %% + already loaded ->
  1057. %% index should already be
  1058. %% available, and
  1059. %% we don't have to do anything.
  1060. Acc
  1061. end
  1062. end
  1063. end
  1064. end, [], Indexes),
  1065. io:format("~p - InitIndexes = ~p~n", [self(), InitIndexes]),
  1066. _F =
  1067. fun(start) ->
  1068. io:format("fun(start)~n", []),
  1069. {ok,{_,Tid,Ts}} =
  1070. mnesia_tm:begin_activity(async,rdbms),
  1071. Store = Ts#tidstore.store,
  1072. lists:foreach(
  1073. fun(#index{table_name = IxTab}) ->
  1074. mnesia_locker:wlock_table(Tid,Store,IxTab)
  1075. end, InitIndexes),
  1076. io:format("fun(start) worked~n", []);
  1077. (done) ->
  1078. io:format("fun(done)~n", []),
  1079. {_, _, _} = TidTs = get(mnesia_activity_state),
  1080. mnesia_tm:commit_activity(ok, async, TidTs),
  1081. io:format("fun(done) worked~n", []);
  1082. (Objs) ->
  1083. lists:foreach(
  1084. fun(Ix) ->
  1085. lists:foreach(
  1086. fun({Op, Obj}) ->
  1087. update_index(Op, Obj, Ix)
  1088. end, Objs)
  1089. end, InitIndexes)
  1090. end.
  1091. index_value_fun(Tab, #index{pos = Pos, m_f = {M,F}, arg = Arg}, VMod) ->
  1092. case attr_pos(Tab, Pos, VMod) of
  1093. 1 ->
  1094. fun(Obj) ->
  1095. M:F(Obj, Arg)
  1096. end;
  1097. P when P > 1 ->
  1098. fun(Obj) ->
  1099. M:F(element(P, Obj), Arg)
  1100. end
  1101. end.
  1102. update_index(Op, Obj, #index{pos = Pos,
  1103. m_f = {Mod, Fun},
  1104. arg = Arg,
  1105. type = Type,
  1106. table_name = Tab}) ->
  1107. Oid = element(2, Obj),
  1108. Keys =
  1109. case Pos of
  1110. {1,_} ->
  1111. Mod:Fun(Obj, Arg);
  1112. P when is_integer(P), P > 0 ->
  1113. Value = element(P, Obj),
  1114. Mod:Fun(Value, Arg);
  1115. {P,_} when is_integer(P), P > 0 ->
  1116. Value = element(P, Obj),
  1117. Mod:Fun(Value, Arg)
  1118. end,
  1119. F =
  1120. case {Type,Op} of
  1121. {ordered,write} ->
  1122. fun(K) -> mnesia_lib:db_put(Tab, #ord_ix{key = {K, Oid}}) end;
  1123. {ordered,Del} when Del==delete; Del==delete_object ->
  1124. fun(K) -> mnesia_lib:db_erase(Tab, {K, Oid}) end;
  1125. {weighted,write} ->
  1126. fun({K,W}) ->
  1127. mnesia_lib:db_put(Tab, #w_ix{key = {K, W, Oid}})
  1128. end;
  1129. {weighted,Del} when Del==delete; Del==delete_object ->
  1130. fun({K,W}) -> mnesia_lib:db_erase(Tab, {K,W,Oid}) end;
  1131. {T, write} when T==set; T==bag ->
  1132. fun(K) ->
  1133. mnesia_lib:db_put(Tab, #ix{key = K, oid = Oid})
  1134. end;
  1135. {set, Del} when Del==delete; Del==delete_object ->
  1136. fun(K) -> mnesia_lib:db_erase(Tab, K) end;
  1137. {bag, Del} when Del==delete; Del==delete_object ->
  1138. fun(K) ->
  1139. mnesia_lib:db_match_erase(
  1140. Tab, #ix{key = K, oid=Oid, _='_'})
  1141. end
  1142. end,
  1143. lists:foreach(F, Keys).
  1144. %%% lists:foreach(F, Keys).
  1145. %%% lists:foreach(
  1146. %%% fun(K) ->
  1147. %%% mnesia_lib:db_put(Tab, #ord_ix{key = {K, Oid}})
  1148. %%% end, Keys);
  1149. %%% weighted ->
  1150. %%% lists:foreach(
  1151. %%% fun({K,W}) ->
  1152. %%% mnesia_lib:db_pu(Tab, #w_ix{key = {K, W, Oid}})
  1153. %%% end, Keys);
  1154. %%% bag ->
  1155. %%% lists:foreach(
  1156. %%% fun(K) ->
  1157. %%% mnesia_lib:db_put(Tab, #ix{key = K, oid = Oid})
  1158. %%% end, Keys)
  1159. %%% end.
  1160. val(Var) ->
  1161. case ?catch_val(Var) of
  1162. {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
  1163. _VaLuE_ -> _VaLuE_
  1164. end.