diff --git a/src/mongo/db/query/compiler/logical_model/projection/projection.cpp b/src/mongo/db/query/compiler/logical_model/projection/projection.cpp index f65a6e7ee56..ab1d9db33a2 100644 --- a/src/mongo/db/query/compiler/logical_model/projection/projection.cpp +++ b/src/mongo/db/query/compiler/logical_model/projection/projection.cpp @@ -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* context) : _context{context} { - invariant(_context); - } + DepsAnalysisVisitor(PathTrackingVisitorContext& 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: } 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* _context; + PathTrackingVisitorContext& _context; }; auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) { ProjectionDependencies deps; PathTrackingVisitorContext context; - DepsAnalysisVisitor depsAnalysisVisitor{&context}; - ProjectionAnalysisVisitor projectionAnalysisVisitor{&deps}; + DepsAnalysisVisitor depsAnalysisVisitor{context}; + ProjectionAnalysisVisitor projectionAnalysisVisitor{deps}; PathTrackingWalker walker{&context, {&depsAnalysisVisitor, &projectionAnalysisVisitor}, {}}; tree_walker::walk(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; */ 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(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 Projection::extractSortKeyMetaFields() const { MetaFieldVisitorContext ctx; - MetaFieldVisitor visitor(&ctx); + MetaFieldVisitor visitor(ctx); PathTrackingConstWalker walker{&ctx, {&visitor}, {}}; tree_walker::walk(root(), &walker); diff --git a/src/mongo/db/query/compiler/logical_model/projection/projection.h b/src/mongo/db/query/compiler/logical_model/projection/projection.h index de08b33ea88..3c3113ead36 100644 --- a/src/mongo/db/query/compiler/logical_model/projection/projection.h +++ b/src/mongo/db/query/compiler/logical_model/projection/projection.h @@ -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; } diff --git a/src/mongo/db/query/compiler/logical_model/projection/projection_ast.h b/src/mongo/db/query/compiler/logical_model/projection/projection_ast.h index c01c1c2ef40..a5459fa946c 100644 --- a/src/mongo/db/query/compiler/logical_model/projection/projection_ast.h +++ b/src/mongo/db/query/compiler/logical_model/projection/projection_ast.h @@ -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 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 child) { - invariant(child); + tassert(11051950, + "Projection element match AST node is missing MatchExpression child node", + child); addChildToInternalVector(std::move(child)); } diff --git a/src/mongo/db/query/compiler/logical_model/projection/projection_ast_path_tracking_visitor.h b/src/mongo/db/query/compiler/logical_model/projection/projection_ast_path_tracking_visitor.h index e35860e4a4f..cd68b86e9fd 100644 --- a/src/mongo/db/query/compiler/logical_model/projection/projection_ast_path_tracking_visitor.h +++ b/src/mongo/db/query/compiler/logical_model/projection/projection_ast_path_tracking_visitor.h @@ -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 PathTrackingPreVisitor final : public ProjectionASTVisitor { public: PathTrackingPreVisitor(PathTrackingVisitorContext* context) : _context{context} { - invariant(_context); + tassert(11051946, "PathTrackingPreVisitor is missing the context", _context); } void visit(tree_walker::MaybeConstPtr node) final { @@ -147,7 +151,7 @@ template class PathTrackingPostVisitor final : public ProjectionASTVisitor { public: PathTrackingPostVisitor(PathTrackingVisitorContext* context) : _context{context} { - invariant(_context); + tassert(11051945, "PathTrackingPostVisitor is missing the context", _context); } void visit(tree_walker::MaybeConstPtr node) final { diff --git a/src/mongo/db/query/compiler/logical_model/projection/projection_ast_util.cpp b/src/mongo/db/query/compiler/logical_model/projection/projection_ast_util.cpp index e47f5a2e296..3ed47edf6be 100644 --- a/src/mongo/db/query/compiler/logical_model/projection/projection_ast_util.cpp +++ b/src/mongo/db/query/compiler/logical_model/projection/projection_ast_util.cpp @@ -212,7 +212,9 @@ BSONObj astToDebugBSON(const ASTNode* root) { tree_walker::walk(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(&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 diff --git a/src/mongo/db/query/compiler/logical_model/projection/projection_parser.cpp b/src/mongo/db/query/compiler/logical_model/projection/projection_parser.cpp index 3959e05fe3e..bcf4c26f724 100644 --- a/src/mongo/db/query/compiler/logical_model/projection/projection_parser.cpp +++ b/src/mongo/db/query/compiler/logical_model/projection/projection_parser.cpp @@ -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( root->getChild(path.getFieldName(componentIndex))); @@ -174,8 +176,11 @@ void addNodeAtPathHelper(ProjectionPathASTNode* root, const FieldPath& path, size_t componentIndex, std::unique_ptr 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( @@ -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(false)); @@ -669,7 +675,6 @@ Projection parseAndAnalyze(boost::intrusive_ptr 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 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 diff --git a/src/mongo/db/query/compiler/metadata/index_entry.cpp b/src/mongo/db/query/compiler/metadata/index_entry.cpp index d4f1b4b397a..85ffb8cd45b 100644 --- a/src/mongo/db/query/compiler/metadata/index_entry.cpp +++ b/src/mongo/db/query/compiler/metadata/index_entry.cpp @@ -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(); } diff --git a/src/mongo/db/query/compiler/metadata/index_entry.h b/src/mongo/db/query/compiler/metadata/index_entry.h index 1fa0d4909ac..808aee43942 100644 --- a/src/mongo/db/query/compiler/metadata/index_entry.h +++ b/src/mongo/db/query/compiler/metadata/index_entry.h @@ -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; diff --git a/src/mongo/db/query/compiler/optimizer/index_bounds_builder/index_bounds_builder.cpp b/src/mongo/db/query/compiler/optimizer/index_bounds_builder/index_bounds_builder.cpp index 38e1584dea2..28cb2e97271 100644 --- a/src/mongo/db/query/compiler/optimizer/index_bounds_builder/index_bounds_builder.cpp +++ b/src/mongo/db/query/compiler/optimizer/index_bounds_builder/index_bounds_builder.cpp @@ -81,15 +81,17 @@ void assertOILIsAscendingLocally(const vector& 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& 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()) { diff --git a/src/mongo/db/query/compiler/optimizer/index_bounds_builder/interval_evaluation_tree.cpp b/src/mongo/db/query/compiler/optimizer/index_bounds_builder/interval_evaluation_tree.cpp index d8e335a60de..0ab4492a93b 100644 --- a/src/mongo/db/query/compiler/optimizer/index_bounds_builder/interval_evaluation_tree.cpp +++ b/src/mongo/db/query/compiler/optimizer/index_bounds_builder/interval_evaluation_tree.cpp @@ -196,10 +196,12 @@ public: return algebra::walk(child, *this); }(); - invariant(node.index() < static_cast(childOil.intervals.size())); + tassert(11051920, + "Expect the node index to be less than the number of childOil intervals", + node.index() < static_cast(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; } diff --git a/src/mongo/db/query/compiler/parsers/matcher/expression_parser.cpp b/src/mongo/db/query/compiler/parsers/matcher/expression_parser.cpp index 0bca700e4ad..1fafaf9af1a 100644 --- a/src/mongo/db/query/compiler/parsers/matcher/expression_parser.cpp +++ b/src/mongo/db/query/compiler/parsers/matcher/expression_parser.cpp @@ -1336,7 +1336,9 @@ StatusWithMatchExpression parseGeo(boost::optional name, return {std::make_unique( 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 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); diff --git a/src/mongo/db/query/compiler/parsers/matcher/schema/json_schema_parser.cpp b/src/mongo/db/query/compiler/parsers/matcher/schema/json_schema_parser.cpp index a3c2308070c..dd3309f8186 100644 --- a/src/mongo/db/query/compiler/parsers/matcher/schema/json_schema_parser.cpp +++ b/src/mongo/db/query/compiler/parsers/matcher/schema/json_schema_parser.cpp @@ -148,7 +148,9 @@ std::unique_ptr makeRestriction( boost::optional path, std::unique_ptr 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& expCtx, boost::optional 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, diff --git a/src/mongo/db/query/compiler/physical_model/index_bounds/index_bounds.cpp b/src/mongo/db/query/compiler/physical_model/index_bounds/index_bounds.cpp index 59777678e87..2c2ffe69772 100644 --- a/src/mongo/db/query/compiler/physical_model/index_bounds/index_bounds.cpp +++ b/src/mongo/db/query/compiler/physical_model/index_bounds/index_bounds.cpp @@ -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; } diff --git a/src/mongo/db/query/compiler/physical_model/interval/interval.cpp b/src/mongo/db/query/compiler/physical_model/interval/interval.cpp index eb24766ecd5..fff8766430a 100644 --- a/src/mongo/db/query/compiler/physical_model/interval/interval.cpp +++ b/src/mongo/db/query/compiler/physical_model/interval/interval.cpp @@ -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(); diff --git a/src/mongo/db/query/compiler/physical_model/query_solution/query_solution.cpp b/src/mongo/db/query/compiler/physical_model/query_solution/query_solution.cpp index f08f88f391f..c9d5c7ed988 100644 --- a/src/mongo/db/query/compiler/physical_model/query_solution/query_solution.cpp +++ b/src/mongo/db/query/compiler/physical_model/query_solution/query_solution.cpp @@ -845,9 +845,10 @@ std::set IndexScanNode::getFieldsWithStringBounds(const IndexBounds& } std::set ret; - invariant(bounds.fields.size() == static_cast(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(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 diff --git a/src/mongo/db/query/compiler/rewrites/matcher/expression_optimizer.cpp b/src/mongo/db/query/compiler/rewrites/matcher/expression_optimizer.cpp index a57df0ea9a7..096278c9e19 100644 --- a/src/mongo/db/query/compiler/rewrites/matcher/expression_optimizer.cpp +++ b/src/mongo/db/query/compiler/rewrites/matcher/expression_optimizer.cpp @@ -684,7 +684,7 @@ std::unique_ptr inOptimizer(std::unique_ptr 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( expression->path(), childRe->getString(), childRe->getFlags()); diff --git a/src/mongo/db/query/compiler/rewrites/matcher/expression_parameterization.h b/src/mongo/db/query/compiler/rewrites/matcher/expression_parameterization.h index 365affa7336..d53462b2af2 100644 --- a/src/mongo/db/query/compiler/rewrites/matcher/expression_parameterization.h +++ b/src/mongo/db/query/compiler/rewrites/matcher/expression_parameterization.h @@ -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) { diff --git a/src/mongo/db/query/compiler/rewrites/matcher/rewrite_expr.cpp b/src/mongo/db/query/compiler/rewrites/matcher/rewrite_expr.cpp index 83c7efa1acf..c3ca5d0a43d 100644 --- a/src/mongo/db/query/compiler/rewrites/matcher/rewrite_expr.cpp +++ b/src/mongo/db/query/compiler/rewrites/matcher/rewrite_expr.cpp @@ -177,7 +177,7 @@ std::unique_ptr 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 RewriteExpr::_rewriteComparisonExpression( // Extract left-hand side and right-hand side MatchExpression components. if ((lhs = dynamic_cast(operandList[0].get()))) { rhs = dynamic_cast(operandList[1].get()); - invariant(rhs); + tassert(11051904, "Expect rhs operand to be of type ExpressionConstant", rhs); } else { lhs = dynamic_cast(operandList[1].get()); rhs = dynamic_cast(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 RewriteExpr::_rewriteInExpression( const boost::intrusive_ptr& 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(); diff --git a/src/mongo/db/query/compiler/stats/stats_cache.cpp b/src/mongo/db/query/compiler/stats/stats_cache.cpp index 2e57f4d1b26..2e49b0eee2c 100644 --- a/src/mongo/db/query/compiler/stats/stats_cache.cpp +++ b/src/mongo/db/query/compiler/stats/stats_cache.cpp @@ -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) { diff --git a/src/mongo/db/query/compiler/stats/stats_cache.h b/src/mongo/db/query/compiler/stats/stats_cache.h index 0418bf74088..bb36ef61534 100644 --- a/src/mongo/db/query/compiler/stats/stats_cache.h +++ b/src/mongo/db/query/compiler/stats/stats_cache.h @@ -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(); } diff --git a/src/mongo/db/query/compiler/stats/value_utils.cpp b/src/mongo/db/query/compiler/stats/value_utils.cpp index 31f0aa65f41..09b8b231ee4 100644 --- a/src/mongo/db/query/compiler/stats/value_utils.cpp +++ b/src/mongo/db/query/compiler/stats/value_utils.cpp @@ -573,7 +573,6 @@ absl::flat_hash_map 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;