ISSUES-4006 fix create query convert

This commit is contained in:
zhang2014 2020-05-12 17:38:00 +08:00
parent bd18c1cbf8
commit 86c6be4cd9
11 changed files with 403 additions and 188 deletions

View File

@ -81,7 +81,7 @@ DatabasePtr DatabaseFactory::getImpl(const ASTCreateQuery & create, const String
throw Exception("Database engine " + engine_name + " cannot have arguments", ErrorCodes::BAD_ARGUMENTS);
if (engine_define->engine->parameters || engine_define->partition_by || engine_define->primary_key || engine_define->order_by ||
engine_define->sample_by || engine_define->settings)
engine_define->sample_by || (engine_name != "MySQL" && engine_define->settings))
throw Exception("Database engine " + engine_name + " cannot have parameters, primary_key, order_by, sample_by, settings",
ErrorCodes::UNKNOWN_ELEMENT_IN_AST);

View File

@ -88,7 +88,7 @@ String DatabaseMaterializeMySQL::getCreateQuery(const mysqlxx::Pool::Entry & con
{
Block show_create_table_header{
{std::make_shared<DataTypeString>(), "Table"},
{std::make_shared<DataTypeUInt64>(), "Create Table"},
{std::make_shared<DataTypeString>(), "Create Table"},
};
MySQLBlockInputStream show_create_table(
@ -104,8 +104,12 @@ String DatabaseMaterializeMySQL::getCreateQuery(const mysqlxx::Pool::Entry & con
MySQLParser::ParserCreateQuery p_create_query;
ASTPtr ast = parseQuery(p_create_query, create_query.data, create_query.data + create_query.size, "", 0, 0);
if (!ast || !ast->as<MySQLParser::ASTCreateQuery>())
throw Exception("LOGICAL ERROR: ast cannot cast to MySQLParser::ASTCreateQuery.", ErrorCodes::LOGICAL_ERROR);
WriteBufferFromOwnString out;
MySQLVisitor::CreateQueryConvertVisitor::Data data{.out = out};
ast->as<MySQLParser::ASTCreateQuery>()->database = database;
MySQLVisitor::CreateQueryConvertVisitor::Data data{.out = out, .context = global_context};
MySQLVisitor::CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
return out.str();
@ -152,12 +156,12 @@ void DatabaseMaterializeMySQL::dumpMySQLDatabase()
+ backQuoteIfNeed(database_name) + " Database */ ";
tryToExecuteQuery(query_prefix + " DROP TABLE IF EXISTS " + backQuoteIfNeed(database_name) + "." + backQuoteIfNeed(dumping_table_name));
tryToExecuteQuery(query_prefix + getCreateQuery(connection, mysql_database_name, dumping_table_name));
tryToExecuteQuery(query_prefix + getCreateQuery(connection, database_name, dumping_table_name));
Context context = global_context;
context.getClientInfo().query_kind = ClientInfo::QueryKind::SECONDARY_QUERY;
context.setCurrentQueryId(""); // generate random query_id
BlockIO streams = executeQuery(query_prefix + " INSERT INTO " + backQuoteIfNeed(database_name) + "." + backQuoteIfNeed(dumping_table_name), context, true);
BlockIO streams = executeQuery( query_prefix + " INSERT INTO " + backQuoteIfNeed(database_name) + "." + backQuoteIfNeed(dumping_table_name) + " VALUES", context, true);
if (!streams.out)
throw Exception("LOGICAL ERROR out stream is undefined.", ErrorCodes::LOGICAL_ERROR);
@ -178,10 +182,11 @@ void DatabaseMaterializeMySQL::synchronization()
{
std::unique_lock<std::mutex> lock{sync_mutex};
/// Check database is exists in ClickHouse.
LOG_DEBUG(log, "Checking " + database_name + " database status.");
while (!sync_quit && !DatabaseCatalog::instance().isDatabaseExist(database_name))
sync_cond.wait_for(lock, std::chrono::seconds(1));
LOG_DEBUG(log, database_name + " database status is OK.");
/// 查找一下位点文件, 如果不存在需要清理目前的数据库, 然后dump全量数据.
dumpMySQLDatabase();
}

View File

@ -2,5 +2,11 @@
namespace DB
{
MasterStatusInfo::MasterStatusInfo(
String binlog_file_, UInt64 binlog_position_, String binlog_do_db_, String binlog_ignore_db_, String executed_gtid_set_)
: binlog_file(binlog_file_), binlog_position(binlog_position_), binlog_do_db(binlog_do_db_), binlog_ignore_db(binlog_ignore_db_),
executed_gtid_set(executed_gtid_set_)
{
}
}

View File

@ -20,8 +20,5 @@ struct MasterStatusInfo
};
std::shared_ptr<MasterStatusInfo> fetchMasterStatusInfo(mysqlxx::Connection * connection);
}

View File

@ -1,14 +1,20 @@
#include <Interpreters/MySQL/CreateQueryConvertVisitor.h>
#include <Common/quoteString.h>
#include <IO/Operators.h>
#include <IO/WriteBufferFromString.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/queryToString.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/MySQL/ASTDeclareIndex.h>
#include <Parsers/MySQL/ASTDeclareOption.h>
#include <Poco/String.h>
#include <DataTypes/DataTypeFactory.h>
#include <DataTypes/DataTypeNullable.h>
#include <Interpreters/ExpressionActions.h>
#include <Interpreters/InterpreterCreateQuery.h>
namespace DB
{
@ -22,42 +28,7 @@ namespace ErrorCodes
namespace MySQLVisitor
{
void CreateQueryMatcher::visit(ASTPtr & ast, Data & data)
{
if (auto * t = ast->as<ASTCreateQuery>())
visit(*t, ast, data);
}
void CreateQueryMatcher::visit(ASTCreateQuery & create, ASTPtr & /*ast*/, Data & data)
{
if (create.like_table)
throw Exception("Cannot convert create like statement to ClickHouse SQL", ErrorCodes::NOT_IMPLEMENTED);
data.out << "CREATE TABLE " << (create.if_not_exists ? "IF NOT EXISTS" : "")
<< (create.database.empty() ? "" : backQuoteIfNeed(create.database) + ".") << backQuoteIfNeed(create.table) << "(";
if (create.columns_list)
visitColumns(*create.columns_list->as<ASTCreateDefines>(), create.columns_list, data);
data.out << ") ENGINE = MergeTree()";
}
void CreateQueryMatcher::visitColumns(ASTCreateDefines & create_defines, ASTPtr & /*ast*/, Data & data)
{
if (!create_defines.columns || create_defines.columns->children.empty())
throw Exception("Missing definition of columns.", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED);
bool is_first = true;
for (auto & column : create_defines.columns->children)
{
if (!is_first)
data.out << ",";
is_first = false;
visitColumns(*column->as<ASTDeclareColumn>(), column, data);
}
}
static String convertDataType(const String & type_name, const ASTPtr & arguments, bool is_unsigned, bool /*is_national*/)
static String convertDataType(const String & type_name, const ASTPtr & arguments, bool is_unsigned)
{
if (type_name == "TINYINT")
return is_unsigned ? "UInt8" : "Int8";
@ -74,7 +45,17 @@ static String convertDataType(const String & type_name, const ASTPtr & arguments
else if (type_name == "DOUBLE" || type_name == "PRECISION" || type_name == "REAL")
return "Float64";
else if (type_name == "DECIMAL" || type_name == "DEC" || type_name == "NUMERIC" || type_name == "FIXED")
return arguments ? "Decimal(" + queryToString(arguments) + ")" : "Decimal(10, 0)";
{
if (!arguments)
return "Decimal(10, 0)";
else if (arguments->children.size() == 1)
return "Decimal(" + queryToString(arguments) + ", 0)";
else if (arguments->children.size() == 2)
return "Decimal(" + queryToString(arguments) + ")";
else
throw Exception("Decimal data type family must have exactly two arguments: precision and scale", ErrorCodes::UNKNOWN_TYPE);
}
if (type_name == "DATE")
return "Date";
@ -91,25 +72,74 @@ static String convertDataType(const String & type_name, const ASTPtr & arguments
return "String";
}
void CreateQueryMatcher::visitColumns(const ASTDeclareColumn & declare_column, ASTPtr &, Data & data)
static String convertDataType(const String & type_name, const ASTPtr & arguments, bool is_unsigned, bool is_nullable)
{
data.out << declare_column.name << " ";
return (is_nullable ? "Nullable(" : "") + convertDataType(type_name, arguments, is_unsigned) + (is_nullable ? ")" : "");
}
void CreateQueryMatcher::visit(ASTPtr & ast, Data & data)
{
if (auto * t = ast->as<MySQLParser::ASTCreateQuery>())
visit(*t, ast, data);
}
void CreateQueryMatcher::visit(MySQLParser::ASTCreateQuery & create, ASTPtr &, Data & data)
{
if (create.like_table)
throw Exception("Cannot convert create like statement to ClickHouse SQL", ErrorCodes::NOT_IMPLEMENTED);
if (create.columns_list)
visit(*create.columns_list->as<MySQLParser::ASTCreateDefines>(), create.columns_list, data);
if (create.partition_options)
visit(*create.partition_options->as<MySQLParser::ASTDeclarePartitionOptions>(), create.partition_options, data);
auto expression_list = std::make_shared<ASTExpressionList>();
expression_list->children = data.primary_keys;
data.out << "CREATE TABLE " << (create.if_not_exists ? "IF NOT EXISTS" : "")
<< (create.database.empty() ? "" : backQuoteIfNeed(create.database) + ".") << backQuoteIfNeed(create.table)
<< "(" << queryToString(InterpreterCreateQuery::formatColumns(data.columns_name_and_type)) << ") ENGINE = MergeTree()"
" PARTITION BY " << queryToString(data.getFormattedPartitionByExpression())
<< " ORDER BY " << queryToString(data.getFormattedOrderByExpression());
}
void CreateQueryMatcher::visit(MySQLParser::ASTCreateDefines & create_defines, ASTPtr &, Data & data)
{
if (!create_defines.columns || create_defines.columns->children.empty())
throw Exception("Missing definition of columns.", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED);
if (create_defines.indices)
{
for (auto & index : create_defines.indices->children)
visit(*index->as<MySQLParser::ASTDeclareIndex>(), index, data);
}
for (auto & column : create_defines.columns->children)
visit(*column->as<MySQLParser::ASTDeclareColumn>(), column, data);
}
void CreateQueryMatcher::visit(const MySQLParser::ASTDeclareIndex & declare_index, ASTPtr &, Data & data)
{
if (startsWith(declare_index.index_type, "PRIMARY_KEY_"))
data.addPrimaryKey(declare_index.index_columns);
}
void CreateQueryMatcher::visit(const MySQLParser::ASTDeclareColumn & declare_column, ASTPtr &, Data & data)
{
if (!declare_column.data_type)
throw Exception("Missing type in definition of column.", ErrorCodes::UNKNOWN_TYPE);
bool is_unsigned = false;
bool is_nullable = true;
bool is_national = false;
bool is_nullable = true, is_unsigned = false;
if (declare_column.column_options)
{
if (ASTDeclareOptions * options = declare_column.column_options->as<ASTDeclareOptions>())
if (MySQLParser::ASTDeclareOptions * options = declare_column.column_options->as<MySQLParser::ASTDeclareOptions>())
{
if (options->changes.count("is_null"))
is_nullable = options->changes["is_null"]->as<ASTLiteral>()->value.safeGet<UInt64>();
if (options->changes.count("is_national"))
is_national = options->changes["is_national"]->as<ASTLiteral>()->value.safeGet<UInt64>();
if (options->changes.count("primary_key"))
data.addPrimaryKey(std::make_shared<ASTIdentifier>(declare_column.name));
if (options->changes.count("is_unsigned"))
is_unsigned = options->changes["is_unsigned"]->as<ASTLiteral>()->value.safeGet<UInt64>();
@ -119,13 +149,161 @@ void CreateQueryMatcher::visitColumns(const ASTDeclareColumn & declare_column, A
}
if (ASTFunction * function = declare_column.data_type->as<ASTFunction>())
data.out << (is_nullable ? "Nullable(" : "")
<< convertDataType(Poco::toUpper(function->name), function->arguments, is_unsigned, is_national)
<< (is_nullable ? ")" : "");
data.columns_name_and_type.emplace_back(declare_column.name,
DataTypeFactory::instance().get(convertDataType(Poco::toUpper(function->name), function->arguments, is_unsigned, is_nullable)));
else if (ASTIdentifier * identifier = declare_column.data_type->as<ASTIdentifier>())
data.columns_name_and_type.emplace_back(declare_column.name,
DataTypeFactory::instance().get(convertDataType(Poco::toUpper(identifier->name), ASTPtr{}, is_unsigned, is_nullable)));
else
throw Exception("Unsupported MySQL data type " + queryToString(declare_column.data_type) + ".", ErrorCodes::NOT_IMPLEMENTED);
}
if (ASTIdentifier * identifier = declare_column.data_type->as<ASTIdentifier>())
data.out << (is_nullable ? "Nullable(" : "") << convertDataType(Poco::toUpper(identifier->name), ASTPtr{}, is_unsigned, is_national)
<< (is_nullable ? ")" : "");
void CreateQueryMatcher::visit(const MySQLParser::ASTDeclarePartitionOptions & declare_partition_options, ASTPtr &, Data & data)
{
data.addPartitionKey(declare_partition_options.partition_expression);
}
void CreateQueryMatcher::Data::addPrimaryKey(const ASTPtr & primary_key)
{
if (const auto & function_index_columns = primary_key->as<ASTFunction>())
{
if (function_index_columns->name != "tuple")
throw Exception("Unable to parse function primary key from MySQL.", ErrorCodes::NOT_IMPLEMENTED);
for (const auto & index_column : function_index_columns->arguments->children)
primary_keys.emplace_back(index_column);
}
else if (const auto & expression_index_columns = primary_key->as<ASTExpressionList>())
{
for (const auto & index_column : expression_index_columns->children)
primary_keys.emplace_back(index_column);
}
else
primary_keys.emplace_back(primary_key);
}
void CreateQueryMatcher::Data::addPartitionKey(const ASTPtr & partition_key)
{
if (const auto & function_partition_columns = partition_key->as<ASTFunction>())
{
if (function_partition_columns->name != "tuple")
throw Exception("Unable to parse function partition by from MySQL.", ErrorCodes::NOT_IMPLEMENTED);
for (const auto & partition_column : function_partition_columns->arguments->children)
partition_keys.emplace_back(partition_column);
}
else if (const auto & expression_partition_columns = partition_key->as<ASTExpressionList>())
{
for (const auto & partition_column : expression_partition_columns->children)
partition_keys.emplace_back(partition_column);
}
else
partition_keys.emplace_back(partition_key);
}
ASTPtr CreateQueryMatcher::Data::getFormattedOrderByExpression()
{
if (primary_keys.empty())
return makeASTFunction("tuple");
/// TODO: support unique key & key
const auto function = std::make_shared<ASTFunction>();
function->name = "tuple";
function->arguments = std::make_shared<ASTExpressionList>();
function->children.push_back(function->arguments);
function->arguments->children = primary_keys;
return function;
}
template <typename TType>
Field choiceBetterRangeSize(TType min, TType max, size_t max_ranges, size_t min_size_pre_range)
{
UInt64 interval = UInt64(max) - min;
size_t calc_rows_pre_range = std::ceil(interval / double(max_ranges));
size_t rows_pre_range = std::max(min_size_pre_range, calc_rows_pre_range);
if (rows_pre_range >= interval)
return Null();
return rows_pre_range > std::numeric_limits<TType>::max() ? Field(UInt64(rows_pre_range)) : Field(TType(rows_pre_range));
}
ASTPtr CreateQueryMatcher::Data::getFormattedPartitionByExpression()
{
ASTPtr partition_columns = std::make_shared<ASTExpressionList>();
if (!partition_keys.empty())
partition_columns->children = partition_keys;
else if (!primary_keys.empty())
{
ASTPtr expr_list = std::make_shared<ASTExpressionList>();
expr_list->children = primary_keys;
auto syntax = SyntaxAnalyzer(context).analyze(expr_list, columns_name_and_type);
auto index_expr = ExpressionAnalyzer(expr_list, syntax, context).getActions(false);
const NamesAndTypesList & required_names_and_types = index_expr->getRequiredColumnsWithTypes();
const auto & addPartitionColumn = [&](const String & column_name, const DataTypePtr & type, Field better_pre_range_size)
{
partition_columns->children.emplace_back(std::make_shared<ASTIdentifier>(column_name));
if (type->isNullable())
partition_columns->children.back() = makeASTFunction("assumeNotNull", partition_columns->children.back());
if (!better_pre_range_size.isNull())
partition_columns->children.back()
= makeASTFunction("divide", partition_columns->children.back(), std::make_shared<ASTLiteral>(better_pre_range_size));
};
for (const auto & required_name_and_type : required_names_and_types)
{
DataTypePtr assume_not_null = required_name_and_type.type;
if (assume_not_null->isNullable())
assume_not_null = (static_cast<const DataTypeNullable &>(*assume_not_null)).getNestedType();
WhichDataType which(assume_not_null);
if (which.isInt8())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<Int8>(
std::numeric_limits<Int8>::min(), std::numeric_limits<Int8>::max(), max_ranges, min_rows_pre_range));
else if (which.isInt16())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<Int16>(
std::numeric_limits<Int16>::min(), std::numeric_limits<Int16>::max(), max_ranges, min_rows_pre_range));
else if (which.isInt32())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<Int32>(
std::numeric_limits<Int32>::min(), std::numeric_limits<Int32>::max(), max_ranges, min_rows_pre_range));
else if (which.isInt64())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<Int64>(
std::numeric_limits<Int64>::min(), std::numeric_limits<Int64>::max(), max_ranges, min_rows_pre_range));
else if (which.isUInt8())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<UInt8>(
std::numeric_limits<UInt8>::min(), std::numeric_limits<UInt8>::max(), max_ranges, min_rows_pre_range));
else if (which.isUInt16())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<UInt16>(
std::numeric_limits<UInt16>::min(), std::numeric_limits<UInt16>::max(), max_ranges, min_rows_pre_range));
else if (which.isUInt32())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<UInt32>(
std::numeric_limits<UInt32>::min(), std::numeric_limits<UInt32>::max(), max_ranges, min_rows_pre_range));
else if (which.isUInt64())
addPartitionColumn(required_name_and_type.name, required_name_and_type.type, choiceBetterRangeSize<UInt64>(
std::numeric_limits<UInt64>::min(), std::numeric_limits<UInt64>::max(), max_ranges, min_rows_pre_range));
else if (which.isDateOrDateTime())
{
partition_columns->children.emplace_back(std::make_shared<ASTIdentifier>(required_name_and_type.name));
if (required_name_and_type.type->isNullable())
partition_columns->children.back() = makeASTFunction("assumeNotNull", partition_columns->children.back());
partition_columns->children.back() = makeASTFunction("toYYYYMM", partition_columns->children.back());
}
}
}
const auto function = std::make_shared<ASTFunction>();
function->name = "tuple";
function->arguments = partition_columns;
function->children.push_back(function->arguments);
return function;
}
}

