YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/test/ql-semanalyzer-test.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include <memory>
15
16
#include "yb/common/ql_type.h"
17
18
#include "yb/util/result.h"
19
#include "yb/util/status_log.h"
20
#include "yb/util/varint.h"
21
22
#include "yb/yql/cql/ql/ptree/parse_tree.h"
23
#include "yb/yql/cql/ql/ptree/pt_create_table.h"
24
#include "yb/yql/cql/ql/ptree/pt_expr.h"
25
#include "yb/yql/cql/ql/ptree/pt_select.h"
26
#include "yb/yql/cql/ql/ptree/pt_table_property.h"
27
#include "yb/yql/cql/ql/test/ql-test-base.h"
28
29
namespace yb {
30
namespace ql {
31
32
using std::make_shared;
33
using std::string;
34
using strings::Substitute;
35
using std::dynamic_pointer_cast;
36
37
class QLTestAnalyzer: public QLTestBase {
38
 public:
39
0
  QLTestAnalyzer() : QLTestBase() {
40
0
  }
41
42
  // TODO(omer): should also input the index_tree, in order to ensure the id found is the id of the
43
  // index that should be used, but I do not know how to do this yet, as index_tree has the name of
44
  // the index while select_tree has the id.
45
  void TestIndexSelection(const string& select_stmt,
46
                          const bool use_index,
47
0
                          const bool covers_fully) {
48
0
    ParseTree::UniPtr parse_tree;
49
0
    EXPECT_OK(TestAnalyzer(select_stmt, &parse_tree));
50
51
0
    TreeNode::SharedPtr root = parse_tree->root();
52
0
    CHECK_EQ(TreeNodeOpcode::kPTSelectStmt, root->opcode());
53
0
    auto pt_select_stmt = std::static_pointer_cast<PTSelectStmt>(root);
54
0
    auto pt_child_select = pt_select_stmt->child_select();
55
0
    EXPECT_EQ(pt_child_select != nullptr, use_index) << select_stmt;
56
0
    EXPECT_EQ(pt_child_select != nullptr && pt_child_select->covers_fully(), covers_fully)
57
0
        << select_stmt;
58
0
  }
59
60
  PTJsonColumnWithOperators* RetrieveJsonColumn(const ParseTree::UniPtr& parse_tree,
61
0
                                                const DataType& expected_type) {
62
0
    auto select_stmt = std::dynamic_pointer_cast<PTSelectStmt>(parse_tree->root());
63
0
    auto ptrelation2 = std::dynamic_pointer_cast<PTRelation2>(select_stmt->where_clause());
64
0
    EXPECT_EQ(expected_type, ptrelation2->op1()->ql_type()->main());
65
0
    return std::dynamic_pointer_cast<PTJsonColumnWithOperators>(ptrelation2->op1()).get();
66
0
  }
67
68
0
  std::string RetrieveJsonElement(const PTJsonColumnWithOperators* const jsoncolumn, int index) {
69
0
    auto element = dynamic_pointer_cast<PTJsonOperator>(jsoncolumn->operators()->element(index));
70
0
    auto text = dynamic_pointer_cast<PTConstText>(element->arg());
71
0
    return string(text->value()->c_str());
72
0
  }
73
};
74
75
0
TEST_F(QLTestAnalyzer, TestCreateTablePropertyAnalyzer) {
76
0
  CreateSimulatedCluster();
77
78
  // Analyze the sql statement.
79
0
  ParseTree::UniPtr parse_tree;
80
0
  string create_stmt = "CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY (c1)) WITH "
81
0
      "default_time_to_live = 1000";
82
0
  CHECK_OK(TestAnalyzer(create_stmt, &parse_tree));
83
84
  // Now verify the analysis was done correctly.
85
0
  TreeNode::SharedPtr root = parse_tree->root();
86
0
  EXPECT_EQ(TreeNodeOpcode::kPTCreateTable, root->opcode());
87
0
  PTCreateTable::SharedPtr pt_create_table = std::static_pointer_cast<PTCreateTable>(root);
88
89
  // Verify table properties.
90
0
  PTTablePropertyListNode::SharedPtr table_properties = pt_create_table->table_properties();
91
0
  EXPECT_EQ(1, table_properties->size());
92
0
  PTTableProperty::SharedPtr table_property = table_properties->element(0);
93
0
  EXPECT_EQ(std::string("default_time_to_live"), table_property->lhs()->c_str());
94
0
  PTConstVarInt::SharedPtr rhs = std::static_pointer_cast<PTConstVarInt>(table_property->rhs());
95
0
  auto from_str = ASSERT_RESULT(util::VarInt::CreateFromString(rhs->Eval()->c_str()));
96
0
  EXPECT_EQ(util::VarInt(1000), from_str);
97
0
}
98
99
0
TEST_F(QLTestAnalyzer, TestCreateTableAnalyze) {
100
0
  CreateSimulatedCluster();
101
102
  // Analyze the sql statement.
103
0
  ParseTree::UniPtr parse_tree;
104
105
  // Duplicate hash and cluster columns.
106
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
107
0
      "((c1, c1)))", &parse_tree);
108
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
109
0
      "((c1), c1))", &parse_tree);
110
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
111
0
      "((c1), c2, c2))", &parse_tree);
