Implemented MySQL column comments support

This commit is contained in:
Kostiantyn Storozhuk 2021-06-28 21:50:43 +08:00
parent 71603a7d13
commit c2c78929cb
9 changed files with 49 additions and 29 deletions

View File

@ -232,7 +232,7 @@ void DatabaseMySQL::fetchLatestTablesStructureIntoCache(
wait_update_tables_name.emplace_back(table_modification_time.first); wait_update_tables_name.emplace_back(table_modification_time.first);
} }
std::map<String, NamesAndTypesList> tables_and_columns = fetchTablesColumnsList(wait_update_tables_name, local_context); std::map<String, ColumnsDescription> tables_and_columns = fetchTablesColumnsList(wait_update_tables_name, local_context);
for (const auto & table_and_columns : tables_and_columns) for (const auto & table_and_columns : tables_and_columns)
{ {
@ -296,7 +296,7 @@ std::map<String, UInt64> DatabaseMySQL::fetchTablesWithModificationTime(ContextP
return tables_with_modification_time; return tables_with_modification_time;
} }
std::map<String, NamesAndTypesList> std::map<String, ColumnsDescription>
DatabaseMySQL::fetchTablesColumnsList(const std::vector<String> & tables_name, ContextPtr local_context) const DatabaseMySQL::fetchTablesColumnsList(const std::vector<String> & tables_name, ContextPtr local_context) const
{ {
const auto & settings = local_context->getSettingsRef(); const auto & settings = local_context->getSettingsRef();

View File

@ -8,6 +8,7 @@
#include <Core/MultiEnum.h> #include <Core/MultiEnum.h>
#include <Core/NamesAndTypes.h> #include <Core/NamesAndTypes.h>
#include <Common/ThreadPool.h> #include <Common/ThreadPool.h>
#include <Storages/ColumnsDescription.h>
#include <Databases/DatabasesCommon.h> #include <Databases/DatabasesCommon.h>
#include <Databases/MySQL/ConnectionMySQLSettings.h> #include <Databases/MySQL/ConnectionMySQLSettings.h>
#include <Parsers/ASTCreateQuery.h> #include <Parsers/ASTCreateQuery.h>
@ -111,7 +112,7 @@ private:
std::map<String, UInt64> fetchTablesWithModificationTime(ContextPtr local_context) const; std::map<String, UInt64> fetchTablesWithModificationTime(ContextPtr local_context) const;
std::map<String, NamesAndTypesList> fetchTablesColumnsList(const std::vector<String> & tables_name, ContextPtr context) const; std::map<String, ColumnsDescription> fetchTablesColumnsList(const std::vector<String> & tables_name, ContextPtr context) const;
void destroyLocalCacheExtraTables(const std::map<String, UInt64> & tables_with_modification_time) const; void destroyLocalCacheExtraTables(const std::map<String, UInt64> & tables_with_modification_time) const;

View File

@ -40,14 +40,14 @@ String toQueryStringWithQuote(const std::vector<String> & quote_list)
namespace DB namespace DB
{ {
std::map<String, NamesAndTypesList> fetchTablesColumnsList( std::map<String, ColumnsDescription> fetchTablesColumnsList(
mysqlxx::PoolWithFailover & pool, mysqlxx::PoolWithFailover & pool,
const String & database_name, const String & database_name,
const std::vector<String> & tables_name, const std::vector<String> & tables_name,
const Settings & settings, const Settings & settings,
MultiEnum<MySQLDataTypesSupport> type_support) MultiEnum<MySQLDataTypesSupport> type_support)
{ {
std::map<String, NamesAndTypesList> tables_and_columns; std::map<String, ColumnsDescription> tables_and_columns;
if (tables_name.empty()) if (tables_name.empty())
return tables_and_columns; return tables_and_columns;
@ -62,6 +62,7 @@ std::map<String, NamesAndTypesList> fetchTablesColumnsList(
{ std::make_shared<DataTypeUInt64>(), "length" }, { std::make_shared<DataTypeUInt64>(), "length" },
{ std::make_shared<DataTypeUInt64>(), "precision" }, { std::make_shared<DataTypeUInt64>(), "precision" },
{ std::make_shared<DataTypeUInt64>(), "scale" }, { std::make_shared<DataTypeUInt64>(), "scale" },
{ std::make_shared<DataTypeString>(), "column_comment" },
}; };
WriteBufferFromOwnString query; WriteBufferFromOwnString query;
@ -72,8 +73,9 @@ std::map<String, NamesAndTypesList> fetchTablesColumnsList(
" IS_NULLABLE = 'YES' AS is_nullable," " IS_NULLABLE = 'YES' AS is_nullable,"
" COLUMN_TYPE LIKE '%unsigned' AS is_unsigned," " COLUMN_TYPE LIKE '%unsigned' AS is_unsigned,"
" CHARACTER_MAXIMUM_LENGTH AS length," " CHARACTER_MAXIMUM_LENGTH AS length,"
" NUMERIC_PRECISION as numeric_precision," " NUMERIC_PRECISION AS numeric_precision,"
" IF(ISNULL(NUMERIC_SCALE), DATETIME_PRECISION, NUMERIC_SCALE) AS scale" // we know DATETIME_PRECISION as a scale in CH " IF(ISNULL(NUMERIC_SCALE), DATETIME_PRECISION, NUMERIC_SCALE) AS scale," // we know DATETIME_PRECISION as a scale in CH
" COLUMN_COMMENT AS column_comment"
" FROM INFORMATION_SCHEMA.COLUMNS" " FROM INFORMATION_SCHEMA.COLUMNS"
" WHERE "; " WHERE ";
@ -94,21 +96,24 @@ std::map<String, NamesAndTypesList> fetchTablesColumnsList(
const auto & char_max_length_col = *block.getByPosition(5).column; const auto & char_max_length_col = *block.getByPosition(5).column;
const auto & precision_col = *block.getByPosition(6).column; const auto & precision_col = *block.getByPosition(6).column;
const auto & scale_col = *block.getByPosition(7).column; const auto & scale_col = *block.getByPosition(7).column;
const auto & column_comment_col = *block.getByPosition(8).column;
size_t rows = block.rows(); size_t rows = block.rows();
for (size_t i = 0; i < rows; ++i) for (size_t i = 0; i < rows; ++i)
{ {
String table_name = table_name_col[i].safeGet<String>(); String table_name = table_name_col[i].safeGet<String>();
tables_and_columns[table_name].emplace_back( tables_and_columns[table_name].add(
column_name_col[i].safeGet<String>(), ColumnDescription(
convertMySQLDataType( column_name_col[i].safeGet<String>(),
type_support, convertMySQLDataType(
column_type_col[i].safeGet<String>(), type_support,
settings.external_table_functions_use_nulls && is_nullable_col[i].safeGet<UInt64>(), column_type_col[i].safeGet<String>(),
is_unsigned_col[i].safeGet<UInt64>(), settings.external_table_functions_use_nulls && is_nullable_col[i].safeGet<UInt64>(),
char_max_length_col[i].safeGet<UInt64>(), is_unsigned_col[i].safeGet<UInt64>(),
precision_col[i].safeGet<UInt64>(), char_max_length_col[i].safeGet<UInt64>(),
scale_col[i].safeGet<UInt64>())); precision_col[i].safeGet<UInt64>(),
scale_col[i].safeGet<UInt64>()),
column_comment_col[i].safeGet<String>()));
} }
} }
return tables_and_columns; return tables_and_columns;

View File

@ -7,8 +7,8 @@
#include <common/types.h> #include <common/types.h>
#include <Core/MultiEnum.h> #include <Core/MultiEnum.h>
#include <Core/NamesAndTypes.h>
#include <Core/SettingsEnums.h> #include <Core/SettingsEnums.h>
#include <Storages/ColumnsDescription.h>
#include <map> #include <map>
#include <vector> #include <vector>
@ -17,7 +17,7 @@
namespace DB namespace DB
{ {
std::map<String, NamesAndTypesList> fetchTablesColumnsList( std::map<String, ColumnsDescription> fetchTablesColumnsList(
mysqlxx::PoolWithFailover & pool, mysqlxx::PoolWithFailover & pool,
const String & database_name, const String & database_name,
const std::vector<String> & tables_name, const std::vector<String> & tables_name,

View File

@ -123,7 +123,6 @@ static ColumnsDescription createColumnsDescription(const NamesAndTypesList & col
throw Exception("Columns of different size provided.", ErrorCodes::LOGICAL_ERROR); throw Exception("Columns of different size provided.", ErrorCodes::LOGICAL_ERROR);
ColumnsDescription columns_description; ColumnsDescription columns_description;
ColumnDescription column_description;
for ( for (
auto [column_name_and_type, declare_column_ast] = std::tuple{columns_name_and_type.begin(), columns_definition->children.begin()}; auto [column_name_and_type, declare_column_ast] = std::tuple{columns_name_and_type.begin(), columns_definition->children.begin()};
@ -139,11 +138,7 @@ static ColumnsDescription createColumnsDescription(const NamesAndTypesList & col
if (options->changes.count("comment")) if (options->changes.count("comment"))
comment = options->changes.at("comment")->as<ASTLiteral>()->value.safeGet<String>(); comment = options->changes.at("comment")->as<ASTLiteral>()->value.safeGet<String>();
column_description.name = column_name_and_type->name; columns_description.add(ColumnDescription(column_name_and_type->name, column_name_and_type->type, comment));
column_description.type = column_name_and_type->type;
if (!comment.empty())
column_description.comment = std::move(comment);
columns_description.add(column_description);
} }
return columns_description; return columns_description;

View File

@ -43,8 +43,8 @@ namespace ErrorCodes
extern const int LOGICAL_ERROR; extern const int LOGICAL_ERROR;
} }
ColumnDescription::ColumnDescription(String name_, DataTypePtr type_) ColumnDescription::ColumnDescription(String name_, DataTypePtr type_, String comment_)
: name(std::move(name_)), type(std::move(type_)) : name(std::move(name_)), type(std::move(type_)), comment(std::move(comment_))
{ {
} }

View File

@ -39,7 +39,7 @@ struct ColumnDescription
ColumnDescription() = default; ColumnDescription() = default;
ColumnDescription(ColumnDescription &&) = default; ColumnDescription(ColumnDescription &&) = default;
ColumnDescription(const ColumnDescription &) = default; ColumnDescription(const ColumnDescription &) = default;
ColumnDescription(String name_, DataTypePtr type_); ColumnDescription(String name_, DataTypePtr type_, String comment_ = "");
bool operator==(const ColumnDescription & other) const; bool operator==(const ColumnDescription & other) const;
bool operator!=(const ColumnDescription & other) const { return !(*this == other); } bool operator!=(const ColumnDescription & other) const { return !(*this == other); }

View File

@ -87,7 +87,7 @@ ColumnsDescription TableFunctionMySQL::getActualTableStructure(ContextPtr contex
throw Exception("MySQL table " + (remote_database_name.empty() ? "" : (backQuote(remote_database_name) + ".")) throw Exception("MySQL table " + (remote_database_name.empty() ? "" : (backQuote(remote_database_name) + "."))
+ backQuote(remote_table_name) + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE); + backQuote(remote_table_name) + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE);
return ColumnsDescription{columns->second}; return columns->second;
} }
StoragePtr TableFunctionMySQL::executeImpl( StoragePtr TableFunctionMySQL::executeImpl(

View File

@ -167,6 +167,25 @@ def test_bad_arguments_for_mysql_database_engine(started_cluster):
assert 'Database engine MySQL requested literal argument.' in str(exception.value) assert 'Database engine MySQL requested literal argument.' in str(exception.value)
mysql_node.query("DROP DATABASE test_bad_arguments") mysql_node.query("DROP DATABASE test_bad_arguments")
def test_column_comments_for_mysql_database_engine(started_cluster):
with contextlib.closing(MySQLNodeInstance('root', 'clickhouse', started_cluster.mysql_ip, started_cluster.mysql_port)) as mysql_node:
mysql_node.query("DROP DATABASE IF EXISTS test_database")
mysql_node.query("CREATE DATABASE test_database DEFAULT CHARACTER SET 'utf8'")
clickhouse_node.query(
"CREATE DATABASE test_database ENGINE = MySQL('mysql57:3306', 'test_database', 'root', 'clickhouse')")
assert 'test_database' in clickhouse_node.query('SHOW DATABASES')
mysql_node.query(
"CREATE TABLE `test_database`.`test_table` ( `id` int(11) NOT NULL, PRIMARY KEY (`id`), `test` int COMMENT 'test comment') ENGINE=InnoDB;")
assert 'test comment' in clickhouse_node.query('DESCRIBE TABLE `test_database`.`test_table`')
mysql_node.query("ALTER TABLE `test_database`.`test_table` ADD COLUMN `add_column` int(11) COMMENT 'add_column comment'")
assert 'add_column comment' in clickhouse_node.query(
"SELECT comment FROM system.columns WHERE table = 'test_table' AND database = 'test_database'")
mysql_node.query("DROP DATABASE test_database")
def test_data_types_support_level_for_mysql_database_engine(started_cluster): def test_data_types_support_level_for_mysql_database_engine(started_cluster):
with contextlib.closing(MySQLNodeInstance('root', 'clickhouse', started_cluster.mysql_ip, started_cluster.mysql_port)) as mysql_node: with contextlib.closing(MySQLNodeInstance('root', 'clickhouse', started_cluster.mysql_ip, started_cluster.mysql_port)) as mysql_node: