TEST(MatchExpressionParserTreeTest, NOT1) {
    BSONObj query = BSON("x" << BSON("$not" << BSON("$gt" << 5)));
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    StatusWithMatchExpression result = MatchExpressionParser::parse(query, expCtx);
    ASSERT_TRUE(result.isOK());

    ASSERT(result.getValue()->matchesBSON(BSON("x" << 2)));
    ASSERT(!result.getValue()->matchesBSON(BSON("x" << 8)));
}
TEST(InternalSchemaMatchArrayIndexMatchExpression, EquivalentToClone) {
    auto filter = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: {$type: 'number'}}}}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    auto expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    auto clone = expr.getValue()->shallowClone();
    ASSERT_TRUE(expr.getValue()->equivalent(clone.get()));
}
TEST(InternalSchemaMatchArrayIndexMatchExpression, HasSingleChild) {
    auto query = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: {$type: 'number'}}}}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    auto objMatch = MatchExpressionParser::parse(query, expCtx);
    ASSERT_OK(objMatch.getStatus());

    ASSERT_EQ(objMatch.getValue()->numChildren(), 1U);
    ASSERT(objMatch.getValue()->getChild(0));
}
TEST(InternalSchemaAllowedPropertiesMatchExpression, MatchesObjectsWithListedProperties) {
    auto filter = fromjson(
        "{$_internalSchemaAllowedProperties: {properties: ['a', 'b'],"
        "namePlaceholder: 'i', patternProperties: [], otherwise: {i: 0}}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    auto expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());

    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{a: 1, b: 1}")));
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{a: 1}")));
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{b: 1}")));
}
TEST(MatchExpressionParserTreeTest, OREmbedded) {
    BSONObj query1 = BSON("$or" << BSON_ARRAY(BSON("x" << 1) << BSON("y" << 2)));
    BSONObj query2 = BSON("$or" << BSON_ARRAY(query1));
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    StatusWithMatchExpression result = MatchExpressionParser::parse(query2, expCtx);
    ASSERT_TRUE(result.isOK());

    ASSERT(result.getValue()->matchesBSON(BSON("x" << 1)));
    ASSERT(result.getValue()->matchesBSON(BSON("y" << 2)));
    ASSERT(!result.getValue()->matchesBSON(BSON("x" << 3)));
    ASSERT(!result.getValue()->matchesBSON(BSON("y" << 1)));
}
TEST_F(UnsetNodeTest, ApplyCannotRemoveRequiredPartOfDBRef) {
    auto update = fromjson("{$unset: {'a.$id': true}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a.$id"], expCtx));

    mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}"));
    setPathTaken("a.$id");
    ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"]["$id"])),
                                AssertionException,
                                ErrorCodes::InvalidDBRef,
                                "The DBRef $ref field must be followed by a $id field");
}
TEST_F(UnsetNodeTest, ApplyCannotRemoveSuffixOfImmutablePath) {
    auto update = fromjson("{$unset: {'a.b.c': true}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a.b.c"], expCtx));

    mutablebson::Document doc(fromjson("{a: {b: {c: 1}}}"));
    setPathTaken("a.b.c");
    addImmutablePath("a.b");
    ASSERT_THROWS_CODE_AND_WHAT(
        node.apply(getApplyParams(doc.root()["a"]["b"]["c"])),
        AssertionException,
        ErrorCodes::ImmutableField,
        "Performing an update on the path 'a.b.c' would modify the immutable field 'a.b'");
}
Esempio n. 8
0
TEST_F(PopNodeTest, NoopWhenFirstPathComponentDoesNotExist) {
    auto update = fromjson("{$pop: {'a.b': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    PopNode popNode;
    ASSERT_OK(popNode.init(update["$pop"]["a.b"], expCtx));

    mmb::Document doc(fromjson("{b: [1, 2, 3]}"));
    setPathToCreate("a.b");
    addIndexedPath("a.b");
    auto result = popNode.apply(getApplyParams(doc.root()));
    ASSERT_TRUE(result.noop);
    ASSERT_FALSE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{b: [1, 2, 3]}"), doc);
    ASSERT_EQUALS(fromjson("{}"), getLogDoc());
}
TEST(MatchExpressionParserLeafTest, NotRegex1) {
    BSONObjBuilder b;
    b.appendRegex("$not", "abc", "i");
    BSONObj query = BSON("x" << b.obj());
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    StatusWithMatchExpression result = MatchExpressionParser::parse(query, expCtx);
    ASSERT_TRUE(result.isOK());

    ASSERT(!result.getValue()->matchesBSON(BSON("x"
                                                << "abc")));
    ASSERT(!result.getValue()->matchesBSON(BSON("x"
                                                << "ABC")));
    ASSERT(result.getValue()->matchesBSON(BSON("x"
                                               << "AC")));
}
Esempio n. 10
0
TEST_F(PopNodeTest, ThrowsWhenPathIsBlockedByAScalar) {
    auto update = fromjson("{$pop: {'a.b': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    PopNode popNode;
    ASSERT_OK(popNode.init(update["$pop"]["a.b"], expCtx));

    mmb::Document doc(fromjson("{a: 'foo'}"));
    setPathToCreate("b");
    setPathTaken("a");
    addIndexedPath("a.b");
    ASSERT_THROWS_CODE_AND_WHAT(
        popNode.apply(getApplyParams(doc.root()["a"])),
        AssertionException,
        ErrorCodes::PathNotViable,
        "Cannot use the part (b) of (a.b) to traverse the element ({a: \"foo\"})");
}
TEST(InternalSchemaMatchArrayIndexMatchExpression, DoesNotMatchArrayIfMatchingElementNotAtIndex) {
    auto filter = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: {$lte: 7}}}}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    auto expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{foo: [33, 0, 1, 2]}")));

    filter = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 1, namePlaceholder: 'i', expression: {i: {$lte: 7}}}}}");
    expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{foo: [0, 99, 1, 2]}")));
}
Esempio n. 12
0
TEST_F(PopNodeTest, NoopWhenPathPartiallyExists) {
    auto update = fromjson("{$pop: {'a.b.c': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    PopNode popNode;
    ASSERT_OK(popNode.init(update["$pop"]["a.b.c"], expCtx));

    mmb::Document doc(fromjson("{a: {}}"));
    setPathToCreate("b.c");
    setPathTaken("a");
    addIndexedPath("a.b.c");
    auto result = popNode.apply(getApplyParams(doc.root()["a"]));
    ASSERT_TRUE(result.noop);
    ASSERT_FALSE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{a: {}}"), doc);
    ASSERT_EQUALS(fromjson("{}"), getLogDoc());
}
Esempio n. 13
0
TEST_F(PopNodeTest, NoopWhenNumericalPathComponentExceedsArrayLength) {
    auto update = fromjson("{$pop: {'a.0': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    PopNode popNode;
    ASSERT_OK(popNode.init(update["$pop"]["a.0"], expCtx));

    mmb::Document doc(fromjson("{a: []}"));
    setPathToCreate("0");
    setPathTaken("a");
    addIndexedPath("a.0");
    auto result = popNode.apply(getApplyParams(doc.root()["a"]));
    ASSERT_TRUE(result.noop);
    ASSERT_FALSE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{a: []}"), doc);
    ASSERT_EQUALS(fromjson("{}"), getLogDoc());
}
TEST_F(UnsetNodeTest, ApplyNoIndexDataNoLogBuilder) {
    auto update = fromjson("{$unset: {a: 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a"], expCtx));

    mutablebson::Document doc(fromjson("{a: 5}"));
    setPathTaken("a");
    setLogBuilderToNull();
    auto result = node.apply(getApplyParams(doc.root()["a"]));
    ASSERT_FALSE(result.noop);
    ASSERT_FALSE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{}"), doc);
    ASSERT_FALSE(doc.isInPlaceModeEnabled());
    ASSERT_EQUALS(getModifiedPaths(), "{a}");
}
TEST(InternalSchemaMatchArrayIndexMatchExpression, MatchesIfNotEnoughArrayElements) {
    auto filter = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: 1}}}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    auto expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{foo: []}")));

    filter = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 4, namePlaceholder: 'i', expression: {i: 1}}}}");
    expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{foo: ['no', 'no', 'no', 'no']}")));
}
TEST_F(UnsetNodeTest, ApplyFieldWithDot) {
    auto update = fromjson("{$unset: {'a.b': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a.b"], expCtx));

    mutablebson::Document doc(fromjson("{'a.b':4, a: {b: 2}}"));
    setPathTaken("a.b");
    addIndexedPath("a");
    auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
    ASSERT_FALSE(result.noop);
    ASSERT_TRUE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{'a.b':4, a: {}}"), doc);
    ASSERT_FALSE(doc.isInPlaceModeEnabled());
    ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc());
    ASSERT_EQUALS(getModifiedPaths(), "{a.b}");
}
TEST_F(UnsetNodeTest, UnsetNoOpEmptyDoc) {
    auto update = fromjson("{$unset: {a: 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a"], expCtx));

    mutablebson::Document doc(fromjson("{}"));
    setPathToCreate("a");
    addIndexedPath("a");
    auto result = node.apply(getApplyParams(doc.root()));
    ASSERT_TRUE(result.noop);
    ASSERT_FALSE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{}"), doc);
    ASSERT_TRUE(doc.isInPlaceModeEnabled());
    ASSERT_EQUALS(fromjson("{}"), getLogDoc());
    ASSERT_EQUALS(getModifiedPaths(), "{a}");
}
TEST(InternalSchemaMatchArrayIndexMatchExpression, MatchesArraysWithMatchingElement) {
    auto filter = fromjson(
        "{foo: {$_internalSchemaMatchArrayIndex:"
        "{index: 0, namePlaceholder: 'i', expression: {i: {$elemMatch: {'bar': 7}}}}}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    auto expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{foo: [[{bar: 7}], [{bar: 5}]]}")));
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{foo: [[{bar: [3, 5, 7]}], [{bar: 5}]]}")));

    filter = fromjson(
        "{baz: {$_internalSchemaMatchArrayIndex:"
        "{index: 2, namePlaceholder: 'i', expression: {i: {$type: 'string'}}}}}");
    expr = MatchExpressionParser::parse(filter, expCtx);
    ASSERT_OK(expr.getStatus());
    ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{baz: [0, 1, '2']}")));
}
    ProjectionExecutor(BSONObj projSpec,
                       DefaultIdPolicy defaultIdPolicy,
                       ArrayRecursionPolicy arrayRecursionPolicy) {
        // Construct a dummy ExpressionContext for ParsedAggregationProjection. It's OK to set the
        // ExpressionContext's OperationContext and CollatorInterface to 'nullptr' here; since we
        // ban computed fields from the projection, the ExpressionContext will never be used.
        boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(nullptr, nullptr));

        // Create a ProjectionPolicies object, to be populated based on the passed arguments.
        ParsedAggregationProjection::ProjectionPolicies projectionPolicies;

        // Default projection behaviour is to include _id if the projection spec omits it. If the
        // caller has specified that we should *exclude* _id by default, do so here. We translate
        // DefaultIdPolicy to ProjectionPolicies::DefaultIdPolicy in order to avoid exposing
        // internal aggregation types to the query system.
        projectionPolicies.idPolicy =
            (defaultIdPolicy == ProjectionExecAgg::DefaultIdPolicy::kIncludeId
                 ? ProjectionPolicies::DefaultIdPolicy::kIncludeId
                 : ProjectionPolicies::DefaultIdPolicy::kExcludeId);

        // By default, $project will recurse through nested arrays. If the caller has specified that
        // it should not, we inhibit it from doing so here. We separate this class' internal enum
        // ArrayRecursionPolicy from ProjectionPolicies::ArrayRecursionPolicy in order to avoid
        // exposing aggregation types to the query system.
        projectionPolicies.arrayRecursionPolicy =
            (arrayRecursionPolicy == ArrayRecursionPolicy::kRecurseNestedArrays
                 ? ProjectionPolicies::ArrayRecursionPolicy::kRecurseNestedArrays
                 : ProjectionPolicies::ArrayRecursionPolicy::kDoNotRecurseNestedArrays);

        // Inclusion projections permit computed fields by default, so we must explicitly ban them.
        // Computed fields are implicitly banned for exclusions.
        projectionPolicies.computedFieldsPolicy =
            ProjectionPolicies::ComputedFieldsPolicy::kBanComputedFields;

        // Construct a ParsedAggregationProjection for the given projection spec and policies.
        _projection = ParsedAggregationProjection::create(expCtx, projSpec, projectionPolicies);

        // For an inclusion, record the exhaustive set of fields retained by the projection.
        if (getType() == ProjectionType::kInclusionProjection) {
            DepsTracker depsTracker;
            _projection->addDependencies(&depsTracker);
            for (auto&& field : depsTracker.fields)
                _exhaustivePaths.insert(FieldRef{field});
        }
    }