View File

@ -1,15 +1,16 @@
#pragma once
#include <Core/NamesAndTypes.h>
#include <Interpreters/InDepthNodeVisitor.h>
#include <Parsers/MySQL/ASTCreateQuery.h>
#include <Parsers/MySQL/ASTCreateDefines.h>
#include <Parsers/MySQL/ASTDeclareIndex.h>
#include <Parsers/MySQL/ASTDeclareColumn.h>
#include <Parsers/MySQL/ASTDeclarePartitionOptions.h>
namespace DB
{
using namespace MySQLParser;
namespace MySQLVisitor
{
@ -23,18 +24,42 @@ public:
{
/// SETTINGS
WriteBuffer & out;
std::string declare_columns;
const Context & context;
size_t max_ranges;
size_t min_rows_pre_range;
ASTs primary_keys;
ASTs partition_keys;
NamesAndTypesList columns_name_and_type;
void addPrimaryKey(const ASTPtr & primary_key);
void addPartitionKey(const ASTPtr & partition_key);
ASTPtr getFormattedOrderByExpression();
ASTPtr getFormattedPartitionByExpression();
};
static void visit(ASTPtr & ast, Data & data);
static bool needChildVisit(ASTPtr &, const ASTPtr &) { return false; }
private:
static void visit(ASTCreateQuery & create, ASTPtr & ast, Data &);
static void visit(MySQLParser::ASTCreateQuery & create, ASTPtr & ast, Data &);
static void visitColumns(ASTCreateDefines & create_defines, ASTPtr & ast, Data & data);
static void visit(MySQLParser::ASTCreateDefines & create_defines, ASTPtr & ast, Data & data);
static void visitColumns(const ASTDeclareColumn & declare_column, ASTPtr & ast, Data & data);
static void visit(const MySQLParser::ASTDeclareIndex & declare_index, ASTPtr & ast, Data & data);
static void visit(const MySQLParser::ASTDeclareColumn & declare_column, ASTPtr & ast, Data & data);
static void visit(const MySQLParser::ASTDeclarePartitionOptions & declare_partition_options, ASTPtr & ast, Data & data);
// static void visitPartitionBy(MySQLParser::ASTCreateQuery & create, ASTPtr & ast, Data & data);
// static void visitPartitionBy(MySQLParser::ASTCreateDefines & create_defines, ASTPtr & ast, Data & data);
// static void visitPartitionBy(const MySQLParser::ASTDeclarePartitionOptions & partition_options, ASTPtr & ast, Data & data);
// static void visitColumns(const ASTFunction & declare_column, ASTPtr & ast, Data & data);
// static void visit(ASTTableJoin & join, const ASTPtr & ast, Data &);

View File

@ -2,153 +2,149 @@
#include <Parsers/parseQuery.h>
#include <IO/WriteBufferFromString.h>
#include <Parsers/MySQL/ASTCreateQuery.h>
#include <Interpreters/Context.h>
#include <Interpreters/MySQL/CreateQueryConvertVisitor.h>
using namespace DB;
using namespace MySQLParser;
using namespace MySQLVisitor;
TEST(CreateQueryConvert, SimpleNumbersType)
static ContextShared * contextShared()
{
static SharedContextHolder shared = Context::createShared();
return shared.get();
}
static String convert(const String & input)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a tinyint, b SMALLINT, c MEDIUMINT, d INT, e INTEGER, f BIGINT, g DECIMAL, h DEC, i NUMERIC, j "
"FIXED, k FLOAT, l DOUBLE, m DOUBLE PRECISION, n REAL)";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor::Data data{
.out = out, .context = Context::createGlobal(contextShared()), .max_ranges = 1000, .min_rows_pre_range = 1000000};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
return out.str();
}
TEST(CreateQueryConvert, TestConvertNumberColumnsType)
{
EXPECT_EQ(
convert("CREATE TABLE test(a tinyint, b SMALLINT, c MEDIUMINT, d INT, e INTEGER, f BIGINT, g DECIMAL, h DEC, i NUMERIC, j FIXED, k "
"FLOAT, l DOUBLE, m DOUBLE PRECISION, n REAL)"),
"CREATE TABLE test(`a` Nullable(Int8), `b` Nullable(Int16), `c` Nullable(Int32), `d` Nullable(Int32), `e` Nullable(Int32), `f` "
"Nullable(Int64), `g` Nullable(Decimal(10, 0)), `h` Nullable(Decimal(10, 0)), `i` Nullable(Decimal(10, 0)), `j` "
"Nullable(Decimal(10, 0)), `k` Nullable(Float32), `l` Nullable(Float64), `m` Nullable(Float64), `n` Nullable(Float64)) ENGINE = "
"MergeTree() PARTITION BY tuple() ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Nullable(Int8),b Nullable(Int16),c Nullable(Int32),d Nullable(Int32),e Nullable(Int32),f Nullable(Int64),g "
"Nullable(Decimal(10, 0)),h Nullable(Decimal(10, 0)),i Nullable(Decimal(10, 0)),j Nullable(Decimal(10, 0)),k Nullable(Float32),l "
"Nullable(Float64),m Nullable(Float64),n Nullable(Float64)) ENGINE = MergeTree()");
convert("CREATE TABLE test(a tinyint(1), b SMALLINT(1), c MEDIUMINT(1), d INT(1), e INTEGER(1), f BIGINT(1), g DECIMAL(1), h "
"DEC(2, 1), i NUMERIC(4, 3), j FIXED(6, 5), k FLOAT(1), l DOUBLE(1, 2), m DOUBLE PRECISION(3, 4), n REAL(5, 6))"),
"CREATE TABLE test(`a` Nullable(Int8), `b` Nullable(Int16), `c` Nullable(Int32), `d` Nullable(Int32), `e` Nullable(Int32), `f` "
"Nullable(Int64), `g` Nullable(Decimal(1, 0)), `h` Nullable(Decimal(2, 1)), `i` Nullable(Decimal(4, 3)), `j` Nullable(Decimal(6, "
"5)), `k` Nullable(Float32), `l` Nullable(Float64), `m` Nullable(Float64), `n` Nullable(Float64)) ENGINE = MergeTree() PARTITION "
"BY tuple() ORDER BY tuple()");
/// UNSIGNED
EXPECT_EQ(
convert("CREATE TABLE test(a tinyint UNSIGNED, b SMALLINT(1) UNSIGNED, c MEDIUMINT(1) UNSIGNED, d INT(1) UNSIGNED, e INTEGER(1), f "
"BIGINT(1) UNSIGNED, g DECIMAL(1) UNSIGNED, h DEC(2, 1) UNSIGNED, i NUMERIC(4, 3) UNSIGNED, j FIXED(6, 5) UNSIGNED, k FLOAT(1) "
"UNSIGNED, l DOUBLE(1, 2) UNSIGNED, m DOUBLE PRECISION(3, 4) UNSIGNED, n REAL(5, 6) UNSIGNED)"),
"CREATE TABLE test(`a` Nullable(UInt8), `b` Nullable(UInt16), `c` Nullable(UInt32), `d` Nullable(UInt32), `e` Nullable(Int32), `f` "
"Nullable(UInt64), `g` Nullable(Decimal(1, 0)), `h` Nullable(Decimal(2, 1)), `i` Nullable(Decimal(4, 3)), `j` Nullable(Decimal(6, "
"5)), `k` Nullable(Float32), `l` Nullable(Float64), `m` Nullable(Float64), `n` Nullable(Float64)) ENGINE = MergeTree() PARTITION "
"BY tuple() ORDER BY tuple()");
/// NOT NULL
EXPECT_EQ(
convert("CREATE TABLE test(a tinyint NOT NULL, b SMALLINT(1) NOT NULL, c MEDIUMINT(1) NOT NULL, d INT(1) NOT NULL, e INTEGER(1), f "
"BIGINT(1) NOT NULL, g DECIMAL(1) NOT NULL, h DEC(2, 1) NOT NULL, i NUMERIC(4, 3) NOT NULL, j FIXED(6, 5) NOT NULL, k FLOAT(1) NOT "
"NULL, l DOUBLE(1, 2) NOT NULL, m DOUBLE PRECISION(3, 4) NOT NULL, n REAL(5, 6) NOT NULL)"),
"CREATE TABLE test(`a` Int8, `b` Int16, `c` Int32, `d` Int32, `e` Nullable(Int32), `f` Int64, `g` Decimal(1, 0), `h` Decimal(2, "
"1), `i` Decimal(4, 3), `j` Decimal(6, 5), `k` Float32, `l` Float64, `m` Float64, `n` Float64) ENGINE = MergeTree() PARTITION BY "
"tuple() ORDER BY tuple()");
/// ZEROFILL
EXPECT_EQ(
convert("CREATE TABLE test(a tinyint ZEROFILL, b SMALLINT(1) ZEROFILL, c MEDIUMINT(1) ZEROFILL, d INT(1) ZEROFILL, e INTEGER(1), f "
"BIGINT(1) ZEROFILL, g DECIMAL(1) ZEROFILL, h DEC(2, 1) ZEROFILL, i NUMERIC(4, 3) ZEROFILL, j FIXED(6, 5) ZEROFILL, k FLOAT(1) "
"ZEROFILL, l DOUBLE(1, 2) ZEROFILL, m DOUBLE PRECISION(3, 4) ZEROFILL, n REAL(5, 6) ZEROFILL)"),
"CREATE TABLE test(`a` Nullable(UInt8), `b` Nullable(UInt16), `c` Nullable(UInt32), `d` Nullable(UInt32), `e` Nullable(Int32), `f` "
"Nullable(UInt64), `g` Nullable(Decimal(1, 0)), `h` Nullable(Decimal(2, 1)), `i` Nullable(Decimal(4, 3)), `j` Nullable(Decimal(6, "
"5)), `k` Nullable(Float32), `l` Nullable(Float64), `m` Nullable(Float64), `n` Nullable(Float64)) ENGINE = MergeTree() PARTITION "
"BY tuple() ORDER BY tuple()");
}
TEST(CreateQueryConvert, NumbersTypeWithLength)
TEST(CreateQueryConvert, TestConvertDateTimesColumnsType)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a tinyint(1), b SMALLINT(1), c MEDIUMINT(1), d INT(1), e INTEGER(1), f BIGINT(1), g DECIMAL(1), h DEC(1, 2), i NUMERIC(3, 4), j "
"FIXED(5, 6), k FLOAT(1), l DOUBLE(1, 2), m DOUBLE PRECISION(3, 4), n REAL(5, 6))";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
EXPECT_EQ(
convert("CREATE TABLE test(a DATE, b DATETIME, c TIMESTAMP, d TIME, e year)"),
"CREATE TABLE test(`a` Nullable(Date), `b` Nullable(DateTime), `c` Nullable(DateTime), `d` Nullable(DateTime64(3)), `e` "
"Nullable(Int16)) ENGINE = MergeTree() PARTITION BY tuple() ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Nullable(Int8),b Nullable(Int16),c Nullable(Int32),d Nullable(Int32),e Nullable(Int32),f Nullable(Int64),g "
"Nullable(Decimal(1)),h Nullable(Decimal(1, 2)),i Nullable(Decimal(3, 4)),j Nullable(Decimal(5, 6)),k Nullable(Float32),l "
"Nullable(Float64),m Nullable(Float64),n Nullable(Float64)) ENGINE = MergeTree()");
}
TEST(CreateQueryConvert, NumbersTypeWithUnsigned)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a tinyint UNSIGNED, b SMALLINT(1) UNSIGNED, c MEDIUMINT(1) UNSIGNED, d INT(1) UNSIGNED, e "
"INTEGER(1), f BIGINT(1) UNSIGNED, g DECIMAL(1) UNSIGNED, h DEC(1, 2) UNSIGNED, i NUMERIC(3, 4) UNSIGNED, j FIXED(5, 6) "
"UNSIGNED, k FLOAT(1) UNSIGNED, l DOUBLE(1, 2) UNSIGNED, m DOUBLE PRECISION(3, 4) UNSIGNED, n REAL(5, 6) UNSIGNED)";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
convert("CREATE TABLE test(a DATE, b DATETIME(1), c TIMESTAMP(1), d TIME(1), e year(4))"),
"CREATE TABLE test(`a` Nullable(Date), `b` Nullable(DateTime), `c` Nullable(DateTime), `d` Nullable(DateTime64(3)), `e` "
"Nullable(Int16)) ENGINE = MergeTree() PARTITION BY tuple() ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Nullable(UInt8),b Nullable(UInt16),c Nullable(UInt32),d Nullable(UInt32),e Nullable(Int32),f "
"Nullable(UInt64),g Nullable(Decimal(1)),h Nullable(Decimal(1, 2)),i Nullable(Decimal(3, 4)),j Nullable(Decimal(5, 6)),k "
"Nullable(Float32),l Nullable(Float64),m Nullable(Float64),n Nullable(Float64)) ENGINE = MergeTree()");
convert(
"CREATE TABLE test(a DATE NOT NULL, b DATETIME(1) NOT NULL, c TIMESTAMP(1) NOT NULL, d TIME(1) NOT NULL, e year(4) NOT NULL)"),
"CREATE TABLE test(`a` Date, `b` DateTime, `c` DateTime, `d` DateTime64(3), `e` Int16) ENGINE = MergeTree() PARTITION BY tuple() "
"ORDER BY tuple()");
}
TEST(CreateQueryConvert, NumbersTypeWithNotNull)
TEST(CreateQueryConvert, TestConvertParitionOptions)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a tinyint NOT NULL, b SMALLINT(1) NOT NULL, c MEDIUMINT(1) NOT NULL, d INT(1) NOT NULL, e "
"INTEGER(1), f BIGINT(1) NOT NULL, g DECIMAL(1) NOT NULL, h DEC(1, 2) NOT NULL, i NUMERIC(3, 4) NOT NULL, j FIXED(5, 6) "
"NOT NULL, k FLOAT(1) NOT NULL, l DOUBLE(1, 2) NOT NULL, m DOUBLE PRECISION(3, 4) NOT NULL, n REAL(5, 6) NOT NULL)";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
EXPECT_EQ(
convert("CREATE TABLE test(a DATE NOT NULL) PARTITION BY HASH a"),
"CREATE TABLE test(`a` Date) ENGINE = MergeTree() PARTITION BY tuple(a) ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Int8,b Int16,c Int32,d Int32,e Nullable(Int32),f Int64,g Decimal(1),h Decimal(1, 2),i Decimal(3, 4),j "
"Decimal(5, 6),k Float32,l Float64,m Float64,n Float64) ENGINE = MergeTree()");
}
TEST(CreateQueryConvert, NumbersTypeWithZeroFill)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a tinyint ZEROFILL, b SMALLINT(1) ZEROFILL, c MEDIUMINT(1) ZEROFILL, d INT(1) ZEROFILL, e "
"INTEGER(1), f BIGINT(1) ZEROFILL, g DECIMAL(1) ZEROFILL, h DEC(1, 2) ZEROFILL, i NUMERIC(3, 4) ZEROFILL, j FIXED(5, 6) "
"ZEROFILL, k FLOAT(1) ZEROFILL, l DOUBLE(1, 2) ZEROFILL, m DOUBLE PRECISION(3, 4) ZEROFILL, n REAL(5, 6) ZEROFILL)";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
convert("CREATE TABLE test(a DATE NOT NULL) PARTITION BY LINEAR HASH a"),
"CREATE TABLE test(`a` Date) ENGINE = MergeTree() PARTITION BY tuple(a) ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Nullable(UInt8),b Nullable(UInt16),c Nullable(UInt32),d Nullable(UInt32),e Nullable(Int32),f "
"Nullable(UInt64),g Nullable(Decimal(1)),h Nullable(Decimal(1, 2)),i Nullable(Decimal(3, 4)),j Nullable(Decimal(5, 6)),k "
"Nullable(Float32),l Nullable(Float64),m Nullable(Float64),n Nullable(Float64)) ENGINE = MergeTree()");
}
TEST(CreateQueryConvert, SimpleDateTimesType)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a DATE, b DATETIME, c TIMESTAMP, d TIME, e year)";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
convert("CREATE TABLE test(a DATE NOT NULL) PARTITION BY RANGE(a)"),
"CREATE TABLE test(`a` Date) ENGINE = MergeTree() PARTITION BY tuple(a) ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Nullable(Date),b Nullable(DateTime),c Nullable(DateTime),d Nullable(DateTime64),e Nullable(Int16)) ENGINE = "
"MergeTree()");
}
TEST(CreateQueryConvert, DateTimeTypesWithLength)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a DATE, b DATETIME(1), c TIMESTAMP(1), d TIME(1), e year(4))";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
convert("CREATE TABLE test(a DATE NOT NULL, b INT) PARTITION BY RANGE COLUMNS(a, b)"),
"CREATE TABLE test(`a` Date, `b` Nullable(Int32)) ENGINE = MergeTree() PARTITION BY (a, b) ORDER BY tuple()");
EXPECT_EQ(
out.str(),
"CREATE TABLE test(a Nullable(Date),b Nullable(DateTime),c Nullable(DateTime),d Nullable(DateTime64),e Nullable(Int16)) ENGINE = "
"MergeTree()");
convert("CREATE TABLE test(a DATE NOT NULL) PARTITION BY LIST(a)"),
"CREATE TABLE test(`a` Date) ENGINE = MergeTree() PARTITION BY tuple(a) ORDER BY tuple()");
EXPECT_EQ(
convert("CREATE TABLE test(a DATE NOT NULL, b INT) PARTITION BY LIST COLUMNS(a, b)"),
"CREATE TABLE test(`a` Date, `b` Nullable(Int32)) ENGINE = MergeTree() PARTITION BY (a, b) ORDER BY tuple()");
}
TEST(CreateQueryConvert, DateTimeTypesWithNotNull)
TEST(CreateQueryConvert, TestConvertPrimaryToPartitionBy)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE test(a DATE NOT NULL, b DATETIME(1) NOT NULL, c TIMESTAMP(1) NOT NULL, d TIME(1) NOT NULL, e year(4) NOT NULL)";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
EXPECT_EQ(convert("CREATE TABLE test(a DATE NOT NULL PRIMARY KEY)"),
"CREATE TABLE test(`a` Date) ENGINE = MergeTree() PARTITION BY tuple(toYYYYMM(a)) ORDER BY tuple(a)");
WriteBufferFromOwnString out;
CreateQueryConvertVisitor::Data data{.out = out};
CreateQueryConvertVisitor visitor(data);
visitor.visit(ast);
EXPECT_EQ(convert("CREATE TABLE test(a DATETIME NOT NULL PRIMARY KEY)"),
"CREATE TABLE test(`a` DateTime) ENGINE = MergeTree() PARTITION BY tuple(toYYYYMM(a)) ORDER BY tuple(a)");
EXPECT_EQ(out.str(), "CREATE TABLE test(a Date,b DateTime,c DateTime,d DateTime64,e Int16) ENGINE = MergeTree()");
EXPECT_EQ(convert("CREATE TABLE test(a TINYINT NOT NULL PRIMARY KEY)"),
"CREATE TABLE test(`a` Int8) ENGINE = MergeTree() PARTITION BY tuple(a) ORDER BY tuple(a)");
EXPECT_EQ(convert("CREATE TABLE test(a SMALLINT NOT NULL PRIMARY KEY)"),
"CREATE TABLE test(`a` Int16) ENGINE = MergeTree() PARTITION BY tuple(a) ORDER BY tuple(a)");
EXPECT_EQ(convert("CREATE TABLE test(a INT NOT NULL PRIMARY KEY)"),
"CREATE TABLE test(`a` Int32) ENGINE = MergeTree() PARTITION BY tuple(a / 4294968) ORDER BY tuple(a)");
EXPECT_EQ(convert("CREATE TABLE test(a BIGINT NOT NULL PRIMARY KEY)"),
"CREATE TABLE test(`a` Int64) ENGINE = MergeTree() PARTITION BY tuple(a / 18446744073709552) ORDER BY tuple(a)");
EXPECT_EQ(
convert("CREATE TABLE test(a BIGINT PRIMARY KEY)"),
"CREATE TABLE test(`a` Nullable(Int64)) ENGINE = MergeTree() PARTITION BY tuple(assumeNotNull(a) / 18446744073709552) ORDER BY "
"tuple(a)");
}

