1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/db/pipeline/resume_token.h"
32
33 #include <boost/optional/optional_io.hpp>
34
35 #include "mongo/bson/bsonmisc.h"
36 #include "mongo/bson/bsonobjbuilder.h"
37 #include "mongo/db/pipeline/document_sources_gen.h"
38 #include "mongo/db/pipeline/value_comparator.h"
39 #include "mongo/db/storage/key_string.h"
40
41 namespace mongo {
42 constexpr StringData ResumeToken::kDataFieldName;
43 constexpr StringData ResumeToken::kTypeBitsFieldName;
44
operator ==(const ResumeTokenData & other) const45 bool ResumeTokenData::operator==(const ResumeTokenData& other) const {
46 return clusterTime == other.clusterTime &&
47 (Value::compare(this->documentKey, other.documentKey, nullptr) == 0) && uuid == other.uuid;
48 }
49
operator <<(std::ostream & out,const ResumeTokenData & tokenData)50 std::ostream& operator<<(std::ostream& out, const ResumeTokenData& tokenData) {
51 return out << "{clusterTime: " << tokenData.clusterTime.toString()
52 << " documentKey: " << tokenData.documentKey << " uuid: " << tokenData.uuid << "}";
53 }
54
ResumeToken(const Document & resumeDoc)55 ResumeToken::ResumeToken(const Document& resumeDoc) {
56 _keyStringData = resumeDoc[kDataFieldName];
57 _typeBits = resumeDoc[kTypeBitsFieldName];
58 uassert(40647,
59 str::stream() << "Bad resume token: _data of missing or of wrong type"
60 << resumeDoc.toString(),
61 _keyStringData.getType() == BinData &&
62 _keyStringData.getBinData().type == BinDataGeneral);
63 uassert(40648,
64 str::stream() << "Bad resume token: _typeBits of wrong type" << resumeDoc.toString(),
65 _typeBits.missing() ||
66 (_typeBits.getType() == BinData && _typeBits.getBinData().type == BinDataGeneral));
67 }
68
69 // We encode the resume token as a KeyString with the sequence: clusterTime, documentKey, uuid.
70 // Only the clusterTime is required.
ResumeToken(const ResumeTokenData & data)71 ResumeToken::ResumeToken(const ResumeTokenData& data) {
72 BSONObjBuilder builder;
73 builder.append("", data.clusterTime);
74 data.documentKey.addToBsonObj(&builder, "");
75 if (data.uuid) {
76 if (data.documentKey.missing()) {
77 // Never allow a missing document key with a UUID present, as that will mess up
78 // the field order.
79 builder.appendNull("");
80 }
81 data.uuid->appendToBuilder(&builder, "");
82 }
83 auto keyObj = builder.obj();
84 KeyString encodedToken(KeyString::Version::V1, keyObj, Ordering::make(BSONObj()));
85 _keyStringData = Value(
86 BSONBinData(encodedToken.getBuffer(), encodedToken.getSize(), BinDataType::BinDataGeneral));
87 const auto& typeBits = encodedToken.getTypeBits();
88 if (!typeBits.isAllZeros())
89 _typeBits = Value(
90 BSONBinData(typeBits.getBuffer(), typeBits.getSize(), BinDataType::BinDataGeneral));
91 }
92
getData() const93 ResumeTokenData ResumeToken::getData() const {
94 KeyString::TypeBits typeBits(KeyString::Version::V1);
95 if (!_typeBits.missing()) {
96 BSONBinData typeBitsBinData = _typeBits.getBinData();
97 BufReader typeBitsReader(typeBitsBinData.data, typeBitsBinData.length);
98 typeBits.resetFromBuffer(&typeBitsReader);
99 }
100
101 BSONBinData keyStringBinData = _keyStringData.getBinData();
102 auto internalBson = KeyString::toBsonSafe(static_cast<const char*>(keyStringBinData.data),
103 keyStringBinData.length,
104 Ordering::make(BSONObj()),
105 typeBits);
106
107 BSONObjIterator i(internalBson);
108 ResumeTokenData result;
109 uassert(40649, "invalid empty resume token", i.more());
110 result.clusterTime = i.next().timestamp();
111 if (i.more())
112 result.documentKey = Value(i.next());
113 if (i.more())
114 result.uuid = uassertStatusOK(UUID::parse(i.next()));
115 uassert(40646, "invalid oversized resume token", !i.more());
116 return result;
117 }
118
compare(const ResumeToken & other) const119 int ResumeToken::compare(const ResumeToken& other) const {
120 BSONBinData thisData = _keyStringData.getBinData();
121 BSONBinData otherData = other._keyStringData.getBinData();
122 return StringData(static_cast<const char*>(thisData.data), thisData.length)
123 .compare(StringData(static_cast<const char*>(otherData.data), otherData.length));
124 }
125
operator ==(const ResumeToken & other) const126 bool ResumeToken::operator==(const ResumeToken& other) const {
127 return compare(other) == 0;
128 }
129
operator !=(const ResumeToken & other) const130 bool ResumeToken::operator!=(const ResumeToken& other) const {
131 return compare(other) != 0;
132 }
133
operator <(const ResumeToken & other) const134 bool ResumeToken::operator<(const ResumeToken& other) const {
135 return compare(other) < 0;
136 }
137
operator <=(const ResumeToken & other) const138 bool ResumeToken::operator<=(const ResumeToken& other) const {
139 return compare(other) <= 0;
140 }
141
operator >(const ResumeToken & other) const142 bool ResumeToken::operator>(const ResumeToken& other) const {
143 return compare(other) > 0;
144 }
145
operator >=(const ResumeToken & other) const146 bool ResumeToken::operator>=(const ResumeToken& other) const {
147 return compare(other) >= 0;
148 }
149
toDocument() const150 Document ResumeToken::toDocument() const {
151 return Document{{kDataFieldName, _keyStringData}, {kTypeBitsFieldName, _typeBits}};
152 }
153
parse(const Document & resumeDoc)154 ResumeToken ResumeToken::parse(const Document& resumeDoc) {
155 return ResumeToken(resumeDoc);
156 }
157
158 } // namespace mongo
159