/Users/deen/code/yugabyte-db/src/yb/docdb/docrowwiseiterator-test.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 <memory> |
15 | | #include <string> |
16 | | |
17 | | #include "yb/common/common.pb.h" |
18 | | #include "yb/common/ql_expr.h" |
19 | | #include "yb/common/ql_value.h" |
20 | | #include "yb/common/read_hybrid_time.h" |
21 | | #include "yb/common/transaction-test-util.h" |
22 | | |
23 | | #include "yb/docdb/doc_key.h" |
24 | | #include "yb/docdb/doc_rowwise_iterator.h" |
25 | | #include "yb/docdb/docdb.h" |
26 | | #include "yb/docdb/docdb_rocksdb_util.h" |
27 | | #include "yb/docdb/docdb_test_base.h" |
28 | | #include "yb/docdb/docdb_test_util.h" |
29 | | |
30 | | #include "yb/server/hybrid_clock.h" |
31 | | |
32 | | #include "yb/util/size_literals.h" |
33 | | #include "yb/util/test_macros.h" |
34 | | #include "yb/util/test_util.h" |
35 | | |
36 | | DECLARE_bool(TEST_docdb_sort_weak_intents); |
37 | | |
38 | | namespace yb { |
39 | | namespace docdb { |
40 | | |
41 | | class DocRowwiseIteratorTest : public DocDBTestBase { |
42 | | protected: |
43 | 0 | DocRowwiseIteratorTest() { |
44 | 0 | SeedRandom(); |
45 | 0 | } |
46 | 0 | ~DocRowwiseIteratorTest() override {} |
47 | | |
48 | | // TODO Could define them out of class, so one line would be enough for them. |
49 | | static const KeyBytes kEncodedDocKey1; |
50 | | static const KeyBytes kEncodedDocKey2; |
51 | | static const Schema kSchemaForIteratorTests; |
52 | | static Schema kProjectionForIteratorTests; |
53 | | |
54 | 0 | void SetUp() override { |
55 | 0 | FLAGS_TEST_docdb_sort_weak_intents = true; |
56 | 0 | DocDBTestBase::SetUp(); |
57 | 0 | } |
58 | | |
59 | 0 | static void SetUpTestCase() { |
60 | 0 | ASSERT_OK(kSchemaForIteratorTests.CreateProjectionByNames({"c", "d", "e"}, |
61 | 0 | &kProjectionForIteratorTests)); |
62 | 0 | } |
63 | | }; |
64 | | |
65 | | const KeyBytes DocRowwiseIteratorTest::kEncodedDocKey1( |
66 | | DocKey(PrimitiveValues("row1", 11111)).Encode()); |
67 | | |
68 | | const KeyBytes DocRowwiseIteratorTest::kEncodedDocKey2( |
69 | | DocKey(PrimitiveValues("row2", 22222)).Encode()); |
70 | | |
71 | | const Schema DocRowwiseIteratorTest::kSchemaForIteratorTests({ |
72 | | ColumnSchema("a", DataType::STRING, /* is_nullable = */ false), |
73 | | ColumnSchema("b", DataType::INT64, false), |
74 | | // Non-key columns |
75 | | ColumnSchema("c", DataType::STRING, true), |
76 | | ColumnSchema("d", DataType::INT64, true), |
77 | | ColumnSchema("e", DataType::STRING, true) |
78 | | }, { |
79 | | 10_ColId, |
80 | | 20_ColId, |
81 | | 30_ColId, |
82 | | 40_ColId, |
83 | | 50_ColId |
84 | | }, 2); |
85 | | |
86 | | Schema DocRowwiseIteratorTest::kProjectionForIteratorTests; |
87 | | |
88 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorTest) { |
89 | | // Row 1 |
90 | | // We don't need any seeks for writes, where column values are primitives. |
91 | 0 | ASSERT_OK(SetPrimitive( |
92 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
93 | 0 | PrimitiveValue("row1_c"), HybridTime::FromMicros(1000))); |
94 | 0 | ASSERT_OK(SetPrimitive( |
95 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
96 | 0 | PrimitiveValue(10000), HybridTime::FromMicros(1000))); |
97 | 0 | ASSERT_OK(SetPrimitive( |
98 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
99 | 0 | PrimitiveValue("row1_e"), HybridTime::FromMicros(1000))); |
100 | | |
101 | | // Row 2: one null column, one column that gets deleted and overwritten, another that just gets |
102 | | // overwritten. No seeks needed for writes. |
103 | 0 | ASSERT_OK(SetPrimitive( |
104 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
105 | 0 | PrimitiveValue(20000), HybridTime::FromMicros(2000))); |
106 | | |
107 | | // Deletions normally perform a lookup of the key to see whether it's already there. We will use |
108 | | // that to provide the expected result (the number of rows deleted in SQL or whether a key was |
109 | | // deleted in Redis). However, because we've just set a value at this path, we don't expect to |
110 | | // perform any reads for this deletion. |
111 | 0 | ASSERT_OK(DeleteSubDoc( |
112 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
113 | 0 | HybridTime::FromMicros(2500))); |
114 | | |
115 | | // The entire subdocument under DocPath(encoded_doc_key2, 40) just got deleted, and that fact |
116 | | // should still be in the write batch's cache, so we should not perform a seek to overwrite it. |
117 | 0 | ASSERT_OK(SetPrimitive( |
118 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
119 | 0 | PrimitiveValue(30000), HybridTime::FromMicros(3000))); |
120 | 0 | ASSERT_OK(SetPrimitive( |
121 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
122 | 0 | PrimitiveValue("row2_e"), HybridTime::FromMicros(2000))); |
123 | |
|
124 | 0 | ASSERT_OK(SetPrimitive( |
125 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
126 | 0 | PrimitiveValue("row2_e_prime"), HybridTime::FromMicros(4000))); |
127 | |
|
128 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
129 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30); HT{ physical: 1000 }]) -> "row1_c" |
130 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
131 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 1000 }]) -> "row1_e" |
132 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 3000 }]) -> 30000 |
133 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2500 }]) -> DEL |
134 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2000 }]) -> 20000 |
135 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50); HT{ physical: 4000 }]) -> "row2_e_prime" |
136 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50); HT{ physical: 2000 }]) -> "row2_e" |
137 | 0 | )#"); |
138 | |
|
139 | 0 | const Schema &schema = kSchemaForIteratorTests; |
140 | 0 | const Schema &projection = kProjectionForIteratorTests; |
141 | 0 | QLTableRow row; |
142 | 0 | QLValue value; |
143 | |
|
144 | 0 | { |
145 | 0 | DocRowwiseIterator iter( |
146 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
147 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2000)); |
148 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
149 | |
|
150 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
151 | 0 | ASSERT_OK(iter.NextRow(&row)); |
152 | |
|
153 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
154 | 0 | ASSERT_FALSE(value.IsNull()); |
155 | 0 | ASSERT_EQ("row1_c", value.string_value()); |
156 | |
|
157 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
158 | 0 | ASSERT_FALSE(value.IsNull()); |
159 | 0 | ASSERT_EQ(10000, value.int64_value()); |
160 | |
|
161 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
162 | 0 | ASSERT_FALSE(value.IsNull()); |
163 | 0 | ASSERT_EQ("row1_e", value.string_value()); |
164 | |
|
165 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
166 | 0 | ASSERT_OK(iter.NextRow(&row)); |
167 | |
|
168 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
169 | 0 | ASSERT_TRUE(value.IsNull()); |
170 | |
|
171 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
172 | 0 | ASSERT_FALSE(value.IsNull()); |
173 | 0 | ASSERT_EQ(20000, value.int64_value()); |
174 | |
|
175 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
176 | 0 | ASSERT_FALSE(value.IsNull()); |
177 | 0 | ASSERT_EQ("row2_e", value.string_value()); |
178 | |
|
179 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
180 | 0 | } |
181 | | |
182 | | // Scan at a later hybrid_time. |
183 | |
|
184 | 0 | { |
185 | 0 | DocRowwiseIterator iter( |
186 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
187 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(5000)); |
188 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
189 | |
|
190 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
191 | 0 | ASSERT_OK(iter.NextRow(&row)); |
192 | | |
193 | | // This row is exactly the same as in the previous case. TODO: deduplicate. |
194 | |
|
195 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
196 | 0 | ASSERT_FALSE(value.IsNull()); |
197 | 0 | ASSERT_EQ("row1_c", value.string_value()); |
198 | |
|
199 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
200 | 0 | ASSERT_FALSE(value.IsNull()); |
201 | 0 | ASSERT_EQ(10000, value.int64_value()); |
202 | |
|
203 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
204 | 0 | ASSERT_FALSE(value.IsNull()); |
205 | 0 | ASSERT_EQ("row1_e", value.string_value()); |
206 | |
|
207 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
208 | 0 | ASSERT_OK(iter.NextRow(&row)); |
209 | |
|
210 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
211 | 0 | ASSERT_TRUE(value.IsNull()); |
212 | |
|
213 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
214 | 0 | ASSERT_FALSE(value.IsNull()); |
215 | | |
216 | | // These two rows have different values compared to the previous case. |
217 | 0 | ASSERT_EQ(30000, value.int64_value()); |
218 | |
|
219 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
220 | 0 | ASSERT_FALSE(value.IsNull()); |
221 | 0 | ASSERT_EQ("row2_e_prime", value.string_value()); |
222 | |
|
223 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorDeletedDocumentTest) { |
228 | 0 | ASSERT_OK(SetPrimitive( |
229 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
230 | 0 | PrimitiveValue("row1_c"), HybridTime::FromMicros(1000))); |
231 | 0 | ASSERT_OK(SetPrimitive( |
232 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
233 | 0 | PrimitiveValue(10000), HybridTime::FromMicros(1000))); |
234 | 0 | ASSERT_OK(SetPrimitive( |
235 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
236 | 0 | PrimitiveValue("row1_e"), HybridTime::FromMicros(1000))); |
237 | 0 | ASSERT_OK(SetPrimitive( |
238 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
239 | 0 | PrimitiveValue(20000), HybridTime::FromMicros(2000))); |
240 | | |
241 | | // Delete entire row1 document to test that iterator can successfully jump to next document |
242 | | // when it finds deleted document. |
243 | 0 | ASSERT_OK(DeleteSubDoc( |
244 | 0 | DocPath(kEncodedDocKey1), HybridTime::FromMicros(2500))); |
245 | |
|
246 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
247 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 2500 }]) -> DEL |
248 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30); HT{ physical: 1000 }]) -> "row1_c" |
249 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
250 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 1000 }]) -> "row1_e" |
251 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2000 }]) -> 20000 |
252 | 0 | )#"); |
253 | |
|
254 | 0 | const Schema &schema = kSchemaForIteratorTests; |
255 | 0 | const Schema &projection = kProjectionForIteratorTests; |
256 | |
|
257 | 0 | { |
258 | 0 | DocRowwiseIterator iter( |
259 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
260 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2500)); |
261 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
262 | |
|
263 | 0 | QLTableRow row; |
264 | 0 | QLValue value; |
265 | |
|
266 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
267 | 0 | ASSERT_OK(iter.NextRow(&row)); |
268 | |
|
269 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
270 | 0 | ASSERT_TRUE(value.IsNull()); |
271 | |
|
272 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
273 | 0 | ASSERT_FALSE(value.IsNull()); |
274 | 0 | ASSERT_EQ(20000, value.int64_value()); |
275 | |
|
276 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
277 | 0 | ASSERT_TRUE(value.IsNull()); |
278 | |
|
279 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorTestRowDeletes) { |
284 | 0 | auto dwb = MakeDocWriteBatch(); |
285 | |
|
286 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
287 | 0 | PrimitiveValue("row1_c"))); |
288 | |
|
289 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
290 | 0 | PrimitiveValue(10000))); |
291 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(1000))); |
292 | |
|
293 | 0 | ASSERT_OK(dwb.DeleteSubDoc(DocPath(kEncodedDocKey1))); |
294 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2500))); |
295 | |
|
296 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
297 | 0 | PrimitiveValue("row1_e"))); |
298 | |
|
299 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
300 | 0 | PrimitiveValue(20000))); |
301 | 0 | ASSERT_OK(WriteToRocksDB(dwb, HybridTime::FromMicros(2800))); |
302 | |
|
303 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
304 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 2500 }]) -> DEL |
305 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30); HT{ physical: 1000 }]) -> "row1_c" |
306 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 w: 1 }]) -> 10000 |
307 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 2800 }]) -> "row1_e" |
308 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2800 w: 1 }]) -> 20000 |
309 | 0 | )#"); |
310 | |
|
311 | 0 | const Schema &schema = kSchemaForIteratorTests; |
312 | 0 | const Schema &projection = kProjectionForIteratorTests; |
313 | |
|
314 | 0 | { |
315 | 0 | DocRowwiseIterator iter( |
316 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
317 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2800)); |
318 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
319 | |
|
320 | 0 | QLTableRow row; |
321 | 0 | QLValue value; |
322 | |
|
323 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
324 | 0 | ASSERT_OK(iter.NextRow(&row)); |
325 | | |
326 | | // ColumnId 30, 40 should be hidden whereas ColumnId 50 should be visible. |
327 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
328 | 0 | ASSERT_TRUE(value.IsNull()); |
329 | |
|
330 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
331 | 0 | ASSERT_TRUE(value.IsNull()); |
332 | |
|
333 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
334 | 0 | ASSERT_FALSE(value.IsNull()); |
335 | 0 | ASSERT_EQ("row1_e", value.string_value()); |
336 | |
|
337 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
338 | 0 | ASSERT_OK(iter.NextRow(&row)); |
339 | |
|
340 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
341 | 0 | ASSERT_TRUE(value.IsNull()); |
342 | |
|
343 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
344 | 0 | ASSERT_FALSE(value.IsNull()); |
345 | 0 | ASSERT_EQ(20000, value.int64_value()); |
346 | |
|
347 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
348 | 0 | ASSERT_TRUE(value.IsNull()); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | void VerifyOldestRecordTime(IntentAwareIterator *iter, const DocKey &doc_key, |
353 | | const SubDocKey &subkey, HybridTime min_hybrid_time, |
354 | 0 | HybridTime expected_oldest_record_time) { |
355 | 0 | iter->Seek(doc_key); |
356 | 0 | const KeyBytes subkey_bytes = subkey.EncodeWithoutHt(); |
357 | 0 | const Slice subkey_slice = subkey_bytes.AsSlice(); |
358 | 0 | Slice read_value; |
359 | 0 | HybridTime oldest_past_min_ht = |
360 | 0 | ASSERT_RESULT(iter->FindOldestRecord(subkey_slice, min_hybrid_time)); |
361 | 0 | LOG(INFO) << "iter->FindOldestRecord returned " << oldest_past_min_ht |
362 | 0 | << " for " << SubDocKey::DebugSliceToString(subkey_slice); |
363 | 0 | ASSERT_EQ(oldest_past_min_ht, expected_oldest_record_time); |
364 | 0 | } |
365 | | |
366 | | void VerifyOldestRecordTime(IntentAwareIterator *iter, const DocKey &doc_key, |
367 | | const SubDocKey &subkey, uint64_t min_hybrid_time, |
368 | 0 | uint64_t expected_oldest_record_time) { |
369 | 0 | VerifyOldestRecordTime(iter, doc_key, subkey, |
370 | 0 | HybridTime::FromMicros(min_hybrid_time), |
371 | 0 | HybridTime::FromMicros(expected_oldest_record_time)); |
372 | 0 | } |
373 | | |
374 | | void VerifyOldestRecordTimeIsInvalid(IntentAwareIterator *iter, |
375 | | const DocKey &doc_key, |
376 | | const SubDocKey &subkey, |
377 | 0 | uint64_t min_hybrid_time) { |
378 | 0 | VerifyOldestRecordTime(iter, doc_key, subkey, |
379 | 0 | HybridTime::FromMicros(min_hybrid_time), |
380 | 0 | HybridTime::kInvalid); |
381 | 0 | } |
382 | | |
383 | 0 | TEST_F(DocRowwiseIteratorTest, BackfillInsert) { |
384 | 0 | ASSERT_OK(DeleteSubDoc(DocPath(kEncodedDocKey1), 5000_usec_ht)); |
385 | 0 | ASSERT_OK(SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
386 | 0 | PrimitiveValue(10000), 1000_usec_ht)); |
387 | |
|
388 | 0 | ASSERT_OK(SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
389 | 0 | PrimitiveValue("row1_e"), 1000_usec_ht)); |
390 | |
|
391 | 0 | ASSERT_OK(SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
392 | 0 | PrimitiveValue(10000), 900_usec_ht)); |
393 | |
|
394 | 0 | ASSERT_OK(SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
395 | 0 | PrimitiveValue("row1_e"), 900_usec_ht)); |
396 | |
|
397 | 0 | ASSERT_OK(DeleteSubDoc(DocPath(kEncodedDocKey1), 500_usec_ht)); |
398 | 0 | ASSERT_OK(SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
399 | 0 | PrimitiveValue(10000), 300_usec_ht)); |
400 | |
|
401 | 0 | ASSERT_OK(SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
402 | 0 | PrimitiveValue("row1_e"), 300_usec_ht)); |
403 | |
|
404 | 0 | ASSERT_OK(DeleteSubDoc(DocPath(kEncodedDocKey2), 900_usec_ht)); |
405 | 0 | ASSERT_OK(DeleteSubDoc(DocPath(kEncodedDocKey2), 700_usec_ht)); |
406 | |
|
407 | 0 | SetTransactionIsolationLevel(IsolationLevel::SNAPSHOT_ISOLATION); |
408 | 0 | Result<TransactionId> txn1 = FullyDecodeTransactionId("0000000000000001"); |
409 | 0 | ASSERT_OK(txn1); |
410 | 0 | SetCurrentTransactionId(*txn1); |
411 | 0 | ASSERT_OK(DeleteSubDoc(DocPath(kEncodedDocKey2), 800_usec_ht)); |
412 | |
|
413 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
414 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 5000 }]) -> DEL |
415 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 500 }]) -> DEL |
416 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
417 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 900 }]) -> 10000 |
418 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 300 }]) -> 10000 |
419 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 1000 }]) -> "row1_e" |
420 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 900 }]) -> "row1_e" |
421 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 300 }]) -> "row1_e" |
422 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [HT{ physical: 900 }]) -> DEL |
423 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [HT{ physical: 700 }]) -> DEL |
424 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 800 w: 1 } -> \ |
425 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
426 | 0 | SubDocKey(DocKey([], ["row2"]), []) [kWeakRead, kWeakWrite] HT{ physical: 800 w: 2 } -> \ |
427 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
428 | 0 | SubDocKey(DocKey([], ["row2", 22222]), []) [kStrongRead, kStrongWrite] HT{ physical: 800 } -> \ |
429 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(0) DEL |
430 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 800 } -> \ |
431 | 0 | SubDocKey(DocKey([], ["row2", 22222]), []) [kStrongRead, kStrongWrite] HT{ physical: 800 } |
432 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 800 w: 1 } -> \ |
433 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 800 w: 1 } |
434 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 800 w: 2 } -> \ |
435 | 0 | SubDocKey(DocKey([], ["row2"]), []) [kWeakRead, kWeakWrite] HT{ physical: 800 w: 2 } |
436 | 0 | )#"); |
437 | |
|
438 | 0 | TransactionStatusManagerMock myTransactionalOperationContext; |
439 | 0 | const TransactionOperationContext kMockTransactionalOperationContext = { |
440 | 0 | TransactionId::GenerateRandom(), &myTransactionalOperationContext}; |
441 | 0 | myTransactionalOperationContext.Commit(*txn1, 800_usec_ht); |
442 | |
|
443 | 0 | const HybridTime kSafeTime = 50000_usec_ht; |
444 | 0 | { |
445 | 0 | DocKey doc_key(PrimitiveValues("row1", 11111)); |
446 | 0 | const KeyBytes doc_key_bytes = doc_key.Encode(); |
447 | 0 | boost::optional<const yb::Slice> doc_key_optional(doc_key_bytes.AsSlice()); |
448 | 0 | auto iter = CreateIntentAwareIterator( |
449 | 0 | doc_db(), BloomFilterMode::USE_BLOOM_FILTER, doc_key_optional, |
450 | 0 | rocksdb::kDefaultQueryId, kMockTransactionalOperationContext, |
451 | 0 | CoarseTimePoint::max(), ReadHybridTime::SingleTime(kSafeTime)); |
452 | |
|
453 | 0 | { |
454 | 0 | SubDocKey subkey(doc_key); |
455 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 499, 500); |
456 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 500, 5000); |
457 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 501, 5000); |
458 | |
|
459 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 4999, 5000); |
460 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 5000); |
461 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 5001); |
462 | 0 | } |
463 | |
|
464 | 0 | { |
465 | 0 | SubDocKey subkey(doc_key, PrimitiveValue(40_ColId)); |
466 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 299, 300); |
467 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 300, 900); |
468 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 301, 900); |
469 | |
|
470 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 500, 900); |
471 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 600, 900); |
472 | |
|
473 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 899, 900); |
474 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 900, 1000); |
475 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 901, 1000); |
476 | |
|
477 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 999, 1000); |
478 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 1000); |
479 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 1001); |
480 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 40000); |
481 | 0 | } |
482 | 0 | } |
483 | |
|
484 | 0 | { |
485 | 0 | DocKey doc_key(PrimitiveValues("row2", 22222)); |
486 | 0 | const KeyBytes doc_key_bytes = doc_key.Encode(); |
487 | 0 | boost::optional<const yb::Slice> doc_key_optional(doc_key_bytes.AsSlice()); |
488 | 0 | auto iter = CreateIntentAwareIterator( |
489 | 0 | doc_db(), BloomFilterMode::USE_BLOOM_FILTER, doc_key_optional, |
490 | 0 | rocksdb::kDefaultQueryId, kMockTransactionalOperationContext, |
491 | 0 | CoarseTimePoint::max(), ReadHybridTime::SingleTime(kSafeTime)); |
492 | |
|
493 | 0 | { |
494 | 0 | SubDocKey subkey(doc_key); |
495 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 400, 700); |
496 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 699, 700); |
497 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 700, 800); |
498 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 701, 800); |
499 | |
|
500 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 750, 800); |
501 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 800, 900); |
502 | 0 | VerifyOldestRecordTime(iter.get(), doc_key, subkey, 801, 900); |
503 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 900); |
504 | 0 | VerifyOldestRecordTimeIsInvalid(iter.get(), doc_key, subkey, 1000); |
505 | 0 | } |
506 | 0 | } |
507 | 0 | } |
508 | | |
509 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorHasNextIdempotence) { |
510 | 0 | ASSERT_OK(SetPrimitive( |
511 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
512 | 0 | PrimitiveValue(10000), HybridTime::FromMicros(1000))); |
513 | |
|
514 | 0 | ASSERT_OK(SetPrimitive( |
515 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
516 | 0 | PrimitiveValue("row1_e"), HybridTime::FromMicros(2800))); |
517 | |
|
518 | 0 | ASSERT_OK(DeleteSubDoc(DocPath(kEncodedDocKey1), HybridTime::FromMicros(2500))); |
519 | |
|
520 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
521 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 2500 }]) -> DEL |
522 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
523 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 2800 }]) -> "row1_e" |
524 | 0 | )#"); |
525 | |
|
526 | 0 | const Schema &schema = kSchemaForIteratorTests; |
527 | 0 | const Schema &projection = kProjectionForIteratorTests; |
528 | |
|
529 | 0 | { |
530 | 0 | DocRowwiseIterator iter( |
531 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
532 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2800)); |
533 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
534 | |
|
535 | 0 | QLTableRow row; |
536 | 0 | QLValue value; |
537 | |
|
538 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
539 | | // Ensure calling HasNext() again doesn't mess up anything. |
540 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
541 | 0 | ASSERT_OK(iter.NextRow(&row)); |
542 | | |
543 | | // ColumnId 40 should be deleted whereas ColumnId 50 should be visible. |
544 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
545 | 0 | ASSERT_TRUE(value.IsNull()); |
546 | |
|
547 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
548 | 0 | ASSERT_TRUE(value.IsNull()); |
549 | |
|
550 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
551 | 0 | ASSERT_FALSE(value.IsNull()); |
552 | 0 | ASSERT_EQ("row1_e", value.string_value()); |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorIncompleteProjection) { |
557 | 0 | auto dwb = MakeDocWriteBatch(); |
558 | |
|
559 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
560 | 0 | PrimitiveValue(10000))); |
561 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
562 | 0 | PrimitiveValue("row1_e"))); |
563 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
564 | 0 | PrimitiveValue(20000))); |
565 | |
|
566 | 0 | ASSERT_OK(WriteToRocksDB(dwb, HybridTime::FromMicros(1000))); |
567 | |
|
568 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
569 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
570 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 1000 w: 1 }]) -> "row1_e" |
571 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 1000 w: 2 }]) -> 20000 |
572 | 0 | )#"); |
573 | |
|
574 | 0 | const Schema &schema = kSchemaForIteratorTests; |
575 | 0 | Schema projection; |
576 | 0 | ASSERT_OK(kSchemaForIteratorTests.CreateProjectionByNames({"c", "d"}, |
577 | 0 | &projection)); |
578 | |
|
579 | 0 | { |
580 | 0 | DocRowwiseIterator iter( |
581 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
582 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2800)); |
583 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
584 | |
|
585 | 0 | QLTableRow row; |
586 | 0 | QLValue value; |
587 | |
|
588 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
589 | 0 | ASSERT_OK(iter.NextRow(&row)); |
590 | |
|
591 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
592 | 0 | ASSERT_TRUE(value.IsNull()); |
593 | |
|
594 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
595 | 0 | ASSERT_FALSE(value.IsNull()); |
596 | 0 | ASSERT_EQ(10000, value.int64_value()); |
597 | | |
598 | | // Now find next row. |
599 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
600 | 0 | ASSERT_OK(iter.NextRow(&row)); |
601 | |
|
602 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
603 | 0 | ASSERT_TRUE(value.IsNull()); |
604 | |
|
605 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
606 | 0 | ASSERT_FALSE(value.IsNull()); |
607 | 0 | ASSERT_EQ(20000, value.int64_value()); |
608 | |
|
609 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
610 | 0 | } |
611 | 0 | } |
612 | | |
613 | 0 | TEST_F(DocRowwiseIteratorTest, ColocatedTableTombstoneTest) { |
614 | 0 | constexpr PgTableOid pgtable_id(0x4001); |
615 | 0 | auto dwb = MakeDocWriteBatch(); |
616 | |
|
617 | 0 | DocKey encoded_1_with_tableid; |
618 | |
|
619 | 0 | ASSERT_OK(encoded_1_with_tableid.FullyDecodeFrom(kEncodedDocKey1)); |
620 | 0 | encoded_1_with_tableid.set_pgtable_id(pgtable_id); |
621 | |
|
622 | 0 | ASSERT_OK(dwb.SetPrimitive( |
623 | 0 | DocPath(encoded_1_with_tableid.Encode(), PrimitiveValue::kLivenessColumn), |
624 | 0 | PrimitiveValue(ValueType::kNullLow))); |
625 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(1000))); |
626 | |
|
627 | 0 | DocKey table_id(pgtable_id); |
628 | 0 | ASSERT_OK(dwb.DeleteSubDoc(DocPath(table_id.Encode()))); |
629 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2000))); |
630 | |
|
631 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
632 | 0 | SubDocKey(DocKey(PgTableId=16385, [], []), [HT{ physical: 2000 }]) -> DEL |
633 | 0 | SubDocKey(DocKey(PgTableId=16385, [], ["row1", 11111]), [SystemColumnId(0); HT{ physical: 1000 }]) \ |
634 | 0 | -> null |
635 | 0 | )#"); |
636 | 0 | Schema schema_copy = kSchemaForIteratorTests; |
637 | 0 | schema_copy.set_pgtable_id(pgtable_id); |
638 | 0 | Schema projection; |
639 | | // Read should have results before delete... |
640 | 0 | { |
641 | 0 | DocRowwiseIterator iter( |
642 | 0 | projection, schema_copy, kNonTransactionalOperationContext, doc_db(), |
643 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(1500)); |
644 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
645 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
646 | 0 | } |
647 | | // ...but there should be no results after delete. |
648 | 0 | { |
649 | 0 | DocRowwiseIterator iter( |
650 | 0 | projection, schema_copy, kNonTransactionalOperationContext, doc_db(), |
651 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::Max()); |
652 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
653 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorMultipleDeletes) { |
658 | 0 | auto dwb = MakeDocWriteBatch(); |
659 | |
|
660 | 0 | MonoDelta ttl = MonoDelta::FromMilliseconds(1); |
661 | 0 | MonoDelta ttl_expiry = MonoDelta::FromMilliseconds(2); |
662 | 0 | auto read_time = ReadHybridTime::SingleTime(server::HybridClock::AddPhysicalTimeToHybridTime( |
663 | 0 | HybridTime::FromMicros(2800), ttl_expiry)); |
664 | |
|
665 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
666 | 0 | PrimitiveValue("row1_c"))); |
667 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
668 | 0 | PrimitiveValue(10000))); |
669 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(1000))); |
670 | | |
671 | | // Deletes. |
672 | 0 | ASSERT_OK(dwb.DeleteSubDoc(DocPath(kEncodedDocKey1))); |
673 | 0 | ASSERT_OK(dwb.DeleteSubDoc(DocPath(kEncodedDocKey2))); |
674 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2500))); |
675 | 0 | dwb.Clear(); |
676 | |
|
677 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
678 | 0 | Value(PrimitiveValue("row1_e"), ttl))); |
679 | |
|
680 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(30_ColId)), |
681 | 0 | PrimitiveValue::kTombstone)); |
682 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
683 | 0 | PrimitiveValue(20000))); |
684 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
685 | 0 | Value(PrimitiveValue("row2_e"), MonoDelta::FromMilliseconds(3)))); |
686 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2800))); |
687 | |
|
688 | 0 | ASSERT_OK(WriteToRocksDB(dwb, HybridTime::FromMicros(1000))); |
689 | |
|
690 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
691 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 2500 }]) -> DEL |
692 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30); HT{ physical: 1000 }]) -> "row1_c" |
693 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 w: 1 }]) -> 10000 |
694 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 2800 }]) -> \ |
695 | 0 | "row1_e"; ttl: 0.001s |
696 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [HT{ physical: 2500 w: 1 }]) -> DEL |
697 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(30); HT{ physical: 2800 w: 1 }]) -> DEL |
698 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2800 w: 2 }]) -> 20000 |
699 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50); HT{ physical: 2800 w: 3 }]) -> \ |
700 | 0 | "row2_e"; ttl: 0.003s |
701 | 0 | )#"); |
702 | |
|
703 | 0 | const Schema &schema = kSchemaForIteratorTests; |
704 | 0 | Schema projection; |
705 | 0 | ASSERT_OK(kSchemaForIteratorTests.CreateProjectionByNames({"c", "e"}, &projection)); |
706 | |
|
707 | 0 | { |
708 | 0 | DocRowwiseIterator iter( |
709 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
710 | 0 | CoarseTimePoint::max() /* deadline */, read_time); |
711 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
712 | |
|
713 | 0 | QLTableRow row; |
714 | 0 | QLValue value; |
715 | |
|
716 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
717 | | // Ensure Idempotency. |
718 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
719 | 0 | ASSERT_OK(iter.NextRow(&row)); |
720 | |
|
721 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
722 | 0 | ASSERT_TRUE(value.IsNull()); |
723 | |
|
724 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
725 | 0 | ASSERT_FALSE(value.IsNull()); |
726 | 0 | ASSERT_EQ("row2_e", value.string_value()); |
727 | |
|
728 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
729 | 0 | } |
730 | 0 | } |
731 | | |
732 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorValidColumnNotInProjection) { |
733 | 0 | auto dwb = MakeDocWriteBatch(); |
734 | |
|
735 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
736 | 0 | PrimitiveValue(10000))); |
737 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
738 | 0 | PrimitiveValue(20000))); |
739 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(1000))); |
740 | |
|
741 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
742 | 0 | PrimitiveValue("row2_e"))); |
743 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey2, PrimitiveValue(30_ColId)), |
744 | 0 | PrimitiveValue("row2_c"))); |
745 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2000))); |
746 | |
|
747 | 0 | ASSERT_OK(dwb.DeleteSubDoc(DocPath(kEncodedDocKey1))); |
748 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2500))); |
749 | |
|
750 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
751 | 0 | PrimitiveValue("row1_e"))); |
752 | 0 | ASSERT_OK(WriteToRocksDBAndClear(&dwb, HybridTime::FromMicros(2800))); |
753 | | |
754 | |
|
755 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
756 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [HT{ physical: 2500 }]) -> DEL |
757 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
758 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 2800 }]) -> "row1_e" |
759 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(30); HT{ physical: 2000 w: 1 }]) -> "row2_c" |
760 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 1000 w: 1 }]) -> 20000 |
761 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50); HT{ physical: 2000 }]) -> "row2_e" |
762 | 0 | )#"); |
763 | |
|
764 | 0 | const Schema &schema = kSchemaForIteratorTests; |
765 | 0 | Schema projection; |
766 | 0 | ASSERT_OK(kSchemaForIteratorTests.CreateProjectionByNames({"c", "d"}, &projection)); |
767 | |
|
768 | 0 | { |
769 | 0 | DocRowwiseIterator iter( |
770 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
771 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2800)); |
772 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
773 | |
|
774 | 0 | QLTableRow row; |
775 | 0 | QLValue value; |
776 | |
|
777 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
778 | 0 | ASSERT_OK(iter.NextRow(&row)); |
779 | |
|
780 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
781 | 0 | ASSERT_TRUE(value.IsNull()); |
782 | |
|
783 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
784 | 0 | ASSERT_TRUE(value.IsNull()); |
785 | |
|
786 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
787 | 0 | ASSERT_OK(iter.NextRow(&row)); |
788 | |
|
789 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
790 | 0 | ASSERT_FALSE(value.IsNull()); |
791 | 0 | ASSERT_EQ("row2_c", value.string_value()); |
792 | |
|
793 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
794 | 0 | ASSERT_FALSE(value.IsNull()); |
795 | 0 | ASSERT_EQ(20000, value.int64_value()); |
796 | |
|
797 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
798 | 0 | } |
799 | 0 | } |
800 | | |
801 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorKeyProjection) { |
802 | 0 | auto dwb = MakeDocWriteBatch(); |
803 | | |
804 | | // Row 1 |
805 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
806 | 0 | PrimitiveValue(10000))); |
807 | 0 | ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
808 | 0 | PrimitiveValue("row1_e"))); |
809 | |
|
810 | 0 | ASSERT_OK(WriteToRocksDB(dwb, HybridTime::FromMicros(1000))); |
811 | |
|
812 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
813 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
814 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 1000 w: 1 }]) -> "row1_e" |
815 | 0 | )#"); |
816 | |
|
817 | 0 | const Schema &schema = kSchemaForIteratorTests; |
818 | 0 | Schema projection; |
819 | 0 | ASSERT_OK(kSchemaForIteratorTests.CreateProjectionByNames({"a", "b"}, |
820 | 0 | &projection, 2)); |
821 | |
|
822 | 0 | { |
823 | 0 | DocRowwiseIterator iter( |
824 | 0 | projection, schema, kNonTransactionalOperationContext, doc_db(), |
825 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2800)); |
826 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
827 | |
|
828 | 0 | QLTableRow row; |
829 | 0 | QLValue value; |
830 | |
|
831 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
832 | 0 | ASSERT_OK(iter.NextRow(&row)); |
833 | |
|
834 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
835 | 0 | ASSERT_EQ("row1", value.string_value()); |
836 | |
|
837 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
838 | 0 | ASSERT_EQ(11111, value.int64_value()); |
839 | |
|
840 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
841 | 0 | } |
842 | 0 | } |
843 | | |
844 | 0 | TEST_F(DocRowwiseIteratorTest, DocRowwiseIteratorResolveWriteIntents) { |
845 | 0 | SetTransactionIsolationLevel(IsolationLevel::SNAPSHOT_ISOLATION); |
846 | |
|
847 | 0 | TransactionStatusManagerMock txn_status_manager; |
848 | |
|
849 | 0 | auto txn1 = ASSERT_RESULT(FullyDecodeTransactionId("0000000000000001")); |
850 | 0 | auto txn2 = ASSERT_RESULT(FullyDecodeTransactionId("0000000000000002")); |
851 | |
|
852 | 0 | SetCurrentTransactionId(txn1); |
853 | 0 | ASSERT_OK(SetPrimitive( |
854 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
855 | 0 | PrimitiveValue("row1_c_t1"), HybridTime::FromMicros(500))); |
856 | 0 | ASSERT_OK(SetPrimitive( |
857 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
858 | 0 | PrimitiveValue(40000), HybridTime::FromMicros(500))); |
859 | 0 | ASSERT_OK(SetPrimitive( |
860 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
861 | 0 | PrimitiveValue("row1_e_t1"), HybridTime::FromMicros(500))); |
862 | 0 | ASSERT_OK(SetPrimitive( |
863 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
864 | 0 | PrimitiveValue(42000), HybridTime::FromMicros(500))); |
865 | 0 | ASSERT_OK(SetPrimitive( |
866 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
867 | 0 | PrimitiveValue("row2_e_t1"), HybridTime::FromMicros(500))); |
868 | 0 | ResetCurrentTransactionId(); |
869 | |
|
870 | 0 | ASSERT_OK(SetPrimitive( |
871 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
872 | 0 | PrimitiveValue("row1_c"), HybridTime::FromMicros(1000))); |
873 | 0 | ASSERT_OK(SetPrimitive( |
874 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
875 | 0 | PrimitiveValue(10000), HybridTime::FromMicros(1000))); |
876 | 0 | ASSERT_OK(SetPrimitive( |
877 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(50_ColId)), |
878 | 0 | PrimitiveValue("row1_e"), HybridTime::FromMicros(1000))); |
879 | |
|
880 | 0 | ASSERT_OK(SetPrimitive( |
881 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
882 | 0 | PrimitiveValue(20000), HybridTime::FromMicros(2000))); |
883 | |
|
884 | 0 | ASSERT_OK(DeleteSubDoc( |
885 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
886 | 0 | HybridTime::FromMicros(2500))); |
887 | 0 | ASSERT_OK(SetPrimitive( |
888 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
889 | 0 | PrimitiveValue(30000), HybridTime::FromMicros(3000))); |
890 | 0 | ASSERT_OK(SetPrimitive( |
891 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
892 | 0 | PrimitiveValue("row2_e"), HybridTime::FromMicros(2000))); |
893 | 0 | ASSERT_OK(SetPrimitive( |
894 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
895 | 0 | PrimitiveValue("row2_e_prime"), HybridTime::FromMicros(4000))); |
896 | |
|
897 | 0 | txn_status_manager.Commit(txn1, HybridTime::FromMicros(3500)); |
898 | |
|
899 | 0 | SetCurrentTransactionId(txn2); |
900 | 0 | ASSERT_OK(DeleteSubDoc( |
901 | 0 | DocPath(kEncodedDocKey1), |
902 | 0 | HybridTime::FromMicros(4000))); |
903 | 0 | ASSERT_OK(SetPrimitive( |
904 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(50_ColId)), |
905 | 0 | PrimitiveValue("row2_e_t2"), HybridTime::FromMicros(4000))); |
906 | 0 | ResetCurrentTransactionId(); |
907 | 0 | txn_status_manager.Commit(txn2, HybridTime::FromMicros(6000)); |
908 | |
|
909 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
910 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30); HT{ physical: 1000 }]) -> "row1_c" |
911 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
912 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50); HT{ physical: 1000 }]) -> "row1_e" |
913 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 3000 }]) -> 30000 |
914 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2500 }]) -> DEL |
915 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 2000 }]) -> 20000 |
916 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50); HT{ physical: 4000 }]) -> "row2_e_prime" |
917 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50); HT{ physical: 2000 }]) -> "row2_e" |
918 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 1 } -> \ |
919 | 0 | TransactionId(30303030-3030-3030-3030-303030303032) none |
920 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 1 } -> \ |
921 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
922 | 0 | SubDocKey(DocKey([], ["row1"]), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 2 } -> \ |
923 | 0 | TransactionId(30303030-3030-3030-3030-303030303032) none |
924 | 0 | SubDocKey(DocKey([], ["row1"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } -> \ |
925 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
926 | 0 | SubDocKey(DocKey([], ["row1", 11111]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } -> \ |
927 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
928 | 0 | SubDocKey(DocKey([], ["row1", 11111]), []) [kStrongRead, kStrongWrite] HT{ physical: 4000 } -> \ |
929 | 0 | TransactionId(30303030-3030-3030-3030-303030303032) WriteId(5) DEL |
930 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30)]) [kStrongRead, kStrongWrite] \ |
931 | 0 | HT{ physical: 500 } -> \ |
932 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(0) "row1_c_t1" |
933 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40)]) [kStrongRead, kStrongWrite] \ |
934 | 0 | HT{ physical: 500 } -> \ |
935 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(1) 40000 |
936 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(50)]) [kStrongRead, kStrongWrite] \ |
937 | 0 | HT{ physical: 500 } -> \ |
938 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(2) "row1_e_t1" |
939 | 0 | SubDocKey(DocKey([], ["row2"]), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 2 } -> \ |
940 | 0 | TransactionId(30303030-3030-3030-3030-303030303032) none |
941 | 0 | SubDocKey(DocKey([], ["row2"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } -> \ |
942 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
943 | 0 | SubDocKey(DocKey([], ["row2", 22222]), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 3 } -> \ |
944 | 0 | TransactionId(30303030-3030-3030-3030-303030303032) none |
945 | 0 | SubDocKey(DocKey([], ["row2", 22222]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } -> \ |
946 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
947 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40)]) [kStrongRead, kStrongWrite] \ |
948 | 0 | HT{ physical: 500 } -> \ |
949 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(3) 42000 |
950 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50)]) [kStrongRead, kStrongWrite] \ |
951 | 0 | HT{ physical: 4000 } \ |
952 | 0 | -> TransactionId(30303030-3030-3030-3030-303030303032) WriteId(6) "row2_e_t2" |
953 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50)]) [kStrongRead, kStrongWrite] \ |
954 | 0 | HT{ physical: 500 } -> \ |
955 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(4) "row2_e_t1" |
956 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 } -> \ |
957 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50)]) [kStrongRead, kStrongWrite] \ |
958 | 0 | HT{ physical: 500 } |
959 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 1 } -> \ |
960 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 1 } |
961 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 2 } -> \ |
962 | 0 | SubDocKey(DocKey([], ["row2"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } |
963 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 3 } -> \ |
964 | 0 | SubDocKey(DocKey([], ["row2", 22222]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } |
965 | 0 | TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 4000 } -> \ |
966 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(50)]) [kStrongRead, kStrongWrite] \ |
967 | 0 | HT{ physical: 4000 } |
968 | 0 | TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 4000 w: 1 } -> \ |
969 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 1 } |
970 | 0 | TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 4000 w: 2 } -> \ |
971 | 0 | SubDocKey(DocKey([], ["row2"]), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 2 } |
972 | 0 | TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 4000 w: 3 } -> \ |
973 | 0 | SubDocKey(DocKey([], ["row2", 22222]), []) [kWeakRead, kWeakWrite] HT{ physical: 4000 w: 3 } |
974 | 0 | )#"); |
975 | |
|
976 | 0 | const Schema &schema = kSchemaForIteratorTests; |
977 | 0 | const Schema &projection = kProjectionForIteratorTests; |
978 | 0 | const auto txn_context = TransactionOperationContext( |
979 | 0 | TransactionId::GenerateRandom(), &txn_status_manager); |
980 | |
|
981 | 0 | { |
982 | 0 | DocRowwiseIterator iter( |
983 | 0 | projection, schema, txn_context, doc_db(), |
984 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(2000)); |
985 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
986 | |
|
987 | 0 | QLTableRow row; |
988 | 0 | QLValue value; |
989 | |
|
990 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
991 | 0 | ASSERT_OK(iter.NextRow(&row)); |
992 | |
|
993 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
994 | 0 | ASSERT_FALSE(value.IsNull()); |
995 | 0 | ASSERT_EQ("row1_c", value.string_value()); |
996 | |
|
997 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
998 | 0 | ASSERT_FALSE(value.IsNull()); |
999 | 0 | ASSERT_EQ(10000, value.int64_value()); |
1000 | |
|
1001 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1002 | 0 | ASSERT_FALSE(value.IsNull()); |
1003 | 0 | ASSERT_EQ("row1_e", value.string_value()); |
1004 | |
|
1005 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
1006 | 0 | ASSERT_OK(iter.NextRow(&row)); |
1007 | |
|
1008 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
1009 | 0 | ASSERT_TRUE(value.IsNull()); |
1010 | |
|
1011 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
1012 | 0 | ASSERT_FALSE(value.IsNull()); |
1013 | 0 | ASSERT_EQ(20000, value.int64_value()); |
1014 | |
|
1015 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1016 | 0 | ASSERT_FALSE(value.IsNull()); |
1017 | 0 | ASSERT_EQ("row2_e", value.string_value()); |
1018 | |
|
1019 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
1020 | 0 | } |
1021 | | |
1022 | | // Scan at a later hybrid_time. |
1023 | |
|
1024 | 0 | LOG(INFO) << "==============================================="; |
1025 | 0 | { |
1026 | 0 | DocRowwiseIterator iter( |
1027 | 0 | projection, schema, txn_context, doc_db(), |
1028 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(5000)); |
1029 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
1030 | 0 | QLTableRow row; |
1031 | 0 | QLValue value; |
1032 | |
|
1033 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
1034 | 0 | ASSERT_OK(iter.NextRow(&row)); |
1035 | |
|
1036 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
1037 | 0 | ASSERT_FALSE(value.IsNull()); |
1038 | 0 | ASSERT_EQ("row1_c_t1", value.string_value()); |
1039 | |
|
1040 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
1041 | 0 | ASSERT_FALSE(value.IsNull()); |
1042 | 0 | ASSERT_EQ(40000, value.int64_value()); |
1043 | |
|
1044 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1045 | 0 | ASSERT_FALSE(value.IsNull()); |
1046 | 0 | ASSERT_EQ("row1_e_t1", value.string_value()); |
1047 | |
|
1048 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
1049 | 0 | ASSERT_OK(iter.NextRow(&row)); |
1050 | |
|
1051 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
1052 | 0 | ASSERT_TRUE(value.IsNull()); |
1053 | |
|
1054 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
1055 | 0 | ASSERT_FALSE(value.IsNull()); |
1056 | 0 | ASSERT_EQ(42000, value.int64_value()); |
1057 | |
|
1058 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1059 | 0 | ASSERT_FALSE(value.IsNull()); |
1060 | 0 | ASSERT_EQ("row2_e_prime", value.string_value()); |
1061 | |
|
1062 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
1063 | 0 | } |
1064 | | |
1065 | | // Scan at a later hybrid_time. |
1066 | |
|
1067 | 0 | { |
1068 | 0 | DocRowwiseIterator iter( |
1069 | 0 | projection, schema, txn_context, doc_db(), |
1070 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(6000)); |
1071 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
1072 | |
|
1073 | 0 | QLTableRow row; |
1074 | 0 | QLValue value; |
1075 | |
|
1076 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
1077 | 0 | ASSERT_OK(iter.NextRow(&row)); |
1078 | |
|
1079 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
1080 | 0 | ASSERT_TRUE(value.IsNull()); |
1081 | |
|
1082 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
1083 | 0 | ASSERT_FALSE(value.IsNull()); |
1084 | 0 | ASSERT_EQ(42000, value.int64_value()); |
1085 | |
|
1086 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1087 | 0 | ASSERT_FALSE(value.IsNull()); |
1088 | 0 | ASSERT_EQ("row2_e_t2", value.string_value()); |
1089 | |
|
1090 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
1091 | 0 | } |
1092 | 0 | } |
1093 | | |
1094 | 0 | TEST_F(DocRowwiseIteratorTest, IntentAwareIteratorSeek) { |
1095 | 0 | SetTransactionIsolationLevel(IsolationLevel::SNAPSHOT_ISOLATION); |
1096 | |
|
1097 | 0 | TransactionStatusManagerMock txn_status_manager; |
1098 | |
|
1099 | 0 | Result<TransactionId> txn = FullyDecodeTransactionId("0000000000000001"); |
1100 | 0 | ASSERT_OK(txn); |
1101 | | |
1102 | | // Have a mix of transactional / non-transaction writes. |
1103 | 0 | SetCurrentTransactionId(*txn); |
1104 | 0 | ASSERT_OK(SetPrimitive( |
1105 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
1106 | 0 | PrimitiveValue("row1_c_txn"), HybridTime::FromMicros(500))); |
1107 | |
|
1108 | 0 | txn_status_manager.Commit(*txn, HybridTime::FromMicros(600)); |
1109 | |
|
1110 | 0 | ResetCurrentTransactionId(); |
1111 | |
|
1112 | 0 | ASSERT_OK(SetPrimitive( |
1113 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
1114 | 0 | PrimitiveValue("row1_c"), HybridTime::FromMicros(1000))); |
1115 | 0 | ASSERT_OK(SetPrimitive( |
1116 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(40_ColId)), |
1117 | 0 | PrimitiveValue(10000), HybridTime::FromMicros(1000))); |
1118 | 0 | ASSERT_OK(SetPrimitive( |
1119 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(30_ColId)), |
1120 | 0 | PrimitiveValue("row2_c"), HybridTime::FromMicros(1000))); |
1121 | 0 | ASSERT_OK(SetPrimitive( |
1122 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(40_ColId)), |
1123 | 0 | PrimitiveValue(20000), HybridTime::FromMicros(1000))); |
1124 | | |
1125 | | // Verify the content of RocksDB. |
1126 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
1127 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30); HT{ physical: 1000 }]) -> "row1_c" |
1128 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(40); HT{ physical: 1000 }]) -> 10000 |
1129 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(30); HT{ physical: 1000 }]) -> "row2_c" |
1130 | 0 | SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(40); HT{ physical: 1000 }]) -> 20000 |
1131 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 1 } -> \ |
1132 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
1133 | 0 | SubDocKey(DocKey([], ["row1"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } -> \ |
1134 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
1135 | 0 | SubDocKey(DocKey([], ["row1", 11111]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } -> \ |
1136 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
1137 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30)]) [kStrongRead, kStrongWrite] \ |
1138 | 0 | HT{ physical: 500 } -> \ |
1139 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(0) "row1_c_txn" |
1140 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 } -> \ |
1141 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30)]) [kStrongRead, kStrongWrite] \ |
1142 | 0 | HT{ physical: 500 } |
1143 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 1 } -> \ |
1144 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 1 } |
1145 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 2 } -> \ |
1146 | 0 | SubDocKey(DocKey([], ["row1"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } |
1147 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 3 } -> \ |
1148 | 0 | SubDocKey(DocKey([], ["row1", 11111]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } |
1149 | 0 | )#"); |
1150 | | |
1151 | | // Create a new IntentAwareIterator and seek to an empty DocKey. Verify that it returns the |
1152 | | // first non-intent key. |
1153 | 0 | IntentAwareIterator iter( |
1154 | 0 | doc_db(), rocksdb::ReadOptions(), CoarseTimePoint::max() /* deadline */, |
1155 | 0 | ReadHybridTime::FromMicros(1000), TransactionOperationContext()); |
1156 | 0 | iter.Seek(DocKey()); |
1157 | 0 | ASSERT_TRUE(iter.valid()); |
1158 | 0 | auto key_data = ASSERT_RESULT(iter.FetchKey()); |
1159 | 0 | SubDocKey subdoc_key; |
1160 | 0 | ASSERT_OK(subdoc_key.FullyDecodeFrom(key_data.key, HybridTimeRequired::kFalse)); |
1161 | 0 | ASSERT_EQ(subdoc_key.ToString(), R"#(SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30)]))#"); |
1162 | 0 | ASSERT_EQ(key_data.write_time.ToString(), "HT{ physical: 1000 }"); |
1163 | 0 | } |
1164 | | |
1165 | 0 | TEST_F(DocRowwiseIteratorTest, SeekTwiceWithinTheSameTxn) { |
1166 | 0 | SetTransactionIsolationLevel(IsolationLevel::SNAPSHOT_ISOLATION); |
1167 | |
|
1168 | 0 | TransactionStatusManagerMock txn_status_manager; |
1169 | |
|
1170 | 0 | Result<TransactionId> txn = FullyDecodeTransactionId("0000000000000001"); |
1171 | 0 | ASSERT_OK(txn); |
1172 | |
|
1173 | 0 | SetCurrentTransactionId(*txn); |
1174 | 0 | ASSERT_OK(SetPrimitive( |
1175 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
1176 | 0 | PrimitiveValue("row1_c_t1"), HybridTime::FromMicros(500))); |
1177 | | |
1178 | | // Verify the content of RocksDB. |
1179 | 0 | ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(R"#( |
1180 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 1 } -> \ |
1181 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
1182 | 0 | SubDocKey(DocKey([], ["row1"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } -> \ |
1183 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
1184 | 0 | SubDocKey(DocKey([], ["row1", 11111]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } -> \ |
1185 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) none |
1186 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30)]) [kStrongRead, kStrongWrite] \ |
1187 | 0 | HT{ physical: 500 } -> \ |
1188 | 0 | TransactionId(30303030-3030-3030-3030-303030303031) WriteId(0) "row1_c_t1" |
1189 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 } -> \ |
1190 | 0 | SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(30)]) [kStrongRead, kStrongWrite] \ |
1191 | 0 | HT{ physical: 500 } |
1192 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 1 } -> \ |
1193 | 0 | SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 1 } |
1194 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 2 } -> \ |
1195 | 0 | SubDocKey(DocKey([], ["row1"]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 2 } |
1196 | 0 | TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 500 w: 3 } -> \ |
1197 | 0 | SubDocKey(DocKey([], ["row1", 11111]), []) [kWeakRead, kWeakWrite] HT{ physical: 500 w: 3 } |
1198 | 0 | )#"); |
1199 | |
|
1200 | 0 | IntentAwareIterator iter( |
1201 | 0 | doc_db(), rocksdb::ReadOptions(), CoarseTimePoint::max() /* deadline */, |
1202 | 0 | ReadHybridTime::FromMicros(1000), TransactionOperationContext(*txn, &txn_status_manager)); |
1203 | 0 | for (int i = 1; i <= 2; ++i) { |
1204 | 0 | iter.Seek(DocKey()); |
1205 | 0 | ASSERT_TRUE(iter.valid()) << "Seek #" << i << " failed"; |
1206 | 0 | } |
1207 | 0 | } |
1208 | | |
1209 | 0 | TEST_F(DocRowwiseIteratorTest, ScanWithinTheSameTxn) { |
1210 | 0 | SetTransactionIsolationLevel(IsolationLevel::SNAPSHOT_ISOLATION); |
1211 | |
|
1212 | 0 | TransactionStatusManagerMock txn_status_manager; |
1213 | |
|
1214 | 0 | Result<TransactionId> txn = FullyDecodeTransactionId("0000000000000001"); |
1215 | 0 | ASSERT_OK(txn); |
1216 | |
|
1217 | 0 | SetCurrentTransactionId(*txn); |
1218 | 0 | ASSERT_OK(SetPrimitive( |
1219 | 0 | DocPath(kEncodedDocKey2, PrimitiveValue(30_ColId)), |
1220 | 0 | PrimitiveValue("row2_c_t1"), HybridTime::FromMicros(500))); |
1221 | 0 | ASSERT_OK(SetPrimitive( |
1222 | 0 | DocPath(kEncodedDocKey1, PrimitiveValue(30_ColId)), |
1223 | 0 | PrimitiveValue("row1_c_t1"), HybridTime::FromMicros(600))); |
1224 | |
|
1225 | 0 | LOG(INFO) << "Dump:\n" << DocDBDebugDumpToStr(); |
1226 | |
|
1227 | 0 | const auto txn_context = TransactionOperationContext(*txn, &txn_status_manager); |
1228 | 0 | const Schema &projection = kProjectionForIteratorTests; |
1229 | |
|
1230 | 0 | DocRowwiseIterator iter( |
1231 | 0 | projection, kSchemaForIteratorTests, txn_context, doc_db(), |
1232 | 0 | CoarseTimePoint::max() /* deadline */, ReadHybridTime::FromMicros(1000)); |
1233 | 0 | ASSERT_OK(iter.Init(YQL_TABLE_TYPE)); |
1234 | |
|
1235 | 0 | QLTableRow row; |
1236 | 0 | QLValue value; |
1237 | |
|
1238 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
1239 | 0 | ASSERT_OK(iter.NextRow(&row)); |
1240 | |
|
1241 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
1242 | 0 | ASSERT_FALSE(value.IsNull()); |
1243 | 0 | ASSERT_EQ("row1_c_t1", value.string_value()); |
1244 | |
|
1245 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
1246 | 0 | ASSERT_TRUE(value.IsNull()); |
1247 | |
|
1248 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1249 | 0 | ASSERT_TRUE(value.IsNull()); |
1250 | |
|
1251 | 0 | ASSERT_TRUE(ASSERT_RESULT(iter.HasNext())); |
1252 | 0 | ASSERT_OK(iter.NextRow(&row)); |
1253 | |
|
1254 | 0 | ASSERT_OK(row.GetValue(projection.column_id(0), &value)); |
1255 | 0 | ASSERT_FALSE(value.IsNull()); |
1256 | 0 | ASSERT_EQ("row2_c_t1", value.string_value()); |
1257 | |
|
1258 | 0 | ASSERT_OK(row.GetValue(projection.column_id(1), &value)); |
1259 | 0 | ASSERT_TRUE(value.IsNull()); |
1260 | |
|
1261 | 0 | ASSERT_OK(row.GetValue(projection.column_id(2), &value)); |
1262 | 0 | ASSERT_TRUE(value.IsNull()); |
1263 | |
|
1264 | 0 | ASSERT_FALSE(ASSERT_RESULT(iter.HasNext())); |
1265 | | |
1266 | | // Empirically we require 3 seeks to perform this test. |
1267 | | // If this number increased, then something got broken and should be fixed. |
1268 | | // IF this number decreased because of optimization, then we should adjust this check. |
1269 | 0 | ASSERT_EQ(intents_db_options_.statistics->getTickerCount(rocksdb::Tickers::NUMBER_DB_SEEK), 3); |
1270 | 0 | } |
1271 | | |
1272 | | } // namespace docdb |
1273 | | } // namespace yb |