View File

@ -207,7 +207,7 @@ bool ParserDeclareIndex::parseDeclareConstraintIndex(IParser::Pos & pos, String
if (!p_identifier.parse(pos, temp_node, expected))
return false;
index_type = temp_node->as<ASTIdentifier>()->name;
index_type = "UNIQUE_" + temp_node->as<ASTIdentifier>()->name;
}
}
else if (ParserKeyword("PRIMARY KEY").ignore(pos, expected))
@ -218,7 +218,7 @@ bool ParserDeclareIndex::parseDeclareConstraintIndex(IParser::Pos & pos, String
if (!p_identifier.parse(pos, temp_node, expected))
return false;
index_type = temp_node->as<ASTIdentifier>()->name;
index_type = "PRIMARY_KEY_" + temp_node->as<ASTIdentifier>()->name;
}
}
else if (ParserKeyword("FOREIGN KEY").ignore(pos, expected))

View File

@ -122,12 +122,10 @@ bool ParserCharsetName::parseImpl(IParser::Pos & pos, ASTPtr & node, Expected &)
while (true)
{
if (isWhitespaceASCII(*pos->end))
break;
else
{
if (!isWhitespaceASCII(*pos->end) && pos->type != TokenType::EndOfStream)
++pos;
}
else
break;
}
node = std::make_shared<ASTIdentifier>(String(begin, pos->end));