112
113
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
114
0
      "(c1)) WITH default_time_to_live = 1000 AND default_time_to_live = 2000", &parse_tree);
115
0
  ANALYZE_VALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
116
0
      "(c1)) WITH default_time_to_live = 1000", &parse_tree);
117
0
  ANALYZE_VALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
118
0
      "(c1))", &parse_tree);
119
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (c1 int, c2 int, c3 int, PRIMARY KEY "
120
0
      "(c1)) WITH default_time_to_live = 1000.1", &parse_tree);
121
0
}
122
123
0
TEST_F(QLTestAnalyzer, TestCreateTableWithStaticColumn) {
124
0
  CreateSimulatedCluster();
125
126
  // Test static column analysis.
127
0
  ParseTree::UniPtr parse_tree;
128
0
  ANALYZE_VALID_STMT("CREATE TABLE foo (h1 int, r1 int, s1 int static, "
129
0
                     "PRIMARY KEY ((h1), r1));", &parse_tree);
130
  // Invalid: hash column cannot be static.
131
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (h1 int, h2 int static, r1 int, "
132
0
                       "PRIMARY KEY ((h1, h2), r1));", &parse_tree);
133
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (h1 int static primary key);", &parse_tree);
134
  // Invalid: range column cannot be static.
135
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (h1 int, r1 int, r2 int static, "
136
0
                       "PRIMARY KEY ((h1), r1, r2));", &parse_tree);
137
  // Invalid: no static column for table hash key column only.
138
0
  ANALYZE_INVALID_STMT("CREATE TABLE foo (h1 int, h2 int, s int static, c int, "
139
0
                       "PRIMARY KEY ((h1, h2)));", &parse_tree);
140
0
}
141
142
TEST_F(QLTestAnalyzer, TestJsonColumn) {
143
  CreateSimulatedCluster();
144
  TestQLProcessor *processor = GetQLProcessor();
145
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, c1 jsonb, c2 int, PRIMARY KEY (h1));"));
146
147
  ParseTree::UniPtr parse_tree;
148
149
  ANALYZE_VALID_STMT("SELECT * FROM t WHERE c1->'a'->'b'->>'c' = '1'", &parse_tree);
150
  auto jsoncolumn = RetrieveJsonColumn(parse_tree, DataType::STRING);
151
  EXPECT_TRUE(jsoncolumn != nullptr);
152
  EXPECT_TRUE(jsoncolumn->name()->IsSimpleName());
153
  EXPECT_EQ("c1", jsoncolumn->name()->first_name());
154
155
  EXPECT_EQ(3, jsoncolumn->operators()->size());
