PageRenderTime 206ms CodeModel.GetById 116ms app.highlight 79ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/rdbms/src/rdbms_index.erl

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