YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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