156
  EXPECT_EQ(JsonOperator::JSON_OBJECT,
157
            dynamic_pointer_cast<PTJsonOperator>(
158
            jsoncolumn->operators()->element(0))->json_operator());
159
  EXPECT_EQ("a", RetrieveJsonElement(jsoncolumn, 0));
160
161
  EXPECT_EQ(JsonOperator::JSON_OBJECT,
162
            dynamic_pointer_cast<PTJsonOperator>(
163
            jsoncolumn->operators()->element(1))->json_operator());
164
  EXPECT_EQ("b", RetrieveJsonElement(jsoncolumn, 1));
165
166
  EXPECT_EQ(JsonOperator::JSON_TEXT,
167
            dynamic_pointer_cast<PTJsonOperator>(
168
            jsoncolumn->operators()->element(2))->json_operator());
169
  EXPECT_EQ("c", RetrieveJsonElement(jsoncolumn, 2));
170
171
  ANALYZE_VALID_STMT("SELECT * FROM t WHERE c1->>'a' = '1'", &parse_tree);
172
  auto jsoncolumn1 = RetrieveJsonColumn(parse_tree, DataType::STRING);
173
  EXPECT_EQ(1, jsoncolumn1->operators()->size());
174
  EXPECT_EQ(JsonOperator::JSON_TEXT,
175
            dynamic_pointer_cast<PTJsonOperator>(
176
            jsoncolumn1->operators()->element(0))->json_operator());
177
  EXPECT_EQ("a", RetrieveJsonElement(jsoncolumn1, 0));
178
  ANALYZE_VALID_STMT("SELECT * FROM t WHERE c1->'a' = '1'", &parse_tree);
179
  auto jsoncolumn2 = RetrieveJsonColumn(parse_tree, DataType::JSONB);
180
  EXPECT_EQ(1, jsoncolumn2->operators()->size());
181
  EXPECT_EQ(JsonOperator::JSON_OBJECT,
182
            dynamic_pointer_cast<PTJsonOperator>(
183
            jsoncolumn2->operators()->element(0))->json_operator());
184
  EXPECT_EQ("a", RetrieveJsonElement(jsoncolumn2, 0));
185
186
  // Comparing string and integer.
187
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE c1->'a'->'b'->>'c' = 1", &parse_tree);
188
  // Column is not json.
189
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE c2->>'a' = '1'", &parse_tree);
190
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE h1->'a' = '1'", &parse_tree);
191
192
  // Update json column.
193
  ANALYZE_VALID_STMT("UPDATE t SET c1->'a'->'b'->'c' = '2' WHERE h1 = 1", &parse_tree);
194
  ANALYZE_VALID_STMT("UPDATE t SET c1->'a' = '2' WHERE h1 = 1", &parse_tree);
195
196
  // Invalid updates.
197
  ANALYZE_INVALID_STMT("UPDATE t SET c1->>'a' = '2' WHERE h1 = 1", &parse_tree);
198
  ANALYZE_INVALID_STMT("UPDATE t SET c1.a.b.c = '2' WHERE h1 = 1", &parse_tree);
199
}
200
201
0
TEST_F(QLTestAnalyzer, TestDmlWithStaticColumn) {
202
0
  CreateSimulatedCluster();
203
0
  TestQLProcessor *processor = GetQLProcessor();
204
0
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, h2 int, r1 int, r2 int, s1 int static, c1 int, "
205
0
                          "PRIMARY KEY ((h1, h2), r1, r2));"));
206
207
0
  ParseTree::UniPtr parse_tree;
208
209
  // Test insert with hash key only.
210
0
  ANALYZE_VALID_STMT("INSERT INTO t (h1, h2, s1) VALUES (1, 1, 1);", &parse_tree);
211
212
  // Test update with hash key only.
213
0
  ANALYZE_VALID_STMT("UPDATE t SET s1 = 1 WHERE h1 = 1 AND h2 = 1;", &parse_tree);
214
215
  // TODO: Test delete with hash key only.
