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
26namespace DB
27{
28
29namespace 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
38std::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 & ast_comment = 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_comment = 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 & ast_comment = 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
217void 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
407bool 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
418bool AlterCommand::isSettingsAlter() const
419{
420 return type == MODIFY_SETTING;
421}
422
423bool 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
441String 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
471void 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
485void 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
646void 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
705bool 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
716bool AlterCommands::isSettingsAlter() const
717{
718 return std::all_of(begin(), end(), [](const AlterCommand & c) { return c.isSettingsAlter(); });
719}
720
721bool AlterCommands::isCommentAlter() const
722{
723 return std::all_of(begin(), end(), [](const AlterCommand & c) { return c.isCommentAlter(); });
724}
725}
726