2013-08-07 13:07:42 +00:00
|
|
|
|
#include <DB/Interpreters/InterpreterAlterQuery.h>
|
|
|
|
|
#include <DB/Parsers/ASTAlterQuery.h>
|
|
|
|
|
#include <DB/Parsers/ASTCreateQuery.h>
|
|
|
|
|
#include <DB/Parsers/ASTExpressionList.h>
|
|
|
|
|
#include <DB/Parsers/ASTNameTypePair.h>
|
|
|
|
|
#include <DB/Parsers/ASTIdentifier.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/Parsers/ParserCreateQuery.h>
|
|
|
|
|
#include <DB/IO/copyData.h>
|
|
|
|
|
#include <DB/Common/escapeForFileName.h>
|
|
|
|
|
#include <DB/Parsers/formatAST.h>
|
2013-09-23 12:01:19 +00:00
|
|
|
|
#include <DB/Storages/StorageMerge.h>
|
2014-03-05 18:07:35 +00:00
|
|
|
|
#include <DB/Storages/StorageMergeTree.h>
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
|
#include <boost/bind/placeholders.hpp>
|
|
|
|
|
|
2013-08-09 00:12:59 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
using namespace DB;
|
|
|
|
|
|
2013-08-07 13:07:42 +00:00
|
|
|
|
InterpreterAlterQuery::InterpreterAlterQuery(ASTPtr query_ptr_, Context & context_)
|
2013-08-09 00:12:59 +00:00
|
|
|
|
: query_ptr(query_ptr_), context(context_)
|
2013-08-07 13:07:42 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-18 11:19:37 +00:00
|
|
|
|
static bool namesEqual(const String &name, const ASTPtr & name_type_)
|
2013-08-07 13:07:42 +00:00
|
|
|
|
{
|
2013-08-09 00:12:59 +00:00
|
|
|
|
const ASTNameTypePair & name_type = dynamic_cast<const ASTNameTypePair &>(*name_type_);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
return name_type.name == name;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-18 11:19:37 +00:00
|
|
|
|
/// одинаковыми считаются имена, если они совпадают целиком или name_without_dot совпадает с частью имени до точки
|
|
|
|
|
static bool namesEqualIgnoreAfterDot(const String & name_without_dot, const ASTPtr & name_type_)
|
|
|
|
|
{
|
|
|
|
|
const ASTNameTypePair & name_type = dynamic_cast<const ASTNameTypePair &>(*name_type_);
|
|
|
|
|
|
|
|
|
|
String name_with_dot = name_without_dot + ".";
|
|
|
|
|
return (name_without_dot == name_type.name || name_with_dot == name_type.name.substr(0, name_with_dot.length()));
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-07 13:07:42 +00:00
|
|
|
|
void InterpreterAlterQuery::execute()
|
|
|
|
|
{
|
2014-03-05 18:07:35 +00:00
|
|
|
|
ASTAlterQuery & alter = dynamic_cast<ASTAlterQuery &>(*query_ptr);
|
|
|
|
|
String & table_name = alter.table;
|
|
|
|
|
String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database;
|
|
|
|
|
|
|
|
|
|
StoragePtr table = context.getTable(database_name, table_name);
|
2014-03-19 10:45:13 +00:00
|
|
|
|
auto table_lock = table->lockStructureForAlter();
|
2014-03-05 18:07:35 +00:00
|
|
|
|
|
2013-08-07 13:07:42 +00:00
|
|
|
|
/// Poco::Mutex является рекурсивным, т.е. взятие мьютекса дважды из одного потока не приводит к блокировке
|
|
|
|
|
Poco::ScopedLock<Poco::Mutex> lock(context.getMutex());
|
2014-03-05 18:07:35 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
const DataTypeFactory & data_type_factory = context.getDataTypeFactory();
|
|
|
|
|
String path = context.getPath();
|
2014-03-05 18:07:35 +00:00
|
|
|
|
|
2013-08-07 13:07:42 +00:00
|
|
|
|
String database_name_escaped = escapeForFileName(database_name);
|
|
|
|
|
String table_name_escaped = escapeForFileName(table_name);
|
2014-03-05 18:07:35 +00:00
|
|
|
|
|
2013-08-09 00:12:59 +00:00
|
|
|
|
String metadata_path = path + "metadata/" + database_name_escaped + "/" + table_name_escaped + ".sql";
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
2013-08-09 00:12:59 +00:00
|
|
|
|
ASTPtr attach_ptr = context.getCreateQuery(database_name, table_name);
|
|
|
|
|
ASTCreateQuery & attach = dynamic_cast< ASTCreateQuery &>(*attach_ptr);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
attach.attach = true;
|
2013-08-09 00:12:59 +00:00
|
|
|
|
ASTs & columns = dynamic_cast<ASTExpressionList &>(*attach.columns).children;
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
2013-09-24 15:05:40 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
/// Различные проверки, на возможность выполнения запроса
|
2013-09-24 15:05:40 +00:00
|
|
|
|
ASTs columns_copy = columns;
|
2013-08-13 08:54:28 +00:00
|
|
|
|
IdentifierNameSet identifier_names;
|
|
|
|
|
attach.storage->collectIdentifierNames(identifier_names);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
for (ASTAlterQuery::ParameterContainer::const_iterator alter_it = alter.parameters.begin();
|
|
|
|
|
alter_it != alter.parameters.end(); ++alter_it)
|
|
|
|
|
{
|
|
|
|
|
const ASTAlterQuery::Parameters & params = *alter_it;
|
|
|
|
|
|
|
|
|
|
if (params.type == ASTAlterQuery::ADD)
|
|
|
|
|
{
|
2014-02-28 10:15:21 +00:00
|
|
|
|
const ASTNameTypePair & name_type = dynamic_cast<const ASTNameTypePair &>(*params.name_type);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
StringRange type_range = name_type.type->range;
|
|
|
|
|
|
|
|
|
|
/// проверяем корректность типа. В случае некоректного типа будет исключение
|
|
|
|
|
data_type_factory.get(String(type_range.first, type_range.second - type_range.first));
|
|
|
|
|
|
|
|
|
|
/// Проверяем, что колонка еще не существует
|
2013-09-24 15:05:40 +00:00
|
|
|
|
if (std::find_if(columns_copy.begin(), columns_copy.end(), boost::bind(namesEqual, name_type.name, _1)) != columns_copy.end())
|
2013-12-23 09:29:42 +00:00
|
|
|
|
throw Exception("Wrong column name. Column " + name_type.name + " already exists", DB::ErrorCodes::ILLEGAL_COLUMN);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
|
|
|
|
/// Проверяем опциональный аргумент AFTER
|
2013-09-24 15:05:40 +00:00
|
|
|
|
ASTs::iterator insert_it = columns_copy.end();
|
2013-08-07 13:07:42 +00:00
|
|
|
|
if (params.column)
|
|
|
|
|
{
|
|
|
|
|
const ASTIdentifier & col_after = dynamic_cast<const ASTIdentifier &>(*params.column);
|
2013-12-18 11:19:37 +00:00
|
|
|
|
insert_it = std::find_if(columns_copy.begin(), columns_copy.end(), boost::bind(namesEqualIgnoreAfterDot, col_after.name, _1)) ;
|
2013-09-24 15:05:40 +00:00
|
|
|
|
if (insert_it == columns_copy.end())
|
2013-12-23 09:29:42 +00:00
|
|
|
|
throw Exception("Wrong column name. Cannot find column " + col_after.name + " to insert after", DB::ErrorCodes::ILLEGAL_COLUMN);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
}
|
2013-09-24 15:05:40 +00:00
|
|
|
|
columns_copy.insert(insert_it, params.name_type);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
}
|
2013-08-08 09:50:15 +00:00
|
|
|
|
else if (params.type == ASTAlterQuery::DROP)
|
|
|
|
|
{
|
|
|
|
|
const ASTIdentifier & drop_column = dynamic_cast <const ASTIdentifier &>(*params.column);
|
2013-08-13 08:54:28 +00:00
|
|
|
|
|
|
|
|
|
/// Проверяем, что поле не является ключевым
|
|
|
|
|
if (identifier_names.find(drop_column.name) != identifier_names.end())
|
2013-10-26 03:20:51 +00:00
|
|
|
|
throw Exception("Cannot drop key column", DB::ErrorCodes::ILLEGAL_COLUMN);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
2013-09-24 15:05:40 +00:00
|
|
|
|
ASTs::iterator drop_it = std::find_if(columns_copy.begin(), columns_copy.end(), boost::bind(namesEqual, drop_column.name, _1));
|
|
|
|
|
if (drop_it == columns_copy.end())
|
2013-12-23 09:29:42 +00:00
|
|
|
|
throw Exception("Wrong column name. Cannot find column " + drop_column.name +" to drop", DB::ErrorCodes::ILLEGAL_COLUMN);
|
2013-09-24 15:05:40 +00:00
|
|
|
|
else
|
|
|
|
|
columns_copy.erase(drop_it);
|
2013-08-08 09:50:15 +00:00
|
|
|
|
}
|
2014-02-28 10:15:21 +00:00
|
|
|
|
else if (params.type == ASTAlterQuery::MODIFY)
|
|
|
|
|
{
|
|
|
|
|
const ASTNameTypePair & name_type = dynamic_cast<const ASTNameTypePair &>(*params.name_type);
|
|
|
|
|
StringRange type_range = name_type.type->range;
|
|
|
|
|
|
|
|
|
|
/// проверяем корректность типа. В случае некоректного типа будет исключение
|
|
|
|
|
String type(type_range.first, type_range.second - type_range.first);
|
|
|
|
|
data_type_factory.get(type);
|
|
|
|
|
|
|
|
|
|
/// проверяем, что колонка существует
|
|
|
|
|
auto modified_column = std::find_if(columns_copy.begin(), columns_copy.end(), boost::bind(namesEqual, name_type.name, _1));
|
|
|
|
|
if ( modified_column == columns_copy.end())
|
|
|
|
|
throw Exception("Wrong column name. Column " + name_type.name + " not exists", DB::ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
|
2014-03-04 14:06:34 +00:00
|
|
|
|
/// Проверяем, что поле не является ключевым
|
|
|
|
|
if (identifier_names.find(name_type.name) != identifier_names.end())
|
|
|
|
|
throw Exception("Modification of primary column not supported", DB::ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
|
2014-02-28 10:15:21 +00:00
|
|
|
|
/// к сожалению, проверить на возможно ли это приведение типов можно только во время выполнения
|
|
|
|
|
}
|
2013-09-23 12:01:19 +00:00
|
|
|
|
}
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
/// todo cycle over sub tables and tables
|
|
|
|
|
/// Применяем изменения
|
|
|
|
|
for (ASTAlterQuery::ParameterContainer::const_iterator alter_it = alter.parameters.begin();
|
|
|
|
|
alter_it != alter.parameters.end(); ++alter_it)
|
|
|
|
|
{
|
|
|
|
|
const ASTAlterQuery::Parameters & params = *alter_it;
|
2013-08-07 13:07:42 +00:00
|
|
|
|
table->alter(params);
|
2013-09-23 12:01:19 +00:00
|
|
|
|
|
|
|
|
|
if (params.type == ASTAlterQuery::ADD)
|
|
|
|
|
{
|
2013-12-18 11:19:37 +00:00
|
|
|
|
/// Применяем опциональный аргумент AFTER
|
2013-09-23 12:01:19 +00:00
|
|
|
|
ASTs::iterator insert_it = columns.end();
|
|
|
|
|
if (params.column)
|
|
|
|
|
{
|
|
|
|
|
const ASTIdentifier & col_after = dynamic_cast<const ASTIdentifier &>(*params.column);
|
2013-12-18 11:19:37 +00:00
|
|
|
|
insert_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqualIgnoreAfterDot, col_after.name, _1)) ;
|
2013-09-23 12:01:19 +00:00
|
|
|
|
++insert_it; /// increase iterator because we want to insert after found element not before
|
|
|
|
|
}
|
|
|
|
|
columns.insert(insert_it, params.name_type);
|
|
|
|
|
}
|
|
|
|
|
else if (params.type == ASTAlterQuery::DROP)
|
|
|
|
|
{
|
|
|
|
|
const ASTIdentifier & drop_column = dynamic_cast <const ASTIdentifier &>(*params.column);
|
|
|
|
|
ASTs::iterator drop_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqual, drop_column.name, _1));
|
|
|
|
|
columns.erase(drop_it);
|
|
|
|
|
}
|
2014-02-28 10:15:21 +00:00
|
|
|
|
else if (params.type == ASTAlterQuery::MODIFY)
|
|
|
|
|
{
|
|
|
|
|
const ASTNameTypePair & name_type = dynamic_cast<const ASTNameTypePair &>(*params.name_type);
|
|
|
|
|
ASTs::iterator modify_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqual, name_type.name, _1));
|
|
|
|
|
ASTNameTypePair & modified_column = dynamic_cast<ASTNameTypePair &>(**modify_it);
|
|
|
|
|
modified_column.type = name_type.type;
|
|
|
|
|
}
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
|
|
|
|
/// Перезаписываем файл метадата каждую итерацию
|
|
|
|
|
Poco::FileOutputStream ostr(metadata_path);
|
|
|
|
|
formatAST(attach, ostr, 0, false);
|
|
|
|
|
}
|
|
|
|
|
}
|