216
  // ANALYZE_VALID_STMT("DELETE c1 FROM t WHERE h1 = 1 and r1 = 1;", &parse_tree);
217
218
  // Test select with distinct columns.
219
0
  ANALYZE_VALID_STMT("SELECT DISTINCT h1, h2, s1 FROM t;", &parse_tree);
220
0
  ANALYZE_VALID_STMT("SELECT DISTINCT s1 FROM t WHERE h1 = 1 AND h2 = 1;", &parse_tree);
221
0
  ANALYZE_VALID_STMT("SELECT DISTINCT s1 FROM t;", &parse_tree);
222
223
  // Invalid: Missing a hash primary-key column.
224
0
  ANALYZE_INVALID_STMT("SELECT DISTINCT h1, s1 FROM t;", &parse_tree);
225
0
  ANALYZE_INVALID_STMT("SELECT DISTINCT h1, s1 FROM t WHERE h2 = 1;", &parse_tree);
226
227
  // Invalid: cannot select distinct with non hash primary-key column.
228
0
  ANALYZE_INVALID_STMT("SELECT DISTINCT h1, h2, r1, s1 FROM t;", &parse_tree);
229
230
  // Invalid: cannot select distinct with non-static column.
231
0
  ANALYZE_INVALID_STMT("SELECT DISTINCT h1, h2, c1 FROM t;", &parse_tree);
232
233
  // Invalid: cannot select distinct with non hash primary-key / non-static column.
234
0
  ANALYZE_INVALID_STMT("SELECT DISTINCT * FROM t;", &parse_tree);
235
236
  // Invalid: cannot insert or update with partial range columns.
237
0
  ANALYZE_INVALID_STMT("INSERT INTO t (h1, h2, r1, s1) VALUES (1, 1, 1, 1);", &parse_tree);
238
0
  ANALYZE_INVALID_STMT("UPDATE t SET s1 = 1 WHERE h1 = 1 AND h2 = 1 AND r1 = 1;", &parse_tree);
239
0
}
240
241
0
TEST_F(QLTestAnalyzer, TestWhereClauseAnalyzer) {
242
0
  CreateSimulatedCluster();
243
0
  TestQLProcessor *processor = GetQLProcessor();
244
0
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, r1 varchar, c1 varchar, "
245
0
                          "PRIMARY KEY ((h1), r1));"));
246
247
0
  ParseTree::UniPtr parse_tree;
248
  // OR and NOT logical operator are not supported yet.
249
0
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE h1 = 1 AND r1 = 2 OR r2 = 3", &parse_tree);
250
0
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE h1 = 1 AND NOT r1 = 2", &parse_tree);
251
252
0
  CHECK_OK(processor->Run("DROP TABLE t;"));
253
0
}
254
255
0
TEST_F(QLTestAnalyzer, TestIfClauseAnalyzer) {
256
0
  CreateSimulatedCluster();
257
0
  TestQLProcessor *processor = GetQLProcessor();
258
0
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, r1 int, c1 int, "
259
0
                          "PRIMARY KEY ((h1), r1));"));
260
261
0
  ParseTree::UniPtr parse_tree;
262
  // Valid case: if not exists or if <col> = xxx.
263
0
  ANALYZE_VALID_STMT("UPDATE t SET c1 = 1 WHERE h1 = 1 AND r1 = 1 IF NOT EXISTS or c1 = 0",
264
0
                     &parse_tree);
265
266
  // Invalid cases: primary key columns not allowed in if clause.
267
0
  ANALYZE_INVALID_STMT("UPDATE t SET c1 = 1 WHERE h1 = 1 AND r1 = 1 IF h1 = 1", &parse_tree);
268
0
  ANALYZE_INVALID_STMT("UPDATE t SET c1 = 1 WHERE h1 = 1 AND r1 = 1 IF r1 = 1", &parse_tree);
269
0
  CHECK_OK(processor->Run("DROP TABLE t;"));
