/Users/deen/code/yugabyte-db/src/yb/docdb/value.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include "yb/docdb/value.h" |
15 | | |
16 | | #include <string> |
17 | | |
18 | | #include "yb/common/table_properties_constants.h" |
19 | | |
20 | | #include "yb/docdb/value_type.h" |
21 | | |
22 | | #include "yb/gutil/strings/substitute.h" |
23 | | |
24 | | #include "yb/util/fast_varint.h" |
25 | | #include "yb/util/kv_util.h" |
26 | | #include "yb/util/result.h" |
27 | | |
28 | | namespace yb { |
29 | | namespace docdb { |
30 | | |
31 | | using std::string; |
32 | | using strings::Substitute; |
33 | | |
34 | | const MonoDelta Value::kMaxTtl = yb::common::kMaxTtl; |
35 | | const MonoDelta Value::kResetTtl = MonoDelta::FromNanoseconds(0); |
36 | | const int64_t Value::kInvalidUserTimestamp = yb::common::kInvalidUserTimestamp; |
37 | | |
38 | | template <typename T> |
39 | | bool DecodeType(ValueType expected_value_type, const T& default_value, Slice* slice, |
40 | 1.00G | T* val) { |
41 | 1.00G | if (!slice->TryConsumeByte(static_cast<char>(expected_value_type))) { |
42 | 960M | *val = default_value; |
43 | 960M | return false; |
44 | 960M | } |
45 | | |
46 | 43.1M | return true; |
47 | 43.1M | } _ZN2yb5docdb10DecodeTypeIyEEbNS0_9ValueTypeERKT_PNS_5SliceEPS3_ Line | Count | Source | 40 | 250M | T* val) { | 41 | 250M | if (!slice->TryConsumeByte(static_cast<char>(expected_value_type))) { | 42 | 250M | *val = default_value; | 43 | 250M | return false; | 44 | 250M | } | 45 | | | 46 | 18.4E | return true; | 47 | 18.4E | } |
_ZN2yb5docdb10DecodeTypeINS_13DocHybridTimeEEEbNS0_9ValueTypeERKT_PNS_5SliceEPS4_ Line | Count | Source | 40 | 250M | T* val) { | 41 | 250M | if (!slice->TryConsumeByte(static_cast<char>(expected_value_type))) { | 42 | 207M | *val = default_value; | 43 | 207M | return false; | 44 | 207M | } | 45 | | | 46 | 43.3M | return true; | 47 | 43.3M | } |
_ZN2yb5docdb10DecodeTypeINS_9MonoDeltaEEEbNS0_9ValueTypeERKT_PNS_5SliceEPS4_ Line | Count | Source | 40 | 250M | T* val) { | 41 | 251M | if (!slice->TryConsumeByte(static_cast<char>(expected_value_type))) { | 42 | 251M | *val = default_value; | 43 | 251M | return false; | 44 | 251M | } | 45 | | | 46 | 18.4E | return true; | 47 | 18.4E | } |
_ZN2yb5docdb10DecodeTypeIxEEbNS0_9ValueTypeERKT_PNS_5SliceEPS3_ Line | Count | Source | 40 | 251M | T* val) { | 41 | 251M | if (!slice->TryConsumeByte(static_cast<char>(expected_value_type))) { | 42 | 251M | *val = default_value; | 43 | 251M | return false; | 44 | 251M | } | 45 | | | 46 | 18.4E | return true; | 47 | 18.4E | } |
|
48 | | |
49 | 250M | CHECKED_STATUS Value::DecodeMergeFlags(Slice* slice, uint64_t* merge_flags) { |
50 | 250M | if (DecodeType(ValueType::kMergeFlags, (uint64_t) 0, slice, merge_flags)) { |
51 | 162 | *merge_flags = VERIFY_RESULT(util::FastDecodeUnsignedVarInt(slice)); |
52 | 162 | } |
53 | 250M | return Status::OK(); |
54 | 250M | } |
55 | | |
56 | 0 | CHECKED_STATUS Value::DecodeMergeFlags(const rocksdb::Slice& slice, uint64_t* merge_flags) { |
57 | 0 | rocksdb::Slice value_copy = slice; |
58 | 0 | return DecodeMergeFlags(&value_copy, merge_flags); |
59 | 0 | } |
60 | | |
61 | 250M | CHECKED_STATUS DecodeIntentDocHT(Slice* slice, DocHybridTime* doc_ht) { |
62 | 250M | if (!DecodeType(ValueType::kHybridTime, DocHybridTime::kInvalid, slice, doc_ht)) { |
63 | 207M | return Status::OK(); |
64 | 207M | } |
65 | 43.0M | return doc_ht->DecodeFrom(slice); |
66 | 43.0M | } |
67 | | |
68 | 250M | Status Value::DecodeTTL(rocksdb::Slice* slice, MonoDelta* ttl) { |
69 | 250M | if (DecodeType(ValueType::kTtl, kMaxTtl, slice, ttl)) { |
70 | 1.15k | *ttl = MonoDelta::FromMilliseconds(VERIFY_RESULT(util::FastDecodeSignedVarIntUnsafe(slice))); |
71 | 1.15k | } |
72 | 250M | return Status::OK(); |
73 | 250M | } |
74 | | |
75 | 0 | Status Value::DecodeTTL(const rocksdb::Slice& rocksdb_value, MonoDelta* ttl) { |
76 | 0 | rocksdb::Slice value_copy = rocksdb_value; |
77 | 0 | uint64_t merge_flags; |
78 | 0 | RETURN_NOT_OK(DecodeMergeFlags(&value_copy, &merge_flags)); |
79 | 0 | return DecodeTTL(&value_copy, ttl); |
80 | 0 | } |
81 | | |
82 | | Status Value::DecodeUserTimestamp(const rocksdb::Slice& rocksdb_value, |
83 | 0 | UserTimeMicros* user_timestamp) { |
84 | 0 | MonoDelta ttl; |
85 | 0 | auto slice_copy = rocksdb_value; |
86 | 0 | RETURN_NOT_OK(DecodeTTL(&slice_copy, &ttl)); |
87 | 0 | return DecodeUserTimestamp(&slice_copy, user_timestamp); |
88 | 0 | } |
89 | | |
90 | 251M | Status Value::DecodeUserTimestamp(rocksdb::Slice* slice, UserTimeMicros* user_timestamp) { |
91 | 251M | if (DecodeType(ValueType::kUserTimestamp, kInvalidUserTimestamp, slice, |
92 | 315 | user_timestamp)) { |
93 | 315 | if (slice->size() < kBytesPerInt64) { |
94 | 0 | return STATUS(Corruption, Substitute( |
95 | 0 | "Failed to decode TTL from value, size too small: $1, need $2", |
96 | 0 | slice->size(), kBytesPerInt64)); |
97 | 0 | } |
98 | | |
99 | 315 | *user_timestamp = BigEndian::Load64(slice->data()); |
100 | 315 | slice->remove_prefix(kBytesPerInt64); |
101 | 315 | } |
102 | 251M | return Status::OK(); |
103 | 251M | } |
104 | | |
105 | 250M | Status Value::DecodeControlFields(Slice* slice) { |
106 | 250M | if (slice->empty()) { |
107 | 0 | return STATUS(Corruption, "Cannot decode a value from an empty slice"); |
108 | 0 | } |
109 | | |
110 | 250M | Slice original = *slice; |
111 | 250M | RETURN_NOT_OK_PREPEND( |
112 | 250M | DecodeMergeFlags(slice, &merge_flags_), |
113 | 250M | Format("Failed to decode merge flags in $0", original.ToDebugHexString())); |
114 | 250M | RETURN_NOT_OK_PREPEND( |
115 | 250M | DecodeIntentDocHT(slice, &intent_doc_ht_), |
116 | 250M | Format("Failed to decode intent ht in $0", original.ToDebugHexString())); |
117 | 250M | RETURN_NOT_OK_PREPEND( |
118 | 250M | DecodeTTL(slice, &ttl_), |
119 | 250M | Format("Failed to decode TTL in $0", original.ToDebugHexString())); |
120 | 250M | RETURN_NOT_OK_PREPEND( |
121 | 250M | DecodeUserTimestamp(slice, &user_timestamp_), |
122 | 250M | Format("Failed to decode user timestamp in $0", original.ToDebugHexString())); |
123 | 250M | return Status::OK(); |
124 | 250M | } |
125 | | |
126 | 250M | Status Value::Decode(const Slice& rocksdb_value) { |
127 | 250M | Slice slice = rocksdb_value; |
128 | 250M | RETURN_NOT_OK(DecodeControlFields(&slice)); |
129 | 250M | RETURN_NOT_OK_PREPEND( |
130 | 250M | primitive_value_.DecodeFromValue(slice), |
131 | 250M | Format("Failed to decode value in $0", rocksdb_value.ToDebugHexString())); |
132 | 250M | return Status::OK(); |
133 | 250M | } |
134 | | |
135 | 180k | std::string Value::ToString() const { |
136 | 180k | std::string result = primitive_value_.ToString(); |
137 | 180k | if (merge_flags_) { |
138 | 0 | result += Format("; merge flags: $0", merge_flags_); |
139 | 0 | } |
140 | 180k | if (intent_doc_ht_.is_valid()) { |
141 | 0 | result += Format("; intent doc ht: $0", intent_doc_ht_); |
142 | 0 | } |
143 | 180k | if (!ttl_.Equals(kMaxTtl)) { |
144 | 0 | result += Format("; ttl: $0", ttl_); |
145 | 0 | } |
146 | 180k | if (user_timestamp_ != kInvalidUserTimestamp) { |
147 | 0 | result += Format("; user timestamp: $0", user_timestamp_); |
148 | 0 | } |
149 | 180k | return result; |
150 | 180k | } |
151 | | |
152 | 0 | std::string Value::DebugSliceToString(const Slice& encoded_value) { |
153 | 0 | Value value; |
154 | 0 | auto status = value.Decode(encoded_value); |
155 | 0 | if (!status.ok()) { |
156 | 0 | return status.ToString(); |
157 | 0 | } |
158 | | |
159 | 0 | return value.ToString(); |
160 | 0 | } |
161 | | |
162 | 21.3M | std::string Value::Encode(const Slice* external_value) const { |
163 | 21.3M | std::string result; |
164 | 21.3M | EncodeAndAppend(&result, external_value); |
165 | 21.3M | return result; |
166 | 21.3M | } |
167 | | |
168 | 21.3M | void Value::EncodeAndAppend(std::string *value_bytes, const Slice* external_value) const { |
169 | 21.3M | if (merge_flags_) { |
170 | 18 | value_bytes->push_back(ValueTypeAsChar::kMergeFlags); |
171 | 18 | util::FastAppendUnsignedVarIntToStr(merge_flags_, value_bytes); |
172 | 18 | } |
173 | 21.3M | if (intent_doc_ht_.is_valid()) { |
174 | 0 | value_bytes->push_back(ValueTypeAsChar::kHybridTime); |
175 | 0 | intent_doc_ht_.AppendEncodedInDocDbFormat(value_bytes); |
176 | 0 | } |
177 | 21.3M | if (!ttl_.Equals(kMaxTtl)) { |
178 | 265 | value_bytes->push_back(ValueTypeAsChar::kTtl); |
179 | 265 | util::FastAppendSignedVarIntToBuffer(ttl_.ToMilliseconds(), value_bytes); |
180 | 265 | } |
181 | 21.3M | if (user_timestamp_ != kInvalidUserTimestamp) { |
182 | 138 | value_bytes->push_back(ValueTypeAsChar::kUserTimestamp); |
183 | 138 | util::AppendBigEndianUInt64(user_timestamp_, value_bytes); |
184 | 138 | } |
185 | 21.3M | if (!external_value) { |
186 | 21.3M | value_bytes->append(primitive_value_.ToValue()); |
187 | 18.4E | } else { |
188 | 18.4E | value_bytes->append(external_value->cdata(), external_value->size()); |
189 | 18.4E | } |
190 | 21.3M | } |
191 | | |
192 | | Status Value::DecodePrimitiveValueType( |
193 | | const rocksdb::Slice& rocksdb_value, |
194 | | ValueType* value_type, |
195 | | uint64_t* merge_flags, |
196 | | MonoDelta* ttl, |
197 | 170k | int64_t* user_ts) { |
198 | 170k | auto slice_copy = rocksdb_value; |
199 | 170k | uint64_t local_merge_flags; |
200 | 170k | DocHybridTime local_doc_ht; |
201 | 170k | MonoDelta local_ttl; |
202 | 170k | int64_t local_user_ts; |
203 | 170k | RETURN_NOT_OK(DecodeMergeFlags(&slice_copy, merge_flags ? merge_flags : &local_merge_flags)); |
204 | 170k | RETURN_NOT_OK(DecodeIntentDocHT(&slice_copy, &local_doc_ht)); |
205 | 170k | RETURN_NOT_OK(DecodeTTL(&slice_copy, ttl ? ttl : &local_ttl)); |
206 | 170k | RETURN_NOT_OK(DecodeUserTimestamp(&slice_copy, user_ts ? user_ts : &local_user_ts)); |
207 | 170k | *value_type = DecodeValueType(slice_copy); |
208 | 170k | return Status::OK(); |
209 | 170k | } |
210 | | |
211 | 42 | const Value& Value::Tombstone() { |
212 | 42 | static const auto kTombstone = Value(PrimitiveValue::kTombstone); |
213 | 42 | return kTombstone; |
214 | 42 | } |
215 | | |
216 | 0 | const string& Value::EncodedTombstone() { |
217 | 0 | static const string kEncodedTombstone = Tombstone().Encode(); |
218 | 0 | return kEncodedTombstone; |
219 | 0 | } |
220 | | |
221 | 0 | void Value::ClearIntentDocHt() { |
222 | 0 | intent_doc_ht_ = DocHybridTime::kInvalid; |
223 | 0 | } |
224 | | |
225 | 0 | Result<bool> Value::IsTombstoned(const Slice& slice) { |
226 | 0 | Value doc_value; |
227 | 0 | Slice value = slice; |
228 | 0 | RETURN_NOT_OK(doc_value.DecodeControlFields(&value)); |
229 | 0 | return value[0] == ValueTypeAsChar::kTombstone; |
230 | 0 | } |
231 | | |
232 | | } // namespace docdb |
233 | | } // namespace yb |