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