270
0
}
271
272
0
TEST_F(QLTestAnalyzer, TestBindVariableAnalyzer) {
273
0
  CreateSimulatedCluster();
274
0
  TestQLProcessor *processor = GetQLProcessor();
275
0
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, r1 varchar, c1 varchar, "
276
0
                          "PRIMARY KEY ((h1), r1));"));
277
278
  // Analyze the sql statement.
279
0
  ParseTree::UniPtr parse_tree;
280
0
  ANALYZE_VALID_STMT("SELECT * FROM t WHERE h1 = ? AND r1 = ?;", &parse_tree);
281
0
  ANALYZE_VALID_STMT("UPDATE t set c1 = :1 WHERE h1 = :2 AND r1 = :3;", &parse_tree);
282
0
  ANALYZE_VALID_STMT("UPDATE t set c1 = ? WHERE h1 = ? AND r1 = ?;", &parse_tree);
283
0
  ANALYZE_VALID_STMT("INSERT INTO t (h1, r1, c1) VALUES (?, ?, ?);", &parse_tree);
284
285
  // Bind var cannot be used in a logical boolean context.
286
0
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE NOT ?", &parse_tree);
287
288
  // Bind var not supported in an expression (yet).
289
0
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE h1 = (- ?)", &parse_tree);
290
0
  ANALYZE_INVALID_STMT("SELECT * FROM t WHERE h1 = (- :1)", &parse_tree);
291
292
0
  CHECK_OK(processor->Run("DROP TABLE t;"));
293
0
}
294
295
0
TEST_F(QLTestAnalyzer, TestCreateIndex) {
296
0
  CreateSimulatedCluster();
297
0
  TestQLProcessor *processor = GetQLProcessor();
298
0
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, h2 text, r1 int, r2 text, c1 int, c2 text, "
299
0
                          "PRIMARY KEY ((h1, h2), r1, r2)) "
300
0
                          "with transactions = {'enabled':true};"));
301
302
  // Analyze the sql statement.
303
0
  ParseTree::UniPtr parse_tree;
304
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t ((r1), r2);", &parse_tree);
305
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t ((r1, r2), h1, h2);", &parse_tree);
306
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t ((h1, h2), r1, r2);", &parse_tree);
307
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (h1);", &parse_tree);
308
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (r1);", &parse_tree);
309
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (c1);", &parse_tree);
310
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (r2, r1) INCLUDE (c1);", &parse_tree);
311
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (r2, r1, h1, h2) INCLUDE (c1) WITH CLUSTERING ORDER BY "
312
0
                     "(r1 DESC, h1 DESC, h2 ASC);", &parse_tree);
313
314
  // Duplicate covering columns - should succeed
315
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t ((r1), r2) INCLUDE (r1);", &parse_tree);
316
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t ((r1), r2) INCLUDE (r2);", &parse_tree);
317
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (r1, r2, c1) INCLUDE (c1);", &parse_tree);
318
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t (r1, r2) INCLUDE (c1, c1);", &parse_tree);
319
320
  // Duplicate primary key columns.
321
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t (r1, r1);", &parse_tree);
322
323
  // Non-clustering key column in order by.
324
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t (r2, r1) INCLUDE (c1) WITH CLUSTERING ORDER BY "
325
0
                       "(r2 DESC, r1 ASC);", &parse_tree);
326
327
  // Non-existent table.
328
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t2 (r1, r2);", &parse_tree);
329
330
  // Index on system table cannot be created.
331
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON system_schema.tables (id);", &parse_tree);
332
333
334
0
  CHECK_OK(processor->Run("CREATE TABLE t2 (h1 int, h2 text, r1 int, r2 text, c list<int>, "
335
0
                          "PRIMARY KEY ((h1, h2), r1, r2)) "
336
0
                          "with transactions = {'enabled':true};"));
337
  // Unsupported complex type.