View File

@ -1,11 +1,10 @@
#include <Parsers/MySQL/ASTDeclareTableOptions.h>
#include <IO/ReadBufferFromMemory.h>
#include <IO/ReadHelpers.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ExpressionElementParsers.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/ExpressionElementParsers.h>
#include <Parsers/MySQL/ASTDeclareOption.h>
namespace DB
@ -68,6 +67,8 @@ bool ParserDeclareTableOptions::parseImpl(IParser::Pos & pos, ASTPtr & node, Exp
{
OptionDescribe("AUTO_INCREMENT", "auto_increment", std::make_shared<ParserLiteral>()),
OptionDescribe("AVG_ROW_LENGTH", "avg_row_length", std::make_shared<ParserLiteral>()),
OptionDescribe("CHARSET", "character_set", std::make_shared<ParserCharsetName>()),
OptionDescribe("DEFAULT CHARSET", "character_set", std::make_shared<ParserCharsetName>()),
OptionDescribe("CHARACTER SET", "character_set", std::make_shared<ParserCharsetName>()),
OptionDescribe("DEFAULT CHARACTER SET", "character_set", std::make_shared<ParserIdentifier>()),
OptionDescribe("CHECKSUM", "checksum", std::make_shared<ParserBoolOption<false>>()),

View File

@ -31,3 +31,12 @@ TEST(CreateTableParser, SimpleCreate)
EXPECT_EQ(ast->as<ASTCreateQuery>()->table_options->as<ASTDeclareOptions>()->changes["engine"]->as<ASTIdentifier>()->name, "INNODB");
EXPECT_TRUE(ast->as<ASTCreateQuery>()->partition_options->as<ASTDeclarePartitionOptions>());
}
TEST(CreateTableParser, SS)
{
ParserCreateQuery p_create_query;
String input = "CREATE TABLE `test_table_1` (`a` int DEFAULT NULL, `b` int DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci";
ASTPtr ast = parseQuery(p_create_query, input.data(), input.data() + input.size(), "", 0, 0);
ast->dumpTree(std::cerr);
}