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/ptree/pt_create_table.cc
Line
Count
Source (jump to first uncovered line)
1
//--------------------------------------------------------------------------------------------------
2
// Copyright (c) YugaByte, Inc.
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
// in compliance with the License.  You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software distributed under the License
10
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
// or implied.  See the License for the specific language governing permissions and limitations
12
// under the License.
13
//
14
//
15
// Treenode definitions for CREATE TABLE statements.
16
//--------------------------------------------------------------------------------------------------
17
18
#include "yb/yql/cql/ql/ptree/pt_create_table.h"
19
20
#include "yb/client/schema.h"
21
#include "yb/common/schema.h"
22
23
#include "yb/util/flag_tags.h"
24
25
#include "yb/yql/cql/ql/ptree/pt_column_definition.h"
26
#include "yb/yql/cql/ql/ptree/pt_option.h"
27
#include "yb/yql/cql/ql/ptree/pt_table_property.h"
28
#include "yb/yql/cql/ql/ptree/sem_context.h"
29
#include "yb/yql/cql/ql/ptree/sem_state.h"
30
#include "yb/yql/cql/ql/ptree/yb_location.h"
31
#include "yb/yql/cql/ql/util/errcodes.h"
32
33
DECLARE_bool(use_cassandra_authentication);
34
35
DEFINE_bool(cql_table_is_transactional_by_default, false,
36
            "When the 'transactions' property is not specified at CREATE TABLE time "
37
            "for a YCQL table, this flag determines the default setting for whether "
38
            "the table is transactional or not.");