338
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t2 (c);", &parse_tree);
339
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t2 ((r1), c);", &parse_tree);
340
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t2 (r1, r2) INCLUDE (c);", &parse_tree);
341
342
0
  CHECK_OK(processor->Run("CREATE TABLE t3 (k int primary key, c int);"));
343
344
  // Index on non-transactional table.
345
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t3 (c);", &parse_tree);
346
347
  // JSON secondary index on attributes.
348
0
  CHECK_OK(processor->Run("CREATE TABLE t4 (h1 int, h2 text, r1 int, r2 text, j jsonb, "
349
0
                          "PRIMARY KEY ((h1, h2), r1, r2)) "
350
0
                          "with transactions = {'enabled':true};"));
351
352
  // Analyze the sql statement.
353
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->>'a');", &parse_tree);
354
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->'a'->>'b');", &parse_tree);
355
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->'a'->'b'->>'c');", &parse_tree);
356
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->>'a'));", &parse_tree);
357
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->'a'->>'b'));", &parse_tree);
358
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->'a'->'b'->>'c'));", &parse_tree);
359
  // Multiple JSON columns, as hash and range columns.
360
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->>'a', j->>'x');", &parse_tree);
361
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->>'a'), j->>'x');", &parse_tree);
362
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->>'a', j->>'x'));", &parse_tree);
363
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->'a'->>'b', j->'x'->>'y');", &parse_tree);
364
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->'a'->>'b'), j->'x'->>'y');", &parse_tree);
365
0
  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 ((j->'a'->>'b', j->'x'->>'y'));", &parse_tree);
366
  // Covering column.
367
  // TODO: sem_context.h:215: Check failed: sem_state_ State variable is not set for the expression
368
//  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->>'a') INCLUDE (j->>'x');", &parse_tree);
369
//  ANALYZE_VALID_STMT("CREATE INDEX i ON t4 (j->>'a') INCLUDE (j->>'x', j->>'k');", &parse_tree);
370
0
}
371
372
0
TEST_F(QLTestAnalyzer, TestCreateLocalIndex) {
373
0
  CreateSimulatedCluster();
374
0
  TestQLProcessor *processor = GetQLProcessor();
375
0
  CHECK_OK(processor->Run("CREATE TABLE t (h1 int, h2 text, r1 int, r2 text, c1 int, c2 text, "
376
0
                          "PRIMARY KEY ((h1, h2), r1, r2));"));
377
378
  // Analyze the sql statement.
379
  // Because transactions are not enabled, an index creation will fail iff the index is not local.
380
  // TODO: Mark indexes on ((h1, h2)...) as valid once local index is implemented.
381
0
  ParseTree::UniPtr parse_tree;
382
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2));", &parse_tree);
383
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2), c2, c1);", &parse_tree);
384
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2), r2, c1);", &parse_tree);
385
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2), r1, r2);", &parse_tree);
386
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2), r1, r2, c1, c2);", &parse_tree);
387
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t (h1, h2, r1, r2);", &parse_tree);
388
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1), h2, r1, r2);", &parse_tree);
389
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((r1, r2), h1, h2);", &parse_tree);
390
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h2), r1, r2);", &parse_tree);
391
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2, r1));", &parse_tree);
392
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, h2, r1, r2), c1, c2);", &parse_tree);
393
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t ((h1, r1), h2);", &parse_tree);
394
0
  ANALYZE_INVALID_STMT("CREATE INDEX i ON t (h1, h2);", &parse_tree);
