1 | #include <Storages/AlterCommands.h> |
2 | #include <Storages/IStorage.h> |
3 | #include <DataTypes/DataTypeFactory.h> |
4 | #include <DataTypes/DataTypesNumber.h> |
5 | #include <DataTypes/NestedUtils.h> |
6 | #include <Interpreters/Context.h> |
7 | #include <Interpreters/SyntaxAnalyzer.h> |
8 | #include <Interpreters/ExpressionAnalyzer.h> |
9 | #include <Interpreters/ExpressionActions.h> |
10 | #include <Parsers/ASTIdentifier.h> |
11 | #include <Parsers/ASTIndexDeclaration.h> |
12 | #include <Parsers/ASTConstraintDeclaration.h> |
13 | #include <Parsers/ASTExpressionList.h> |
14 | #include <Parsers/ASTLiteral.h> |
15 | #include <Parsers/ASTFunction.h> |
16 | #include <Parsers/ASTAlterQuery.h> |
17 | #include <Parsers/ASTColumnDeclaration.h> |
18 | #include <Parsers/ASTSetQuery.h> |
19 | #include <Parsers/ASTCreateQuery.h> |
20 | #include <Common/typeid_cast.h> |
21 | #include <Compression/CompressionFactory.h> |
22 | |
23 | #include <Parsers/queryToString.h> |
24 | |
25 | |
26 | namespace DB |
27 | { |
28 | |
29 | namespace ErrorCodes |
30 | { |
31 | extern const int ILLEGAL_COLUMN; |
32 | extern const int BAD_ARGUMENTS; |
33 | extern const int LOGICAL_ERROR; |
34 | extern const int UNKNOWN_SETTING; |
35 | } |
36 | |
37 | |
38 | std::optional<AlterCommand> AlterCommand::parse(const ASTAlterCommand * command_ast) |
39 | { |
40 | const DataTypeFactory & data_type_factory = DataTypeFactory::instance(); |
41 | const CompressionCodecFactory & compression_codec_factory = CompressionCodecFactory::instance(); |
42 | |
43 | if (command_ast->type == ASTAlterCommand::ADD_COLUMN) |
44 | { |
45 | AlterCommand command; |
46 | command.type = AlterCommand::ADD_COLUMN; |
47 | |
48 | const auto & ast_col_decl = command_ast->col_decl->as<ASTColumnDeclaration &>(); |
49 | |
50 | command.column_name = ast_col_decl.name; |
51 | if (ast_col_decl.type) |
52 | { |
53 | command.data_type = data_type_factory.get(ast_col_decl.type); |
54 | } |
55 | if (ast_col_decl.default_expression) |
56 | { |
57 | command.default_kind = columnDefaultKindFromString(ast_col_decl.default_specifier); |
58 | command.default_expression = ast_col_decl.default_expression; |
59 | } |
60 | |
61 | if (ast_col_decl.comment) |
62 | { |
63 | const auto & = typeid_cast<ASTLiteral &>(*ast_col_decl.comment); |
64 | command.comment = ast_comment.value.get<String>(); |
65 | } |
66 | |
67 | if (ast_col_decl.codec) |
68 | command.codec = compression_codec_factory.get(ast_col_decl.codec, command.data_type); |
69 | |
70 | if (command_ast->column) |
71 | command.after_column = getIdentifierName(command_ast->column); |
72 | |
73 | if (ast_col_decl.ttl) |
74 | command.ttl = ast_col_decl.ttl; |
75 | |
76 | command.if_not_exists = command_ast->if_not_exists; |
77 | |
78 | return command; |
79 | } |
80 | else if (command_ast->type == ASTAlterCommand::DROP_COLUMN && !command_ast->partition) |
81 | { |
82 | if (command_ast->clear_column) |
83 | throw Exception("\"ALTER TABLE table CLEAR COLUMN column\" queries are not supported yet. Use \"CLEAR COLUMN column IN PARTITION\"." , ErrorCodes::NOT_IMPLEMENTED); |
84 | |
85 | AlterCommand command; |
86 | command.type = AlterCommand::DROP_COLUMN; |
87 | command.column_name = getIdentifierName(command_ast->column); |
88 | command.if_exists = command_ast->if_exists; |
89 | return command; |
90 | } |
91 | else if (command_ast->type == ASTAlterCommand::MODIFY_COLUMN) |
92 | { |
93 | AlterCommand command; |
94 | command.type = AlterCommand::MODIFY_COLUMN; |
95 | |
96 | const auto & ast_col_decl = command_ast->col_decl->as<ASTColumnDeclaration &>(); |
97 | |
98 | command.column_name = ast_col_decl.name; |
99 | if (ast_col_decl.type) |
100 | { |
101 | command.data_type = data_type_factory.get(ast_col_decl.type); |
102 | } |
103 | |
104 | if (ast_col_decl.default_expression) |
105 | { |
106 | command.default_kind = columnDefaultKindFromString(ast_col_decl.default_specifier); |
107 | command.default_expression = ast_col_decl.default_expression; |
108 | } |
109 | |
110 | if (ast_col_decl.comment) |
111 | { |
112 | const auto & = ast_col_decl.comment->as<ASTLiteral &>(); |
113 | command.comment.emplace(ast_comment.value.get<String>()); |
114 | } |
115 | |
116 | if (ast_col_decl.ttl) |
117 | command.ttl = ast_col_decl.ttl; |
118 | |
119 | if (ast_col_decl.codec) |
120 | command.codec = compression_codec_factory.get(ast_col_decl.codec, command.data_type); |
121 | |
122 | command.if_exists = command_ast->if_exists; |
123 | |
124 | return command; |
125 | } |
126 | else if (command_ast->type == ASTAlterCommand::COMMENT_COLUMN) |
127 | { |
128 | AlterCommand command; |
129 | command.type = COMMENT_COLUMN; |
130 | command.column_name = getIdentifierName(command_ast->column); |
131 | const auto & = command_ast->comment->as<ASTLiteral &>(); |
132 | command.comment = ast_comment.value.get<String>(); |
133 | command.if_exists = command_ast->if_exists; |
134 | return command; |
135 | } |
136 | else if (command_ast->type == ASTAlterCommand::MODIFY_ORDER_BY) |
137 | { |
138 | AlterCommand command; |
139 | command.type = AlterCommand::MODIFY_ORDER_BY; |
140 | command.order_by = command_ast->order_by; |
141 | return command; |
142 | } |
143 | else if (command_ast->type == ASTAlterCommand::ADD_INDEX) |
144 | { |
145 | AlterCommand command; |
146 | command.index_decl = command_ast->index_decl; |
147 | command.type = AlterCommand::ADD_INDEX; |
148 | |
149 | const auto & ast_index_decl = command_ast->index_decl->as<ASTIndexDeclaration &>(); |
150 | |
151 | command.index_name = ast_index_decl.name; |
152 | |
153 | if (command_ast->index) |
154 | command.after_index_name = command_ast->index->as<ASTIdentifier &>().name; |
155 | |
156 | command.if_not_exists = command_ast->if_not_exists; |
157 | |
158 | return command; |
159 | } |
160 | else if (command_ast->type == ASTAlterCommand::ADD_CONSTRAINT) |
161 | { |
162 | AlterCommand command; |
163 | command.constraint_decl = command_ast->constraint_decl; |
164 | command.type = AlterCommand::ADD_CONSTRAINT; |
165 | |
166 | const auto & ast_constraint_decl = command_ast->constraint_decl->as<ASTConstraintDeclaration &>(); |
167 | |
168 | command.constraint_name = ast_constraint_decl.name; |
169 | |
170 | command.if_not_exists = command_ast->if_not_exists; |
171 | |
172 | return command; |
173 | } |
174 | else if (command_ast->type == ASTAlterCommand::DROP_CONSTRAINT && !command_ast->partition) |
175 | { |
176 | if (command_ast->clear_column) |
177 | throw Exception("\"ALTER TABLE table CLEAR COLUMN column\" queries are not supported yet. Use \"CLEAR COLUMN column IN PARTITION\"." , ErrorCodes::NOT_IMPLEMENTED); |
178 | |
179 | AlterCommand command; |
180 | command.if_exists = command_ast->if_exists; |
181 | command.type = AlterCommand::DROP_CONSTRAINT; |
182 | command.constraint_name = command_ast->constraint->as<ASTIdentifier &>().name; |
183 | |
184 | return command; |
185 | } |
186 | else if (command_ast->type == ASTAlterCommand::DROP_INDEX && !command_ast->partition) |
187 | { |
188 | if (command_ast->clear_column) |
189 | throw Exception("\"ALTER TABLE table CLEAR INDEX index\" queries are not supported yet. Use \"CLEAR INDEX index IN PARTITION\"." , ErrorCodes::NOT_IMPLEMENTED); |
190 | |
191 | AlterCommand command; |
192 | command.type = AlterCommand::DROP_INDEX; |
193 | command.index_name = command_ast->index->as<ASTIdentifier &>().name; |
194 | command.if_exists = command_ast->if_exists; |
195 | |
196 | return command; |
197 | } |
198 | else if (command_ast->type == ASTAlterCommand::MODIFY_TTL) |
199 | { |
200 | AlterCommand command; |
201 | command.type = AlterCommand::MODIFY_TTL; |
202 | command.ttl = command_ast->ttl; |
203 | return command; |
204 | } |
205 | else if (command_ast->type == ASTAlterCommand::MODIFY_SETTING) |
206 | { |
207 | AlterCommand command; |
208 | command.type = AlterCommand::MODIFY_SETTING; |
209 | command.settings_changes = command_ast->settings_changes->as<ASTSetQuery &>().changes; |
210 | return command; |
211 | } |
212 | else |
213 | return {}; |
214 | } |
215 | |
216 | |
217 | void AlterCommand::apply(StorageInMemoryMetadata & metadata) const |
218 | { |
219 | if (type == ADD_COLUMN) |
220 | { |
221 | ColumnDescription column(column_name, data_type, false); |
222 | if (default_expression) |
223 | { |
224 | column.default_desc.kind = default_kind; |
225 | column.default_desc.expression = default_expression; |
226 | } |
227 | if (comment) |
228 | column.comment = *comment; |
229 | |
230 | column.codec = codec; |
231 | column.ttl = ttl; |
232 | |
233 | metadata.columns.add(column, after_column); |
234 | |
235 | /// Slow, because each time a list is copied |
236 | metadata.columns.flattenNested(); |
237 | } |
238 | else if (type == DROP_COLUMN) |
239 | { |
240 | metadata.columns.remove(column_name); |
241 | } |
242 | else if (type == MODIFY_COLUMN) |
243 | { |
244 | metadata.columns.modify(column_name, [&](ColumnDescription & column) |
245 | { |
246 | if (codec) |
247 | { |
248 | /// User doesn't specify data type, it means that datatype doesn't change |
249 | /// let's use info about old type |
250 | if (data_type == nullptr) |
251 | codec->useInfoAboutType(column.type); |
252 | column.codec = codec; |
253 | } |
254 | |
255 | if (comment) |
256 | column.comment = *comment; |
257 | |
258 | if (ttl) |
259 | column.ttl = ttl; |
260 | |
261 | if (data_type) |
262 | column.type = data_type; |
263 | |
264 | /// User specified default expression or changed |
265 | /// datatype. We have to replace default. |
266 | if (default_expression || data_type) |
267 | { |
268 | column.default_desc.kind = default_kind; |
269 | column.default_desc.expression = default_expression; |
270 | } |
271 | }); |
272 | } |
273 | else if (type == MODIFY_ORDER_BY) |
274 | { |
275 | if (!metadata.primary_key_ast && metadata.order_by_ast) |
276 | { |
277 | /// Primary and sorting key become independent after this ALTER so we have to |
278 | /// save the old ORDER BY expression as the new primary key. |
279 | metadata.primary_key_ast = metadata.order_by_ast->clone(); |
280 | } |
281 | |
282 | metadata.order_by_ast = order_by; |
283 | } |
284 | else if (type == COMMENT_COLUMN) |
285 | { |
286 | metadata.columns.modify(column_name, [&](ColumnDescription & column) { column.comment = *comment; }); |
287 | } |
288 | else if (type == ADD_INDEX) |
289 | { |
290 | if (std::any_of( |
291 | metadata.indices.indices.cbegin(), |
292 | metadata.indices.indices.cend(), |
293 | [this](const ASTPtr & index_ast) |
294 | { |
295 | return index_ast->as<ASTIndexDeclaration &>().name == index_name; |
296 | })) |
297 | { |
298 | if (if_not_exists) |
299 | return; |
300 | else |
301 | throw Exception{"Cannot add index " + index_name + ": index with this name already exists" , |
302 | ErrorCodes::ILLEGAL_COLUMN}; |
303 | } |
304 | |
305 | auto insert_it = metadata.indices.indices.end(); |
306 | |
307 | if (!after_index_name.empty()) |
308 | { |
309 | insert_it = std::find_if( |
310 | metadata.indices.indices.begin(), |
311 | metadata.indices.indices.end(), |
312 | [this](const ASTPtr & index_ast) |
313 | { |
314 | return index_ast->as<ASTIndexDeclaration &>().name == after_index_name; |
315 | }); |
316 | |
317 | if (insert_it == metadata.indices.indices.end()) |
318 | throw Exception("Wrong index name. Cannot find index " + backQuote(after_index_name) + " to insert after." , |
319 | ErrorCodes::LOGICAL_ERROR); |
320 | |
321 | ++insert_it; |
322 | } |
323 | |
324 | metadata.indices.indices.emplace(insert_it, std::dynamic_pointer_cast<ASTIndexDeclaration>(index_decl)); |
325 | } |
326 | else if (type == DROP_INDEX) |
327 | { |
328 | auto erase_it = std::find_if( |
329 | metadata.indices.indices.begin(), |
330 | metadata.indices.indices.end(), |
331 | [this](const ASTPtr & index_ast) |
332 | { |
333 | return index_ast->as<ASTIndexDeclaration &>().name == index_name; |
334 | }); |
335 | |
336 | if (erase_it == metadata.indices.indices.end()) |
337 | { |
338 | if (if_exists) |
339 | return; |
340 | throw Exception("Wrong index name. Cannot find index " + backQuote(index_name) + " to drop." , |
341 | ErrorCodes::LOGICAL_ERROR); |
342 | } |
343 | |
344 | metadata.indices.indices.erase(erase_it); |
345 | } |
346 | else if (type == ADD_CONSTRAINT) |
347 | { |
348 | if (std::any_of( |
349 | metadata.constraints.constraints.cbegin(), |
350 | metadata.constraints.constraints.cend(), |
351 | [this](const ASTPtr & constraint_ast) |
352 | { |
353 | return constraint_ast->as<ASTConstraintDeclaration &>().name == constraint_name; |
354 | })) |
355 | { |
356 | if (if_not_exists) |
357 | return; |
358 | throw Exception("Cannot add constraint " + constraint_name + ": constraint with this name already exists" , |
359 | ErrorCodes::ILLEGAL_COLUMN); |
360 | } |
361 | |
362 | auto insert_it = metadata.constraints.constraints.end(); |
363 | |
364 | metadata.constraints.constraints.emplace(insert_it, std::dynamic_pointer_cast<ASTConstraintDeclaration>(constraint_decl)); |
365 | } |
366 | else if (type == DROP_CONSTRAINT) |
367 | { |
368 | auto erase_it = std::find_if( |
369 | metadata.constraints.constraints.begin(), |
370 | metadata.constraints.constraints.end(), |
371 | [this](const ASTPtr & constraint_ast) |
372 | { |
373 | return constraint_ast->as<ASTConstraintDeclaration &>().name == constraint_name; |
374 | }); |
375 | |
376 | if (erase_it == metadata.constraints.constraints.end()) |
377 | { |
378 | if (if_exists) |
379 | return; |
380 | throw Exception("Wrong constraint name. Cannot find constraint `" + constraint_name + "` to drop." , |
381 | ErrorCodes::LOGICAL_ERROR); |
382 | } |
383 | metadata.constraints.constraints.erase(erase_it); |
384 | } |
385 | else if (type == MODIFY_TTL) |
386 | { |
387 | metadata.ttl_for_table_ast = ttl; |
388 | } |
389 | else if (type == MODIFY_SETTING) |
390 | { |
391 | auto & settings_from_storage = metadata.settings_ast->as<ASTSetQuery &>().changes; |
392 | for (const auto & change : settings_changes) |
393 | { |
394 | auto finder = [&change](const SettingChange & c) { return c.name == change.name; }; |
395 | auto it = std::find_if(settings_from_storage.begin(), settings_from_storage.end(), finder); |
396 | |
397 | if (it != settings_from_storage.end()) |
398 | it->value = change.value; |
399 | else |
400 | settings_from_storage.push_back(change); |
401 | } |
402 | } |
403 | else |
404 | throw Exception("Wrong parameter type in ALTER query" , ErrorCodes::LOGICAL_ERROR); |
405 | } |
406 | |
407 | bool AlterCommand::isModifyingData() const |
408 | { |
409 | /// Possible change data representation on disk |
410 | if (type == MODIFY_COLUMN) |
411 | return data_type != nullptr; |
412 | |
413 | return type == ADD_COLUMN /// We need to change columns.txt in each part for MergeTree |
414 | || type == DROP_COLUMN /// We need to change columns.txt in each part for MergeTree |
415 | || type == DROP_INDEX; /// We need to remove file from filesystem for MergeTree |
416 | } |
417 | |
418 | bool AlterCommand::isSettingsAlter() const |
419 | { |
420 | return type == MODIFY_SETTING; |
421 | } |
422 | |
423 | bool AlterCommand::isCommentAlter() const |
424 | { |
425 | if (type == COMMENT_COLUMN) |
426 | { |
427 | return true; |
428 | } |
429 | else if (type == MODIFY_COLUMN) |
430 | { |
431 | return comment.has_value() |
432 | && codec == nullptr |
433 | && data_type == nullptr |
434 | && default_expression == nullptr |
435 | && ttl == nullptr; |
436 | } |
437 | return false; |
438 | } |
439 | |
440 | |
441 | String alterTypeToString(const AlterCommand::Type type) |
442 | { |
443 | switch (type) |
444 | { |
445 | case AlterCommand::Type::ADD_COLUMN: |
446 | return "ADD COLUMN" ; |
447 | case AlterCommand::Type::ADD_CONSTRAINT: |
448 | return "ADD CONSTRAINT" ; |
449 | case AlterCommand::Type::ADD_INDEX: |
450 | return "ADD INDEX" ; |
451 | case AlterCommand::Type::COMMENT_COLUMN: |
452 | return "COMMENT COLUMN" ; |
453 | case AlterCommand::Type::DROP_COLUMN: |
454 | return "DROP COLUMN" ; |
455 | case AlterCommand::Type::DROP_CONSTRAINT: |
456 | return "DROP CONSTRAINT" ; |
457 | case AlterCommand::Type::DROP_INDEX: |
458 | return "DROP INDEX" ; |
459 | case AlterCommand::Type::MODIFY_COLUMN: |
460 | return "MODIFY COLUMN" ; |
461 | case AlterCommand::Type::MODIFY_ORDER_BY: |
462 | return "MODIFY ORDER BY" ; |
463 | case AlterCommand::Type::MODIFY_TTL: |
464 | return "MODIFY TTL" ; |
465 | case AlterCommand::Type::MODIFY_SETTING: |
466 | return "MODIFY SETTING" ; |
467 | } |
468 | __builtin_unreachable(); |
469 | } |
470 | |
471 | void AlterCommands::apply(StorageInMemoryMetadata & metadata) const |
472 | { |
473 | if (!prepared) |
474 | throw DB::Exception("Alter commands is not prepared. Cannot apply. It's a bug" , ErrorCodes::LOGICAL_ERROR); |
475 | |
476 | auto metadata_copy = metadata; |
477 | for (const AlterCommand & command : *this) |
478 | if (!command.ignore) |
479 | command.apply(metadata_copy); |
480 | |
481 | metadata = std::move(metadata_copy); |
482 | } |
483 | |
484 | |
485 | void AlterCommands::prepare(const StorageInMemoryMetadata & metadata, const Context & context) |
486 | { |
487 | /// A temporary object that is used to keep track of the current state of columns after applying a subset of commands. |
488 | auto columns = metadata.columns; |
489 | |
490 | /// Default expressions will be added to this list for type deduction. |
491 | auto default_expr_list = std::make_shared<ASTExpressionList>(); |
492 | /// We will save ALTER ADD/MODIFY command indices (only the last for each column) for possible modification |
493 | /// (we might need to add deduced types or modify default expressions). |
494 | /// Saving indices because we can add new commands later and thus cause vector resize. |
495 | std::unordered_map<String, size_t> column_to_command_idx; |
496 | |
497 | for (size_t i = 0; i < size(); ++i) |
498 | { |
499 | auto & command = (*this)[i]; |
500 | if (command.type == AlterCommand::ADD_COLUMN || command.type == AlterCommand::MODIFY_COLUMN) |
501 | { |
502 | const auto & column_name = command.column_name; |
503 | |
504 | if (command.type == AlterCommand::ADD_COLUMN) |
505 | { |
506 | if (columns.has(column_name) || columns.hasNested(column_name)) |
507 | { |
508 | if (command.if_not_exists) |
509 | command.ignore = true; |
510 | } |
511 | } |
512 | else if (command.type == AlterCommand::MODIFY_COLUMN) |
513 | { |
514 | if (!columns.has(column_name)) |
515 | if (command.if_exists) |
516 | command.ignore = true; |
517 | |
518 | if (!command.ignore) |
519 | columns.remove(column_name); |
520 | } |
521 | |
522 | if (!command.ignore) |
523 | { |
524 | column_to_command_idx[column_name] = i; |
525 | |
526 | /// we're creating dummy DataTypeUInt8 in order to prevent the NullPointerException in ExpressionActions |
527 | columns.add( |
528 | ColumnDescription(column_name, command.data_type ? command.data_type : std::make_shared<DataTypeUInt8>(), false)); |
529 | |
530 | if (command.default_expression) |
531 | { |
532 | if (command.data_type) |
533 | { |
534 | const auto & final_column_name = column_name; |
535 | const auto tmp_column_name = final_column_name + "_tmp" ; |
536 | |
537 | default_expr_list->children.emplace_back(setAlias( |
538 | makeASTFunction("CAST" , std::make_shared<ASTIdentifier>(tmp_column_name), |
539 | std::make_shared<ASTLiteral>(command.data_type->getName())), |
540 | final_column_name)); |
541 | |
542 | default_expr_list->children.emplace_back(setAlias(command.default_expression->clone(), tmp_column_name)); |
543 | } |
544 | else |
545 | { |
546 | /// no type explicitly specified, will deduce later |
547 | default_expr_list->children.emplace_back( |
548 | setAlias(command.default_expression->clone(), column_name)); |
549 | } |
550 | } |
551 | } |
552 | } |
553 | else if (command.type == AlterCommand::DROP_COLUMN) |
554 | { |
555 | if (columns.has(command.column_name) || columns.hasNested(command.column_name)) |
556 | columns.remove(command.column_name); |
557 | else if (command.if_exists) |
558 | command.ignore = true; |
559 | } |
560 | else if (command.type == AlterCommand::COMMENT_COLUMN) |
561 | { |
562 | if (!columns.has(command.column_name) && command.if_exists) |
563 | command.ignore = true; |
564 | } |
565 | } |
566 | |
567 | /** Existing defaulted columns may require default expression extensions with a type conversion, |
568 | * therefore we add them to default_expr_list to recalculate their types */ |
569 | for (const auto & column : columns) |
570 | { |
571 | if (column.default_desc.expression) |
572 | { |
573 | const auto tmp_column_name = column.name + "_tmp" ; |
574 | |
575 | default_expr_list->children.emplace_back(setAlias( |
576 | makeASTFunction("CAST" , std::make_shared<ASTIdentifier>(tmp_column_name), |
577 | std::make_shared<ASTLiteral>(column.type->getName())), |
578 | column.name)); |
579 | |
580 | default_expr_list->children.emplace_back(setAlias(column.default_desc.expression->clone(), tmp_column_name)); |
581 | } |
582 | } |
583 | |
584 | ASTPtr query = default_expr_list; |
585 | auto syntax_result = SyntaxAnalyzer(context).analyze(query, columns.getAll()); |
586 | const auto actions = ExpressionAnalyzer(query, syntax_result, context).getActions(true); |
587 | const auto block = actions->getSampleBlock(); |
588 | |
589 | /// set deduced types, modify default expression if necessary |
590 | for (const auto & column : columns) |
591 | { |
592 | AlterCommand * command = nullptr; |
593 | auto command_it = column_to_command_idx.find(column.name); |
594 | if (command_it != column_to_command_idx.end()) |
595 | command = &(*this)[command_it->second]; |
596 | |
597 | if (!(command && command->default_expression) && !column.default_desc.expression) |
598 | continue; |
599 | |
600 | const DataTypePtr & explicit_type = command ? command->data_type : column.type; |
601 | if (explicit_type) |
602 | { |
603 | const auto & tmp_column = block.getByName(column.name + "_tmp" ); |
604 | const auto & deduced_type = tmp_column.type; |
605 | if (!explicit_type->equals(*deduced_type)) |
606 | { |
607 | if (!command) |
608 | { |
609 | #if !__clang__ |
610 | # pragma GCC diagnostic push |
611 | # pragma GCC diagnostic ignored "-Wmissing-field-initializers" |
612 | #endif |
613 | /// We completely sure, that we initialize all required fields |
614 | AlterCommand aux_command{ |
615 | .type = AlterCommand::MODIFY_COLUMN, |
616 | .column_name = column.name, |
617 | .data_type = explicit_type, |
618 | .default_kind = column.default_desc.kind, |
619 | .default_expression = column.default_desc.expression |
620 | }; |
621 | #if !__clang__ |
622 | # pragma GCC diagnostic pop |
623 | #endif |
624 | |
625 | /// column has no associated alter command, let's create it |
626 | /// add a new alter command to modify existing column |
627 | this->emplace_back(aux_command); |
628 | |
629 | command = &back(); |
630 | } |
631 | |
632 | command->default_expression = makeASTFunction("CAST" , |
633 | command->default_expression->clone(), |
634 | std::make_shared<ASTLiteral>(explicit_type->getName())); |
635 | } |
636 | } |
637 | else |
638 | { |
639 | /// just set deduced type |
640 | command->data_type = block.getByName(column.name).type; |
641 | } |
642 | } |
643 | prepared = true; |
644 | } |
645 | |
646 | void AlterCommands::validate(const StorageInMemoryMetadata & metadata, const Context & context) const |
647 | { |
648 | for (size_t i = 0; i < size(); ++i) |
649 | { |
650 | auto & command = (*this)[i]; |
651 | if (command.type == AlterCommand::ADD_COLUMN || command.type == AlterCommand::MODIFY_COLUMN) |
652 | { |
653 | const auto & column_name = command.column_name; |
654 | |
655 | if (command.type == AlterCommand::ADD_COLUMN) |
656 | { |
657 | if (metadata.columns.has(column_name) || metadata.columns.hasNested(column_name)) |
658 | if (!command.if_not_exists) |
659 | throw Exception{"Cannot add column " + column_name + ": column with this name already exists" , ErrorCodes::ILLEGAL_COLUMN}; |
660 | } |
661 | else if (command.type == AlterCommand::MODIFY_COLUMN) |
662 | { |
663 | if (!metadata.columns.has(column_name)) |
664 | if (!command.if_exists) |
665 | throw Exception{"Wrong column name. Cannot find column " + column_name + " to modify" , ErrorCodes::ILLEGAL_COLUMN}; |
666 | } |
667 | |
668 | } |
669 | else if (command.type == AlterCommand::DROP_COLUMN) |
670 | { |
671 | if (metadata.columns.has(command.column_name) || metadata.columns.hasNested(command.column_name)) |
672 | { |
673 | for (const ColumnDescription & column : metadata.columns) |
674 | { |
675 | const auto & default_expression = column.default_desc.expression; |
676 | if (!default_expression) |
677 | continue; |
678 | |
679 | ASTPtr query = default_expression->clone(); |
680 | auto syntax_result = SyntaxAnalyzer(context).analyze(query, metadata.columns.getAll()); |
681 | const auto actions = ExpressionAnalyzer(query, syntax_result, context).getActions(true); |
682 | const auto required_columns = actions->getRequiredColumns(); |
683 | |
684 | if (required_columns.end() != std::find(required_columns.begin(), required_columns.end(), command.column_name)) |
685 | throw Exception( |
686 | "Cannot drop column " + command.column_name + ", because column " + column.name + |
687 | " depends on it" , ErrorCodes::ILLEGAL_COLUMN); |
688 | } |
689 | } |
690 | else if (!command.if_exists) |
691 | throw Exception("Wrong column name. Cannot find column " + command.column_name + " to drop" , |
692 | ErrorCodes::ILLEGAL_COLUMN); |
693 | } |
694 | else if (command.type == AlterCommand::COMMENT_COLUMN) |
695 | { |
696 | if (!metadata.columns.has(command.column_name)) |
697 | { |
698 | if (!command.if_exists) |
699 | throw Exception{"Wrong column name. Cannot find column " + command.column_name + " to comment" , ErrorCodes::ILLEGAL_COLUMN}; |
700 | } |
701 | } |
702 | } |
703 | } |
704 | |
705 | bool AlterCommands::isModifyingData() const |
706 | { |
707 | for (const auto & param : *this) |
708 | { |
709 | if (param.isModifyingData()) |
710 | return true; |
711 | } |
712 | |
713 | return false; |
714 | } |
715 | |
716 | bool AlterCommands::isSettingsAlter() const |
717 | { |
718 | return std::all_of(begin(), end(), [](const AlterCommand & c) { return c.isSettingsAlter(); }); |
719 | } |
720 | |
721 | bool AlterCommands::isCommentAlter() const |
722 | { |
723 | return std::all_of(begin(), end(), [](const AlterCommand & c) { return c.isCommentAlter(); }); |
724 | } |
725 | } |
726 | |