mirror of https://github.com/mongodb/mongo
SERVER-110519: Audit invariant calls in db/query/compiler (#42288)
Co-authored-by: Serhii Lysenko <serhiilysenko@mongodb.com> GitOrigin-RevId: 2bfc1147af1f580e1faeb378f7d30d8d13548085
This commit is contained in:
parent
f8ab79670b
commit
a4a462e03a
|
|
@ -98,40 +98,38 @@ public:
|
|||
*/
|
||||
class ProjectionAnalysisVisitor final : public ProjectionASTConstVisitor {
|
||||
public:
|
||||
ProjectionAnalysisVisitor(ProjectionDependencies* deps) : _deps(deps) {
|
||||
invariant(_deps);
|
||||
}
|
||||
ProjectionAnalysisVisitor(ProjectionDependencies& deps) : _deps(deps) {}
|
||||
|
||||
void visit(const ProjectionPathASTNode* node) final {
|
||||
if (node->parent()) {
|
||||
_deps->hasDottedPath = true;
|
||||
_deps.hasDottedPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const ProjectionPositionalASTNode* node) final {
|
||||
_deps->requiresMatchDetails = true;
|
||||
_deps->requiresDocument = true;
|
||||
_deps.requiresMatchDetails = true;
|
||||
_deps.requiresDocument = true;
|
||||
}
|
||||
|
||||
void visit(const ProjectionSliceASTNode* node) final {
|
||||
_deps->requiresDocument = true;
|
||||
_deps->hasExpressions = true;
|
||||
_deps.requiresDocument = true;
|
||||
_deps.hasExpressions = true;
|
||||
}
|
||||
|
||||
void visit(const ProjectionElemMatchASTNode* node) final {
|
||||
_deps->requiresDocument = true;
|
||||
_deps->hasExpressions = true;
|
||||
_deps->containsElemMatch = true;
|
||||
_deps.requiresDocument = true;
|
||||
_deps.hasExpressions = true;
|
||||
_deps.containsElemMatch = true;
|
||||
}
|
||||
|
||||
void visit(const ExpressionASTNode* node) final {
|
||||
_deps->hasExpressions = true;
|
||||
_deps.hasExpressions = true;
|
||||
}
|
||||
void visit(const BooleanConstantASTNode* node) final {}
|
||||
void visit(const MatchExpressionASTNode* node) final {}
|
||||
|
||||
private:
|
||||
ProjectionDependencies* _deps;
|
||||
ProjectionDependencies& _deps;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -143,13 +141,12 @@ private:
|
|||
*/
|
||||
class DepsAnalysisVisitor final : public ProjectionASTConstVisitor {
|
||||
public:
|
||||
DepsAnalysisVisitor(PathTrackingVisitorContext<DepsAnalysisData>* context) : _context{context} {
|
||||
invariant(_context);
|
||||
}
|
||||
DepsAnalysisVisitor(PathTrackingVisitorContext<DepsAnalysisData>& context)
|
||||
: _context{context} {}
|
||||
|
||||
void visit(const MatchExpressionASTNode* node) final {
|
||||
dependency_analysis::addDependencies(&(*node->matchExpression()),
|
||||
&_context->data().fieldDependencyTracker);
|
||||
&_context.data().fieldDependencyTracker);
|
||||
}
|
||||
|
||||
void visit(const ProjectionPositionalASTNode* node) final {
|
||||
|
|
@ -170,11 +167,10 @@ public:
|
|||
|
||||
void visit(const ExpressionASTNode* node) final {
|
||||
// The output of an expression on a dotted path depends on whether that field is an array.
|
||||
invariant(node->parent());
|
||||
expression::addDependencies(node->expressionRaw(),
|
||||
&_context->data().fieldDependencyTracker);
|
||||
tassert(11051954, "Projection AST node is missing parent", node->parent());
|
||||
expression::addDependencies(node->expressionRaw(), &_context.data().fieldDependencyTracker);
|
||||
|
||||
if (_context->fullPath().getPathLength() > 1) {
|
||||
if (_context.fullPath().getPathLength() > 1) {
|
||||
// If assigning to a top-level field, the value of that field is not actually required.
|
||||
// Otherwise, any assignment of an expression to a field requires the first component
|
||||
// of that field. e.g. {a.b.c: <expression>} will require all of 'a' since it may be an
|
||||
|
|
@ -188,7 +184,7 @@ public:
|
|||
if (node->value()) {
|
||||
addFullPathAsDependency();
|
||||
} else {
|
||||
_context->data().addExcludedPath(_context->fullPath().fullPath());
|
||||
_context.data().addExcludedPath(_context.fullPath().fullPath());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,25 +192,25 @@ public:
|
|||
|
||||
private:
|
||||
void addTopLevelPathAsDependency() {
|
||||
const auto& path = _context->fullPath();
|
||||
const auto& path = _context.fullPath();
|
||||
|
||||
_context->data().addRequiredField(std::string{path.front()});
|
||||
_context.data().addRequiredField(std::string{path.front()});
|
||||
}
|
||||
|
||||
void addFullPathAsDependency() {
|
||||
const auto& path = _context->fullPath();
|
||||
const auto& path = _context.fullPath();
|
||||
|
||||
_context->data().addRequiredField(path.fullPath());
|
||||
_context.data().addRequiredField(path.fullPath());
|
||||
}
|
||||
|
||||
PathTrackingVisitorContext<DepsAnalysisData>* _context;
|
||||
PathTrackingVisitorContext<DepsAnalysisData>& _context;
|
||||
};
|
||||
|
||||
auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) {
|
||||
ProjectionDependencies deps;
|
||||
PathTrackingVisitorContext<DepsAnalysisData> context;
|
||||
DepsAnalysisVisitor depsAnalysisVisitor{&context};
|
||||
ProjectionAnalysisVisitor projectionAnalysisVisitor{&deps};
|
||||
DepsAnalysisVisitor depsAnalysisVisitor{context};
|
||||
ProjectionAnalysisVisitor projectionAnalysisVisitor{deps};
|
||||
PathTrackingWalker walker{&context, {&depsAnalysisVisitor, &projectionAnalysisVisitor}, {}};
|
||||
|
||||
tree_walker::walk<true, projection_ast::ASTNode>(root, &walker);
|
||||
|
|
@ -224,8 +220,7 @@ auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) {
|
|||
|
||||
if (type == ProjectType::kInclusion || type == ProjectType::kAddition) {
|
||||
deps.paths = userData.requiredFields();
|
||||
} else {
|
||||
invariant(type == ProjectType::kExclusion);
|
||||
} else { // type == ProjectType::kExclusion
|
||||
deps.requiresDocument = true;
|
||||
deps.paths = std::move(userData.excludedPaths);
|
||||
}
|
||||
|
|
@ -349,7 +344,7 @@ using MetaFieldVisitorContext = PathTrackingVisitorContext<MetaFieldData>;
|
|||
*/
|
||||
class MetaFieldVisitor final : public ProjectionASTConstVisitor {
|
||||
public:
|
||||
MetaFieldVisitor(MetaFieldVisitorContext* context) : _context(context) {}
|
||||
MetaFieldVisitor(MetaFieldVisitorContext& context) : _context(context) {}
|
||||
|
||||
void visit(const ExpressionASTNode* node) final {
|
||||
const auto* metaExpr = dynamic_cast<const ExpressionMeta*>(node->expressionRaw());
|
||||
|
|
@ -357,7 +352,7 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
_context->data().metaPaths.push_back(_context->fullPath());
|
||||
_context.data().metaPaths.push_back(_context.fullPath());
|
||||
}
|
||||
|
||||
void visit(const ProjectionPositionalASTNode* node) final {}
|
||||
|
|
@ -368,13 +363,13 @@ public:
|
|||
void visit(const MatchExpressionASTNode* node) final {}
|
||||
|
||||
private:
|
||||
MetaFieldVisitorContext* _context;
|
||||
MetaFieldVisitorContext& _context;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::vector<FieldPath> Projection::extractSortKeyMetaFields() const {
|
||||
MetaFieldVisitorContext ctx;
|
||||
MetaFieldVisitor visitor(&ctx);
|
||||
MetaFieldVisitor visitor(ctx);
|
||||
PathTrackingConstWalker<MetaFieldData> walker{&ctx, {&visitor}, {}};
|
||||
tree_walker::walk<true, ASTNode>(root(), &walker);
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@ public:
|
|||
}
|
||||
|
||||
const OrderedPathSet& getExcludedPaths() const {
|
||||
invariant(_type == ProjectType::kExclusion);
|
||||
tassert(11051953,
|
||||
"Expecting projection to be of exclusion type",
|
||||
_type == ProjectType::kExclusion);
|
||||
return *_deps.paths;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,10 @@ public:
|
|||
}
|
||||
|
||||
ASTNode* child(size_t index) const {
|
||||
invariant(index < _children.size());
|
||||
tassert(11051952,
|
||||
str::stream() << "Expect child index to be less than " << _children.size()
|
||||
<< " but got " << index,
|
||||
index < _children.size());
|
||||
return _children[index].get();
|
||||
}
|
||||
|
||||
|
|
@ -290,7 +293,9 @@ private:
|
|||
class ProjectionPositionalASTNode final : public ASTNode {
|
||||
public:
|
||||
ProjectionPositionalASTNode(std::unique_ptr<MatchExpressionASTNode> child) {
|
||||
invariant(child);
|
||||
tassert(11051951,
|
||||
"Projection positional AST node is missing MatchExpression child node",
|
||||
child);
|
||||
addChildToInternalVector(std::move(child));
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +344,9 @@ private:
|
|||
class ProjectionElemMatchASTNode final : public ASTNode {
|
||||
public:
|
||||
ProjectionElemMatchASTNode(std::unique_ptr<MatchExpressionASTNode> child) {
|
||||
invariant(child);
|
||||
tassert(11051950,
|
||||
"Projection element match AST node is missing MatchExpression child node",
|
||||
child);
|
||||
addChildToInternalVector(std::move(child));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,10 @@ public:
|
|||
PathTrackingVisitorContext(UserData data) : _data{std::move(data)} {}
|
||||
|
||||
auto fullPath() const {
|
||||
invariant(!_fieldNames.empty());
|
||||
invariant(!_fieldNames.top().empty());
|
||||
tassert(11051949, "Empty stack of field names", !_fieldNames.empty());
|
||||
tassert(11051948,
|
||||
"Empty list of field names to visit at the top of the stack",
|
||||
!_fieldNames.top().empty());
|
||||
|
||||
if (!_basePath) {
|
||||
return FieldPath(_fieldNames.top().front());
|
||||
|
|
@ -80,7 +82,9 @@ public:
|
|||
}
|
||||
|
||||
void popFieldNames() {
|
||||
invariant(_fieldNames.top().empty());
|
||||
tassert(11051947,
|
||||
"List of field names to visit at the top of the stack is not empty",
|
||||
_fieldNames.top().empty());
|
||||
_fieldNames.pop();
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +118,7 @@ template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
|
|||
class PathTrackingPreVisitor final : public ProjectionASTVisitor<IsConst> {
|
||||
public:
|
||||
PathTrackingPreVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} {
|
||||
invariant(_context);
|
||||
tassert(11051946, "PathTrackingPreVisitor is missing the context", _context);
|
||||
}
|
||||
|
||||
void visit(tree_walker::MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final {
|
||||
|
|
@ -147,7 +151,7 @@ template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
|
|||
class PathTrackingPostVisitor final : public ProjectionASTVisitor<IsConst> {
|
||||
public:
|
||||
PathTrackingPostVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} {
|
||||
invariant(_context);
|
||||
tassert(11051945, "PathTrackingPostVisitor is missing the context", _context);
|
||||
}
|
||||
|
||||
void visit(tree_walker::MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final {
|
||||
|
|
|
|||
|
|
@ -212,7 +212,9 @@ BSONObj astToDebugBSON(const ASTNode* root) {
|
|||
|
||||
tree_walker::walk<true, projection_ast::ASTNode>(root, &walker);
|
||||
|
||||
invariant(context.data().builders.size() == 1);
|
||||
tassert(11051944,
|
||||
"Stack of BSONObjBuilders doesn't contain a single element",
|
||||
context.data().builders.size() == 1);
|
||||
return context.data().builders.top().obj();
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +225,9 @@ BSONObj serialize(const ProjectionPathASTNode& root, const SerializationOptions&
|
|||
PathTrackingWalker walker{&context, {&preVisitor}, {&postVisitor}};
|
||||
tree_walker::walk<true, projection_ast::ASTNode>(&root, &walker);
|
||||
|
||||
invariant(context.data().builders.size() == 1);
|
||||
tassert(11051943,
|
||||
"Stack of BSONObjBuilders doesn't contain a single element",
|
||||
context.data().builders.size() == 1);
|
||||
return context.data().builders.top().obj();
|
||||
}
|
||||
} // namespace mongo::projection_ast
|
||||
|
|
|
|||
|
|
@ -156,8 +156,10 @@ bool isInclusionOrExclusionType(BSONType type) {
|
|||
ProjectionPathASTNode* findLastInnerNodeOnPath(ProjectionPathASTNode* root,
|
||||
const FieldPath& path,
|
||||
size_t componentIndex) {
|
||||
invariant(root);
|
||||
invariant(path.getPathLength() > componentIndex);
|
||||
tassert(11051942, "Missing root node", root);
|
||||
tassert(11051941,
|
||||
"Field path is longer than the size of componentIndex",
|
||||
path.getPathLength() > componentIndex);
|
||||
|
||||
auto child = exact_pointer_cast<ProjectionPathASTNode*>(
|
||||
root->getChild(path.getFieldName(componentIndex)));
|
||||
|
|
@ -174,8 +176,11 @@ void addNodeAtPathHelper(ProjectionPathASTNode* root,
|
|||
const FieldPath& path,
|
||||
size_t componentIndex,
|
||||
std::unique_ptr<ASTNode> newChild) {
|
||||
invariant(root);
|
||||
invariant(path.getPathLength() > componentIndex);
|
||||
tassert(11051940, "Missing root node", root);
|
||||
tassert(11051939,
|
||||
"Field path is longer than the size of componentIndex",
|
||||
path.getPathLength() > componentIndex);
|
||||
|
||||
const auto nextComponent = path.getFieldName(componentIndex);
|
||||
|
||||
ASTNode* child = root->getChild(nextComponent);
|
||||
|
|
@ -366,7 +371,6 @@ bool parseSubObjectAsExpression(ParseContext* parseCtx,
|
|||
|
||||
// Create a MatchExpression for the elemMatch.
|
||||
BSONObj elemMatchObj = BSON(path.fullPath() << subObj);
|
||||
invariant(elemMatchObj.isOwned());
|
||||
|
||||
auto matcher = CopyableMatchExpression{elemMatchObj,
|
||||
parseCtx->expCtx,
|
||||
|
|
@ -443,7 +447,7 @@ void parseInclusion(ParseContext* ctx, BSONElement elem, ProjectionPathASTNode*
|
|||
// attached to the tree.
|
||||
CopyableMatchExpression matcher{ctx->queryObj, ctx->query->clone()};
|
||||
|
||||
invariant(ctx->query);
|
||||
tassert(11051938, "ParseContext is missing MatchExpression", ctx->query);
|
||||
addNodeAtPath(parent,
|
||||
path,
|
||||
std::make_unique<ProjectionPositionalASTNode>(
|
||||
|
|
@ -465,7 +469,9 @@ void parseInclusion(ParseContext* ctx, BSONElement elem, ProjectionPathASTNode*
|
|||
* Treats the given element as an exclusion projection and updates the tree as necessary.
|
||||
*/
|
||||
void parseExclusion(ParseContext* ctx, BSONElement elem, ProjectionPathASTNode* parent) {
|
||||
invariant(!elem.trueValue());
|
||||
tassert(11051937,
|
||||
str::stream() << " field " << elem.fieldNameStringData() << " is missing value",
|
||||
!elem.trueValue());
|
||||
FieldPath path(elem.fieldNameStringData());
|
||||
addNodeAtPath(parent, path, std::make_unique<BooleanConstantASTNode>(false));
|
||||
|
||||
|
|
@ -669,7 +675,6 @@ Projection parseAndAnalyze(boost::intrusive_ptr<ExpressionContext> expCtx,
|
|||
ctx.idIncludedEntirely,
|
||||
ctx.idExcludedEntirely);
|
||||
}
|
||||
invariant(ctx.type);
|
||||
|
||||
if (!ctx.idSpecified) {
|
||||
if (policies.idPolicy == ProjectionPolicies::DefaultIdPolicy::kIncludeId &&
|
||||
|
|
@ -686,7 +691,7 @@ Projection parseAndAnalyze(boost::intrusive_ptr<ExpressionContext> expCtx,
|
|||
if (*ctx.type == ProjectType::kExclusion && ctx.idSpecified && ctx.idIncludedEntirely) {
|
||||
// The user explicitly included _id in an exclusion projection. This is legal syntax, but
|
||||
// the node indicating that _id is included doesn't need to be in the tree.
|
||||
invariant(root.removeChild("_id"));
|
||||
tassert(11051936, "_id field is missing in the projection AST", root.removeChild("_id"));
|
||||
}
|
||||
|
||||
// Optimize the projection expression if requested and as long as not explicitly disabled
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ BSONElement IndexEntry::getWildcardField() const {
|
|||
BSONObjIterator it(keyPattern);
|
||||
BSONElement wildcardElt = it.next();
|
||||
for (size_t i = 0; i < wildcardFieldPos; ++i) {
|
||||
invariant(it.more());
|
||||
tassert(11051935, "the wildcardFieldPos is larger than the keyPattern length", it.more());
|
||||
wildcardElt = it.next();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,9 +84,10 @@ struct CoreIndexInfo {
|
|||
collator(ci),
|
||||
indexPathProjection(indexPathProj),
|
||||
indexCatalogEntryStorage(std::move(iceStorage)) {
|
||||
// If a projection executor exists, we always expect a $** index
|
||||
if (indexPathProjection != nullptr)
|
||||
invariant(type == IndexType::INDEX_WILDCARD);
|
||||
tassert(11051934,
|
||||
"Wildcard index is expected if index path projection is provided",
|
||||
type == IndexType::INDEX_WILDCARD);
|
||||
}
|
||||
|
||||
virtual ~CoreIndexInfo() = default;
|
||||
|
|
@ -209,8 +210,9 @@ struct MONGO_MOD_NEEDS_REPLACEMENT IndexEntry : CoreIndexInfo {
|
|||
multikeyPathSet(std::move(multikeyPathSet)),
|
||||
infoObj(io),
|
||||
wildcardFieldPos(wildcardPos) {
|
||||
// The caller must not supply multikey metadata in two different formats.
|
||||
invariant(this->multikeyPaths.empty() || this->multikeyPathSet.empty());
|
||||
tassert(11051933,
|
||||
"Multikey data must not be supplied in two different formats",
|
||||
this->multikeyPaths.empty() || this->multikeyPathSet.empty());
|
||||
}
|
||||
|
||||
IndexEntry(const IndexEntry&) = default;
|
||||
|
|
|
|||
|
|
@ -81,15 +81,17 @@ void assertOILIsAscendingLocally(const vector<Interval>& intervals, size_t idx)
|
|||
// Each individual interval being examined should be ascending or none.
|
||||
const auto dir = intervals[idx].getDirection();
|
||||
|
||||
// Should be either ascending, or have no direction (be a point/null/empty interval).
|
||||
invariant(dir == Interval::Direction::kDirectionAscending ||
|
||||
dir == Interval::Direction::kDirectionNone);
|
||||
tassert(11051932,
|
||||
"Expect interval direction to be either ascending, or have no direction",
|
||||
dir == Interval::Direction::kDirectionAscending ||
|
||||
dir == Interval::Direction::kDirectionNone /* point/null/empty interval */);
|
||||
|
||||
// The previous OIL's end value should be <= the next OIL's start value.
|
||||
if (idx > 0) {
|
||||
// Pass 'false' to avoid comparing the field names.
|
||||
const int res = intervals[idx - 1].end.woCompare(intervals[idx].start, false);
|
||||
invariant(res <= 0);
|
||||
tassert(11051931,
|
||||
"Expect previous OIL's end value to be not more than the next OIL's start value",
|
||||
res <= 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,8 +192,10 @@ IndexBoundsBuilder::BoundsTightness translateWildcardIndexBoundsAndTightness(
|
|||
// planning, *before* finishWildcardIndexScanNode has been invoked. The IndexEntry should thus
|
||||
// only have a single keyPattern field and multikeyPath entry, but this is sufficient to
|
||||
// determine whether it will be necessary to adjust the tightness.
|
||||
invariant(index.type == IndexType::INDEX_WILDCARD);
|
||||
invariant(oil);
|
||||
tassert(11051930,
|
||||
str::stream() << "Expecting Wildcard index type, got " << index.type,
|
||||
index.type == IndexType::INDEX_WILDCARD);
|
||||
tassert(11051929, "Missing OrderedIntervalList parameter", oil);
|
||||
|
||||
// If 'oil' was not filled the filter type may not be supported, but we can still use this
|
||||
// wildcard index for queries on prefix fields. The index bounds for the wildcard field will be
|
||||
|
|
@ -392,9 +396,11 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
|
|||
namespace {
|
||||
IndexBoundsBuilder::BoundsTightness computeTightnessForTypeSet(const MatcherTypeSet& typeSet,
|
||||
const IndexEntry& index) {
|
||||
// The Array case will not be handled because a typeSet with Array should not reach this
|
||||
// function
|
||||
invariant(!typeSet.hasType(BSONType::array));
|
||||
if (typeSet.hasType(BSONType::array)) {
|
||||
// The Array case will not be handled because a typeSet with Array should not reach this
|
||||
// function
|
||||
MONGO_UNREACHABLE_TASSERT(11051928);
|
||||
}
|
||||
|
||||
// The String and Object types with collation require an inexact fetch.
|
||||
if (index.collator != nullptr &&
|
||||
|
|
@ -587,8 +593,7 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
|
|||
OrderedIntervalList* oilOut,
|
||||
BoundsTightness* tightnessOut,
|
||||
interval_evaluation_tree::Builder* ietBuilder) {
|
||||
// We expect that the OIL we are constructing starts out empty.
|
||||
invariant(oilOut->intervals.empty());
|
||||
tassert(11051927, "Expect the OIL to start out empty", oilOut->intervals.empty());
|
||||
|
||||
oilOut->name = elt.fieldName();
|
||||
|
||||
|
|
@ -597,8 +602,9 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
|
|||
isHashed = true;
|
||||
}
|
||||
|
||||
// We should never be asked to translate an unsupported predicate for a hashed index.
|
||||
invariant(!isHashed || Indexability::nodeIsSupportedByHashedIndex(expr));
|
||||
tassert(11051926,
|
||||
"Translating unsupported predicate for a hashed index",
|
||||
!isHashed || Indexability::nodeIsSupportedByHashedIndex(expr));
|
||||
|
||||
if (MatchExpression::ELEM_MATCH_VALUE == expr->matchType()) {
|
||||
_translatePredicate(expr->getChild(0), elt, index, oilOut, tightnessOut, ietBuilder);
|
||||
|
|
@ -627,8 +633,7 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
|
|||
|
||||
// If we have a NOT -> EXISTS, we must handle separately.
|
||||
if (MatchExpression::EXISTS == child->matchType()) {
|
||||
// We should never try to use a sparse index for $exists:false.
|
||||
invariant(!index.sparse);
|
||||
tassert(11051925, "Expect dense index for $exists:false", !index.sparse);
|
||||
// {$exists:false} is a point-interval on [null,null] that requires a fetch.
|
||||
oilOut->intervals.push_back(makeNullPointInterval(isHashed));
|
||||
*tightnessOut = IndexBoundsBuilder::INEXACT_FETCH;
|
||||
|
|
@ -1182,8 +1187,6 @@ void IndexBoundsBuilder::_translatePredicate(const MatchExpression* expr,
|
|||
ime = clonedInMatchExpr;
|
||||
}
|
||||
|
||||
invariant(ime->isBSONOwned());
|
||||
|
||||
// Because we own the BSON buffer for the $in array, this allows us to create Interval
|
||||
// objects which point directly to this BSON (without having to make copies just to strip
|
||||
// out the field name as is usually done in IndexBoundsBuilder::translateEquality()).
|
||||
|
|
@ -1287,8 +1290,8 @@ Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj, BoundInclusio
|
|||
|
||||
// static
|
||||
void IndexBoundsBuilder::intersectize(const OrderedIntervalList& oilA, OrderedIntervalList* oilB) {
|
||||
invariant(oilB);
|
||||
invariant(oilA.name == oilB->name);
|
||||
tassert(11051924, "missing oilB parameter", oilB);
|
||||
tassert(11051923, "Expect oilA and oilB to have the same name", oilA.name == oilB->name);
|
||||
|
||||
size_t oilAIdx = 0;
|
||||
const vector<Interval>& oilAIntervals = oilA.intervals;
|
||||
|
|
@ -1611,8 +1614,8 @@ void IndexBoundsBuilder::appendTrailingAllValuesInterval(const Interval& interva
|
|||
bool endKeyInclusive,
|
||||
BSONObjBuilder* startBob,
|
||||
BSONObjBuilder* endBob) {
|
||||
invariant(startBob);
|
||||
invariant(endBob);
|
||||
tassert(11051922, "missing startBob parameter", startBob);
|
||||
tassert(11051921, "missing endBob parameter", endBob);
|
||||
|
||||
// Must be min->max or max->min.
|
||||
if (interval.isMinToMax()) {
|
||||
|
|
|
|||
|
|
@ -196,10 +196,12 @@ public:
|
|||
return algebra::walk(child, *this);
|
||||
}();
|
||||
|
||||
invariant(node.index() < static_cast<int>(childOil.intervals.size()));
|
||||
tassert(11051920,
|
||||
"Expect the node index to be less than the number of childOil intervals",
|
||||
node.index() < static_cast<int>(childOil.intervals.size()));
|
||||
childOil.intervals[0] = childOil.intervals[node.index()];
|
||||
childOil.intervals.resize(1);
|
||||
invariant(childOil.isPoint());
|
||||
tassert(11051919, "Expect the childOil to be a point interval", childOil.isPoint());
|
||||
|
||||
return childOil;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1336,7 +1336,9 @@ StatusWithMatchExpression parseGeo(boost::optional<StringData> name,
|
|||
return {std::make_unique<GeoMatchExpression>(
|
||||
name, gq.release(), section, createAnnotation(expCtx, operatorName, name, section))};
|
||||
} else {
|
||||
invariant(PathAcceptingKeyword::GEO_NEAR == type);
|
||||
tassert(11051918,
|
||||
"Expect PathAcceptingKeyword to be $geoNear",
|
||||
PathAcceptingKeyword::GEO_NEAR == type);
|
||||
|
||||
if ((allowedFeatures & MatchExpressionParser::AllowedFeatures::kGeoNear) == 0u) {
|
||||
return {Status(ErrorCodes::Error(5626500),
|
||||
|
|
@ -1418,7 +1420,9 @@ StatusWithMatchExpression parseElemMatch(boost::optional<StringData> name,
|
|||
bool isElemMatchValue = false;
|
||||
if (isExpressionDocument(e, true)) {
|
||||
auto elt = obj.firstElement();
|
||||
invariant(elt);
|
||||
if (!elt) {
|
||||
return {Status(ErrorCodes::BadValue, "$elemMatch needs a non-empty Object")};
|
||||
}
|
||||
|
||||
isElemMatchValue = !retrievePathlessParser(elt.fieldNameStringData().substr(1));
|
||||
}
|
||||
|
|
@ -1714,7 +1718,7 @@ StatusWithMatchExpression parseSubField(const BSONObj& context,
|
|||
const ExtensionsCallback* extensionsCallback,
|
||||
MatchExpressionParser::AllowedFeatureSet allowedFeatures,
|
||||
DocumentParseLevel currentLevel) {
|
||||
invariant(e);
|
||||
tassert(11051917, "Missing match expression parameter", e);
|
||||
|
||||
if ("$not"_sd == e.fieldNameStringData()) {
|
||||
return parseNot(name, e, expCtx, extensionsCallback, allowedFeatures, currentLevel);
|
||||
|
|
|
|||
|
|
@ -148,7 +148,9 @@ std::unique_ptr<MatchExpression> makeRestriction(
|
|||
boost::optional<StringData> path,
|
||||
std::unique_ptr<MatchExpression> restrictionExpr,
|
||||
InternalSchemaTypeExpression* statedType) {
|
||||
invariant(restrictionType.isSingleType());
|
||||
tassert(11051916,
|
||||
"Expecting restriction type set to contain a single type",
|
||||
restrictionType.isSingleType());
|
||||
|
||||
if (statedType && statedType->typeSet().isSingleType()) {
|
||||
// Use NumberInt in the "number" case as a stand-in.
|
||||
|
|
@ -866,7 +868,8 @@ StatusWithMatchExpression translateSchemaDependency(
|
|||
BSONElement dependency,
|
||||
AllowedFeatureSet allowedFeatures,
|
||||
bool ignoreUnknownKeywords) {
|
||||
invariant(dependency.type() == BSONType::object);
|
||||
tassert(
|
||||
11051915, "Expect dependency bson to be an object", dependency.type() == BSONType::object);
|
||||
|
||||
auto nestedSchemaMatch =
|
||||
_parse(expCtx, path, dependency.embeddedObject(), allowedFeatures, ignoreUnknownKeywords);
|
||||
|
|
@ -898,7 +901,8 @@ StatusWithMatchExpression translatePropertyDependency(
|
|||
const boost::intrusive_ptr<ExpressionContext>& expCtx,
|
||||
boost::optional<StringData> path,
|
||||
BSONElement dependency) {
|
||||
invariant(dependency.type() == BSONType::array);
|
||||
tassert(
|
||||
11051914, "Expect dependency bson to be an array", dependency.type() == BSONType::array);
|
||||
|
||||
if (dependency.embeddedObject().isEmpty()) {
|
||||
return {ErrorCodes::FailedToParse,
|
||||
|
|
|
|||
|
|
@ -915,7 +915,9 @@ IndexBoundsChecker::Location IndexBoundsChecker::findIntervalForField(
|
|||
|
||||
// Additional check to determine if interval contains key.
|
||||
Location where = intervalCmp(*i, elt, expectedDirection);
|
||||
invariant(BEHIND == where || WITHIN == where);
|
||||
tassert(11051913,
|
||||
"Expect the element to be either behind, or within the interval",
|
||||
BEHIND == where || WITHIN == where);
|
||||
|
||||
return where;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ Interval::Direction Interval::getDirection() const {
|
|||
// 'false' to not consider the field name.
|
||||
const int res = start.woCompare(end, false);
|
||||
|
||||
invariant(res != 0);
|
||||
tassert(11051912, "Calling getDirection on a point interval", res != 0);
|
||||
return res < 0 ? Direction::kDirectionAscending : Direction::kDirectionDescending;
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ bool Interval::equals(const Interval& other) const {
|
|||
}
|
||||
|
||||
bool Interval::intersects(const Interval& other) const {
|
||||
if (kDebugBuild) {
|
||||
if constexpr (kDebugBuild) {
|
||||
// This function assumes that both intervals are ascending (or are empty/point intervals).
|
||||
// Determining this may be expensive, so we only do these checks when in a debug build.
|
||||
const auto thisDir = getDirection();
|
||||
|
|
|
|||
|
|
@ -845,9 +845,10 @@ std::set<StringData> IndexScanNode::getFieldsWithStringBounds(const IndexBounds&
|
|||
}
|
||||
|
||||
std::set<StringData> ret;
|
||||
invariant(bounds.fields.size() == static_cast<size_t>(indexKeyPattern.nFields()));
|
||||
tassert(11051911,
|
||||
"Expect the number of input bounds to match the number of fields in index key pattern",
|
||||
bounds.fields.size() == static_cast<size_t>(indexKeyPattern.nFields()));
|
||||
for (const auto& oil : bounds.fields) {
|
||||
invariant(keyPatternIterator.more());
|
||||
BSONElement el = keyPatternIterator.next();
|
||||
OrderedIntervalList intersection = buildStringBoundsOil(el.fieldName());
|
||||
IndexBoundsBuilder::intersectize(oil, &intersection);
|
||||
|
|
@ -924,7 +925,9 @@ bool confirmBoundsProvideSortComponentGivenMultikeyness(
|
|||
// This is because if they are equal and do not have [minKey, maxKey] bounds, we would
|
||||
// already have bailed out of the function. If they do have [minKey, maxKey] bounds,
|
||||
// they will be skipped in the check for [minKey, maxKey] bounds above.
|
||||
invariant(refName != boundsPath);
|
||||
if (refName == boundsPath) {
|
||||
MONGO_UNREACHABLE_TASSERT(11051910);
|
||||
}
|
||||
// Checks if there's a common prefix between the interval list name and the sort pattern
|
||||
// name.
|
||||
if (commonPrefixSize > 0) {
|
||||
|
|
@ -1360,7 +1363,7 @@ void ProjectionNode::appendToString(str::stream* ss, int indent) const {
|
|||
}
|
||||
|
||||
void ProjectionNode::computeProperties() {
|
||||
invariant(children.size() == 1U);
|
||||
tassert(11051909, "Expect projection to have 1 query solution", children.size() == 1U);
|
||||
children[0]->computeProperties();
|
||||
|
||||
// Our input sort is not necessarily maintained if we project some fields that are part of the
|
||||
|
|
|
|||
|
|
@ -684,7 +684,7 @@ std::unique_ptr<MatchExpression> inOptimizer(std::unique_ptr<MatchExpression> ex
|
|||
if (regexes.size() == 1 && ime.equalitiesIsEmpty()) {
|
||||
// Simplify IN of exactly one regex to be a regex match.
|
||||
auto& childRe = regexes.front();
|
||||
invariant(!childRe->getTag());
|
||||
tassert(11051908, "Expect RegexMatchExpression to be untagged", !childRe->getTag());
|
||||
|
||||
auto simplifiedExpression = std::make_unique<RegexMatchExpression>(
|
||||
expression->path(), childRe->getString(), childRe->getFlags());
|
||||
|
|
|
|||
|
|
@ -275,7 +275,8 @@ class MatchExpressionParameterizationVisitor final : public MatchExpressionMutab
|
|||
public:
|
||||
MatchExpressionParameterizationVisitor(MatchExpressionParameterizationVisitorContext* context)
|
||||
: _context{context} {
|
||||
invariant(_context);
|
||||
tassert(
|
||||
11051907, "MatchExpressionParameterizationVisitor is missing the context", _context);
|
||||
}
|
||||
|
||||
void visit(AlwaysFalseMatchExpression* expr) final {}
|
||||
|
|
@ -356,7 +357,7 @@ class MatchExpressionParameterizationWalker {
|
|||
public:
|
||||
MatchExpressionParameterizationWalker(MatchExpressionParameterizationVisitor* visitor)
|
||||
: _visitor{visitor} {
|
||||
invariant(_visitor);
|
||||
tassert(11051906, "MatchExpressionParameterizationWalker is missing the visitor", _visitor);
|
||||
}
|
||||
|
||||
void preVisit(MatchExpression* expr) {
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteComparisonExpression(
|
|||
}
|
||||
|
||||
const auto& operandList = expr->getOperandList();
|
||||
invariant(operandList.size() == 2);
|
||||
tassert(11051905, "Expect comparison expression to have 2 operands", operandList.size() == 2);
|
||||
|
||||
ExpressionFieldPath* lhs{nullptr};
|
||||
ExpressionConstant* rhs{nullptr};
|
||||
|
|
@ -186,11 +186,14 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteComparisonExpression(
|
|||
// Extract left-hand side and right-hand side MatchExpression components.
|
||||
if ((lhs = dynamic_cast<ExpressionFieldPath*>(operandList[0].get()))) {
|
||||
rhs = dynamic_cast<ExpressionConstant*>(operandList[1].get());
|
||||
invariant(rhs);
|
||||
tassert(11051904, "Expect rhs operand to be of type ExpressionConstant", rhs);
|
||||
} else {
|
||||
lhs = dynamic_cast<ExpressionFieldPath*>(operandList[1].get());
|
||||
rhs = dynamic_cast<ExpressionConstant*>(operandList[0].get());
|
||||
invariant(lhs && rhs);
|
||||
tassert(11051903,
|
||||
"Expect lhs and rhs operands to be of types ExpressionFieldPath and "
|
||||
"ExpressionConstant respectively",
|
||||
lhs && rhs);
|
||||
|
||||
// The MatchExpression is normalized so that the field path expression is on the left. For
|
||||
// cases like {$gt: [1, "$x"]} where the order of the child expressions matter, we also
|
||||
|
|
@ -334,7 +337,7 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteInExpression(
|
|||
const boost::intrusive_ptr<ExpressionIn>& expr) {
|
||||
|
||||
const auto& operandList = expr->getOperandList();
|
||||
invariant(operandList.size() == 2);
|
||||
tassert(11051902, "Expect InExpression to have 2 operands", operandList.size() == 2);
|
||||
|
||||
auto lhs = operandList[0].get();
|
||||
auto rhs = operandList[1].get();
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ StatsCache::LookupResult StatsCache::_lookupStats(OperationContext* opCtx,
|
|||
const StatsCacheValueHandle& stats) {
|
||||
|
||||
try {
|
||||
invariant(_statsCacheLoader);
|
||||
tassert(11051901, "Expecting stats cache loader to be provided", _statsCacheLoader);
|
||||
auto newStats = _statsCacheLoader->getStats(opCtx, statsPath).get();
|
||||
return LookupResult(std::move(newStats));
|
||||
} catch (const DBException& ex) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public:
|
|||
* Returns statsCacheLoader currently used for testing only.
|
||||
*/
|
||||
StatsCacheLoader* getStatsCacheLoader() {
|
||||
invariant(_statsCacheLoader);
|
||||
tassert(11051900, "Expecting stats cache loader to be provided", _statsCacheLoader);
|
||||
|
||||
return _statsCacheLoader.get();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -573,7 +573,6 @@ absl::flat_hash_map<sbe::value::TypeTags, sbe::value::TypeTags> nextTypeTagsMap(
|
|||
|
||||
// Skips the last one as there is no next type.
|
||||
sbe::value::TypeTags nextTag = kTypeTagsSorted[kTypeTagsSorted.size() - 1];
|
||||
invariant(!kTypeTagsSorted.empty());
|
||||
for (int32_t index = kTypeTagsSorted.size() - 2; index >= 0; --index) {
|
||||
auto tag = kTypeTagsSorted[index];
|
||||
nextTypeTagsMap[tag] = nextTag;
|
||||
|
|
|
|||
Loading…
Reference in New Issue