395
0
}
396
397
398
0
TEST_F(QLTestAnalyzer, TestIndexSelection) {
399
0
  CreateSimulatedCluster();
400
0
  TestQLProcessor *processor = GetQLProcessor();
401
0
  EXPECT_OK(processor->Run("CREATE TABLE t (h1 int, h2 int, r1 int, r2 int, c1 int, c2 int, "
402
0
                           "PRIMARY KEY ((h1, h2), r1, r2)) "
403
0
                           "with transactions = {'enabled':true};"));
404
405
0
  ParseTree::UniPtr select_parse_tree, parse_tree;
406
407
  // Should not use index if there is no index.
408
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND h2 = 1 AND r1 = 1", false, false);
409
410
0
  EXPECT_OK(processor->Run("CREATE INDEX i1 ON t ((h1, h2), r1);"));
411
0
  EXPECT_OK(processor->Run("CREATE INDEX i2 ON t ((h1, h2), c2, c1);"));
412
0
  EXPECT_OK(processor->Run("CREATE INDEX i3 ON t ((h1, h2), r2) INCLUDE (c1);"));
413
0
  EXPECT_OK(processor->Run("CREATE INDEX i4 ON t ((h1, h2), r2) INCLUDE (c2);"));
414
0
  EXPECT_OK(processor->Run("CREATE INDEX i5 ON t ((h1, h2), c1);"));
415
0
  EXPECT_OK(processor->Run("CREATE INDEX i6 ON t ((c1, r2), c2);"));
416
0
  EXPECT_OK(processor->Run("CREATE INDEX i7 ON t ((h2, h1), c1) INCLUDE (c2);"));
417
418
0
  client::YBTableName table_name(YQL_DATABASE_CQL, kDefaultKeyspaceName, "t");
419
0
  processor->RemoveCachedTableDesc(table_name);
420
421
  // Should select from the indexed table.
422
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND c2 = 1 AND c1 = 1", false, false);
423
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND h2 = 1 AND r1 = 1", false, false);
424
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND c1 = 1 AND c2 = 1", false, false);
425
426
  // None for i1, as the table itself is always better.
427
428
  // Should use i2.
429
0
  TestIndexSelection("SELECT c2, r1 FROM t WHERE h1 = 1 AND h2 = 1 AND c2 = 1 AND c1 = 1",
430
0
                     true, true);
431
  // Check that fully specified beats range clauses.
432
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND h2 = 1 AND c2 = 1 AND r1 > 0 AND r1 < 2",
433
0
                     true, true);
434
435
  // Should use i3.
436
0
  TestIndexSelection("SELECT c1 FROM t WHERE h1 = 1 AND h2 = 1 AND r2 = 1", true, true);
437
438
  // Should use i3 as uncovered index.
439
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND h2 = 1 AND r2 = 1", true, false);
440
441
  // Should use i4.
442
0
  TestIndexSelection("SELECT c2, r1 FROM t WHERE h1 = 1 AND h2 = 1 AND r2 = 1", true, true);
443
0
  TestIndexSelection("SELECT h2, c2, r1 FROM t WHERE h1 = 1 AND h2 = 1 AND r2 = 1 AND "
444
0
                     "c2 = 1 AND r1 > 0 AND r1 < 2", true, true);
445
446
  // Should use i5.
447
  // Check that local beats-non-local.
448
0
  TestIndexSelection("SELECT c1 FROM t WHERE h1 = 1 AND h2 = 1 AND c1 = 1", true, true);
449
450
  // Should use i6.
451
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND h2 = 1 AND r2 = 1 AND c1 = 1 AND c2 = 1",
452
0
                     true, true);
453
454
  // Should use i7.
455
  // Check that non-local beats local if it can perform the read.
456
0
  TestIndexSelection("SELECT * FROM t WHERE h1 = 1 AND h2 = 1 AND c1 = 1", true, true);
457
0
  TestIndexSelection("SELECT c2 FROM t WHERE h1 = 1 AND h2 = 1 AND r2 = 1 AND c1 = 1", true, true);
