1 | //===----------------------------------------------------------------------===// |
2 | // DuckDB |
3 | // |
4 | // duckdb/optimizer/matcher/set_matcher.hpp |
5 | // |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #pragma once |
10 | |
11 | #include "duckdb/common/common.hpp" |
12 | #include "duckdb/common/unordered_set.hpp" |
13 | |
14 | namespace duckdb { |
15 | |
16 | class SetMatcher { |
17 | public: |
18 | //! The policy used by the SetMatcher |
19 | enum class Policy { |
20 | //! All entries have to be matched, and the matches have to be ordered |
21 | ORDERED, |
22 | //! All entries have to be matched, but the order of the matches does not matter |
23 | UNORDERED, |
24 | //! Only some entries have to be matched, the order of the matches does not matter |
25 | SOME, |
26 | //! Not initialized |
27 | INVALID |
28 | }; |
29 | |
30 | /* The double {{}} in the intializer for excluded_entries is intentional, workaround for bug in gcc-4.9 */ |
31 | template <class T, class MATCHER> |
32 | static bool MatchRecursive(vector<unique_ptr<MATCHER>> &matchers, vector<reference<T>> &entries, |
33 | vector<reference<T>> &bindings, unordered_set<idx_t> excluded_entries, idx_t m_idx = 0) { |
34 | if (m_idx == matchers.size()) { |
35 | // matched all matchers! |
36 | return true; |
37 | } |
38 | // try to find a match for the current matcher (m_idx) |
39 | idx_t previous_binding_count = bindings.size(); |
40 | for (idx_t e_idx = 0; e_idx < entries.size(); e_idx++) { |
41 | // first check if this entry has already been matched |
42 | if (excluded_entries.find(x: e_idx) != excluded_entries.end()) { |
43 | // it has been matched: skip this entry |
44 | continue; |
45 | } |
46 | // otherwise check if the current matcher matches this entry |
47 | if (matchers[m_idx]->Match(entries[e_idx], bindings)) { |
48 | // m_idx matches e_idx! |
49 | // check if we can find a complete match for this path |
50 | // first add e_idx to the new set of excluded entries |
51 | unordered_set<idx_t> new_excluded_entries; |
52 | new_excluded_entries = excluded_entries; |
53 | new_excluded_entries.insert(x: e_idx); |
54 | // then match the next matcher in the set |
55 | if (MatchRecursive(matchers, entries, bindings, new_excluded_entries, m_idx + 1)) { |
56 | // we found a match for this path! success |
57 | return true; |
58 | } else { |
59 | // we did not find a match! remove any bindings we added in the call to Match() |
60 | bindings.erase(bindings.begin() + previous_binding_count, bindings.end()); |
61 | } |
62 | } |
63 | } |
64 | return false; |
65 | } |
66 | |
67 | template <class T, class MATCHER> |
68 | static bool Match(vector<unique_ptr<MATCHER>> &matchers, vector<reference<T>> &entries, |
69 | vector<reference<T>> &bindings, Policy policy) { |
70 | if (policy == Policy::ORDERED) { |
71 | // ordered policy, count has to match |
72 | if (matchers.size() != entries.size()) { |
73 | return false; |
74 | } |
75 | // now entries have to match in order |
76 | for (idx_t i = 0; i < matchers.size(); i++) { |
77 | if (!matchers[i]->Match(entries[i], bindings)) { |
78 | return false; |
79 | } |
80 | } |
81 | return true; |
82 | } else { |
83 | if (policy == Policy::UNORDERED && matchers.size() != entries.size()) { |
84 | // unordered policy, count does not match: no match |
85 | return false; |
86 | } else if (policy == Policy::SOME && matchers.size() > entries.size()) { |
87 | // some policy, every matcher has to match a unique entry |
88 | // this is not possible if there are more matchers than entries |
89 | return false; |
90 | } |
91 | // now perform the actual matching |
92 | // every matcher has to match a UNIQUE entry |
93 | // we perform this matching in a recursive way |
94 | unordered_set<idx_t> excluded_entries; |
95 | if (!MatchRecursive(matchers, entries, bindings, excluded_entries)) { |
96 | return false; |
97 | } |
98 | return true; |
99 | } |
100 | } |
101 | |
102 | template <class T, class MATCHER> |
103 | static bool Match(vector<unique_ptr<MATCHER>> &matchers, vector<unique_ptr<T>> &entries, |
104 | vector<reference<T>> &bindings, Policy policy) { |
105 | // convert vector of unique_ptr to vector of normal pointers |
106 | vector<reference<T>> ptr_entries; |
107 | for (auto &entry : entries) { |
108 | ptr_entries.push_back(*entry); |
109 | } |
110 | // then just call the normal match function |
111 | return Match(matchers, ptr_entries, bindings, policy); |
112 | } |
113 | }; |
114 | |
115 | } // namespace duckdb |
116 | |