Esempio n. 20
0
/**
 * Updates roleGraph for an update-type oplog operation on admin.system.roles.
 *
 * Treats all updates as upserts.
 */
Status handleOplogUpdate(OperationContext* opCtx,
                         RoleGraph* roleGraph,
                         const BSONObj& updatePattern,
                         const BSONObj& queryPattern) {
    RoleName roleToUpdate;
    Status status = getRoleNameFromIdField(queryPattern["_id"], &roleToUpdate);
    if (!status.isOK())
        return status;

    boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(opCtx, nullptr));
    UpdateDriver driver(std::move(expCtx));
    driver.setFromOplogApplication(true);

    // Oplog updates do not have array filters.
    std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
    driver.parse(updatePattern, arrayFilters);

    mutablebson::Document roleDocument;
    status = RoleGraph::getBSONForRole(roleGraph, roleToUpdate, roleDocument.root());
    if (status == ErrorCodes::RoleNotFound) {
        // The query pattern will only contain _id, no other immutable fields are present
        const FieldRef idFieldRef("_id");
        FieldRefSet immutablePaths;
        invariant(immutablePaths.insert(&idFieldRef));
        status = driver.populateDocumentWithQueryFields(
            opCtx, queryPattern, immutablePaths, roleDocument);
    }
    if (!status.isOK())
        return status;

    const bool validateForStorage = false;
    const FieldRefSet emptyImmutablePaths;
    status = driver.update(StringData(), &roleDocument, validateForStorage, emptyImmutablePaths);
    if (!status.isOK())
        return status;

    // Now use the updated document to totally replace the role in the graph!
    RoleInfo role;
    status = parseRoleFromDocument(roleDocument.getObject(), &role);
    if (!status.isOK())
        return status;
    status = roleGraph->replaceRole(role.name, role.roles, role.privileges, role.restrictions);

    return status;
}
TEST_F(UnsetNodeTest, UnsetPositional) {
    auto update = fromjson("{$unset: {'a.$': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a.$"], expCtx));

    mutablebson::Document doc(fromjson("{a: [0, 1, 2]}"));
    setPathTaken("a.1");
    setMatchedField("1");
    addIndexedPath("a");
    auto result = node.apply(getApplyParams(doc.root()["a"][1]));
    ASSERT_FALSE(result.noop);
    ASSERT_TRUE(result.indexesAffected);
    ASSERT_EQUALS(fromjson("{a: [0, null, 2]}"), doc);
    ASSERT_FALSE(doc.isInPlaceModeEnabled());
    ASSERT_EQUALS(fromjson("{$unset: {'a.1': true}}"), getLogDoc());
    ASSERT_EQUALS(getModifiedPaths(), "{a.1}");
}
TEST_F(UnsetNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFalse) {
    auto update = fromjson("{$unset: {'a.$id': true}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    UnsetNode node;
    ASSERT_OK(node.init(update["$unset"]["a.$id"], expCtx));

    mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}"));
    setPathTaken("a.$id");
    addIndexedPath("a");
    setValidateForStorage(false);
    auto result = node.apply(getApplyParams(doc.root()["a"]["$id"]));
    ASSERT_FALSE(result.noop);
    ASSERT_TRUE(result.indexesAffected);
    auto updated = BSON("a" << BSON("$ref"
                                    << "c"));
    ASSERT_EQUALS(updated, doc);
    ASSERT_FALSE(doc.isInPlaceModeEnabled());
    ASSERT_EQUALS(fromjson("{$unset: {'a.$id': true}}"), getLogDoc());
    ASSERT_EQUALS(getModifiedPaths(), "{a.$id}");
}
Esempio n. 23
0
StatusWith<std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>>>
ParsedUpdate::parseArrayFilters(const std::vector<BSONObj>& rawArrayFiltersIn,
                                OperationContext* opCtx,
                                CollatorInterface* collator) {
    std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFiltersOut;
    for (auto rawArrayFilter : rawArrayFiltersIn) {
        boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(opCtx, collator));
        auto parsedArrayFilter =
            MatchExpressionParser::parse(rawArrayFilter,
                                         std::move(expCtx),
                                         ExtensionsCallbackNoop(),
                                         MatchExpressionParser::kBanAllSpecialFeatures);

        if (!parsedArrayFilter.isOK()) {
            return parsedArrayFilter.getStatus().withContext("Error parsing array filter");
        }
        auto parsedArrayFilterWithPlaceholder =
            ExpressionWithPlaceholder::make(std::move(parsedArrayFilter.getValue()));
        if (!parsedArrayFilterWithPlaceholder.isOK()) {
            return parsedArrayFilterWithPlaceholder.getStatus().withContext(
                "Error parsing array filter");
        }
        auto finalArrayFilter = std::move(parsedArrayFilterWithPlaceholder.getValue());
        auto fieldName = finalArrayFilter->getPlaceholder();
        if (!fieldName) {
            return Status(
                ErrorCodes::FailedToParse,
                "Cannot use an expression without a top-level field name in arrayFilters");
        }
        if (arrayFiltersOut.find(*fieldName) != arrayFiltersOut.end()) {
            return Status(ErrorCodes::FailedToParse,
                          str::stream()
                              << "Found multiple array filters with the same top-level field name "
                              << *fieldName);
        }

        arrayFiltersOut[*fieldName] = std::move(finalArrayFilter);
    }

    return std::move(arrayFiltersOut);
}
Status ParsedUpdate::parseArrayFilters() {
    for (auto rawArrayFilter : _request->getArrayFilters()) {
        boost::intrusive_ptr<ExpressionContext> expCtx(
            new ExpressionContext(_opCtx, _collator.get()));
        auto parsedArrayFilter =
            MatchExpressionParser::parse(rawArrayFilter,
                                         std::move(expCtx),
                                         ExtensionsCallbackNoop(),
                                         MatchExpressionParser::kBanAllSpecialFeatures);
        if (!parsedArrayFilter.isOK()) {
            return parsedArrayFilter.getStatus().withContext("Error parsing array filter");
        }
        auto parsedArrayFilterWithPlaceholder =
            ExpressionWithPlaceholder::make(std::move(parsedArrayFilter.getValue()));
        if (!parsedArrayFilterWithPlaceholder.isOK()) {
            return parsedArrayFilterWithPlaceholder.getStatus().withContext(
                "Error parsing array filter");
        }
        auto finalArrayFilter = std::move(parsedArrayFilterWithPlaceholder.getValue());
        auto fieldName = finalArrayFilter->getPlaceholder();
        if (!fieldName) {
            return Status(
                ErrorCodes::FailedToParse,
                "Cannot use an expression without a top-level field name in arrayFilters");
        }
        if (_arrayFilters.find(*fieldName) != _arrayFilters.end()) {
            return Status(ErrorCodes::FailedToParse,
                          str::stream()
                              << "Found multiple array filters with the same top-level field name "
                              << *fieldName);
        }

        _arrayFilters[*fieldName] = std::move(finalArrayFilter);
    }

    return Status::OK();
}
Esempio n. 25
0
void updateSessionEntry(OperationContext* opCtx, const UpdateRequest& updateRequest) {
    // Current code only supports replacement update.
    dassert(UpdateDriver::isDocReplacement(updateRequest.getUpdates()));

    AutoGetCollection autoColl(opCtx, NamespaceString::kSessionTransactionsTableNamespace, MODE_IX);

    uassert(40527,
            str::stream() << "Unable to persist transaction state because the session transaction "
                             "collection is missing. This indicates that the "
                          << NamespaceString::kSessionTransactionsTableNamespace.ns()
                          << " collection has been manually deleted.",
            autoColl.getCollection());

    WriteUnitOfWork wuow(opCtx);

    auto collection = autoColl.getCollection();
    auto idIndex = collection->getIndexCatalog()->findIdIndex(opCtx);

    uassert(40672,
            str::stream() << "Failed to fetch _id index for "
                          << NamespaceString::kSessionTransactionsTableNamespace.ns(),
            idIndex);

    auto indexAccess = collection->getIndexCatalog()->getIndex(idIndex);
    // Since we are looking up a key inside the _id index, create a key object consisting of only
    // the _id field.
    auto idToFetch = updateRequest.getQuery().firstElement();
    auto toUpdateIdDoc = idToFetch.wrap();
    dassert(idToFetch.fieldNameStringData() == "_id"_sd);
    auto recordId = indexAccess->findSingle(opCtx, toUpdateIdDoc);
    auto startingSnapshotId = opCtx->recoveryUnit()->getSnapshotId();

    if (recordId.isNull()) {
        // Upsert case.
        auto status = collection->insertDocument(
            opCtx, InsertStatement(updateRequest.getUpdates()), nullptr, true, false);

        if (status == ErrorCodes::DuplicateKey) {
            throw WriteConflictException();
        }

        uassertStatusOK(status);
        wuow.commit();
        return;
    }

    auto originalRecordData = collection->getRecordStore()->dataFor(opCtx, recordId);
    auto originalDoc = originalRecordData.toBson();

    invariant(collection->getDefaultCollator() == nullptr);
    boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(opCtx, nullptr));

    auto matcher = fassertStatusOK(
        40673, MatchExpressionParser::parse(updateRequest.getQuery(), std::move(expCtx)));
    if (!matcher->matchesBSON(originalDoc)) {
        // Document no longer match what we expect so throw WCE to make the caller re-examine.
        throw WriteConflictException();
    }

    OplogUpdateEntryArgs args;
    args.nss = NamespaceString::kSessionTransactionsTableNamespace;
    args.uuid = collection->uuid();
    args.update = updateRequest.getUpdates();
    args.criteria = toUpdateIdDoc;
    args.fromMigrate = false;

    collection->updateDocument(opCtx,
                               recordId,
                               Snapshotted<BSONObj>(startingSnapshotId, originalDoc),
                               updateRequest.getUpdates(),
                               true,   // enforceQuota
                               false,  // indexesAffected = false because _id is the only index
                               nullptr,
                               &args);

    wuow.commit();
}
Esempio n. 26
0
TEST(PopNodeTest, InitFailsBool) {
    auto update = fromjson("{$pop: {a: true}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    PopNode popNode;
    ASSERT_EQ(ErrorCodes::FailedToParse, popNode.init(update["$pop"]["a"], expCtx));
}