458
0
}
459
460
0
TEST_F(QLTestAnalyzer, TestIndexBasedOnJsonAttribute) {
461
0
  CreateSimulatedCluster();
462
0
  TestQLProcessor *processor = GetQLProcessor();
463
0
  EXPECT_OK(processor->Run("CREATE TABLE t (h1 int, h2 int, r1 int, r2 int, j jsonb, "
464
0
                           "PRIMARY KEY ((h1, h2), r1, r2)) "
465
0
                           "with transactions = {'enabled':true};"));
466
467
0
  EXPECT_OK(processor->Run("CREATE INDEX i1 ON t (j->>'a');"));
468
0
  EXPECT_NOT_OK(processor->Run("CREATE INDEX i2 ON t (j->3);"));
469
0
}
470
471
0
TEST_F(QLTestAnalyzer, TestTruncate) {
472
0
  CreateSimulatedCluster();
473
0
  TestQLProcessor *processor = GetQLProcessor();
474
0
  CHECK_OK(processor->Run("CREATE TABLE t (h int PRIMARY KEY);"));
475
476
  // Analyze the TRUNCATE statement.
477
0
  ParseTree::UniPtr parse_tree;
478
0
  ANALYZE_VALID_STMT("TRUNCATE TABLE t;", &parse_tree);
479
0
  ANALYZE_VALID_STMT(Substitute("TRUNCATE TABLE $0.t;", kDefaultKeyspaceName), &parse_tree);
480
481
  // No such keyspace
482
0
  ANALYZE_INVALID_STMT("TRUNCATE TABLE invalid_keyspace.t;", &parse_tree);
483
  // No such table
484
0
  ANALYZE_INVALID_STMT("TRUNCATE TABLE invalid_table;", &parse_tree);
485
  // Only one table can be truncated in each statement.
486
0
  ANALYZE_INVALID_STMT("TRUNCATE TABLE t1, t2;", &parse_tree);
487
  // Invalid qualified table name.
488
0
  ANALYZE_INVALID_STMT("TRUNCATE TABLE k.t.c;", &parse_tree);
489
0
}
490
491
0
TEST_F(QLTestAnalyzer, TestMisc) {
492
0
  CreateSimulatedCluster();
493
0
  TestQLProcessor *processor = GetQLProcessor();
494
495
  // Analyze misc empty sql statements.
496
0
  ParseTree::UniPtr parse_tree;
497
0
  CHECK_OK(TestAnalyzer("", &parse_tree));
498
0
  EXPECT_TRUE(parse_tree->root() == nullptr);
499
0
  CHECK_OK(TestAnalyzer(";", &parse_tree));
500
0
  EXPECT_TRUE(parse_tree->root() == nullptr);
501
0
  CHECK_OK(TestAnalyzer(" ;  ;  ", &parse_tree));
502
0
  EXPECT_TRUE(parse_tree->root() == nullptr);
503
504
  // Invalid: multi-statement not supported.
505
0
  CHECK_OK(processor->Run("CREATE TABLE t (h INT PRIMARY KEY, c INT);"));
506
0
  ANALYZE_INVALID_STMT("SELECT * FROM t; SELECT C FROM t;", &parse_tree);
507
0
}
508
509
0
TEST_F(QLTestAnalyzer, TestOffsetLimitClause) {
510
0
  CreateSimulatedCluster();
511
0
  TestQLProcessor *processor = GetQLProcessor();
512
0
  CHECK_OK(processor->Run("CREATE TABLE t (h int PRIMARY KEY);"));
513
514
  // Analyze the LIMIT/OFFSET clause.
515
0
  ParseTree::UniPtr parse_tree;
516
0
  ANALYZE_VALID_STMT("SELECT * FROM t LIMIT 10 OFFSET 10;", &parse_tree);
517
0
  ANALYZE_VALID_STMT("SELECT * FROM t OFFSET 10;", &parse_tree);
518
0
  ANALYZE_VALID_STMT("SELECT * FROM t LIMIT 10;", &parse_tree);
519
0
  ANALYZE_VALID_STMT("SELECT * FROM t OFFSET 0;", &parse_tree);
520
0
  ANALYZE_VALID_STMT("SELECT * FROM t LIMIT 0;", &parse_tree);
521
0
  ANALYZE_VALID_STMT("SELECT * FROM t OFFSET 10 LIMIT 10;", &parse_tree);
522
0
}
523
524
}  // namespace ql
525
}  // namespace yb