/Users/deen/code/yugabyte-db/src/yb/client/ql-dml-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 <boost/circular_buffer.hpp> |
15 | | |
16 | | #include "yb/client/error.h" |
17 | | #include "yb/client/ql-dml-test-base.h" |
18 | | #include "yb/client/schema.h" |
19 | | #include "yb/client/session.h" |
20 | | #include "yb/client/table_alterer.h" |
21 | | #include "yb/client/table_handle.h" |
22 | | #include "yb/client/yb_op.h" |
23 | | |
24 | | #include "yb/common/ql_value.h" |
25 | | #include "yb/common/schema.h" |
26 | | |
27 | | #include "yb/master/master_util.h" |
28 | | |
29 | | #include "yb/rocksdb/db.h" |
30 | | |
31 | | #include "yb/tablet/tablet.h" |
32 | | #include "yb/tablet/tablet_peer.h" |
33 | | |
34 | | #include "yb/tserver/mini_tablet_server.h" |
35 | | #include "yb/tserver/tablet_server.h" |
36 | | #include "yb/tserver/ts_tablet_manager.h" |
37 | | |
38 | | #include "yb/util/async_util.h" |
39 | | #include "yb/util/backoff_waiter.h" |
40 | | #include "yb/util/format.h" |
41 | | #include "yb/util/random.h" |
42 | | #include "yb/util/random_util.h" |
43 | | #include "yb/util/status_format.h" |
44 | | #include "yb/util/status_log.h" |
45 | | #include "yb/util/tostring.h" |
46 | | #include "yb/util/tsan_util.h" |
47 | | |
48 | | #include "yb/yql/cql/ql/util/statement_result.h" |
49 | | |
50 | | DECLARE_bool(mini_cluster_reuse_data); |
51 | | DECLARE_bool(rocksdb_disable_compactions); |
52 | | DECLARE_int32(yb_num_shards_per_tserver); |
53 | | DECLARE_int64(db_block_cache_size_bytes); |
54 | | DECLARE_bool(flush_rocksdb_on_shutdown); |
55 | | DECLARE_uint64(max_stale_read_bound_time_ms); |
56 | | |
57 | | using namespace std::literals; |
58 | | |
59 | | namespace yb { |
60 | | namespace client { |
61 | | |
62 | | using yb::ql::RowsResult; |
63 | | |
64 | | // Verify all column values of a row. We use a macro instead of a function so that EXPECT_EQ can |
65 | | // show the caller's line number should the test fails. |
66 | | #define EXPECT_ROW_VALUES(row, h1, h2, r1, r2, c1, c2) \ |
67 | 0 | do { \ |
68 | 0 | EXPECT_EQ(row.column(0).int32_value(), h1); \ |
69 | 0 | EXPECT_EQ(row.column(1).string_value(), h2); \ |
70 | 0 | EXPECT_EQ(row.column(2).int32_value(), r1); \ |
71 | 0 | EXPECT_EQ(row.column(3).string_value(), r2); \ |
72 | 0 | EXPECT_EQ(row.column(4).int32_value(), c1); \ |
73 | 0 | EXPECT_EQ(row.column(5).string_value(), c2); \ |
74 | 0 | } while (false) |
75 | | |
76 | | namespace { |
77 | | |
78 | | const std::vector<std::string> kAllColumns = {"h1", "h2", "r1", "r2", "c1", "c2"}; |
79 | | const std::vector<std::string> kValueColumns = {"c1", "c2"}; |
80 | | const size_t kValuePrefixLength = 4096; |
81 | | const std::string kValueFormat = RandomHumanReadableString(kValuePrefixLength) + "_$0"; |
82 | | const int kInsertBatchSize = 20; |
83 | | |
84 | | struct RowKey { |
85 | | int32_t h1; |
86 | | std::string h2; |
87 | | int32_t r1; |
88 | | std::string r2; |
89 | | |
90 | 0 | std::string ToString() const { |
91 | 0 | return Format("{ h1: $0 h2: $1 r1: $2 r2: $3 }", h1, h2, r1, r2); |
92 | 0 | } |
93 | | }; |
94 | | |
95 | | struct RowValue { |
96 | | int32_t c1; |
97 | | std::string c2; |
98 | | |
99 | 0 | std::string ToString() const { |
100 | 0 | return Format("{ c1: $0 c2: $1 }", c1, c2); |
101 | 0 | } |
102 | | }; |
103 | | |
104 | 0 | std::ostream& operator<<(std::ostream& out, const RowValue& value) { |
105 | 0 | return out << value.ToString(); |
106 | 0 | } |
107 | | |
108 | 0 | bool operator==(const RowValue& lhs, const RowValue& rhs) { |
109 | 0 | return lhs.c1 == rhs.c1 && lhs.c2 == rhs.c2; |
110 | 0 | } |
111 | | |
112 | 0 | RowKey KeyForIndex(int32_t index) { |
113 | 0 | return RowKey{index, Format("hash_$0", index), index * 2, Format("range_$0", index)}; |
114 | 0 | } |
115 | | |
116 | 0 | RowValue ValueForIndex(int32_t index) { |
117 | 0 | return RowValue{index * 3, Format(kValueFormat, index)}; |
118 | 0 | } |
119 | | |
120 | | } // namespace |
121 | | |
122 | | class QLDmlTest : public QLDmlTestBase<MiniCluster> { |
123 | | public: |
124 | 16 | QLDmlTest() { |
125 | 16 | } |
126 | | |
127 | 16 | void SetUp() override { |
128 | 16 | QLDmlTestBase::SetUp(); |
129 | | |
130 | 16 | if (!FLAGS_mini_cluster_reuse_data) { |
131 | 0 | YBSchemaBuilder b; |
132 | 0 | b.AddColumn("h1")->Type(INT32)->HashPrimaryKey()->NotNull(); |
133 | 0 | b.AddColumn("h2")->Type(STRING)->HashPrimaryKey()->NotNull(); |
134 | 0 | b.AddColumn("r1")->Type(INT32)->PrimaryKey()->NotNull(); |
135 | 0 | b.AddColumn("r2")->Type(STRING)->PrimaryKey()->NotNull(); |
136 | 0 | b.AddColumn("c1")->Type(INT32); |
137 | 0 | b.AddColumn("c2")->Type(STRING); |
138 | |
|
139 | 0 | ASSERT_OK(table_.Create(kTableName, CalcNumTablets(3), client_.get(), &b)); |
140 | 16 | } else { |
141 | 16 | ASSERT_OK(table_.Open(kTableName, client_.get())); |
142 | 16 | } |
143 | 16 | } |
144 | | |
145 | | // Insert a full, single row, equivalent to the insert statement below. Return a YB write op that |
146 | | // has been applied. |
147 | | // insert into t values (h1, h2, r1, r2, c1, c2); |
148 | | YBqlWriteOpPtr InsertRow( |
149 | | const YBSessionPtr& session, |
150 | | const RowKey& key, |
151 | 0 | const RowValue& value) { |
152 | 0 | return InsertRow(session, key.h1, key.h2, key.r1, key.r2, value.c1, value.c2); |
153 | 0 | } |
154 | | |
155 | | YBqlWriteOpPtr InsertRow( |
156 | | const YBSessionPtr& session, |
157 | | const int32 h1, const string& h2, |
158 | | const int32 r1, const string& r2, |
159 | 0 | const int32 c1, const string& c2) { |
160 | 0 | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
161 | 0 | auto* const req = op->mutable_request(); |
162 | 0 | QLAddInt32HashValue(req, h1); |
163 | 0 | QLAddStringHashValue(req, h2); |
164 | 0 | QLAddInt32RangeValue(req, r1); |
165 | 0 | QLAddStringRangeValue(req, r2); |
166 | 0 | table_.AddInt32ColumnValue(req, "c1", c1); |
167 | 0 | table_.AddStringColumnValue(req, "c2", c2); |
168 | 0 | session->Apply(op); |
169 | 0 | return op; |
170 | 0 | } |
171 | | |
172 | 0 | void InsertRows(int num_rows) { |
173 | 0 | auto session = NewSession(); |
174 | 0 | boost::circular_buffer<std::future<FlushStatus>> futures(kInsertBatchSize); |
175 | 0 | for (int i = 0; i != num_rows; ++i) { |
176 | 0 | for (;;) { |
177 | | // Remove all the futures that are done. |
178 | 0 | while (!futures.empty() && IsReady(futures.front())) { |
179 | 0 | EXPECT_OK(futures.front().get().status); |
180 | 0 | futures.pop_front(); |
181 | 0 | } |
182 | | // Keep collecting futures until we hit our limit. |
183 | 0 | if (futures.size() < kInsertBatchSize) { |
184 | 0 | break; |
185 | 0 | } |
186 | 0 | futures.front().wait(); |
187 | 0 | } |
188 | 0 | InsertRow(session, KeyForIndex(i), ValueForIndex(i)); |
189 | 0 | futures.push_back(session->FlushFuture()); |
190 | 0 | } |
191 | 0 | for (auto& future : futures) { |
192 | 0 | EXPECT_OK(future.get().status); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | // Select the specified columns of a row using a primary key, equivalent to the select statement |
197 | | // below. Return a YB read op that has been applied. |
198 | | // select <columns...> from t where h1 = <h1> and h2 = <h2> and r1 = <r1> and r2 = <r2>; |
199 | | YBqlReadOpPtr SelectRow( |
200 | | const YBSessionPtr& session, |
201 | | const vector<string>& columns, |
202 | | const int32 h1, const string& h2, |
203 | 0 | const int32 r1, const string& r2) { |
204 | 0 | const YBqlReadOpPtr op = table_.NewReadOp(); |
205 | 0 | auto* const req = op->mutable_request(); |
206 | 0 | QLAddInt32HashValue(req, h1); |
207 | 0 | QLAddStringHashValue(req, h2); |
208 | 0 | auto* const condition = req->mutable_where_expr()->mutable_condition(); |
209 | 0 | condition->set_op(QL_OP_AND); |
210 | 0 | table_.AddInt32Condition(condition, "r1", QL_OP_EQUAL, r1); |
211 | 0 | table_.AddStringCondition(condition, "r2", QL_OP_EQUAL, r2); |
212 | 0 | table_.AddColumns(columns, req); |
213 | 0 | session->Apply(op); |
214 | 0 | return op; |
215 | 0 | } |
216 | | |
217 | | YBqlReadOpPtr SelectRow( |
218 | | const YBSessionPtr& session, |
219 | | const vector<string>& columns, |
220 | 0 | const RowKey& key) { |
221 | 0 | return SelectRow(session, columns, key.h1, key.h2, key.r1, key.r2); |
222 | 0 | } |
223 | | |
224 | 0 | YBqlReadOpPtr SelectRow() { |
225 | 0 | auto session = NewSession(); |
226 | 0 | auto result = SelectRow(session, kAllColumns, 1, "a", 2, "b"); |
227 | 0 | EXPECT_OK(session->Flush()); |
228 | 0 | return result; |
229 | 0 | } |
230 | | |
231 | | Result<RowValue> ReadRow(const YBSessionPtr& session, const RowKey& key, |
232 | 0 | YBConsistencyLevel consistency_level = YBConsistencyLevel::STRONG) { |
233 | 0 | auto op = SelectRow(session, kValueColumns, key); |
234 | 0 | op->set_yb_consistency_level(consistency_level); |
235 | 0 | RETURN_NOT_OK(session->Flush()); |
236 | 0 | if (op->response().status() != QLResponsePB::YQL_STATUS_OK) { |
237 | 0 | return STATUS_FORMAT( |
238 | 0 | RemoteError, "Read filed: $0", QLResponsePB::QLStatus_Name(op->response().status())); |
239 | 0 | } |
240 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
241 | 0 | if (rowblock->row_count() != 1) { |
242 | 0 | return STATUS_FORMAT(NotFound, "No row for $0, count: $1", key, rowblock->row_count()); |
243 | 0 | } |
244 | 0 | const auto& row = rowblock->row(0); |
245 | 0 | return RowValue{row.column(0).int32_value(), row.column(1).string_value()}; |
246 | 0 | } |
247 | | |
248 | | MUST_USE_RESULT testing::AssertionResult VerifyRow( |
249 | | const YBSessionPtr& session, |
250 | | int32 h1, const std::string& h2, |
251 | | int32 r1, const std::string& r2, |
252 | 0 | int32 c1, const std::string& c2) { |
253 | 0 | auto op = SelectRow(session, {"c1", "c2"}, h1, h2, r1, r2); |
254 | 0 | EXPECT_OK(session->Flush()); |
255 | |
|
256 | 0 | VERIFY_EQ(QLResponsePB::YQL_STATUS_OK, op->response().status()); |
257 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
258 | 0 | VERIFY_EQ(1U, rowblock->row_count()); |
259 | 0 | const auto& row = rowblock->row(0); |
260 | 0 | VERIFY_EQ(c1, row.column(0).int32_value()); |
261 | 0 | VERIFY_EQ(c2, row.column(1).string_value()); |
262 | |
|
263 | 0 | return testing::AssertionSuccess(); |
264 | 0 | } |
265 | | |
266 | 0 | void AddAllColumns(QLReadRequestPB* req) { |
267 | 0 | table_.AddColumns(kAllColumns, req); |
268 | 0 | } |
269 | | |
270 | | TableHandle table_; |
271 | | }; |
272 | | |
273 | 0 | TEST_F(QLDmlTest, TestInsertUpdateAndSelect) { |
274 | 0 | { |
275 | | // Test inserting a row. |
276 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
277 | 0 | const YBSessionPtr session(NewSession()); |
278 | 0 | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
279 | 0 | ASSERT_OK(session->Flush()); |
280 | 0 | ASSERT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
281 | 0 | } |
282 | |
|
283 | 0 | { |
284 | | // Test selecting a row. |
285 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
286 | 0 | const YBqlReadOpPtr op = SelectRow(); |
287 | | |
288 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
289 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
290 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
291 | 0 | EXPECT_EQ(rowblock->row_count(), 1); |
292 | 0 | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
293 | 0 | } |
294 | |
|
295 | 0 | { |
296 | | // Test updating the row. |
297 | | // update t set c1 = 4, c2 = 'd' where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
298 | 0 | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_UPDATE); |
299 | 0 | auto* const req = op->mutable_request(); |
300 | 0 | QLAddInt32HashValue(req, 1); |
301 | 0 | QLAddStringHashValue(req, "a"); |
302 | 0 | QLAddInt32RangeValue(req, 2); |
303 | 0 | QLAddStringRangeValue(req, "b"); |
304 | 0 | table_.AddInt32ColumnValue(req, "c1", 4); |
305 | 0 | table_.AddStringColumnValue(req, "c2", "d"); |
306 | 0 | const YBSessionPtr session(NewSession()); |
307 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
308 | |
|
309 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
310 | 0 | } |
311 | |
|
312 | 0 | { |
313 | | // Test selecting the row back, but flush manually and using async API (inside FlushSession). |
314 | | // select c1, c2 from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
315 | 0 | const YBSessionPtr session = NewSession(); |
316 | 0 | const YBqlReadOpPtr op = SelectRow(session, {"c1", "c2"}, 1, "a", 2, "b"); |
317 | 0 | EXPECT_OK(session->Flush()); |
318 | | |
319 | | // Expect 4, 'd' returned |
320 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
321 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
322 | 0 | EXPECT_EQ(rowblock->row_count(), 1); |
323 | 0 | const auto& row = rowblock->row(0); |
324 | 0 | EXPECT_EQ(row.column(0).int32_value(), 4); |
325 | 0 | EXPECT_EQ(row.column(1).string_value(), "d"); |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | 0 | TEST_F(QLDmlTest, TestInsertWrongSchema) { |
330 | 0 | const YBSessionPtr session(NewSession()); |
331 | | |
332 | | // Move to schema version 1 by altering table |
333 | 0 | std::unique_ptr<YBTableAlterer> table_alterer(client_->NewTableAlterer(kTableName)); |
334 | 0 | table_alterer->AddColumn("c3")->Type(INT32)->NotNull(); |
335 | 0 | EXPECT_OK(table_alterer->timeout(MonoDelta::FromSeconds(60))->Alter()); |
336 | | |
337 | | // The request created has schema version 0 by default |
338 | 0 | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
339 | |
|
340 | 0 | EXPECT_OK(session->Flush()); |
341 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_SCHEMA_VERSION_MISMATCH); |
342 | 0 | } |
343 | | |
344 | | namespace { |
345 | | |
346 | 0 | std::string RandomValueAt(int32_t idx, size_t len = 2000) { |
347 | 0 | Random rng(idx); |
348 | 0 | return RandomHumanReadableString(len, &rng); |
349 | 0 | } |
350 | | |
351 | 0 | std::string StrRangeFor(int32_t idx) { |
352 | 0 | return "range_" + std::to_string(1000000 + idx); |
353 | 0 | } |
354 | | |
355 | | class QLDmlRangeFilterBase: public QLDmlTest { |
356 | | public: |
357 | 1 | void SetUp() override { |
358 | 1 | FLAGS_rocksdb_disable_compactions = true; |
359 | 1 | FLAGS_yb_num_shards_per_tserver = 1; |
360 | 1 | QLDmlTest::SetUp(); |
361 | 1 | } |
362 | | }; |
363 | | |
364 | 0 | size_t CountIterators(MiniCluster* cluster) { |
365 | 0 | size_t result = 0; |
366 | |
|
367 | 0 | for (size_t i = 0; i != cluster->num_tablet_servers(); ++i) { |
368 | 0 | auto peers = cluster->mini_tablet_server(i)->server()->tablet_manager()->GetTabletPeers(); |
369 | 0 | for (const auto& peer : peers) { |
370 | 0 | auto statistics = peer->tablet()->regulardb_statistics(); |
371 | 0 | auto value = statistics->getTickerCount(rocksdb::NO_TABLE_CACHE_ITERATORS); |
372 | 0 | result += value; |
373 | 0 | } |
374 | 0 | } |
375 | |
|
376 | 0 | return result; |
377 | 0 | } |
378 | | |
379 | | constexpr int32_t kHashInt = 42; |
380 | | const std::string kHashStr = "all_records_have_same_id"; |
381 | | |
382 | | } // namespace |
383 | | |
384 | 0 | TEST_F_EX(QLDmlTest, RangeFilter, QLDmlRangeFilterBase) { |
385 | 0 | constexpr int32_t kTotalLines = NonTsanVsTsan(25000ULL, 5000ULL); |
386 | 0 | auto session = NewSession(); |
387 | 0 | if (!FLAGS_mini_cluster_reuse_data) { |
388 | 0 | for(int32_t i = 0; i != kTotalLines;) { |
389 | 0 | const YBqlWriteOpPtr op = InsertRow(session, |
390 | 0 | kHashInt, |
391 | 0 | kHashStr, |
392 | 0 | i, |
393 | 0 | StrRangeFor(i), |
394 | 0 | -i, |
395 | 0 | RandomValueAt(i)); |
396 | 0 | ASSERT_OK(session->Flush()); |
397 | 0 | ASSERT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
398 | 0 | ++i; |
399 | 0 | if (i % 100 == 0) { |
400 | 0 | ASSERT_OK(session->Flush()); |
401 | 0 | } |
402 | 0 | if (i % 5000 == 0) { |
403 | 0 | LOG(WARNING) << "Inserted " << i << " rows"; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | ASSERT_OK(session->Flush()); |
407 | 0 | session = NewSession(); |
408 | 0 | LOG(WARNING) << "Finished creating DB"; |
409 | 0 | constexpr int32_t kProbeStep = 997; |
410 | 0 | for(int32_t idx = 0; idx < kTotalLines; idx += kProbeStep) { |
411 | 0 | ASSERT_VERIFY(VerifyRow(session, |
412 | 0 | kHashInt, |
413 | 0 | kHashStr, |
414 | 0 | idx, |
415 | 0 | StrRangeFor(idx), |
416 | 0 | -idx, |
417 | 0 | RandomValueAt(idx))); |
418 | 0 | } |
419 | 0 | LOG(WARNING) << "Preliminary check done"; |
420 | 0 | ASSERT_OK(cluster_->FlushTablets()); |
421 | 0 | std::this_thread::sleep_for(1s); |
422 | 0 | } |
423 | 0 | FLAGS_db_block_cache_size_bytes = -2; |
424 | 0 | ASSERT_OK(cluster_->RestartSync()); |
425 | 0 | { |
426 | 0 | constexpr size_t kTotalProbes = 1000; |
427 | 0 | Random rng(GetRandomSeed32()); |
428 | 0 | size_t old_iterators = CountIterators(cluster_.get()); |
429 | 0 | for (size_t i = 0; i != kTotalProbes; ) { |
430 | 0 | int32_t idx = rng.Uniform(kTotalLines); |
431 | 0 | ASSERT_VERIFY(VerifyRow(session, |
432 | 0 | kHashInt, |
433 | 0 | kHashStr, |
434 | 0 | idx, |
435 | 0 | StrRangeFor(idx), |
436 | 0 | -idx, |
437 | 0 | RandomValueAt(idx))); |
438 | |
|
439 | 0 | size_t new_iterators = CountIterators(cluster_.get()); |
440 | 0 | ASSERT_EQ(old_iterators + 1, new_iterators); |
441 | 0 | old_iterators = new_iterators; |
442 | 0 | ++i; |
443 | 0 | if (i % 100 == 0) { |
444 | 0 | LOG(WARNING) << "Checked " << i << " rows"; |
445 | 0 | } |
446 | 0 | } |
447 | 0 | } |
448 | 0 | } |
449 | | |
450 | 0 | TEST_F(QLDmlTest, TestInsertMultipleRows) { |
451 | 0 | { |
452 | 0 | const YBSessionPtr session(NewSession()); |
453 | | |
454 | | // Test inserting 2 rows. |
455 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
456 | | // insert into t values (1, 'a', 2, 'd', 4, 'e'); |
457 | 0 | const YBqlWriteOpPtr op1 = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
458 | 0 | const YBqlWriteOpPtr op2 = InsertRow(session, 1, "a", 2, "d", 4, "e"); |
459 | |
|
460 | 0 | CHECK_OK(session->Flush()); |
461 | 0 | EXPECT_EQ(op1->response().status(), QLResponsePB::YQL_STATUS_OK); |
462 | 0 | EXPECT_EQ(op2->response().status(), QLResponsePB::YQL_STATUS_OK); |
463 | 0 | } |
464 | |
|
465 | 0 | { |
466 | | // Test selecting the first row back. |
467 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
468 | 0 | const YBqlReadOpPtr op = table_.NewReadOp(); |
469 | 0 | auto* const req = op->mutable_request(); |
470 | 0 | QLAddInt32HashValue(req, 1); |
471 | 0 | QLAddStringHashValue(req, "a"); |
472 | 0 | auto* const condition = req->mutable_where_expr()->mutable_condition(); |
473 | 0 | condition->set_op(QL_OP_AND); |
474 | 0 | table_.AddInt32Condition(condition, "r1", QL_OP_EQUAL, 2); |
475 | 0 | table_.AddStringCondition(condition, "r2", QL_OP_EQUAL, "b"); |
476 | 0 | AddAllColumns(req); |
477 | |
|
478 | 0 | const YBSessionPtr session(NewSession()); |
479 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
480 | | |
481 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
482 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
483 | 0 | { |
484 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
485 | 0 | EXPECT_EQ(rowblock->row_count(), 1); |
486 | 0 | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
487 | 0 | } |
488 | | |
489 | | // Test reusing the read op and updating where clause to select the other row. |
490 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'd'; |
491 | 0 | req->mutable_where_expr()->mutable_condition()->mutable_operands()->RemoveLast(); |
492 | 0 | table_.AddStringCondition( |
493 | 0 | req->mutable_where_expr()->mutable_condition(), "r2", QL_OP_EQUAL, "d"); |
494 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
495 | | |
496 | | // Expect 1, 'a', 2, 'd', 4, 'e' returned |
497 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
498 | 0 | { |
499 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
500 | 0 | EXPECT_EQ(rowblock->row_count(), 1); |
501 | 0 | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "d", 4, "e"); |
502 | 0 | } |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | TEST_F(QLDmlTest, TestSelectMultipleRows) { |
507 | | const auto session = NewSession(); |
508 | | { |
509 | | |
510 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
511 | | // insert into t values (1, 'a', 2, 'd', 4, 'e'); |
512 | | const YBqlWriteOpPtr op1 = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
513 | | const YBqlWriteOpPtr op2 = InsertRow(session, 1, "a", 2, "d", 4, "e"); |
514 | | |
515 | | CHECK_OK(session->Flush()); |
516 | | EXPECT_EQ(op1->response().status(), QLResponsePB::YQL_STATUS_OK); |
517 | | EXPECT_EQ(op2->response().status(), QLResponsePB::YQL_STATUS_OK); |
518 | | } |
519 | | |
520 | | { |
521 | | // Test selecting 2 rows with an OR condition. |
522 | | // select * from t where h1 = 1 and h2 = 'a' and r2 = 'b' or r2 = 'd'; |
523 | | const YBqlReadOpPtr op = table_.NewReadOp(); |
524 | | auto* const req = op->mutable_request(); |
525 | | QLAddInt32HashValue(req, 1); |
526 | | QLAddStringHashValue(req, "a"); |
527 | | auto* const condition = req->mutable_where_expr()->mutable_condition(); |
528 | | condition->set_op(QL_OP_OR); |
529 | | table_.AddStringCondition(condition, "r2", QL_OP_EQUAL, "b"); |
530 | | table_.AddStringCondition(condition, "r2", QL_OP_EQUAL, "d"); |
531 | | AddAllColumns(req); |
532 | | |
533 | | CHECK_OK(session->ApplyAndFlush(op)); |
534 | | |
535 | | // Expect 1, 'a', 2, 'b', 3, 'c' and 1, 'a', 2, 'd', 4, 'e' returned |
536 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
537 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
538 | | EXPECT_EQ(rowblock->row_count(), 2); |
539 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
540 | | EXPECT_ROW_VALUES(rowblock->row(1), 1, "a", 2, "d", 4, "e"); |
541 | | } |
542 | | |
543 | | { |
544 | | // Test selecting 2 rows with AND + OR column conditions. |
545 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and (r2 = 'b' or r2 = 'd'); |
546 | | const YBqlReadOpPtr op = table_.NewReadOp(); |
547 | | auto* const req = op->mutable_request(); |
548 | | QLAddInt32HashValue(req, 1); |
549 | | QLAddStringHashValue(req, "a"); |
550 | | auto* condition = req->mutable_where_expr()->mutable_condition(); |
551 | | condition->set_op(QL_OP_AND); |
552 | | table_.AddInt32Condition(condition, "r1", QL_OP_EQUAL, 2); |
553 | | condition = condition->add_operands()->mutable_condition(); |
554 | | condition->set_op(QL_OP_OR); |
555 | | table_.AddStringCondition(condition, "r2", QL_OP_EQUAL, "b"); |
556 | | table_.AddStringCondition(condition, "r2", QL_OP_EQUAL, "d"); |
557 | | AddAllColumns(req); |
558 | | |
559 | | CHECK_OK(session->ApplyAndFlush(op)); |
560 | | |
561 | | // Expect 1, 'a', 2, 'b', 3, 'c' and 1, 'a', 2, 'd', 4, 'e' returned |
562 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
563 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
564 | | EXPECT_EQ(rowblock->row_count(), 2); |
565 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
566 | | EXPECT_ROW_VALUES(rowblock->row(1), 1, "a", 2, "d", 4, "e"); |
567 | | } |
568 | | } |
569 | | |
570 | 0 | TEST_F(QLDmlTest, TestSelectWithoutConditionWithLimit) { |
571 | 0 | { |
572 | | // Insert 100 rows. |
573 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
574 | | // insert into t values (1, 'a', 3, 'b', 4, 'c'); |
575 | | // insert into t values (1, 'a', 4, 'b', 5, 'c'); |
576 | | // ... |
577 | | // insert into t values (1, 'a', 101, 'b', 102, 'c'); |
578 | 0 | const YBSessionPtr session(NewSession()); |
579 | 0 | std::vector<YBqlWriteOpPtr> ops; |
580 | 0 | for (int32_t i = 0; i < 100; i++) { |
581 | 0 | ops.push_back(InsertRow(session, 1, "a", 2 + i, "b", 3 + i, "c")); |
582 | 0 | } |
583 | 0 | EXPECT_OK(session->Flush()); |
584 | 0 | for (const auto& op : ops) { |
585 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
586 | 0 | } |
587 | 0 | } |
588 | |
|
589 | 0 | { |
590 | | // Test selecting multiple rows with a row limit. |
591 | | // select * from t where h1 = 1 and h2 = 'a' limit 5; |
592 | 0 | const YBqlReadOpPtr op = table_.NewReadOp(); |
593 | 0 | auto* const req = op->mutable_request(); |
594 | 0 | QLAddInt32HashValue(req, 1); |
595 | 0 | QLAddStringHashValue(req, "a"); |
596 | 0 | AddAllColumns(req); |
597 | |
|
598 | 0 | req->set_limit(5); |
599 | 0 | const YBSessionPtr session(NewSession()); |
600 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
601 | | |
602 | | // Expect 5 rows: |
603 | | // 1, 'a', 2, 'b', 3, 'c' |
604 | | // 1, 'a', 3, 'b', 4, 'c' |
605 | | // 1, 'a', 4, 'b', 5, 'c' |
606 | | // 1, 'a', 5, 'b', 6, 'c' |
607 | | // 1, 'a', 6, 'b', 7, 'c' |
608 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
609 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
610 | 0 | EXPECT_EQ(rowblock->row_count(), 5); |
611 | 0 | for (int32_t i = 0; i < 5; i++) { |
612 | 0 | EXPECT_ROW_VALUES(rowblock->row(i), 1, "a", 2 + i, "b", 3 + i, "c"); |
613 | 0 | } |
614 | 0 | } |
615 | 0 | } |
616 | | |
617 | | TEST_F(QLDmlTest, TestUpsert) { |
618 | | const YBSessionPtr session(NewSession()); |
619 | | { |
620 | | // Test upserting a row (update as insert). |
621 | | // update t set c1 = 3 where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
622 | | const YBqlWriteOpPtr op(table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT)); |
623 | | auto* const req = op->mutable_request(); |
624 | | req->set_hash_code(0); |
625 | | QLAddInt32HashValue(req, 1); |
626 | | QLAddStringHashValue(req, "a"); |
627 | | QLAddInt32RangeValue(req, 2); |
628 | | QLAddStringRangeValue(req, "b"); |
629 | | table_.AddInt32ColumnValue(req, "c1", 3); |
630 | | CHECK_OK(session->ApplyAndFlush(op)); |
631 | | |
632 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
633 | | } |
634 | | |
635 | | { |
636 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
637 | | const YBqlReadOpPtr op = SelectRow(); |
638 | | |
639 | | // Expect 1, 'a', 2, 'b', 3, null returned |
640 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
641 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
642 | | EXPECT_EQ(rowblock->row_count(), 1); |
643 | | const auto& row = rowblock->row(0); |
644 | | EXPECT_EQ(row.column(0).int32_value(), 1); |
645 | | EXPECT_EQ(row.column(1).string_value(), "a"); |
646 | | EXPECT_EQ(row.column(2).int32_value(), 2); |
647 | | EXPECT_EQ(row.column(3).string_value(), "b"); |
648 | | EXPECT_EQ(row.column(4).int32_value(), 3); |
649 | | EXPECT_TRUE(row.column(5).IsNull()); |
650 | | } |
651 | | |
652 | | { |
653 | | // Test upsert to "insert" an additional column ("c2"). |
654 | | // update t set c2 = 'c' where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
655 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
656 | | auto* const req = op->mutable_request(); |
657 | | QLAddInt32HashValue(req, 1); |
658 | | QLAddStringHashValue(req, "a"); |
659 | | QLAddInt32RangeValue(req, 2); |
660 | | QLAddStringRangeValue(req, "b"); |
661 | | table_.AddStringColumnValue(req, "c2", "c"); |
662 | | CHECK_OK(session->ApplyAndFlush(op)); |
663 | | |
664 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
665 | | } |
666 | | |
667 | | { |
668 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
669 | | const YBqlReadOpPtr op = SelectRow(); |
670 | | |
671 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
672 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
673 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
674 | | EXPECT_EQ(rowblock->row_count(), 1); |
675 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
676 | | } |
677 | | } |
678 | | |
679 | 0 | TEST_F(QLDmlTest, TestDelete) { |
680 | 0 | const YBSessionPtr session(NewSession()); |
681 | 0 | { |
682 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
683 | 0 | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
684 | 0 | ASSERT_OK(session->Flush()); |
685 | 0 | ASSERT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
686 | 0 | } |
687 | |
|
688 | 0 | { |
689 | | // Test deleting a column ("c1"). |
690 | | // delete c1 from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
691 | 0 | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_DELETE); |
692 | 0 | auto* const req = op->mutable_request(); |
693 | 0 | QLAddInt32HashValue(req, 1); |
694 | 0 | QLAddStringHashValue(req, "a"); |
695 | 0 | QLAddInt32RangeValue(req, 2); |
696 | 0 | QLAddStringRangeValue(req, "b"); |
697 | 0 | table_.SetColumn(req->add_column_values(), "c1"); |
698 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
699 | |
|
700 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
701 | 0 | } |
702 | |
|
703 | 0 | { |
704 | | // select c1, c2 from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
705 | 0 | const YBqlReadOpPtr op = SelectRow(session, {"c1", "c2"}, 1, "a", 2, "b"); |
706 | 0 | ASSERT_OK(session->Flush()); |
707 | | |
708 | | // Expect null, 'c' returned |
709 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
710 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
711 | 0 | EXPECT_EQ(rowblock->row_count(), 1); |
712 | 0 | const auto& row = rowblock->row(0); |
713 | 0 | EXPECT_TRUE(row.column(0).IsNull()); |
714 | 0 | EXPECT_EQ(row.column(1).string_value(), "c"); |
715 | 0 | } |
716 | |
|
717 | 0 | { |
718 | | // Test deleting the whole row. |
719 | | // delete from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
720 | 0 | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_DELETE); |
721 | 0 | auto* const req = op->mutable_request(); |
722 | 0 | QLAddInt32HashValue(req, 1); |
723 | 0 | QLAddStringHashValue(req, "a"); |
724 | 0 | QLAddInt32RangeValue(req, 2); |
725 | 0 | QLAddStringRangeValue(req, "b"); |
726 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
727 | |
|
728 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
729 | 0 | } |
730 | |
|
731 | 0 | { |
732 | | // select c1, c2 from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
733 | 0 | const YBqlReadOpPtr op = SelectRow(session, {"c1", "c2"}, 1, "a", 2, "b"); |
734 | 0 | ASSERT_OK(session->Flush()); |
735 | | |
736 | | // Expect no row returned |
737 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
738 | 0 | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
739 | 0 | EXPECT_EQ(rowblock->row_count(), 0); |
740 | 0 | } |
741 | 0 | } |
742 | | |
743 | | TEST_F(QLDmlTest, TestConditionalInsert) { |
744 | | const YBSessionPtr session(NewSession()); |
745 | | { |
746 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
747 | | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
748 | | ASSERT_OK(session->Flush()); |
749 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
750 | | } |
751 | | |
752 | | { |
753 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
754 | | const YBqlReadOpPtr op = SelectRow(); |
755 | | |
756 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
757 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
758 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
759 | | EXPECT_EQ(rowblock->row_count(), 1); |
760 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
761 | | } |
762 | | |
763 | | { |
764 | | // Test IF NOT EXISTS when the row exists |
765 | | // insert into t values (1, 'a', 2, 'b', 4, 'd') if not exists; |
766 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
767 | | auto* const req = op->mutable_request(); |
768 | | QLAddInt32HashValue(req, 1); |
769 | | QLAddStringHashValue(req, "a"); |
770 | | QLAddInt32RangeValue(req, 2); |
771 | | QLAddStringRangeValue(req, "b"); |
772 | | table_.AddInt32ColumnValue(req, "c1", 4); |
773 | | table_.AddStringColumnValue(req, "c2", "d"); |
774 | | auto* const condition = req->mutable_if_expr()->mutable_condition(); |
775 | | condition->set_op(QL_OP_NOT_EXISTS); |
776 | | CHECK_OK(session->ApplyAndFlush(op)); |
777 | | |
778 | | // Expect not applied |
779 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
780 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
781 | | EXPECT_EQ(rowblock->row_count(), 1); |
782 | | const auto& row = rowblock->row(0); |
783 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
784 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
785 | | EXPECT_FALSE(row.column(0).bool_value()); |
786 | | } |
787 | | |
788 | | { |
789 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
790 | | const YBqlReadOpPtr op = SelectRow(); |
791 | | |
792 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
793 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
794 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
795 | | EXPECT_EQ(rowblock->row_count(), 1); |
796 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
797 | | } |
798 | | |
799 | | { |
800 | | // Test IF NOT EXISTS AND a column condition when the row exists and column value is different. |
801 | | // insert into t values (1, 'a', 2, 'b', 4, 'd') if not exists or c2 = 'd'; |
802 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
803 | | auto* const req = op->mutable_request(); |
804 | | QLAddInt32HashValue(req, 1); |
805 | | QLAddStringHashValue(req, "a"); |
806 | | QLAddInt32RangeValue(req, 2); |
807 | | QLAddStringRangeValue(req, "b"); |
808 | | table_.AddInt32ColumnValue(req, "c1", 4); |
809 | | table_.AddStringColumnValue(req, "c2", "d"); |
810 | | auto* condition = req->mutable_if_expr()->mutable_condition(); |
811 | | condition->set_op(QL_OP_OR); |
812 | | table_.AddCondition(condition, QL_OP_NOT_EXISTS); |
813 | | table_.AddStringCondition(condition, "c2", QL_OP_EQUAL, "d"); |
814 | | req->mutable_column_refs()->add_ids(table_.ColumnId("c2")); |
815 | | CHECK_OK(session->ApplyAndFlush(op)); |
816 | | |
817 | | // Expect not applied, return c2 = 'd'. Verify column names ("[applied]" and "c2") also. |
818 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
819 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
820 | | EXPECT_EQ(rowblock->row_count(), 1); |
821 | | const auto& row = rowblock->row(0); |
822 | | EXPECT_EQ(rowblock->schema().num_columns(), 6); |
823 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
824 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
825 | | EXPECT_EQ(rowblock->schema().column(1).name(), "h1"); |
826 | | EXPECT_EQ(rowblock->schema().column(1).type_info()->type(), INT32); |
827 | | EXPECT_EQ(rowblock->schema().column(2).name(), "h2"); |
828 | | EXPECT_EQ(rowblock->schema().column(2).type_info()->type(), STRING); |
829 | | EXPECT_EQ(rowblock->schema().column(3).name(), "r1"); |
830 | | EXPECT_EQ(rowblock->schema().column(3).type_info()->type(), INT32); |
831 | | EXPECT_EQ(rowblock->schema().column(4).name(), "r2"); |
832 | | EXPECT_EQ(rowblock->schema().column(4).type_info()->type(), STRING); |
833 | | EXPECT_EQ(rowblock->schema().column(5).name(), "c2"); |
834 | | EXPECT_EQ(rowblock->schema().column(5).type_info()->type(), STRING); |
835 | | EXPECT_FALSE(row.column(0).bool_value()); |
836 | | EXPECT_EQ(row.column(1).int32_value(), 1); |
837 | | EXPECT_EQ(row.column(2).string_value(), "a"); |
838 | | EXPECT_EQ(row.column(3).int32_value(), 2); |
839 | | EXPECT_EQ(row.column(4).string_value(), "b"); |
840 | | EXPECT_EQ(row.column(5).string_value(), "c"); |
841 | | } |
842 | | |
843 | | { |
844 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
845 | | const YBqlReadOpPtr op = SelectRow(); |
846 | | |
847 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
848 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
849 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
850 | | EXPECT_EQ(rowblock->row_count(), 1); |
851 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
852 | | } |
853 | | |
854 | | { |
855 | | // Test IF NOT EXISTS AND a column condition when the row exists and column value matches. |
856 | | // insert into t values (1, 'a', 2, 'b', 4, 'd') if not exists or c2 = 'c'; |
857 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
858 | | auto* const req = op->mutable_request(); |
859 | | QLAddInt32HashValue(req, 1); |
860 | | QLAddStringHashValue(req, "a"); |
861 | | QLAddInt32RangeValue(req, 2); |
862 | | QLAddStringRangeValue(req, "b"); |
863 | | table_.AddInt32ColumnValue(req, "c1", 4); |
864 | | table_.AddStringColumnValue(req, "c2", "d"); |
865 | | auto* condition = req->mutable_if_expr()->mutable_condition(); |
866 | | condition->set_op(QL_OP_OR); |
867 | | table_.AddCondition(condition, QL_OP_NOT_EXISTS); |
868 | | table_.AddStringCondition(condition, "c2", QL_OP_EQUAL, "c"); |
869 | | req->mutable_column_refs()->add_ids(table_.ColumnId("c2")); |
870 | | CHECK_OK(session->ApplyAndFlush(op)); |
871 | | |
872 | | // Expect applied |
873 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
874 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
875 | | EXPECT_EQ(rowblock->row_count(), 1); |
876 | | const auto& row = rowblock->row(0); |
877 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
878 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
879 | | EXPECT_TRUE(row.column(0).bool_value()); |
880 | | } |
881 | | |
882 | | { |
883 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
884 | | const YBqlReadOpPtr op = SelectRow(); |
885 | | |
886 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
887 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
888 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
889 | | EXPECT_EQ(rowblock->row_count(), 1); |
890 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 4, "d"); |
891 | | } |
892 | | |
893 | | { |
894 | | // Sanity check: test regular insert to override the old row. |
895 | | // insert into t values (1, 'a', 2, 'b', 5, 'e'); |
896 | | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 5, "e"); |
897 | | ASSERT_OK(session->Flush()); |
898 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
899 | | } |
900 | | |
901 | | { |
902 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
903 | | const YBqlReadOpPtr op = SelectRow(); |
904 | | |
905 | | // Expect 1, 'a', 2, 'b', 5, 'e' returned |
906 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
907 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
908 | | EXPECT_EQ(rowblock->row_count(), 1); |
909 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 5, "e"); |
910 | | } |
911 | | } |
912 | | |
913 | | TEST_F(QLDmlTest, TestConditionalUpdate) { |
914 | | const YBSessionPtr session(NewSession()); |
915 | | { |
916 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
917 | | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
918 | | ASSERT_OK(session->Flush()); |
919 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
920 | | } |
921 | | |
922 | | { |
923 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
924 | | const YBqlReadOpPtr op = SelectRow(); |
925 | | |
926 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
927 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
928 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
929 | | EXPECT_EQ(rowblock->row_count(), 1); |
930 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
931 | | } |
932 | | |
933 | | { |
934 | | // Test IF NOT EXISTS when the row exists. |
935 | | // update t set c1 = 6 where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b' if not exists; |
936 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_UPDATE); |
937 | | auto* const req = op->mutable_request(); |
938 | | QLAddInt32HashValue(req, 1); |
939 | | QLAddStringHashValue(req, "a"); |
940 | | QLAddInt32RangeValue(req, 2); |
941 | | QLAddStringRangeValue(req, "b"); |
942 | | table_.AddInt32ColumnValue(req, "c1", 6); |
943 | | auto* const condition = req->mutable_if_expr()->mutable_condition(); |
944 | | condition->set_op(QL_OP_NOT_EXISTS); |
945 | | CHECK_OK(session->ApplyAndFlush(op)); |
946 | | |
947 | | // Expect not applied |
948 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
949 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
950 | | EXPECT_EQ(rowblock->row_count(), 1); |
951 | | const auto& row = rowblock->row(0); |
952 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
953 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
954 | | EXPECT_FALSE(row.column(0).bool_value()); |
955 | | } |
956 | | |
957 | | { |
958 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
959 | | const YBqlReadOpPtr op = SelectRow(); |
960 | | |
961 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
962 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
963 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
964 | | EXPECT_EQ(rowblock->row_count(), 1); |
965 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
966 | | } |
967 | | |
968 | | { |
969 | | // Test IF EXISTS when the row exists. |
970 | | // update t set c1 = 6 where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b' if exists; |
971 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_UPDATE); |
972 | | auto* const req = op->mutable_request(); |
973 | | QLAddInt32HashValue(req, 1); |
974 | | QLAddStringHashValue(req, "a"); |
975 | | QLAddInt32RangeValue(req, 2); |
976 | | QLAddStringRangeValue(req, "b"); |
977 | | table_.AddInt32ColumnValue(req, "c1", 6); |
978 | | auto* const condition = req->mutable_if_expr()->mutable_condition(); |
979 | | condition->set_op(QL_OP_EXISTS); |
980 | | CHECK_OK(session->ApplyAndFlush(op)); |
981 | | |
982 | | // Expect applied |
983 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
984 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
985 | | EXPECT_EQ(rowblock->row_count(), 1); |
986 | | const auto& row = rowblock->row(0); |
987 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
988 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
989 | | EXPECT_TRUE(row.column(0).bool_value()); |
990 | | } |
991 | | |
992 | | { |
993 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
994 | | const YBqlReadOpPtr op = SelectRow(); |
995 | | |
996 | | // Expect 1, 'a', 2, 'b', 6, 'c' returned |
997 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
998 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
999 | | EXPECT_EQ(rowblock->row_count(), 1); |
1000 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 6, "c"); |
1001 | | } |
1002 | | } |
1003 | | |
1004 | | TEST_F(QLDmlTest, TestConditionalDelete) { |
1005 | | const YBSessionPtr session(NewSession()); |
1006 | | { |
1007 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
1008 | | const YBqlWriteOpPtr op = InsertRow(session, 1, "a", 2, "b", 3, "c"); |
1009 | | ASSERT_OK(session->Flush()); |
1010 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1011 | | } |
1012 | | |
1013 | | { |
1014 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
1015 | | const YBqlReadOpPtr op = SelectRow(); |
1016 | | |
1017 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
1018 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1019 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
1020 | | EXPECT_EQ(rowblock->row_count(), 1); |
1021 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
1022 | | } |
1023 | | |
1024 | | { |
1025 | | // Test IF with a column condition when the column value is different. |
1026 | | // delete c1 from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b' if c1 = 4; |
1027 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_DELETE); |
1028 | | auto* const req = op->mutable_request(); |
1029 | | QLAddInt32HashValue(req, 1); |
1030 | | QLAddStringHashValue(req, "a"); |
1031 | | QLAddInt32RangeValue(req, 2); |
1032 | | QLAddStringRangeValue(req, "b"); |
1033 | | table_.SetColumn(req->add_column_values(), "c1"); |
1034 | | table_.SetInt32Condition(req->mutable_if_expr()->mutable_condition(), "c1", QL_OP_EQUAL, 4); |
1035 | | req->mutable_column_refs()->add_ids(table_.ColumnId("c1")); |
1036 | | CHECK_OK(session->ApplyAndFlush(op)); |
1037 | | |
1038 | | // Expect not applied, return c1 = 3. Verify column names also. |
1039 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1040 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
1041 | | EXPECT_EQ(rowblock->row_count(), 1); |
1042 | | const auto& row = rowblock->row(0); |
1043 | | EXPECT_EQ(rowblock->schema().num_columns(), 6); |
1044 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
1045 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
1046 | | EXPECT_EQ(rowblock->schema().column(1).name(), "h1"); |
1047 | | EXPECT_EQ(rowblock->schema().column(1).type_info()->type(), INT32); |
1048 | | EXPECT_EQ(rowblock->schema().column(2).name(), "h2"); |
1049 | | EXPECT_EQ(rowblock->schema().column(2).type_info()->type(), STRING); |
1050 | | EXPECT_EQ(rowblock->schema().column(3).name(), "r1"); |
1051 | | EXPECT_EQ(rowblock->schema().column(3).type_info()->type(), INT32); |
1052 | | EXPECT_EQ(rowblock->schema().column(4).name(), "r2"); |
1053 | | EXPECT_EQ(rowblock->schema().column(4).type_info()->type(), STRING); |
1054 | | EXPECT_EQ(rowblock->schema().column(5).name(), "c1"); |
1055 | | EXPECT_EQ(rowblock->schema().column(5).type_info()->type(), INT32); |
1056 | | EXPECT_FALSE(row.column(0).bool_value()); |
1057 | | EXPECT_EQ(row.column(1).int32_value(), 1); |
1058 | | EXPECT_EQ(row.column(2).string_value(), "a"); |
1059 | | EXPECT_EQ(row.column(3).int32_value(), 2); |
1060 | | EXPECT_EQ(row.column(4).string_value(), "b"); |
1061 | | EXPECT_EQ(row.column(5).int32_value(), 3); |
1062 | | } |
1063 | | |
1064 | | { |
1065 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
1066 | | const YBqlReadOpPtr op = SelectRow(); |
1067 | | |
1068 | | // Expect 1, 'a', 2, 'b', 3, 'c' returned |
1069 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1070 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
1071 | | EXPECT_EQ(rowblock->row_count(), 1); |
1072 | | EXPECT_ROW_VALUES(rowblock->row(0), 1, "a", 2, "b", 3, "c"); |
1073 | | } |
1074 | | |
1075 | | { |
1076 | | // Test IF EXISTS AND a column condition when the row exists and the column value matches. |
1077 | | // delete c1 from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b' if exists and c1 = 3; |
1078 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_DELETE); |
1079 | | auto* const req = op->mutable_request(); |
1080 | | QLAddInt32HashValue(req, 1); |
1081 | | QLAddStringHashValue(req, "a"); |
1082 | | QLAddInt32RangeValue(req, 2); |
1083 | | QLAddStringRangeValue(req, "b"); |
1084 | | table_.SetColumn(req->add_column_values(), "c1"); |
1085 | | auto* const condition = req->mutable_if_expr()->mutable_condition(); |
1086 | | condition->set_op(QL_OP_AND); |
1087 | | table_.AddCondition(condition, QL_OP_EXISTS); |
1088 | | table_.AddInt32Condition(condition, "c1", QL_OP_EQUAL, 3); |
1089 | | req->mutable_column_refs()->add_ids(table_.ColumnId("c1")); |
1090 | | CHECK_OK(session->ApplyAndFlush(op)); |
1091 | | |
1092 | | // Expect applied |
1093 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1094 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
1095 | | EXPECT_EQ(rowblock->row_count(), 1); |
1096 | | const auto& row = rowblock->row(0); |
1097 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
1098 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
1099 | | EXPECT_TRUE(row.column(0).bool_value()); |
1100 | | } |
1101 | | |
1102 | | { |
1103 | | // select * from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'b'; |
1104 | | const YBqlReadOpPtr op = SelectRow(); |
1105 | | |
1106 | | // Expect 1, 'a', 2, 'b', null, 'c' returned |
1107 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1108 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
1109 | | EXPECT_EQ(rowblock->row_count(), 1); |
1110 | | const auto& row = rowblock->row(0); |
1111 | | EXPECT_EQ(row.column(0).int32_value(), 1); |
1112 | | EXPECT_EQ(row.column(1).string_value(), "a"); |
1113 | | EXPECT_EQ(row.column(2).int32_value(), 2); |
1114 | | EXPECT_EQ(row.column(3).string_value(), "b"); |
1115 | | EXPECT_TRUE(row.column(4).IsNull()); |
1116 | | EXPECT_EQ(row.column(5).string_value(), "c"); |
1117 | | } |
1118 | | |
1119 | | { |
1120 | | // Test deleting the whole row with IF EXISTS when the row does not exist (wrong "r1"). |
1121 | | // delete from t where h1 = 1 and h2 = 'a' and r1 = 2 and r2 = 'c' if exists; |
1122 | | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_DELETE); |
1123 | | auto* const req = op->mutable_request(); |
1124 | | QLAddInt32HashValue(req, 1); |
1125 | | QLAddStringHashValue(req, "a"); |
1126 | | QLAddInt32RangeValue(req, 2); |
1127 | | QLAddStringRangeValue(req, "c"); |
1128 | | auto* const condition = req->mutable_if_expr()->mutable_condition(); |
1129 | | condition->set_op(QL_OP_EXISTS); |
1130 | | CHECK_OK(session->ApplyAndFlush(op)); |
1131 | | |
1132 | | // Expect not applied |
1133 | | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1134 | | auto rowblock = RowsResult(op.get()).GetRowBlock(); |
1135 | | EXPECT_EQ(rowblock->row_count(), 1); |
1136 | | const auto& row = rowblock->row(0); |
1137 | | EXPECT_EQ(rowblock->schema().column(0).name(), "[applied]"); |
1138 | | EXPECT_EQ(rowblock->schema().column(0).type_info()->type(), BOOL); |
1139 | | EXPECT_FALSE(row.column(0).bool_value()); |
1140 | | } |
1141 | | } |
1142 | | |
1143 | 0 | TEST_F(QLDmlTest, TestError) { |
1144 | 0 | const YBSessionPtr session(NewSession()); |
1145 | 0 | { |
1146 | | // insert into t values (1, 'a', 2, 'b', 3, 'c'); |
1147 | 0 | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
1148 | 0 | auto* const req = op->mutable_request(); |
1149 | 0 | QLAddInt32HashValue(req, 1); |
1150 | 0 | QLAddStringHashValue(req, "a"); |
1151 | 0 | QLAddInt32RangeValue(req, 2); |
1152 | 0 | QLAddStringRangeValue(req, "b"); |
1153 | 0 | table_.AddInt32ColumnValue(req, "c1", 3); |
1154 | 0 | table_.AddStringColumnValue(req, "c2", "c"); |
1155 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
1156 | |
|
1157 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1158 | 0 | } |
1159 | 0 | { |
1160 | | // Test selecting with incomparable column condition (int32 column "r1" with a string value). |
1161 | | // select c1, c2 from t where h1 = 1 and h2 = 'a' and r1 <> '2' and r2 <> 'b'; |
1162 | 0 | const YBqlReadOpPtr op = table_.NewReadOp(); |
1163 | 0 | auto* const req = op->mutable_request(); |
1164 | 0 | QLAddInt32HashValue(req, 1); |
1165 | 0 | QLAddStringHashValue(req, "a"); |
1166 | 0 | auto* const condition = req->mutable_where_expr()->mutable_condition(); |
1167 | 0 | condition->set_op(QL_OP_AND); |
1168 | 0 | table_.AddStringCondition(condition, "r1", QL_OP_NOT_EQUAL, "2"); |
1169 | 0 | table_.AddStringCondition(condition, "r2", QL_OP_NOT_EQUAL, "b"); |
1170 | 0 | table_.AddColumns({"c1", "c2"}, req); |
1171 | |
|
1172 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
1173 | | |
1174 | | // Expect values not comparable error. |
1175 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_RUNTIME_ERROR); |
1176 | 0 | EXPECT_EQ(op->response().error_message(), "values not comparable"); |
1177 | 0 | } |
1178 | 0 | } |
1179 | | |
1180 | 0 | TEST_F(QLDmlTest, TestSimultaneousReadAndWrite) { |
1181 | 0 | constexpr int kNumIterations = 10; |
1182 | 0 | const YBSessionPtr session(NewSession()); |
1183 | 0 | for (int i = 0; i != kNumIterations; ++i) { |
1184 | 0 | auto write_op = InsertRow(session, 1, "a", i, "b", i * 2, "c"); |
1185 | 0 | std::shared_ptr<YBqlReadOp> read_op; |
1186 | | // Not reading on the first iteration, because there is no record yet. |
1187 | 0 | if (i > 0) { |
1188 | 0 | read_op = SelectRow(session, { "c1", "c2" }, 1, "a", i - 1, "b"); |
1189 | 0 | } |
1190 | 0 | ASSERT_OK(session->Flush()); |
1191 | 0 | ASSERT_EQ(QLResponsePB::YQL_STATUS_OK, write_op->response().status()); |
1192 | 0 | if (read_op) { |
1193 | 0 | ASSERT_EQ(QLResponsePB::YQL_STATUS_OK, read_op->response().status()); |
1194 | |
|
1195 | 0 | auto rowblock = RowsResult(read_op.get()).GetRowBlock(); |
1196 | 0 | ASSERT_EQ(1, rowblock->row_count()); |
1197 | 0 | const auto& row = rowblock->row(0); |
1198 | 0 | ASSERT_EQ((i - 1) * 2, row.column(0).int32_value()); |
1199 | 0 | ASSERT_EQ("c", row.column(1).string_value()); |
1200 | 0 | } |
1201 | 0 | } |
1202 | 0 | } |
1203 | | |
1204 | 0 | TEST_F(QLDmlTest, OpenRecentlyCreatedTable) { |
1205 | 0 | constexpr int kNumIterations = 10; |
1206 | 0 | constexpr int kNumKeys = 100; |
1207 | 0 | const auto kOpenTimeout = 30s; |
1208 | 0 | const auto kMaxWait = 5s; |
1209 | |
|
1210 | 0 | for (int i = 0; i != kNumIterations; ++i) { |
1211 | 0 | client::YBTableName table_name(master::GetDefaultDatabaseType(kTableName.namespace_name()), |
1212 | 0 | kTableName.namespace_name(), |
1213 | 0 | Format("table_$0", i)); |
1214 | 0 | std::thread table_creation_thread([this, table_name] { |
1215 | 0 | YBSchemaBuilder builder; |
1216 | 0 | builder.AddColumn("k")->Type(INT32)->HashPrimaryKey()->NotNull(); |
1217 | 0 | builder.AddColumn("v")->Type(INT32); |
1218 | 0 | TableHandle table; |
1219 | 0 | ASSERT_OK(table.Create(table_name, 9, client_.get(), &builder)); |
1220 | 0 | }); |
1221 | 0 | TableHandle table; |
1222 | 0 | BackoffWaiter waiter(std::chrono::steady_clock::now() + kOpenTimeout, kMaxWait); |
1223 | 0 | while (!table.Open(table_name, client_.get()).ok()) { |
1224 | 0 | ASSERT_TRUE(waiter.Wait()); |
1225 | 0 | } |
1226 | 0 | auto session = NewSession(); |
1227 | 0 | for (int k = 0; k != kNumKeys; ++k) { |
1228 | 0 | auto op = table.NewWriteOp(QLWriteRequestPB::QL_STMT_INSERT); |
1229 | 0 | auto* const req = op->mutable_request(); |
1230 | 0 | QLAddInt32HashValue(req, k); |
1231 | 0 | table.AddInt32ColumnValue(req, "v", -k); |
1232 | 0 | session->Apply(op); |
1233 | 0 | } |
1234 | 0 | ASSERT_OK(session->Flush()); |
1235 | 0 | table_creation_thread.join(); |
1236 | 0 | } |
1237 | 0 | } |
1238 | | |
1239 | 0 | TEST_F(QLDmlTest, ReadFollower) { |
1240 | 0 | DontVerifyClusterBeforeNextTearDown(); |
1241 | 0 | FLAGS_flush_rocksdb_on_shutdown = false; |
1242 | 0 | constexpr int kNumRows = RegularBuildVsSanitizers(5000, 1000); |
1243 | |
|
1244 | 0 | ASSERT_NO_FATALS(InsertRows(kNumRows)); |
1245 | |
|
1246 | 0 | auto must_see_all_rows_after_this_deadline = MonoTime::Now() + 5s * kTimeMultiplier; |
1247 | 0 | auto session = NewSession(); |
1248 | 0 | for (int i = 0; i != kNumRows; ++i) { |
1249 | 0 | for (;;) { |
1250 | 0 | auto row = ReadRow(session, KeyForIndex(i), YBConsistencyLevel::CONSISTENT_PREFIX); |
1251 | 0 | if (!row.ok() && row.status().IsNotFound()) { |
1252 | 0 | ASSERT_LE(MonoTime::Now(), must_see_all_rows_after_this_deadline); |
1253 | 0 | continue; |
1254 | 0 | } |
1255 | 0 | ASSERT_OK(row); |
1256 | 0 | ASSERT_EQ(*row, ValueForIndex(i)); |
1257 | 0 | break; |
1258 | 0 | } |
1259 | 0 | } |
1260 | |
|
1261 | 0 | LOG(INFO) << "All rows were read successfully"; |
1262 | |
|
1263 | 0 | for (size_t i = 0; i != cluster_->num_tablet_servers(); ++i) { |
1264 | 0 | cluster_->mini_tablet_server(i)->Shutdown(); |
1265 | 0 | } |
1266 | |
|
1267 | 0 | ASSERT_OK(cluster_->mini_tablet_server(0)->Start()); |
1268 | | // Since this will be the only alive tserver, there won't be any |
1269 | | // UpdateConsensus requests to update the safe time. So staleness |
1270 | | // will keep increasing. Disable staleness for the verification |
1271 | | // step. |
1272 | 0 | FLAGS_max_stale_read_bound_time_ms = 0; |
1273 | | |
1274 | | |
1275 | | // Check that after restart we don't miss any rows. |
1276 | 0 | std::vector<size_t> missing_rows; |
1277 | 0 | for (int i = 0; i != kNumRows; ++i) { |
1278 | 0 | auto row = ReadRow(session, KeyForIndex(i), YBConsistencyLevel::CONSISTENT_PREFIX); |
1279 | 0 | if (!row.ok() && row.status().IsNotFound()) { |
1280 | 0 | missing_rows.push_back(i); |
1281 | 0 | continue; |
1282 | 0 | } |
1283 | 0 | ASSERT_OK(row); |
1284 | 0 | ASSERT_EQ(*row, ValueForIndex(i)); |
1285 | 0 | } |
1286 | |
|
1287 | 0 | ASSERT_TRUE(missing_rows.empty()) << "Missing rows: " << yb::ToString(missing_rows); |
1288 | 0 | } |
1289 | | |
1290 | 0 | TEST_F(QLDmlTest, DeletePartialRangeKey) { |
1291 | 0 | auto session = NewSession(); |
1292 | 0 | RowKey row_key{1, "a", 2, "b"}; |
1293 | |
|
1294 | 0 | { |
1295 | 0 | auto op = InsertRow(session, row_key, {3, "c"}); |
1296 | 0 | ASSERT_OK(session->Flush()); |
1297 | 0 | ASSERT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1298 | 0 | } |
1299 | |
|
1300 | 0 | { |
1301 | 0 | const YBqlWriteOpPtr op = table_.NewWriteOp(QLWriteRequestPB::QL_STMT_DELETE); |
1302 | 0 | auto* const req = op->mutable_request(); |
1303 | 0 | QLAddInt32HashValue(req, row_key.h1); |
1304 | 0 | QLAddStringHashValue(req, row_key.h2); |
1305 | 0 | QLAddInt32RangeValue(req, row_key.r1); |
1306 | 0 | CHECK_OK(session->ApplyAndFlush(op)); |
1307 | 0 | EXPECT_EQ(op->response().status(), QLResponsePB::YQL_STATUS_OK); |
1308 | 0 | } |
1309 | |
|
1310 | 0 | auto row = ReadRow(session, row_key); |
1311 | 0 | ASSERT_TRUE(!row.ok() && row.status().IsNotFound()) << "Unexpected result: " << row; |
1312 | 0 | } |
1313 | | |
1314 | | } // namespace client |
1315 | | } // namespace yb |