mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-28 02:21:59 +00:00
Fixed IN <Set> usage inside PK expressions.
Refactoring of type conversion functions convertFieldToType().
This commit is contained in:
parent
fdabcd4e38
commit
544ef67c28
@ -283,7 +283,7 @@ public:
|
|||||||
* node - это список значений: 1, 2, 3 или список tuple-ов: (1, 2), (3, 4), (5, 6).
|
* node - это список значений: 1, 2, 3 или список tuple-ов: (1, 2), (3, 4), (5, 6).
|
||||||
* create_ordered_set - создавать ли вектор упорядоченных элементов. Нужен для работы индекса
|
* create_ordered_set - создавать ли вектор упорядоченных элементов. Нужен для работы индекса
|
||||||
*/
|
*/
|
||||||
void createFromAST(DataTypes & types, ASTPtr node, const Context & context, bool create_ordered_set);
|
void createFromAST(const DataTypes & types, ASTPtr node, const Context & context, bool create_ordered_set);
|
||||||
|
|
||||||
// Возвращает false, если превышено какое-нибудь ограничение, и больше не нужно вставлять.
|
// Возвращает false, если превышено какое-нибудь ограничение, и больше не нужно вставлять.
|
||||||
bool insertFromBlock(const Block & block, bool create_ordered_set = false);
|
bool insertFromBlock(const Block & block, bool create_ordered_set = false);
|
||||||
|
@ -117,6 +117,8 @@ DayNum_t stringToDate(const String & s)
|
|||||||
DayNum_t date{};
|
DayNum_t date{};
|
||||||
|
|
||||||
readDateText(date, in);
|
readDateText(date, in);
|
||||||
|
if (!in.eof())
|
||||||
|
throw Exception("String is too long for Date: " + s);
|
||||||
|
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ static Field extractValueFromNode(ASTPtr & node, const IDataType & type, const C
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Set::createFromAST(DataTypes & types, ASTPtr node, const Context & context, bool create_ordered_set)
|
void Set::createFromAST(const DataTypes & types, ASTPtr node, const Context & context, bool create_ordered_set)
|
||||||
{
|
{
|
||||||
data_types = types;
|
data_types = types;
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include <DB/DataTypes/DataTypeDateTime.h>
|
#include <DB/DataTypes/DataTypeDateTime.h>
|
||||||
#include <DB/DataTypes/DataTypeEnum.h>
|
#include <DB/DataTypes/DataTypeEnum.h>
|
||||||
|
|
||||||
|
#include <DB/Core/FieldVisitors.h>
|
||||||
|
|
||||||
#include <DB/Interpreters/convertFieldToType.h>
|
#include <DB/Interpreters/convertFieldToType.h>
|
||||||
|
|
||||||
|
|
||||||
@ -74,19 +76,12 @@ Field convertFieldToType(const Field & src, const IDataType & type)
|
|||||||
|
|
||||||
const bool is_date = typeid_cast<const DataTypeDate *>(&type);
|
const bool is_date = typeid_cast<const DataTypeDate *>(&type);
|
||||||
bool is_datetime = false;
|
bool is_datetime = false;
|
||||||
bool is_enum8 = false;
|
bool is_enum = false;
|
||||||
bool is_enum16 = false;
|
|
||||||
|
|
||||||
if (!is_date)
|
if (!is_date)
|
||||||
if (!(is_datetime = typeid_cast<const DataTypeDateTime *>(&type)))
|
if (!(is_datetime = typeid_cast<const DataTypeDateTime *>(&type)))
|
||||||
if (!(is_enum8 = typeid_cast<const DataTypeEnum8 *>(&type)))
|
if (!(is_enum = dynamic_cast<const IDataTypeEnum *>(&type)))
|
||||||
if (!(is_enum16 = typeid_cast<const DataTypeEnum16 *>(&type)))
|
throw Exception{"Logical error: unknown numeric type " + type.getName(), ErrorCodes::LOGICAL_ERROR};
|
||||||
throw Exception{
|
|
||||||
"Logical error: unknown numeric type " + type.getName(),
|
|
||||||
ErrorCodes::LOGICAL_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto is_enum = is_enum8 || is_enum16;
|
|
||||||
|
|
||||||
/// Numeric values for Enums should not be used directly in IN section
|
/// Numeric values for Enums should not be used directly in IN section
|
||||||
if (src.getType() == Field::Types::UInt64 && !is_enum)
|
if (src.getType() == Field::Types::UInt64 && !is_enum)
|
||||||
@ -94,32 +89,21 @@ Field convertFieldToType(const Field & src, const IDataType & type)
|
|||||||
|
|
||||||
if (src.getType() == Field::Types::String)
|
if (src.getType() == Field::Types::String)
|
||||||
{
|
{
|
||||||
/// Возможность сравнивать даты и даты-с-временем со строкой.
|
|
||||||
const String & str = src.get<const String &>();
|
|
||||||
ReadBufferFromString in(str);
|
|
||||||
|
|
||||||
if (is_date)
|
if (is_date)
|
||||||
{
|
{
|
||||||
DayNum_t date{};
|
/// Convert 'YYYY-MM-DD' Strings to Date
|
||||||
readDateText(date, in);
|
return UInt64(stringToDate(src.get<const String &>()));
|
||||||
if (!in.eof())
|
|
||||||
throw Exception("String is too long for Date: " + str);
|
|
||||||
|
|
||||||
return Field(UInt64(date));
|
|
||||||
}
|
}
|
||||||
else if (is_datetime)
|
else if (is_datetime)
|
||||||
{
|
{
|
||||||
time_t date_time{};
|
/// Convert 'YYYY-MM-DD hh:mm:ss' Strings to DateTime
|
||||||
readDateTimeText(date_time, in);
|
return stringToDateTime(src.get<const String &>());
|
||||||
if (!in.eof())
|
}
|
||||||
throw Exception("String is too long for DateTime: " + str);
|
else if (is_enum)
|
||||||
|
{
|
||||||
return Field(UInt64(date_time));
|
/// Convert String to Enum's value
|
||||||
|
return dynamic_cast<const IDataTypeEnum &>(type).castToValue(src);
|
||||||
}
|
}
|
||||||
else if (is_enum8)
|
|
||||||
return Field(UInt64(static_cast<const DataTypeEnum8 &>(type).getValue(str)));
|
|
||||||
else if (is_enum16)
|
|
||||||
return Field(UInt64(static_cast<const DataTypeEnum16 &>(type).getValue(str)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("Type mismatch in IN or VALUES section: " + type.getName() + " expected, "
|
throw Exception("Type mismatch in IN or VALUES section: " + type.getName() + " expected, "
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <DB/Parsers/ASTSet.h>
|
#include <DB/Parsers/ASTSet.h>
|
||||||
#include <DB/Functions/FunctionFactory.h>
|
#include <DB/Functions/FunctionFactory.h>
|
||||||
#include <DB/Core/FieldVisitors.h>
|
#include <DB/Core/FieldVisitors.h>
|
||||||
|
#include <DB/Interpreters/convertFieldToType.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -131,7 +132,7 @@ const PKCondition::AtomMap PKCondition::atom_map{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"in",
|
"in",
|
||||||
[] (RPNElement & out, const Field & value, ASTPtr & node)
|
[] (RPNElement & out, const Field &, ASTPtr & node)
|
||||||
{
|
{
|
||||||
out.function = RPNElement::FUNCTION_IN_SET;
|
out.function = RPNElement::FUNCTION_IN_SET;
|
||||||
out.in_function = node;
|
out.in_function = node;
|
||||||
@ -140,7 +141,7 @@ const PKCondition::AtomMap PKCondition::atom_map{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"notIn",
|
"notIn",
|
||||||
[] (RPNElement & out, const Field & value, ASTPtr & node)
|
[] (RPNElement & out, const Field &, ASTPtr & node)
|
||||||
{
|
{
|
||||||
out.function = RPNElement::FUNCTION_NOT_IN_SET;
|
out.function = RPNElement::FUNCTION_NOT_IN_SET;
|
||||||
out.in_function = node;
|
out.in_function = node;
|
||||||
@ -239,23 +240,26 @@ bool PKCondition::addCondition(const String & column, const Range & range)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Получить значение константного выражения.
|
/** Computes value of constant expression and it data type.
|
||||||
* Вернуть false, если выражение не константно.
|
* Returns false, if expression isn't constant.
|
||||||
*/
|
*/
|
||||||
static bool getConstant(const ASTPtr & expr, Block & block_with_constants, Field & value)
|
static bool getConstant(const ASTPtr & expr, Block & block_with_constants, Field & out_value, DataTypePtr & out_type)
|
||||||
{
|
{
|
||||||
String column_name = expr->getColumnName();
|
String column_name = expr->getColumnName();
|
||||||
|
|
||||||
if (const ASTLiteral * lit = typeid_cast<const ASTLiteral *>(&*expr))
|
if (const ASTLiteral * lit = typeid_cast<const ASTLiteral *>(expr.get()))
|
||||||
{
|
{
|
||||||
/// литерал
|
/// Simple literal
|
||||||
value = lit->value;
|
out_value = lit->value;
|
||||||
|
out_type = block_with_constants.getByName(column_name).type;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (block_with_constants.has(column_name) && block_with_constants.getByName(column_name).column->isConst())
|
else if (block_with_constants.has(column_name) && block_with_constants.getByName(column_name).column->isConst())
|
||||||
{
|
{
|
||||||
/// выражение, вычислившееся в константу
|
/// An expression which is dependent on constants only
|
||||||
value = (*block_with_constants.getByName(column_name).column)[0];
|
const auto & expr_info = block_with_constants.getByName(column_name);
|
||||||
|
out_value = (*expr_info.column)[0];
|
||||||
|
out_type = expr_info.type;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -362,46 +366,23 @@ bool PKCondition::isPrimaryKeyPossiblyWrappedByMonotonicFunctionsImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// NOTE: Keep in the mind that such behavior could be incompatible inside ordinary expression.
|
static void castValueToType(const DataTypePtr & desired_type, Field & src_value, const DataTypePtr & src_type, const ASTPtr & node)
|
||||||
/// TODO: Use common methods for types conversions.
|
|
||||||
static bool tryCastValueToType(const DataTypePtr & desired_type, const DataTypePtr & src_type, Field & src_value)
|
|
||||||
{
|
{
|
||||||
if (desired_type->getName() == src_type->getName())
|
if (desired_type->getName() == src_type->getName())
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
/// Try to correct type of constant for correct comparison
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
/// Convert String to Enum's value
|
/// NOTE: We don't need accurate info about src_type at this moment
|
||||||
if (auto data_type_enum = dynamic_cast<const IDataTypeEnum *>(desired_type.get()))
|
src_value = convertFieldToType(src_value, *desired_type);
|
||||||
{
|
|
||||||
src_value = data_type_enum->castToValue(src_value);
|
|
||||||
}
|
|
||||||
/// Convert 'YYYY-MM-DD' Strings to Date
|
|
||||||
else if (typeid_cast<const DataTypeDate *>(desired_type.get()) && typeid_cast<const DataTypeString *>(src_type.get()))
|
|
||||||
{
|
|
||||||
src_value = UInt64(stringToDate(src_value.safeGet<String>()));
|
|
||||||
}
|
|
||||||
/// Convert 'YYYY-MM-DD hh:mm:ss' Strings to DateTime
|
|
||||||
else if (typeid_cast<const DataTypeDateTime *>(desired_type.get()) && typeid_cast<const DataTypeString *>(src_type.get()))
|
|
||||||
{
|
|
||||||
src_value = stringToDateTime(src_value.safeGet<String>());
|
|
||||||
}
|
|
||||||
else if (desired_type->behavesAsNumber() && src_type->behavesAsNumber())
|
|
||||||
{
|
|
||||||
/// Ok, numeric types are almost mutually convertible
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return false;
|
throw Exception("Primary key expression contains comparison between inconvertible types: " +
|
||||||
|
desired_type->getName() + " and " + src_type->getName() +
|
||||||
|
" inside " + DB::toString(node->range),
|
||||||
|
ErrorCodes::BAD_TYPE_OF_FIELD);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -411,8 +392,9 @@ bool PKCondition::atomFromAST(ASTPtr & node, const Context & context, Block & bl
|
|||||||
* либо он же, завёрнутый в цепочку возможно-монотонных функций,
|
* либо он же, завёрнутый в цепочку возможно-монотонных функций,
|
||||||
* либо константное выражение - число.
|
* либо константное выражение - число.
|
||||||
*/
|
*/
|
||||||
Field value;
|
Field const_value;
|
||||||
if (const ASTFunction * func = typeid_cast<const ASTFunction *>(&*node))
|
DataTypePtr const_type;
|
||||||
|
if (const ASTFunction * func = typeid_cast<const ASTFunction *>(node.get()))
|
||||||
{
|
{
|
||||||
const ASTs & args = typeid_cast<const ASTExpressionList &>(*func->arguments).children;
|
const ASTs & args = typeid_cast<const ASTExpressionList &>(*func->arguments).children;
|
||||||
|
|
||||||
@ -423,13 +405,14 @@ bool PKCondition::atomFromAST(ASTPtr & node, const Context & context, Block & bl
|
|||||||
size_t key_arg_pos; /// Position of argument with primary key column (non-const argument)
|
size_t key_arg_pos; /// Position of argument with primary key column (non-const argument)
|
||||||
size_t key_column_num; /// Number of a primary key column (inside sort_descr array)
|
size_t key_column_num; /// Number of a primary key column (inside sort_descr array)
|
||||||
RPNElement::MonotonicFunctionsChain chain;
|
RPNElement::MonotonicFunctionsChain chain;
|
||||||
|
bool is_set_const = false;
|
||||||
|
|
||||||
if (getConstant(args[1], block_with_constants, value)
|
if (getConstant(args[1], block_with_constants, const_value, const_type)
|
||||||
&& isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[0], context, key_column_num, key_expr_type, chain))
|
&& isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[0], context, key_column_num, key_expr_type, chain))
|
||||||
{
|
{
|
||||||
key_arg_pos = 0;
|
key_arg_pos = 0;
|
||||||
}
|
}
|
||||||
else if (getConstant(args[0], block_with_constants, value)
|
else if (getConstant(args[0], block_with_constants, const_value, const_type)
|
||||||
&& isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[1], context, key_column_num, key_expr_type, chain))
|
&& isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[1], context, key_column_num, key_expr_type, chain))
|
||||||
{
|
{
|
||||||
key_arg_pos = 1;
|
key_arg_pos = 1;
|
||||||
@ -438,6 +421,7 @@ bool PKCondition::atomFromAST(ASTPtr & node, const Context & context, Block & bl
|
|||||||
&& isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[0], context, key_column_num, key_expr_type, chain))
|
&& isPrimaryKeyPossiblyWrappedByMonotonicFunctions(args[0], context, key_column_num, key_expr_type, chain))
|
||||||
{
|
{
|
||||||
key_arg_pos = 0;
|
key_arg_pos = 0;
|
||||||
|
is_set_const = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
@ -469,26 +453,19 @@ bool PKCondition::atomFromAST(ASTPtr & node, const Context & context, Block & bl
|
|||||||
if (atom_it == std::end(atom_map))
|
if (atom_it == std::end(atom_map))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const DataTypePtr & const_type = block_with_constants.getByName(args[1 - key_arg_pos]->getColumnName()).type;
|
if (!is_set_const) /// Set args are already casted inside Set::createFromAST
|
||||||
|
castValueToType(key_expr_type, const_value, const_type, node);
|
||||||
|
|
||||||
if (!tryCastValueToType(key_expr_type, const_type, value))
|
return atom_it->second(out, const_value, node);
|
||||||
{
|
|
||||||
throw Exception("Primary key expression contains comparison between inconvertible types: " +
|
|
||||||
key_expr_type->getName() + " and " + const_type->getName() +
|
|
||||||
" inside " + DB::toString(func->range),
|
|
||||||
ErrorCodes::BAD_TYPE_OF_FIELD);
|
|
||||||
}
|
}
|
||||||
|
else if (getConstant(node, block_with_constants, const_value, const_type)) /// Для случаев, когда написано, например, WHERE 0 AND something
|
||||||
return atom_it->second(out, value, node);
|
|
||||||
}
|
|
||||||
else if (getConstant(node, block_with_constants, value)) /// Для случаев, когда написано, например, WHERE 0 AND something
|
|
||||||
{
|
{
|
||||||
if (value.getType() == Field::Types::UInt64
|
if (const_value.getType() == Field::Types::UInt64
|
||||||
|| value.getType() == Field::Types::Int64
|
|| const_value.getType() == Field::Types::Int64
|
||||||
|| value.getType() == Field::Types::Float64)
|
|| const_value.getType() == Field::Types::Float64)
|
||||||
{
|
{
|
||||||
/// Ноль во всех типах представлен в памяти так же, как в UInt64.
|
/// Ноль во всех типах представлен в памяти так же, как в UInt64.
|
||||||
out.function = value.get<UInt64>()
|
out.function = const_value.get<UInt64>()
|
||||||
? RPNElement::ALWAYS_TRUE
|
? RPNElement::ALWAYS_TRUE
|
||||||
: RPNElement::ALWAYS_FALSE;
|
: RPNElement::ALWAYS_FALSE;
|
||||||
|
|
||||||
|
@ -20,5 +20,7 @@
|
|||||||
3447905173014179293
|
3447905173014179293
|
||||||
3051197876967004596
|
3051197876967004596
|
||||||
3051197876967004596
|
3051197876967004596
|
||||||
|
3051197876967004596
|
||||||
|
3051197876967004596
|
||||||
463667963421364848
|
463667963421364848
|
||||||
463667963421364848
|
463667963421364848
|
||||||
|
@ -35,5 +35,8 @@ SELECT cityHash64(groupArray(d)) FROM test.enum_pk WHERE 1 = 1;
|
|||||||
SELECT cityHash64(groupArray(x)) FROM test.enum_pk WHERE (x = '0' OR x = '1');
|
SELECT cityHash64(groupArray(x)) FROM test.enum_pk WHERE (x = '0' OR x = '1');
|
||||||
SELECT cityHash64(groupArray(d)) FROM test.enum_pk WHERE (d = '0' OR d = '1');
|
SELECT cityHash64(groupArray(d)) FROM test.enum_pk WHERE (d = '0' OR d = '1');
|
||||||
|
|
||||||
|
SELECT cityHash64(groupArray(x)) FROM test.enum_pk WHERE x IN ('0', '1');
|
||||||
|
SELECT cityHash64(groupArray(d)) FROM test.enum_pk WHERE d IN ('0', '1');
|
||||||
|
|
||||||
SELECT cityHash64(groupArray(x)) FROM test.enum_pk WHERE (x != '0' AND x != '1');
|
SELECT cityHash64(groupArray(x)) FROM test.enum_pk WHERE (x != '0' AND x != '1');
|
||||||
SELECT cityHash64(groupArray(d)) FROM test.enum_pk WHERE (d != '0' AND d != '1');
|
SELECT cityHash64(groupArray(d)) FROM test.enum_pk WHERE (d != '0' AND d != '1');
|
||||||
|
Loading…
Reference in New Issue
Block a user