/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 |