summaryrefslogtreecommitdiffstats
path: root/storage/src/vespa/storage/persistence/testandsethelper.cpp
blob: 00a48d83e3ab5fc7e3780554802bc3007bb217bc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// @author Vegard Sjonfjell

#include "testandsethelper.h"
#include "persistenceutil.h"
#include "fieldvisitor.h"
#include <vespa/persistence/spi/persistenceprovider.h>
#include <vespa/document/select/parser.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/vespalib/util/stringfmt.h>

using namespace std::string_literals;

namespace storage {

void TestAndSetHelper::resolveDocumentType(const document::DocumentTypeRepo & documentTypeRepo) {
    if (_docTypePtr != nullptr) return;
    if (!_docId.hasDocType()) {
        throw TestAndSetException(api::ReturnCode(api::ReturnCode::ILLEGAL_PARAMETERS, "Document id has no doctype"));
    }

    _docTypePtr = documentTypeRepo.getDocumentType(_docId.getDocType());
    if (_docTypePtr == nullptr) {
        throw TestAndSetException(api::ReturnCode(api::ReturnCode::ILLEGAL_PARAMETERS, "Document type does not exist"));
    }
}

void TestAndSetHelper::parseDocumentSelection(const document::DocumentTypeRepo & documentTypeRepo,
                                              const document::BucketIdFactory & bucketIdFactory) {
    document::select::Parser parser(documentTypeRepo, bucketIdFactory);

    try {
        _docSelectionUp = parser.parse(_cmd.getCondition().getSelection());
    } catch (const document::select::ParsingFailedException & e) {
        throw TestAndSetException(api::ReturnCode(api::ReturnCode::ILLEGAL_PARAMETERS, "Failed to parse test and set condition: "s + e.getMessage()));
    }
}

spi::GetResult TestAndSetHelper::retrieveDocument(const document::FieldSet & fieldSet, spi::Context & context) {
    return _spi.get(_env.getBucket(_docId, _cmd.getBucket()), fieldSet, _cmd.getDocumentId(), context);
}

TestAndSetHelper::TestAndSetHelper(const PersistenceUtil & env, const spi::PersistenceProvider  & spi,
                                   const document::BucketIdFactory & bucketFactory,
                                   const api::TestAndSetCommand & cmd, bool missingDocumentImpliesMatch)
    : _env(env),
      _spi(spi),
      _cmd(cmd),
      _docId(cmd.getDocumentId()),
      _docTypePtr(_cmd.getDocumentType()),
      _missingDocumentImpliesMatch(missingDocumentImpliesMatch)
{
    const auto & repo = _env.getDocumentTypeRepo();
    resolveDocumentType(repo);
    parseDocumentSelection(repo, bucketFactory);
}

TestAndSetHelper::~TestAndSetHelper() = default;

api::ReturnCode
TestAndSetHelper::retrieveAndMatch(spi::Context & context) {
    // Walk document selection tree to build a minimal field set 
    FieldVisitor fieldVisitor(*_docTypePtr);
    _docSelectionUp->visit(fieldVisitor);

    // Retrieve document
    auto result = retrieveDocument(fieldVisitor.getFieldSet(), context);

    // If document exists, match it with selection
    if (result.hasDocument()) {
        auto docPtr = result.getDocumentPtr();
        if (_docSelectionUp->contains(*docPtr) != document::select::Result::True) {
            return api::ReturnCode(api::ReturnCode::TEST_AND_SET_CONDITION_FAILED,
                                   vespalib::make_string("Condition did not match document nodeIndex=%d bucket=%" PRIx64 " %s",
                                                         _env._nodeIndex, _cmd.getBucketId().getRawId(),
                                                         _cmd.hasBeenRemapped() ? "remapped" : ""));
        }

        // Document matches
        return api::ReturnCode();
    } else if (_missingDocumentImpliesMatch) {
        return api::ReturnCode();
    }

    return api::ReturnCode(api::ReturnCode::TEST_AND_SET_CONDITION_FAILED,
                           vespalib::make_string("Document does not exist nodeIndex=%d bucket=%" PRIx64 " %s",
                                                 _env._nodeIndex, _cmd.getBucketId().getRawId(),
                                                 _cmd.hasBeenRemapped() ? "remapped" : ""));
}

} // storage