39
TAG_FLAG(cql_table_is_transactional_by_default, advanced);
40
41
namespace yb {
42
namespace ql {
43
44
using std::shared_ptr;
45
using std::to_string;
46
using client::YBColumnSchema;
47
48
//--------------------------------------------------------------------------------------------------
49
50
PTCreateTable::PTCreateTable(MemoryContext *memctx,
51
                             YBLocation::SharedPtr loc,
52
                             const PTQualifiedName::SharedPtr& name,
53
                             const PTListNodePtr& elements,
54
                             bool create_if_not_exists,
55
                             const PTTablePropertyListNode::SharedPtr& table_properties)
56
    : TreeNode(memctx, loc),
57
      relation_(name),
58
      elements_(elements),
59
      columns_(memctx),
60
      primary_columns_(memctx),
61
      hash_columns_(memctx),
62
      create_if_not_exists_(create_if_not_exists),
63
      contain_counters_(false),
64
1.91k
      table_properties_(table_properties) {
65
1.91k
}
66
67
1.81k
PTCreateTable::~PTCreateTable() {
68
1.81k
}
69
70
3.20k
client::YBTableName PTCreateTable::yb_table_name() const {
71
3.20k
  return relation_->ToTableName();
72
3.20k
}
73
74
1.90k
CHECKED_STATUS PTCreateTable::Analyze(SemContext *sem_context) {
75
1.90k
  SemState sem_state(sem_context);
76
77
  // Processing table name.
78
1.90k
  RETURN_NOT_OK(relation_->AnalyzeName(sem_context, ObjectType::TABLE));
79
80
  // For creating an index operation, SemContext::LookupTable should have already checked that the
81
  // current role has the ALTER permission on the table. We don't need to check for any additional
82
  // permissions for this operation.
83
1.90k
  if (FLAGS_use_cassandra_authentication && opcode() != TreeNodeOpcode::kPTCreateIndex) {
84
245
    RETURN_NOT_OK(sem_context->CheckHasKeyspacePermission(loc(), PermissionType::CREATE_PERMISSION,
85
245
                                                          yb_table_name().namespace_name()));
86
245
  }
87
88
  // Save context state, and set "this" as current column in the context.
89
1.89k
  SymbolEntry cached_entry = *sem_context->current_processing_id();
90
1.89k
  sem_context->set_current_create_table_stmt(this);
91
92
  // Processing table elements.
93
  // - First, process all column definitions to collect all symbols.
94
  // - Process all other elements afterward.
95
1.89k
  sem_state.set_processing_column_definition(true);
96
1.89k
  RETURN_NOT_OK(elements_->Analyze(sem_context));
97
98
1.87k
  sem_state.set_processing_column_definition(false);
99
1.87k
  RETURN_NOT_OK(elements_->Analyze(sem_context));
100
101
  // Move the all partition and primary key columns from columns list to appropriate list.
102
1.86k
  int32_t order = 0;
103
1.86k
  MCList<PTColumnDefinition *>::iterator iter = columns_.begin();
104
6.71k
  while (iter != columns_.end()) {
105
4.85k
    PTColumnDefinition *coldef = *iter;
106
4.85k
    coldef->set_order(order++);
107
108
    // Remove a column from regular column list if it's already in hash or primary list.
109
4.85k
    if (coldef->is_hash_key()) {
110
1.19k
      if (coldef->is_static()) {
111
1
        return sem_context->Error(coldef, "Hash column cannot be static",
112
1
                                  ErrorCode::INVALID_TABLE_DEFINITION);
113
1
      }
114
1.19k
      iter = columns_.erase(iter);
115
3.65k
    } else if (coldef->is_primary_key()) {
116
1.52k
      if (coldef->is_static()) {
117
1
        return sem_context->Error(coldef, "Primary key column cannot be static",
118
1
                                  ErrorCode::INVALID_TABLE_DEFINITION);
119
1
      }
120
1.52k
      iter = columns_.erase(iter);
121
2.13k
    } else {
122
2.13k
      if (contain_counters_ != coldef->is_counter()) {
123
0
        return sem_context->Error(coldef,
124
0
                                  "Table cannot contain both counter and non-counter columns",
125
0
                                  ErrorCode::INVALID_TABLE_DEFINITION);
126
0
      }
127
2.13k
      iter++;
128
2.13k
    }
129
4.85k
  }
130
131
  // When partition key is not defined, the first column is assumed to be the partition key.
132
1.86k
  if (hash_columns_.empty()) {
133
788
    if (primary_columns_.empty()) {
134
0
      return sem_context->Error(elements_, ErrorCode::MISSING_PRIMARY_KEY);
135
0
    }
136
788
    RETURN_NOT_OK(AppendHashColumn(sem_context, primary_columns_.front()));
137
788
    primary_columns_.pop_front();
138
788
  }
139
140
  // After primary key columns are fully analyzed, return error if the table contains no range
141
  // (primary) column but a static column. With no range column, every non-key column is static
142
  // by nature and Cassandra raises error in such a case.
143
1.86k
  if (primary_columns_.empty()) {
144
805
    for (const auto& column : columns_) {
145
805
      if (column->is_static()) {
146
1
        return sem_context->Error(column,
147
1
                                  "Static column not allowed in a table without a range column",
148
1
                                  ErrorCode::INVALID_TABLE_DEFINITION);
149
1
      }
150
805
    }
151
991
  }
152
153
1.86k
  if (table_properties_ != nullptr) {
154
    // Process table properties.
155
843
    RETURN_NOT_OK(table_properties_->Analyze(sem_context));
156
843
  }
157
158
  // Restore the context value as we are done with this table.
159
1.71k
  sem_context->set_current_processing_id(cached_entry);
160
0
  if (VLOG_IS_ON(3)) {
161
0
    PrintSemanticAnalysisResult(sem_context);
162
0
  }
163
164
1.71k
  return Status::OK();
165
1.86k
}
166
167
bool PTCreateTable::ColumnExists(const MCList<PTColumnDefinition *>& columns,
168
0
                                 const PTColumnDefinition* column) {
169
0
  return std::find(columns.begin(), columns.end(), column) != columns.end();
170
0
}
171
172
CHECKED_STATUS PTCreateTable::AppendColumn(SemContext *sem_context,
173
                                           PTColumnDefinition *column,
174
5.27k
                                           const bool check_duplicate) {
175
5.27k
  if (check_duplicate && ColumnExists(columns_, column)) {
176
0
    return sem_context->Error(column, ErrorCode::DUPLICATE_COLUMN);
177
0
  }
178
5.27k
  columns_.push_back(column);
179
180
5.27k
  if (column->is_counter()) {
181
16
    contain_counters_ = true;
182
16
  }
183
5.27k
  return Status::OK();
184
5.27k
}
185
186
CHECKED_STATUS PTCreateTable::AppendPrimaryColumn(SemContext *sem_context,
187
                                                  PTColumnDefinition *column,
188
2.87k
                                                  const bool check_duplicate) {
189
  // The column and its datatype should already have been analyzed at this point.
190
  // Check if the column can be used as primary column.
191
2.87k
  RETURN_NOT_OK(CheckPrimaryType(sem_context, column));
192
2.87k
  if (check_duplicate && ColumnExists(primary_columns_, column)) {
193
0
    return sem_context->Error(column, ErrorCode::DUPLICATE_COLUMN);
194
0
  }
195
2.87k
  column->set_is_primary_key();
196
2.87k
  primary_columns_.push_back(column);
197
2.87k
  return Status::OK();
198
2.87k
}
199
200
CHECKED_STATUS PTCreateTable::AppendHashColumn(SemContext *sem_context,
201
                                               PTColumnDefinition *column,
202
2.17k
                                               const bool check_duplicate) {
203
  // The column and its datatype should already have been analyzed at this point.
204
  // Check if the column can be used as hash column.
205
2.17k
  RETURN_NOT_OK(CheckPrimaryType(sem_context, column));
206
2.17k
  if (check_duplicate && ColumnExists(hash_columns_, column)) {
207
0
    return sem_context->Error(column, ErrorCode::DUPLICATE_COLUMN);
208
0
  }
209
2.17k
  column->set_is_hash_key();
210
2.17k
  hash_columns_.push_back(column);
211
2.17k
  return Status::OK();
212
2.17k
}
213
214
CHECKED_STATUS PTCreateTable::CheckPrimaryType(SemContext *sem_context,
215
5.04k
                                               const PTColumnDefinition *column) const {
216
  // Column must have been analyzed. Check if its datatype is allowed for primary column.
217
5.04k
  if (!QLType::IsValidPrimaryType(column->ql_type()->main())) {
218
6
    return sem_context->Error(column, ErrorCode::INVALID_PRIMARY_COLUMN_TYPE);
219
6
  }
220
5.04k
  return Status::OK();
221
5.04k
}
222
223
0
void PTCreateTable::PrintSemanticAnalysisResult(SemContext *sem_context) {
224
0
  MCString sem_output("\tTable ", sem_context->PTempMem());
225
0
  sem_output += yb_table_name().ToString().c_str();
226
0
  sem_output += "(";
227
228
0
  MCList<PTColumnDefinition *> columns(sem_context->PTempMem());
229
0
  for (auto column : hash_columns_) {
230
0
    columns.push_back(column);
231
0
  }
232
0
  for (auto column : primary_columns_) {
233
0
    columns.push_back(column);
234
0
  }
235
0
  for (auto column : columns_) {
236
0
    columns.push_back(column);
237
0
  }
238
239
0
  bool is_first = true;
240
0
  for (auto column : columns) {
241
0
    if (is_first) {
242
0
      is_first = false;
243
0
    } else {
244
0
      sem_output += ", ";
245
0
    }
246
0
    sem_output += column->yb_name();
247
0
    if (column->is_hash_key()) {
248
0
      sem_output += " <Hash key, Type = ";
249
0
    } else if (column->is_primary_key()) {
250
0
      sem_output += " <Primary key, ";
251
0
      switch (column->sorting_type()) {
252
0
        case SortingType::kNotSpecified: sem_output += "None"; break;
253
0
        case SortingType::kAscending: sem_output += "Asc"; break;
254
0
        case SortingType::kDescending: sem_output += "Desc"; break;
255
0
        case SortingType::kAscendingNullsLast: sem_output += "Asc nulls last"; break;
256
0
        case SortingType::kDescendingNullsLast: sem_output += "Desc nulls last"; break;
257
0
      }
258
0
      sem_output += ", Type = ";
259
0
    } else {
260
0
      sem_output += " <Type = ";
261
0
    }
262
0
    sem_output += column->ql_type()->ToString().c_str();
263
0
    sem_output += ">";
264
0
  }
265
266
0
  sem_output += ")";
267
0
  VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << sem_output;
268
0
}
269
270
2.09k
CHECKED_STATUS PTCreateTable::ToTableProperties(TableProperties *table_properties) const {
271
  // Some external tools need to create indexes for a regular table.
272
  // For such tools any new table can be created as transactional by default.
273
2.09k
  if (PREDICT_FALSE(FLAGS_cql_table_is_transactional_by_default)) {
274
    // Note: the table property can be overrided below by the user specified value.
275
30
    table_properties->SetTransactional(true);
276
30
  }
277
278
2.09k
  if (table_properties_ != nullptr) {
279
1.07k
    for (PTTableProperty::SharedPtr table_property : table_properties_->node_list()) {
280
1.07k
      RETURN_NOT_OK(table_property->SetTableProperty(table_properties));
281
1.07k
    }
282
780
  }
283
284
2.09k
  table_properties->SetContainCounters(contain_counters_);
285
2.09k
  return Status::OK();
286
2.09k
}
287
288
//--------------------------------------------------------------------------------------------------
289
290
PTPrimaryKey::PTPrimaryKey(MemoryContext *memctx,
291
                           YBLocation::SharedPtr loc,
292
                           const PTListNodePtr& columns)
293
    : PTConstraint(memctx, loc),
294
1.92k
      columns_(columns) {
295
1.92k
}
296
297
1.83k
PTPrimaryKey::~PTPrimaryKey() {
298
1.83k
}
299
300
namespace {
301
302
2.02k
CHECKED_STATUS SetupKeyNodeFunc(PTIndexColumn *node, SemContext *sem_context) {
303
2.02k
  return node->SetupPrimaryKey(sem_context);
304
2.02k
}
305
306
1.14k
CHECKED_STATUS SetupNestedKeyNodeFunc(PTIndexColumn *node, SemContext *sem_context) {
307
1.14k
  return node->SetupHashKey(sem_context);
308
1.14k
}
309
310
} // namespace
311
312
3.52k
CHECKED_STATUS PTPrimaryKey::Analyze(SemContext *sem_context) {
313
3.52k
  if (sem_context->processing_column_definition() != is_column_element()) {
314
1.63k
    return Status::OK();
315
1.63k
  }
316
317
  // Check if primary key is defined more than one time.
318
1.88k
  PTCreateTable *table = sem_context->current_create_table_stmt();
319
1.88k
  if (table->primary_columns().size() > 0 || table->hash_columns().size() > 0) {
320
0
    return sem_context->Error(this, "Too many primary key", ErrorCode::INVALID_TABLE_DEFINITION);
321
0
  }
322
323
1.88k
  if (columns_ == nullptr) {
324
    // Decorate the current processing name node as this is a column constraint.
325
241
    PTColumnDefinition *column = sem_context->current_column();
326
241
    RETURN_NOT_OK(table->AppendHashColumn(sem_context, column));
327
1.64k
  } else {
328
    // Decorate all name node of this key as this is a table constraint.
329
1.64k
    TreeNodePtrOperator<SemContext, PTIndexColumn> func = &SetupKeyNodeFunc;
330
1.64k
    TreeNodePtrOperator<SemContext, PTIndexColumn> nested_func = &SetupNestedKeyNodeFunc;
331
1.64k
    RETURN_NOT_OK(
332
1.64k
        (columns_->Apply<SemContext, PTIndexColumn>(sem_context, func, 1, 1, nested_func)));
333
1.64k
  }
334
1.87k
  return Status::OK();
335
1.88k
}
336
337
//--------------------------------------------------------------------------------------------------
338
339
46
CHECKED_STATUS PTStatic::Analyze(SemContext *sem_context) {
340
  // Decorate the current column as static.
341
46
  PTColumnDefinition *column = sem_context->current_column();
342
46
  column->set_is_static();
343
46
  return Status::OK();
344
46
}
345
346
}  // namespace ql
347
}  // namespace yb