ClickHouse/src/Storages/ColumnsDescription.cpp

589 lines
18 KiB
C++
Raw Normal View History

#include <Storages/ColumnsDescription.h>
2020-03-24 17:05:38 +00:00
#include <Parsers/ASTLiteral.h>
2018-11-16 12:01:52 +00:00
#include <Parsers/ExpressionElementParsers.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/ParserCreateQuery.h>
#include <Parsers/parseQuery.h>
#include <Parsers/queryToString.h>
2020-03-03 09:05:17 +00:00
#include <Parsers/ASTSubquery.h>
#include <Parsers/ASTSelectQuery.h>
2020-03-03 12:51:41 +00:00
#include <Parsers/ASTSelectWithUnionQuery.h>
#include <IO/WriteBuffer.h>
#include <IO/WriteHelpers.h>
#include <IO/ReadBuffer.h>
#include <IO/ReadHelpers.h>
#include <IO/WriteBufferFromString.h>
#include <IO/ReadBufferFromString.h>
#include <DataTypes/DataTypeFactory.h>
#include <DataTypes/NestedUtils.h>
#include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeTuple.h>
2020-11-05 13:24:31 +00:00
#include <DataTypes/DataTypeNested.h>
#include <Common/Exception.h>
#include <Interpreters/Context.h>
#include <Storages/IStorage.h>
#include <Common/typeid_cast.h>
#include <Core/Defines.h>
2018-10-11 02:57:48 +00:00
#include <Compression/CompressionFactory.h>
2020-03-03 09:05:17 +00:00
#include <Interpreters/ExpressionAnalyzer.h>
#include <Interpreters/TreeRewriter.h>
2020-03-03 09:05:17 +00:00
#include <Interpreters/ExpressionActions.h>
namespace DB
{
namespace ErrorCodes
{
extern const int NO_SUCH_COLUMN_IN_TABLE;
extern const int ILLEGAL_COLUMN;
extern const int CANNOT_PARSE_TEXT;
2020-03-03 09:05:17 +00:00
extern const int THERE_IS_NO_DEFAULT_VALUE;
2020-03-31 16:18:18 +00:00
extern const int LOGICAL_ERROR;
}
2020-04-27 13:55:30 +00:00
ColumnDescription::ColumnDescription(String name_, DataTypePtr type_)
: name(std::move(name_)), type(std::move(type_))
2019-05-21 11:24:32 +00:00
{
}
bool ColumnDescription::operator==(const ColumnDescription & other) const
{
auto ast_to_str = [](const ASTPtr & ast) { return ast ? queryToString(ast) : String{}; };
return name == other.name
&& type->equals(*other.type)
&& default_desc == other.default_desc
&& comment == other.comment
&& ast_to_str(codec) == ast_to_str(other.codec)
&& ast_to_str(ttl) == ast_to_str(other.ttl);
}
void ColumnDescription::writeText(WriteBuffer & buf) const
{
writeBackQuotedString(name, buf);
writeChar(' ', buf);
2020-11-19 17:16:45 +00:00
writeEscapedString(type->getName(), buf);
if (default_desc.expression)
{
writeChar('\t', buf);
DB::writeText(DB::toString(default_desc.kind), buf);
writeChar('\t', buf);
2020-11-19 17:16:45 +00:00
writeEscapedString(queryToString(default_desc.expression), buf);
}
if (!comment.empty())
{
writeChar('\t', buf);
DB::writeText("COMMENT ", buf);
2020-11-19 17:16:45 +00:00
writeEscapedString(queryToString(ASTLiteral(Field(comment))), buf);
}
if (codec)
{
writeChar('\t', buf);
2020-11-19 17:16:45 +00:00
writeEscapedString(queryToString(codec), buf);
}
if (ttl)
{
writeChar('\t', buf);
DB::writeText("TTL ", buf);
2020-11-19 17:16:45 +00:00
writeEscapedString(queryToString(ttl), buf);
}
writeChar('\n', buf);
}
void ColumnDescription::readText(ReadBuffer & buf)
{
2019-08-08 20:02:30 +00:00
ParserColumnDeclaration column_parser(/* require type */ true);
String column_line;
readEscapedStringUntilEOL(column_line, buf);
ASTPtr ast = parseQuery(column_parser, column_line, "column parser", 0, DBMS_DEFAULT_MAX_PARSER_DEPTH);
if (const auto * col_ast = ast->as<ASTColumnDeclaration>())
{
name = col_ast->name;
type = DataTypeFactory::instance().get(col_ast->type);
if (col_ast->default_expression)
{
default_desc.kind = columnDefaultKindFromString(col_ast->default_specifier);
default_desc.expression = std::move(col_ast->default_expression);
}
if (col_ast->comment)
comment = col_ast->comment->as<ASTLiteral &>().value.get<String>();
if (col_ast->codec)
codec = CompressionCodecFactory::instance().validateCodecAndGetPreprocessedAST(col_ast->codec, type, false);
if (col_ast->ttl)
ttl = col_ast->ttl;
}
else
throw Exception("Cannot parse column description", ErrorCodes::CANNOT_PARSE_TEXT);
}
2020-04-27 13:55:30 +00:00
ColumnsDescription::ColumnsDescription(NamesAndTypesList ordinary)
{
for (auto & elem : ordinary)
2020-04-27 13:55:30 +00:00
add(ColumnDescription(std::move(elem.name), std::move(elem.type)));
}
/// We are trying to find first column from end with name `column_name` or with a name beginning with `column_name` and ".".
/// For example "fruits.bananas"
/// names are considered the same if they completely match or `name_without_dot` matches the part of the name to the point
static auto getNameRange(const ColumnsDescription::Container & columns, const String & name_without_dot)
{
String name_with_dot = name_without_dot + ".";
auto begin = columns.begin();
for (; begin != columns.end(); ++begin)
{
if (begin->name == name_without_dot)
return std::make_pair(begin, std::next(begin));
if (startsWith(begin->name, name_with_dot))
break;
}
if (begin == columns.end())
return std::make_pair(begin, begin);
auto end = std::next(begin);
for (; end != columns.end(); ++end)
{
if (!startsWith(end->name, name_with_dot))
break;
}
return std::make_pair(begin, end);
}
void ColumnsDescription::add(ColumnDescription column, const String & after_column, bool first)
{
if (has(column.name))
throw Exception("Cannot add column " + column.name + ": column with this name already exists",
ErrorCodes::ILLEGAL_COLUMN);
auto insert_it = columns.cend();
if (first)
insert_it = columns.cbegin();
else if (!after_column.empty())
{
auto range = getNameRange(columns, after_column);
if (range.first == range.second)
throw Exception("Wrong column name. Cannot find column " + after_column + " to insert after",
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
insert_it = range.second;
}
2020-10-29 14:14:23 +00:00
addSubcolumns(column.name, column.type);
columns.get<0>().insert(insert_it, std::move(column));
}
void ColumnsDescription::remove(const String & column_name)
{
auto range = getNameRange(columns, column_name);
if (range.first == range.second)
throw Exception("There is no column " + column_name + " in table.",
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
for (auto list_it = range.first; list_it != range.second;)
2020-10-27 11:27:14 +00:00
{
removeSubcolumns(list_it->name, list_it->type);
list_it = columns.get<0>().erase(list_it);
2020-10-27 11:27:14 +00:00
}
}
2020-03-24 17:05:38 +00:00
void ColumnsDescription::rename(const String & column_from, const String & column_to)
{
2020-03-25 18:44:08 +00:00
auto it = columns.get<1>().find(column_from);
if (it == columns.get<1>().end())
throw Exception("Cannot find column " + column_from + " in ColumnsDescription", ErrorCodes::LOGICAL_ERROR);
2020-03-24 17:05:38 +00:00
2020-03-25 18:44:08 +00:00
columns.get<1>().modify_key(it, [&column_to] (String & old_name)
2020-03-24 17:05:38 +00:00
{
2020-03-25 18:44:08 +00:00
old_name = column_to;
});
2020-03-24 17:05:38 +00:00
}
void ColumnsDescription::modifyColumnOrder(const String & column_name, const String & after_column, bool first)
{
const auto & reorder_column = [&](auto get_new_pos)
{
auto column_range = getNameRange(columns, column_name);
if (column_range.first == column_range.second)
throw Exception("There is no column " + column_name + " in table.", ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
std::vector<ColumnDescription> moving_columns;
for (auto list_it = column_range.first; list_it != column_range.second;)
{
moving_columns.emplace_back(*list_it);
list_it = columns.get<0>().erase(list_it);
}
columns.get<0>().insert(get_new_pos(), moving_columns.begin(), moving_columns.end());
};
if (first)
reorder_column([&]() { return columns.cbegin(); });
else if (!after_column.empty() && column_name != after_column)
{
/// Checked first
auto range = getNameRange(columns, after_column);
if (range.first == range.second)
throw Exception("Wrong column name. Cannot find column " + after_column + " to insert after",
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
reorder_column([&]() { return getNameRange(columns, after_column).second; });
}
}
void ColumnsDescription::flattenNested()
{
for (auto it = columns.begin(); it != columns.end();)
{
const auto * type_arr = typeid_cast<const DataTypeArray *>(it->type.get());
if (!type_arr)
{
++it;
continue;
}
const auto * type_tuple = typeid_cast<const DataTypeTuple *>(type_arr->getNestedType().get());
if (!type_tuple)
{
++it;
continue;
}
2020-01-27 20:26:39 +00:00
if (!type_tuple->haveExplicitNames())
{
++it;
continue;
}
ColumnDescription column = std::move(*it);
2020-10-29 14:14:23 +00:00
removeSubcolumns(column.name, column.type);
it = columns.get<0>().erase(it);
const DataTypes & elements = type_tuple->getElements();
const Strings & names = type_tuple->getElementNames();
size_t tuple_size = elements.size();
for (size_t i = 0; i < tuple_size; ++i)
{
auto nested_column = column;
/// TODO: what to do with default expressions?
nested_column.name = Nested::concatenateName(column.name, names[i]);
nested_column.type = std::make_shared<DataTypeArray>(elements[i]);
2020-10-29 14:14:23 +00:00
addSubcolumns(nested_column.name, nested_column.type);
columns.get<0>().insert(it, std::move(nested_column));
}
}
}
NamesAndTypesList ColumnsDescription::getOrdinary() const
{
NamesAndTypesList ret;
for (const auto & col : columns)
2020-04-27 13:55:30 +00:00
if (col.default_desc.kind == ColumnDefaultKind::Default)
ret.emplace_back(col.name, col.type);
return ret;
}
NamesAndTypesList ColumnsDescription::getMaterialized() const
{
NamesAndTypesList ret;
for (const auto & col : columns)
if (col.default_desc.kind == ColumnDefaultKind::Materialized)
ret.emplace_back(col.name, col.type);
return ret;
}
NamesAndTypesList ColumnsDescription::getAliases() const
{
NamesAndTypesList ret;
for (const auto & col : columns)
if (col.default_desc.kind == ColumnDefaultKind::Alias)
ret.emplace_back(col.name, col.type);
return ret;
}
NamesAndTypesList ColumnsDescription::getAll() const
{
NamesAndTypesList ret;
for (const auto & col : columns)
ret.emplace_back(col.name, col.type);
return ret;
}
bool ColumnsDescription::has(const String & column_name) const
{
2020-09-18 17:37:08 +00:00
return columns.get<1>().find(column_name) != columns.get<1>().end()
|| subcolumns.find(column_name) != subcolumns.end();
}
bool ColumnsDescription::hasNested(const String & column_name) const
{
auto range = getNameRange(columns, column_name);
return range.first != range.second && range.first->name.length() > column_name.length();
}
const ColumnDescription & ColumnsDescription::get(const String & column_name) const
{
auto it = columns.get<1>().find(column_name);
if (it == columns.get<1>().end())
throw Exception("There is no column " + column_name + " in table.",
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
return *it;
}
NamesAndTypesList ColumnsDescription::getAllPhysical() const
{
NamesAndTypesList ret;
for (const auto & col : columns)
2020-04-27 13:55:30 +00:00
if (col.default_desc.kind != ColumnDefaultKind::Alias)
ret.emplace_back(col.name, col.type);
return ret;
}
Names ColumnsDescription::getNamesOfPhysical() const
2018-11-16 12:01:52 +00:00
{
Names ret;
for (const auto & col : columns)
2020-04-27 13:55:30 +00:00
if (col.default_desc.kind != ColumnDefaultKind::Alias)
ret.emplace_back(col.name);
return ret;
}
2018-11-16 12:01:52 +00:00
NameAndTypePair ColumnsDescription::getPhysical(const String & column_name) const
{
auto it = columns.get<1>().find(column_name);
2020-04-27 13:55:30 +00:00
if (it == columns.get<1>().end() || it->default_desc.kind == ColumnDefaultKind::Alias)
throw Exception("There is no physical column " + column_name + " in table.", ErrorCodes::NO_SUCH_COLUMN_IN_TABLE);
return NameAndTypePair(it->name, it->type);
}
2018-11-16 12:01:52 +00:00
2020-09-18 17:37:08 +00:00
NameAndTypePair ColumnsDescription::getPhysicalOrSubcolumn(const String & column_name) const
{
2020-09-18 17:37:08 +00:00
if (auto it = columns.get<1>().find(column_name); it != columns.get<1>().end()
&& it->default_desc.kind != ColumnDefaultKind::Alias)
{
return NameAndTypePair(it->name, it->type);
2020-09-18 17:37:08 +00:00
}
2020-09-18 17:37:08 +00:00
if (auto it = subcolumns.find(column_name); it != subcolumns.end())
{
2020-09-18 17:37:08 +00:00
return it->second;
}
2020-09-18 17:37:08 +00:00
throw Exception(ErrorCodes::NO_SUCH_COLUMN_IN_TABLE,
"There is no physical column or subcolumn {} in table.", column_name);
}
bool ColumnsDescription::hasPhysical(const String & column_name) const
{
auto it = columns.get<1>().find(column_name);
2020-04-27 13:55:30 +00:00
return it != columns.get<1>().end() && it->default_desc.kind != ColumnDefaultKind::Alias;
}
2018-11-16 12:01:52 +00:00
bool ColumnsDescription::hasPhysicalOrSubcolumn(const String & column_name) const
{
2020-09-18 17:37:08 +00:00
return hasPhysical(column_name) || subcolumns.find(column_name) != subcolumns.end();
}
2020-11-24 14:14:47 +00:00
static NamesAndTypesList getWithSubcolumns(NamesAndTypesList && source_list)
{
NamesAndTypesList ret;
for (const auto & col : source_list)
{
ret.emplace_back(col.name, col.type);
for (const auto & subcolumn : col.type->getSubcolumnNames())
ret.emplace_back(col.name, subcolumn, col.type, col.type->getSubcolumnType(subcolumn));
}
return ret;
}
NamesAndTypesList ColumnsDescription::getAllWithSubcolumns() const
{
return getWithSubcolumns(getAll());
}
NamesAndTypesList ColumnsDescription::getAllPhysicalWithSubcolumns() const
{
return getWithSubcolumns(getAllPhysical());
}
2020-10-02 12:38:50 +00:00
bool ColumnsDescription::hasDefaults() const
{
for (const auto & column : columns)
if (column.default_desc.expression)
return true;
return false;
}
ColumnDefaults ColumnsDescription::getDefaults() const
{
ColumnDefaults ret;
for (const auto & column : columns)
if (column.default_desc.expression)
ret.emplace(column.name, column.default_desc);
return ret;
}
bool ColumnsDescription::hasDefault(const String & column_name) const
{
auto it = columns.get<1>().find(column_name);
return it != columns.get<1>().end() && it->default_desc.expression;
2018-11-16 12:01:52 +00:00
}
std::optional<ColumnDefault> ColumnsDescription::getDefault(const String & column_name) const
{
auto it = columns.get<1>().find(column_name);
if (it != columns.get<1>().end() && it->default_desc.expression)
return it->default_desc;
return {};
}
2020-08-28 09:07:20 +00:00
bool ColumnsDescription::hasCompressionCodec(const String & column_name) const
{
const auto it = columns.get<1>().find(column_name);
2020-08-28 10:47:29 +00:00
return it != columns.get<1>().end() && it->codec != nullptr;
2020-08-28 09:07:20 +00:00
}
2018-12-21 12:17:30 +00:00
CompressionCodecPtr ColumnsDescription::getCodecOrDefault(const String & column_name, CompressionCodecPtr default_codec) const
2018-10-11 02:57:48 +00:00
{
const auto it = columns.get<1>().find(column_name);
2018-10-11 02:57:48 +00:00
if (it == columns.get<1>().end() || !it->codec)
2018-12-21 12:17:30 +00:00
return default_codec;
2018-10-11 02:57:48 +00:00
return CompressionCodecFactory::instance().get(it->codec, it->type, default_codec);
2018-10-11 02:57:48 +00:00
}
CompressionCodecPtr ColumnsDescription::getCodecOrDefault(const String & column_name) const
{
return getCodecOrDefault(column_name, CompressionCodecFactory::instance().getDefaultCodec());
}
2020-09-21 11:24:10 +00:00
ASTPtr ColumnsDescription::getCodecDescOrDefault(const String & column_name, CompressionCodecPtr default_codec) const
2020-09-21 07:18:23 +00:00
{
const auto it = columns.get<1>().find(column_name);
if (it == columns.get<1>().end() || !it->codec)
return default_codec->getFullCodecDesc();
return it->codec;
}
ColumnsDescription::ColumnTTLs ColumnsDescription::getColumnTTLs() const
{
ColumnTTLs ret;
for (const auto & column : columns)
if (column.ttl)
ret.emplace(column.name, column.ttl);
return ret;
}
String ColumnsDescription::toString() const
{
WriteBufferFromOwnString buf;
writeCString("columns format version: 1\n", buf);
DB::writeText(columns.size(), buf);
writeCString(" columns:\n", buf);
for (const ColumnDescription & column : columns)
column.writeText(buf);
return buf.str();
}
ColumnsDescription ColumnsDescription::parse(const String & str)
{
ReadBufferFromString buf{str};
assertString("columns format version: 1\n", buf);
size_t count{};
readText(count, buf);
assertString(" columns:\n", buf);
ColumnsDescription result;
for (size_t i = 0; i < count; ++i)
{
ColumnDescription column;
column.readText(buf);
buf.ignore(1); /// ignore new line
2020-09-18 17:37:08 +00:00
result.add(column);
}
assertEOF(buf);
return result;
}
2021-01-12 23:20:32 +00:00
void ColumnsDescription::addSubcolumns(const String & name_in_storage, const DataTypePtr & type_in_storage)
2020-09-18 17:37:08 +00:00
{
2021-01-12 23:20:32 +00:00
for (const auto & subcolumn_name : type_in_storage->getSubcolumnNames())
2020-09-18 17:37:08 +00:00
{
2021-01-12 23:20:32 +00:00
auto subcolumn = NameAndTypePair(name_in_storage, subcolumn_name,
type_in_storage, type_in_storage->getSubcolumnType(subcolumn_name));
2020-09-18 17:37:08 +00:00
if (has(subcolumn.name))
throw Exception(ErrorCodes::ILLEGAL_COLUMN,
"Cannot add subcolumn {}: column with this name already exists", subcolumn.name);
subcolumns[subcolumn.name] = subcolumn;
}
}
2020-03-03 09:05:17 +00:00
2021-01-12 23:20:32 +00:00
void ColumnsDescription::removeSubcolumns(const String & name_in_storage, const DataTypePtr & type_in_storage)
2020-10-27 11:27:14 +00:00
{
2021-01-12 23:20:32 +00:00
for (const auto & subcolumn_name : type_in_storage->getSubcolumnNames())
subcolumns.erase(name_in_storage + "." + subcolumn_name);
2020-10-27 11:27:14 +00:00
}
2020-03-03 09:05:17 +00:00
Block validateColumnsDefaultsAndGetSampleBlock(ASTPtr default_expr_list, const NamesAndTypesList & all_columns, const Context & context)
{
for (const auto & child : default_expr_list->children)
2020-03-03 12:51:41 +00:00
if (child->as<ASTSelectQuery>() || child->as<ASTSelectWithUnionQuery>() || child->as<ASTSubquery>())
2020-03-03 09:05:17 +00:00
throw Exception("Select query is not allowed in columns DEFAULT expression", ErrorCodes::THERE_IS_NO_DEFAULT_VALUE);
try
{
auto syntax_analyzer_result = TreeRewriter(context).analyze(default_expr_list, all_columns);
const auto actions = ExpressionAnalyzer(default_expr_list, syntax_analyzer_result, context).getActions(true);
2020-04-22 06:22:14 +00:00
for (const auto & action : actions->getActions())
2020-11-10 14:54:59 +00:00
if (action.node->type == ActionsDAG::ActionType::ARRAY_JOIN)
2020-09-08 10:40:53 +00:00
throw Exception("Unsupported default value that requires ARRAY JOIN action", ErrorCodes::THERE_IS_NO_DEFAULT_VALUE);
2020-03-03 09:05:17 +00:00
return actions->getSampleBlock();
}
catch (Exception & ex)
{
ex.addMessage("default expression and column type are incompatible.");
throw;
}
2020-03-03 09:05:17 +00:00
}
}