1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22-module(mnesia_index_plugin_test).
23-author('ulf@wiger.net').
24
25-export([init_per_testcase/2, end_per_testcase/2,
26         init_per_group/2, end_per_group/2,
27         init_per_suite/1, end_per_suite/1,
28         all/0, groups/0]).
29
30-export([
31         add_rm_plugin/1,
32         tab_with_plugin_index/1,
33         tab_with_multiple_plugin_indexes/1,
34         ix_match_w_plugin/1,
35         ix_match_w_plugin_ordered/1,
36         ix_match_w_plugin_bag/1,
37         ix_update_w_plugin/1
38        ]).
39
40-export([ix_prefixes/3,    % test plugin
41         ix_prefixes2/3]). % test plugin 2
42
43-include("mnesia_test_lib.hrl").
44
45init_per_suite(Conf) ->
46    Conf.
47
48end_per_suite(Conf) ->
49    Conf.
50
51init_per_testcase(Func, Conf) ->
52    mnesia_test_lib:init_per_testcase(Func, Conf).
53
54end_per_testcase(Func, Conf) ->
55    mnesia_test_lib:end_per_testcase(Func, Conf).
56
57all() ->
58    [add_rm_plugin,
59     tab_with_plugin_index,
60     tab_with_multiple_plugin_indexes,
61     ix_match_w_plugin,
62     ix_match_w_plugin_ordered,
63     ix_match_w_plugin_bag,
64     ix_update_w_plugin].
65
66groups() ->
67    [].
68
69init_per_group(_GroupName, Config) ->
70    Config.
71
72end_per_group(_GroupName, Config) ->
73    Config.
74
75
76add_rm_plugin(suite) -> [];
77add_rm_plugin(Config) when is_list(Config) ->
78    [N1, N2] = Nodes = ?acquire_nodes(2, Config),
79    ok = add_plugin(),
80    ok = rpc_check_plugin(N1),
81    ok = rpc_check_plugin(N2),
82    ok = add_plugin2(),
83    ok = del_plugin(),
84    ok = del_plugin2(),
85    ok = add_plugin(),
86    ok = add_plugin2(),
87    ok = del_plugin(),
88    ok = del_plugin2(),
89    ?verify_mnesia(Nodes, []).
90
91-define(PLUGIN1, {{pfx},?MODULE,ix_prefixes}).
92-define(PLUGIN2, {{pfx2},?MODULE,ix_prefixes2}).
93
94add_plugin() ->
95    {atomic, ok} = mnesia_schema:add_index_plugin({pfx}, ?MODULE, ix_prefixes),
96    [?PLUGIN1] = mnesia_schema:index_plugins(),
97    ok.
98
99add_plugin2() ->
100    {atomic, ok} = mnesia_schema:add_index_plugin({pfx2}, ?MODULE, ix_prefixes2),
101    [?PLUGIN1, ?PLUGIN2] = lists:sort(mnesia_schema:index_plugins()),
102    ok.
103
104del_plugin() ->
105    {atomic, ok} = mnesia_schema:delete_index_plugin({pfx}),
106    [?PLUGIN2] = mnesia_schema:index_plugins(),
107    ok.
108
109del_plugin2() ->
110    {atomic, ok} = mnesia_schema:delete_index_plugin({pfx2}),
111    [] = mnesia_schema:index_plugins(),
112    ok.
113
114rpc_check_plugin(N) ->
115    [?PLUGIN1] =
116        rpc:call(N, mnesia_schema, index_plugins, []),
117    ok.
118
119tab_with_plugin_index(suite) -> [];
120tab_with_plugin_index(Config) when is_list(Config) ->
121    [_N1] = Nodes = ?acquire_nodes(1, Config),
122    ok = add_plugin(),
123    {atomic, ok} = mnesia:create_table(t, [{attributes, [k,v1,v2]},
124                                           {index, [{{pfx}, ordered},
125                                                    {v1, ordered},
126                                                    v2]}]),
127    [ok,ok,ok,ok] =
128        [mnesia:dirty_write({t, K, V1, V2})
129         || {K,V1,V2} <- [{1,a,"123"},
130                          {2,b,"12345"},
131                          {3,c,"6789"},
132                          {4,d,nil}]],
133    [{t,1,a,"123"},{t,2,b,"12345"}] =
134        mnesia:dirty_index_read(t,<<"123">>,{pfx}),
135    [{t,3,c,"6789"}] =
136        mnesia:dirty_index_read(t,"6789",v2),
137    [{t,1,a,"123"}] =
138        mnesia:dirty_match_object({t,'_',a,"123"}),
139    [{t,1,a,"123"}] =
140        mnesia:dirty_select(t, [{ {t,'_',a,"123"}, [], ['$_']}]),
141    mnesia:dirty_delete(t,2),
142    [{t,1,a,"123"}] =
143        mnesia:dirty_index_read(t,<<"123">>,{pfx}),
144    ?verify_mnesia(Nodes, []).
145
146tab_with_multiple_plugin_indexes(suite) -> [];
147tab_with_multiple_plugin_indexes(Config) when is_list(Config) ->
148    [_N1] = Nodes = ?acquire_nodes(1, Config),
149    ok = add_plugin(),
150    ok = add_plugin2(),
151    {atomic, ok} =
152        mnesia:create_table(u, [{attributes, [k,v1,v2]},
153                                {index, [{{pfx}, ordered},
154                                         {{pfx2}, ordered}]}]),
155        [ok,ok,ok,ok] =
156        [mnesia:dirty_write({u, K, V1, V2})
157         || {K,V1,V2} <- [{1,a,"123"},
158                          {2,b,"12345"},
159                          {3,c,"6789"},
160                          {4,d,nil}]],
161    [{u,1,a,"123"},{u,2,b,"12345"}] =
162        mnesia:dirty_index_read(u,<<"123">>,{pfx}),
163    [{u,1,a,"123"},{u,2,b,"12345"}] =
164        mnesia:dirty_index_read(u,<<"321">>,{pfx2}),
165    ?verify_mnesia(Nodes, []).
166
167ix_match_w_plugin(suite) -> [];
168ix_match_w_plugin(Config) when is_list(Config) ->
169    [_N1] = Nodes = ?acquire_nodes(1, Config),
170    ok = add_plugin(),
171    {atomic, ok} = mnesia:create_table(im1, [{attributes, [k, v1, v2]},
172                                             {index, [{{pfx}, ordered},
173                                                      {v1, ordered}]}]),
174    fill_and_test_index_match(im1, set),
175    ?verify_mnesia(Nodes, []).
176
177
178ix_match_w_plugin_ordered(suite) -> [];
179ix_match_w_plugin_ordered(Config) when is_list(Config) ->
180    [_N1] = Nodes = ?acquire_nodes(1, Config),
181    ok = add_plugin(),
182    {atomic, ok} = mnesia:create_table(im2, [{attributes, [k, v1, v2]},
183                                             {type, ordered_set},
184                                             {index, [{{pfx}, ordered},
185                                                      {v1, ordered}]}]),
186    fill_and_test_index_match(im2, ordered_set),
187    ?verify_mnesia(Nodes, []).
188
189ix_match_w_plugin_bag(suite) -> [];
190ix_match_w_plugin_bag(Config) when is_list(Config) ->
191    [_N1] = Nodes = ?acquire_nodes(1, Config),
192    ok = add_plugin(),
193    {atomic, ok} = mnesia:create_table(im3, [{attributes, [k, v1, v2]},
194                                             {type, bag},
195                                             {index, [{{pfx}, ordered},
196                                                      {v1, ordered}]}]),
197    fill_and_test_index_match(im3, bag),
198    ?verify_mnesia(Nodes, []).
199
200ix_update_w_plugin(suite) -> [];
201ix_update_w_plugin(Config) when is_list(Config) ->
202    [_N1] = Nodes = ?acquire_nodes(1, Config),
203    ok = add_plugin(),
204    {atomic, ok} = mnesia:create_table(im4, [{attributes, [k, v1, v2]},
205                                             {type, ordered_set},
206                                             {index, [{{pfx}, ordered},
207                                                      {v1, ordered}]}]),
208
209    mnesia:dirty_write({im4, 1, "1234", "abcd"}),
210    ?match([{im4, 1, "1234", "abcd"}], mnesia:dirty_index_read(im4, <<"123">>, {pfx})),
211    ?match([{im4, 1, "1234", "abcd"}], mnesia:dirty_index_read(im4, <<"abc">>, {pfx})),
212    mnesia:dirty_write({im4, 1, "1234", "efgh"}),
213    ?match([{im4, 1, "1234", "efgh"}], mnesia:dirty_index_read(im4, <<"123">>, {pfx})),
214    ?match([{im4, 1, "1234", "efgh"}], mnesia:dirty_index_read(im4, <<"efg">>, {pfx})),
215    ?verify_mnesia(Nodes, []).
216
217fill_and_test_index_match(Tab, Type) ->
218    [ok,ok,ok,ok,ok,ok,ok,ok,ok] =
219        [mnesia:dirty_write({Tab, K, V1, V2})
220         || {K,V1,V2} <- [{1,a,"123"},
221                          {2,b,"12345"},
222                          {3,c,"123"},
223                          {4,d,nil},
224                          {5,e,nil},
225                          {6,f,nil},
226                          {7,g,nil},  %% overwritten if not bag
227                          {7,g,"234"},
228                          {8,h,"123"}]],
229    mnesia:activity(
230      transaction,
231      fun() ->
232              ok = mnesia:write({Tab, 1, aa, "1234"}), %% replaces if not bag
233              ok = mnesia:delete({Tab, 2}),
234              ok = mnesia:delete({Tab, 4}),
235              ok = mnesia:write({Tab, 6, ff, nil}),
236              ok = mnesia:write({Tab, 7, gg, "123"}),
237              ok = mnesia:write({Tab, 100, x, nil}),
238              ok = mnesia:delete_object({Tab,3,c,"123"}),
239              ok = mnesia:delete_object({Tab,5,e,nil}),
240              Res = mnesia:index_read(Tab, <<"123">>, {pfx}),
241              SetRes = [{Tab,1,aa,"1234"}, {Tab,7,gg,"123"}, {Tab,8,h,"123"}],
242              case Type of
243                  set ->
244                      SetRes = lists:sort(Res);
245                  ordered_set ->
246                      SetRes = Res;
247                  bag ->
248                      [{Tab,1,a,"123"}, {Tab,1,aa,"1234"},
249                       {Tab,7,gg,"123"}, {Tab,8,h,"123"}] = lists:sort(Res)
250              end
251      end).
252
253%% ============================================================
254%%
255ix_prefixes(_Tab, _Pos, Obj) ->
256    lists:foldl(
257      fun(V, Acc) when is_list(V) ->
258              try Pfxs = prefixes(list_to_binary(V)),
259                   Pfxs ++ Acc
260              catch
261                  error:_ ->
262                      Acc
263              end;
264         (V, Acc) when is_binary(V) ->
265              Pfxs = prefixes(V),
266              Pfxs ++ Acc;
267         (_, Acc) ->
268              Acc
269      end, [], tl(tuple_to_list(Obj))).
270
271ix_prefixes2(Tab, Pos, Obj) ->
272    [rev(P) || P <- ix_prefixes(Tab, Pos, Obj)].
273
274rev(B) when is_binary(B) ->
275    list_to_binary(lists:reverse(binary_to_list(B))).
276
277prefixes(<<P:3/binary, _/binary>>) ->
278    [P];
279prefixes(_) ->
280    [].
281