1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/platform/basic.h"
32 
33 #include "mongo/db/query/plan_cache_indexability.h"
34 
35 #include "mongo/base/init.h"
36 #include "mongo/base/owned_pointer_vector.h"
37 #include "mongo/db/matcher/expression.h"
38 #include "mongo/db/matcher/expression_algo.h"
39 #include "mongo/db/matcher/expression_internal_expr_eq.h"
40 #include "mongo/db/matcher/expression_leaf.h"
41 #include "mongo/db/query/collation/collation_index_key.h"
42 #include "mongo/db/query/collation/collator_interface.h"
43 #include "mongo/db/query/index_entry.h"
44 #include "mongo/stdx/memory.h"
45 #include <memory>
46 
47 namespace mongo {
48 
processSparseIndex(const std::string & indexName,const BSONObj & keyPattern)49 void PlanCacheIndexabilityState::processSparseIndex(const std::string& indexName,
50                                                     const BSONObj& keyPattern) {
51     for (BSONElement elem : keyPattern) {
52         _pathDiscriminatorsMap[elem.fieldNameStringData()][indexName].addDiscriminator(
53             [](const MatchExpression* queryExpr) {
54                 if (queryExpr->matchType() == MatchExpression::EQ) {
55                     const auto* queryExprEquality =
56                         static_cast<const EqualityMatchExpression*>(queryExpr);
57                     return !queryExprEquality->getData().isNull();
58                 } else if (queryExpr->matchType() == MatchExpression::MATCH_IN) {
59                     const auto* queryExprIn = static_cast<const InMatchExpression*>(queryExpr);
60                     return !queryExprIn->hasNull();
61                 } else {
62                     return true;
63                 }
64             });
65     }
66 }
67 
processPartialIndex(const std::string & indexName,const MatchExpression * filterExpr)68 void PlanCacheIndexabilityState::processPartialIndex(const std::string& indexName,
69                                                      const MatchExpression* filterExpr) {
70     invariant(filterExpr);
71     for (size_t i = 0; i < filterExpr->numChildren(); ++i) {
72         processPartialIndex(indexName, filterExpr->getChild(i));
73     }
74     if (filterExpr->getCategory() != MatchExpression::MatchCategory::kLogical) {
75         _pathDiscriminatorsMap[filterExpr->path()][indexName].addDiscriminator(
76             [filterExpr](const MatchExpression* queryExpr) {
77                 return expression::isSubsetOf(queryExpr, filterExpr);
78             });
79     }
80 }
81 
processIndexCollation(const std::string & indexName,const BSONObj & keyPattern,const CollatorInterface * collator)82 void PlanCacheIndexabilityState::processIndexCollation(const std::string& indexName,
83                                                        const BSONObj& keyPattern,
84                                                        const CollatorInterface* collator) {
85     for (BSONElement elem : keyPattern) {
86         _pathDiscriminatorsMap[elem.fieldNameStringData()][indexName].addDiscriminator([collator](
87             const MatchExpression* queryExpr) {
88             if (const auto* queryExprComparison =
89                     dynamic_cast<const ComparisonMatchExpressionBase*>(queryExpr)) {
90                 const bool collatorsMatch =
91                     CollatorInterface::collatorsMatch(queryExprComparison->getCollator(), collator);
92                 const bool isCollatableType =
93                     CollationIndexKey::isCollatableType(queryExprComparison->getData().type());
94                 return collatorsMatch || !isCollatableType;
95             }
96 
97             if (queryExpr->matchType() == MatchExpression::MATCH_IN) {
98                 const auto* queryExprIn = static_cast<const InMatchExpression*>(queryExpr);
99                 if (CollatorInterface::collatorsMatch(queryExprIn->getCollator(), collator)) {
100                     return true;
101                 }
102                 for (const auto& equality : queryExprIn->getEqualities()) {
103                     if (CollationIndexKey::isCollatableType(equality.type())) {
104                         return false;
105                     }
106                 }
107                 return true;
108             }
109 
110             // The predicate never compares strings so it is not affected by collation.
111             return true;
112         });
113     }
114 }
115 
116 namespace {
117 const IndexToDiscriminatorMap emptyDiscriminators{};
118 }  // namespace
119 
getDiscriminators(StringData path) const120 const IndexToDiscriminatorMap& PlanCacheIndexabilityState::getDiscriminators(
121     StringData path) const {
122     PathDiscriminatorsMap::const_iterator it = _pathDiscriminatorsMap.find(path);
123     if (it == _pathDiscriminatorsMap.end()) {
124         return emptyDiscriminators;
125     }
126     return it->second;
127 }
128 
updateDiscriminators(const std::vector<IndexEntry> & indexEntries)129 void PlanCacheIndexabilityState::updateDiscriminators(const std::vector<IndexEntry>& indexEntries) {
130     _pathDiscriminatorsMap = PathDiscriminatorsMap();
131 
132     for (const IndexEntry& idx : indexEntries) {
133         if (idx.sparse) {
134             processSparseIndex(idx.name, idx.keyPattern);
135         }
136         if (idx.filterExpr) {
137             processPartialIndex(idx.name, idx.filterExpr);
138         }
139         processIndexCollation(idx.name, idx.keyPattern, idx.collator);
140     }
141 }
142 
143 }  // namespace mongo
144