1%% This Source Code Form is subject to the terms of the Mozilla Public
2%% License, v. 2.0. If a copy of the MPL was not distributed with this
3%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4%%
5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates.  All rights reserved.
6%%
7
8-module(rabbit_router).
9-include_lib("stdlib/include/qlc.hrl").
10-include_lib("rabbit_common/include/rabbit.hrl").
11
12-export([match_bindings/2, match_routing_key/2]).
13
14%%----------------------------------------------------------------------------
15
16-export_type([routing_key/0, match_result/0]).
17
18-type routing_key() :: binary().
19-type match_result() :: [rabbit_types:binding_destination()].
20
21%%----------------------------------------------------------------------------
22
23-spec match_bindings(rabbit_types:binding_source(),
24                           fun ((rabbit_types:binding()) -> boolean())) ->
25    match_result().
26
27match_bindings(SrcName, Match) ->
28    MatchHead = #route{binding = #binding{source      = SrcName,
29                                          _           = '_'}},
30    Routes = ets:select(rabbit_route, [{MatchHead, [], [['$_']]}]),
31    [Dest || [#route{binding = Binding = #binding{destination = Dest}}] <-
32        Routes, Match(Binding)].
33
34-spec match_routing_key(rabbit_types:binding_source(),
35                             [routing_key()] | ['_']) ->
36    match_result().
37
38match_routing_key(SrcName, [RoutingKey]) ->
39    find_routes(#route{binding = #binding{source      = SrcName,
40                                          destination = '$1',
41                                          key         = RoutingKey,
42                                          _           = '_'}},
43                []);
44match_routing_key(SrcName, [_|_] = RoutingKeys) ->
45    find_routes(#route{binding = #binding{source      = SrcName,
46                                          destination = '$1',
47                                          key         = '$2',
48                                          _           = '_'}},
49                [list_to_tuple(['orelse' | [{'=:=', '$2', RKey} ||
50                                               RKey <- RoutingKeys]])]).
51
52%%--------------------------------------------------------------------
53
54%% Normally we'd call mnesia:dirty_select/2 here, but that is quite
55%% expensive for the same reasons as above, and, additionally, due to
56%% mnesia 'fixing' the table with ets:safe_fixtable/2, which is wholly
57%% unnecessary. According to the ets docs (and the code in erl_db.c),
58%% 'select' is safe anyway ("Functions that internally traverse over a
59%% table, like select and match, will give the same guarantee as
60%% safe_fixtable.") and, furthermore, even the lower level iterators
61%% ('first' and 'next') are safe on ordered_set tables ("Note that for
62%% tables of the ordered_set type, safe_fixtable/2 is not necessary as
63%% calls to first/1 and next/2 will always succeed."), which
64%% rabbit_route is.
65find_routes(MatchHead, Conditions) ->
66    ets:select(rabbit_route, [{MatchHead, Conditions, ['$1']}]).
67