2014-04-29 00:28:18 +00:00
|
|
|
|
#include <unordered_set>
|
2012-11-30 05:24:24 +00:00
|
|
|
|
#include <sparsehash/dense_hash_map>
|
2014-03-09 17:36:01 +00:00
|
|
|
|
#include <sparsehash/dense_hash_set>
|
2014-03-19 10:45:13 +00:00
|
|
|
|
#include <DB/Storages/ITableDeclaration.h>
|
2013-09-23 12:01:19 +00:00
|
|
|
|
#include <DB/DataTypes/DataTypeNested.h>
|
|
|
|
|
#include <DB/Parsers/ASTIdentifier.h>
|
|
|
|
|
#include <DB/Parsers/ASTNameTypePair.h>
|
|
|
|
|
#include <DB/Interpreters/Context.h>
|
2014-03-09 17:36:01 +00:00
|
|
|
|
#include <boost/bind.hpp>
|
2011-08-15 21:50:08 +00:00
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
bool ITableDeclaration::hasRealColumn(const String & column_name) const
|
2014-01-16 14:52:13 +00:00
|
|
|
|
{
|
|
|
|
|
const NamesAndTypesList & real_columns = getColumnsList();
|
|
|
|
|
for (auto & it : real_columns)
|
|
|
|
|
if (it.first == column_name)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-13 15:00:06 +00:00
|
|
|
|
Names ITableDeclaration::getColumnNamesList() const
|
|
|
|
|
{
|
|
|
|
|
const NamesAndTypesList & real_columns = getColumnsList();
|
|
|
|
|
Names res;
|
|
|
|
|
for (auto & it : real_columns)
|
|
|
|
|
res.push_back(it.first);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
NameAndTypePair ITableDeclaration::getRealColumn(const String & column_name) const
|
2014-01-16 14:52:13 +00:00
|
|
|
|
{
|
|
|
|
|
const NamesAndTypesList & real_columns = getColumnsList();
|
|
|
|
|
for (auto & it : real_columns)
|
|
|
|
|
if (it.first == column_name)
|
|
|
|
|
return it;
|
|
|
|
|
throw Exception("There is no column " + column_name + " in table " + getTableName(), ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
bool ITableDeclaration::hasColumn(const String & column_name) const
|
2014-01-16 14:52:13 +00:00
|
|
|
|
{
|
|
|
|
|
return hasRealColumn(column_name); /// По умолчанию считаем, что виртуальных столбцов в сторадже нет.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
NameAndTypePair ITableDeclaration::getColumn(const String & column_name) const
|
2014-01-16 14:52:13 +00:00
|
|
|
|
{
|
|
|
|
|
return getRealColumn(column_name); /// По умолчанию считаем, что виртуальных столбцов в сторадже нет.
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 17:12:11 +00:00
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
const DataTypePtr ITableDeclaration::getDataTypeByName(const String & column_name) const
|
2011-11-01 17:12:11 +00:00
|
|
|
|
{
|
|
|
|
|
const NamesAndTypesList & names_and_types = getColumnsList();
|
|
|
|
|
for (NamesAndTypesList::const_iterator it = names_and_types.begin(); it != names_and_types.end(); ++it)
|
|
|
|
|
if (it->first == column_name)
|
|
|
|
|
return it->second;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2011-11-01 17:12:11 +00:00
|
|
|
|
throw Exception("There is no column " + column_name + " in table " + getTableName(), ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
Block ITableDeclaration::getSampleBlock() const
|
2011-10-31 06:37:12 +00:00
|
|
|
|
{
|
|
|
|
|
Block res;
|
2011-11-01 17:12:11 +00:00
|
|
|
|
const NamesAndTypesList & names_and_types = getColumnsList();
|
2011-10-31 06:37:12 +00:00
|
|
|
|
|
2011-11-01 17:12:11 +00:00
|
|
|
|
for (NamesAndTypesList::const_iterator it = names_and_types.begin(); it != names_and_types.end(); ++it)
|
2011-10-31 06:37:12 +00:00
|
|
|
|
{
|
|
|
|
|
ColumnWithNameAndType col;
|
|
|
|
|
col.name = it->first;
|
|
|
|
|
col.type = it->second;
|
|
|
|
|
col.column = col.type->createColumn();
|
|
|
|
|
res.insert(col);
|
|
|
|
|
}
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2011-10-31 06:37:12 +00:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-11-30 05:24:24 +00:00
|
|
|
|
static std::string listOfColumns(const NamesAndTypesList & available_columns)
|
|
|
|
|
{
|
|
|
|
|
std::stringstream s;
|
|
|
|
|
for (NamesAndTypesList::const_iterator it = available_columns.begin(); it != available_columns.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
if (it != available_columns.begin())
|
|
|
|
|
s << ", ";
|
|
|
|
|
s << it->first;
|
|
|
|
|
}
|
|
|
|
|
return s.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
typedef google::dense_hash_map<StringRef, const IDataType *, StringRefHash> NamesAndTypesMap;
|
2012-11-30 05:24:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static NamesAndTypesMap getColumnsMap(const NamesAndTypesList & available_columns)
|
|
|
|
|
{
|
|
|
|
|
NamesAndTypesMap res;
|
2012-11-30 05:30:17 +00:00
|
|
|
|
res.set_empty_key(StringRef());
|
2012-11-30 05:24:24 +00:00
|
|
|
|
|
|
|
|
|
for (NamesAndTypesList::const_iterator it = available_columns.begin(); it != available_columns.end(); ++it)
|
|
|
|
|
res.insert(NamesAndTypesMap::value_type(it->first, &*it->second));
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
void ITableDeclaration::check(const Names & column_names) const
|
2011-08-15 21:50:08 +00:00
|
|
|
|
{
|
2012-11-30 05:24:24 +00:00
|
|
|
|
const NamesAndTypesList & available_columns = getColumnsList();
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2011-08-15 21:50:08 +00:00
|
|
|
|
if (column_names.empty())
|
|
|
|
|
throw Exception("Empty list of columns queried for table " + getTableName()
|
|
|
|
|
+ ". There are columns: " + listOfColumns(available_columns),
|
|
|
|
|
ErrorCodes::EMPTY_LIST_OF_COLUMNS_QUERIED);
|
|
|
|
|
|
2012-11-30 05:24:24 +00:00
|
|
|
|
const NamesAndTypesMap & columns_map = getColumnsMap(available_columns);
|
|
|
|
|
|
2014-04-29 00:28:18 +00:00
|
|
|
|
typedef google::dense_hash_set<StringRef, StringRefHash> UniqueStrings;
|
2011-08-15 21:50:08 +00:00
|
|
|
|
UniqueStrings unique_names;
|
2012-11-30 05:30:17 +00:00
|
|
|
|
unique_names.set_empty_key(StringRef());
|
2011-08-15 21:50:08 +00:00
|
|
|
|
|
|
|
|
|
for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it)
|
|
|
|
|
{
|
2012-11-30 05:24:24 +00:00
|
|
|
|
if (columns_map.end() == columns_map.find(*it))
|
2011-08-15 21:50:08 +00:00
|
|
|
|
throw Exception("There is no column with name " + *it + " in table " + getTableName()
|
|
|
|
|
+ ". There are columns: " + listOfColumns(available_columns),
|
|
|
|
|
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
|
|
|
|
|
|
|
|
|
|
if (unique_names.end() != unique_names.find(*it))
|
|
|
|
|
throw Exception("Column " + *it + " queried more than once in table " + getTableName(),
|
|
|
|
|
ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE);
|
|
|
|
|
unique_names.insert(*it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
void ITableDeclaration::check(const Block & block, bool need_all) const
|
2011-08-15 21:50:08 +00:00
|
|
|
|
{
|
2012-11-30 05:24:24 +00:00
|
|
|
|
const NamesAndTypesList & available_columns = getColumnsList();
|
|
|
|
|
const NamesAndTypesMap & columns_map = getColumnsMap(available_columns);
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2014-01-08 16:33:28 +00:00
|
|
|
|
typedef std::unordered_set<String> NameSet;
|
2013-02-25 10:23:31 +00:00
|
|
|
|
NameSet names_in_block;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2011-08-15 21:50:08 +00:00
|
|
|
|
for (size_t i = 0; i < block.columns(); ++i)
|
|
|
|
|
{
|
|
|
|
|
const ColumnWithNameAndType & column = block.getByPosition(i);
|
|
|
|
|
|
2013-02-25 10:23:31 +00:00
|
|
|
|
if (names_in_block.count(column.name))
|
|
|
|
|
throw Exception("Duplicate column " + column.name + " in block",
|
|
|
|
|
ErrorCodes::DUPLICATE_COLUMN);
|
|
|
|
|
|
|
|
|
|
names_in_block.insert(column.name);
|
|
|
|
|
|
2012-11-30 05:24:24 +00:00
|
|
|
|
NamesAndTypesMap::const_iterator it = columns_map.find(column.name);
|
|
|
|
|
if (columns_map.end() == it)
|
2011-08-15 21:50:08 +00:00
|
|
|
|
throw Exception("There is no column with name " + column.name + " in table " + getTableName()
|
|
|
|
|
+ ". There are columns: " + listOfColumns(available_columns),
|
|
|
|
|
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
|
|
|
|
|
|
|
|
|
|
if (column.type->getName() != it->second->getName())
|
|
|
|
|
throw Exception("Type mismatch for column " + column.name + " in table " + getTableName()
|
|
|
|
|
+ ". Column has type " + it->second->getName() + ", got type " + column.type->getName(),
|
|
|
|
|
ErrorCodes::TYPE_MISMATCH);
|
|
|
|
|
}
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2013-02-25 10:23:31 +00:00
|
|
|
|
if (need_all && names_in_block.size() < columns_map.size())
|
|
|
|
|
{
|
2013-02-25 10:51:52 +00:00
|
|
|
|
for (NamesAndTypesList::const_iterator it = available_columns.begin(); it != available_columns.end(); ++it)
|
2013-02-25 10:23:31 +00:00
|
|
|
|
{
|
|
|
|
|
if (!names_in_block.count(it->first))
|
|
|
|
|
throw Exception("Expected column " + it->first, ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-15 21:50:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-13 09:47:12 +00:00
|
|
|
|
/// одинаковыми считаются имена, если они совпадают целиком или name_without_dot совпадает с частью имени до точки
|
2013-12-18 11:19:37 +00:00
|
|
|
|
static bool namesEqual(const String & name_without_dot, const DB::NameAndTypePair & name_type)
|
2013-09-23 12:01:19 +00:00
|
|
|
|
{
|
2013-11-13 09:47:12 +00:00
|
|
|
|
String name_with_dot = name_without_dot + ".";
|
|
|
|
|
return (name_with_dot == name_type.first.substr(0, name_without_dot.length() + 1) || name_without_dot == name_type.first);
|
2013-09-23 12:01:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
void ITableDeclaration::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context)
|
2013-09-23 12:01:19 +00:00
|
|
|
|
{
|
|
|
|
|
if (params.type == ASTAlterQuery::ADD)
|
|
|
|
|
{
|
|
|
|
|
NamesAndTypesList::iterator insert_it = columns->end();
|
|
|
|
|
if (params.column)
|
|
|
|
|
{
|
|
|
|
|
String column_name = dynamic_cast<const ASTIdentifier &>(*params.column).name;
|
|
|
|
|
|
2013-11-13 09:47:12 +00:00
|
|
|
|
/// Пытаемся найти первую с конца колонку с именем column_name или с именем, начинающимся с column_name и ".".
|
|
|
|
|
/// Например "fruits.bananas"
|
2013-09-23 12:01:19 +00:00
|
|
|
|
NamesAndTypesList::reverse_iterator reverse_insert_it = std::find_if(columns->rbegin(), columns->rend(), boost::bind(namesEqual, column_name, _1) );
|
|
|
|
|
|
|
|
|
|
if (reverse_insert_it == columns->rend())
|
2013-12-23 09:29:42 +00:00
|
|
|
|
throw Exception("Wrong column name. Cannot find column " + column_name + " to insert after", DB::ErrorCodes::ILLEGAL_COLUMN);
|
2013-09-23 12:01:19 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/// base возвращает итератор уже смещенный на один элемент вправо
|
|
|
|
|
insert_it = reverse_insert_it.base();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ASTNameTypePair & ast_name_type = dynamic_cast<const ASTNameTypePair &>(*params.name_type);
|
|
|
|
|
StringRange type_range = ast_name_type.type->range;
|
|
|
|
|
String type_string = String(type_range.first, type_range.second - type_range.first);
|
|
|
|
|
|
|
|
|
|
DB::DataTypePtr data_type = context.getDataTypeFactory().get(type_string);
|
|
|
|
|
NameAndTypePair pair(ast_name_type.name, data_type );
|
|
|
|
|
columns->insert(insert_it, pair);
|
|
|
|
|
|
|
|
|
|
/// Медленно, так как каждый раз копируется список
|
|
|
|
|
columns = DataTypeNested::expandNestedColumns(*columns);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (params.type == ASTAlterQuery::DROP)
|
|
|
|
|
{
|
|
|
|
|
String column_name = dynamic_cast<const ASTIdentifier &>(*(params.column)).name;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
/// Удаляем колонки из листа columns
|
|
|
|
|
bool is_first = true;
|
|
|
|
|
NamesAndTypesList::iterator column_it;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
column_it = std::find_if(columns->begin(), columns->end(), boost::bind(namesEqual, column_name, _1));
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
if (column_it == columns->end())
|
|
|
|
|
{
|
|
|
|
|
if (is_first)
|
2013-12-23 09:29:42 +00:00
|
|
|
|
throw Exception("Wrong column name. Cannot find column " + column_name + " to drop", DB::ErrorCodes::ILLEGAL_COLUMN);
|
2013-09-23 12:01:19 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
columns->erase(column_it);
|
|
|
|
|
is_first = false;
|
|
|
|
|
}
|
|
|
|
|
while (column_it != columns->end());
|
|
|
|
|
}
|
2014-02-28 12:09:02 +00:00
|
|
|
|
else if (params.type == ASTAlterQuery::MODIFY)
|
|
|
|
|
{
|
|
|
|
|
const ASTNameTypePair & ast_name_type = dynamic_cast<const ASTNameTypePair &>(*params.name_type);
|
|
|
|
|
StringRange type_range = ast_name_type.type->range;
|
|
|
|
|
String type_string = String(type_range.first, type_range.second - type_range.first);
|
|
|
|
|
|
|
|
|
|
DB::DataTypePtr data_type = context.getDataTypeFactory().get(type_string);
|
2014-03-04 19:11:32 +00:00
|
|
|
|
NameAndTypePair pair(ast_name_type.name, data_type);
|
2014-02-28 12:09:02 +00:00
|
|
|
|
NamesAndTypesList::iterator column_it = std::find_if(columns->begin(), columns->end(), boost::bind(namesEqual, ast_name_type.name, _1) );
|
|
|
|
|
if (column_it == columns->end())
|
|
|
|
|
throw Exception("Wrong column name. Cannot find column " + ast_name_type.name + " to modify.", DB::ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
column_it->second = data_type;
|
|
|
|
|
}
|
2013-09-23 12:01:19 +00:00
|
|
|
|
else
|
2014-03-09 17:36:01 +00:00
|
|
|
|
throw Exception("Wrong parameter type in ALTER query", ErrorCodes::LOGICAL_ERROR);
|
2013-09-23 12:01:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-15 21:50:08 +00:00
|
|
|
|
}
|