mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-28 12:40:49 +00:00
Merge remote-tracking branch 'upstream/master' into fix27
This commit is contained in:
commit
08a6f33511
@ -339,7 +339,7 @@ bool DataTypeAggregateFunction::equals(const IDataType & rhs) const
|
||||
}
|
||||
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
String function_name;
|
||||
AggregateFunctionPtr function;
|
||||
|
@ -508,7 +508,7 @@ size_t DataTypeArray::getNumberOfDimensions() const
|
||||
}
|
||||
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 1)
|
||||
throw Exception("Array data type family must have exactly one argument - type of elements", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
@ -102,13 +102,13 @@ public:
|
||||
|
||||
void registerDataTypeDomainIPv4AndIPv6(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerSimpleDataTypeCustom("IPv4", []
|
||||
factory.registerSimpleDataTypeCustom("IPv4", [&](const String & /*type_name*/)
|
||||
{
|
||||
return std::make_pair(DataTypeFactory::instance().get("UInt32"),
|
||||
std::make_unique<DataTypeCustomDesc>(std::make_unique<DataTypeCustomFixedName>("IPv4"), std::make_unique<DataTypeCustomIPv4Serialization>()));
|
||||
});
|
||||
|
||||
factory.registerSimpleDataTypeCustom("IPv6", []
|
||||
factory.registerSimpleDataTypeCustom("IPv6", [&](const String & /*type_name*/)
|
||||
{
|
||||
return std::make_pair(DataTypeFactory::instance().get("FixedString(16)"),
|
||||
std::make_unique<DataTypeCustomDesc>(std::make_unique<DataTypeCustomFixedName>("IPv6"), std::make_unique<DataTypeCustomIPv6Serialization>()));
|
||||
|
@ -58,7 +58,7 @@ String DataTypeCustomSimpleAggregateFunction::getName() const
|
||||
}
|
||||
|
||||
|
||||
static std::pair<DataTypePtr, DataTypeCustomDescPtr> create(const ASTPtr & arguments)
|
||||
static std::pair<DataTypePtr, DataTypeCustomDescPtr> create(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
String function_name;
|
||||
AggregateFunctionPtr function;
|
||||
|
@ -113,7 +113,12 @@ bool DataTypeDate::equals(const IDataType & rhs) const
|
||||
|
||||
void registerDataTypeDate(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerSimpleDataType("Date", [] { return DataTypePtr(std::make_shared<DataTypeDate>()); }, DataTypeFactory::CaseInsensitive);
|
||||
const auto & creator = [&](const String & /*type_name*/)
|
||||
{
|
||||
return DataTypePtr(std::make_shared<DataTypeDate>());
|
||||
};
|
||||
|
||||
factory.registerSimpleDataType("Date", creator, DataTypeFactory::CaseInsensitive);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ TimezoneMixin::TimezoneMixin(const String & time_zone_name)
|
||||
utc_time_zone(DateLUT::instance("UTC"))
|
||||
{}
|
||||
|
||||
DataTypeDateTime::DataTypeDateTime(const String & time_zone_name)
|
||||
: TimezoneMixin(time_zone_name)
|
||||
DataTypeDateTime::DataTypeDateTime(const String & time_zone_name, const String & type_name_)
|
||||
: TimezoneMixin(time_zone_name), type_name(type_name_)
|
||||
{
|
||||
}
|
||||
|
||||
@ -55,10 +55,10 @@ DataTypeDateTime::DataTypeDateTime(const TimezoneMixin & time_zone_)
|
||||
String DataTypeDateTime::doGetName() const
|
||||
{
|
||||
if (!has_explicit_time_zone)
|
||||
return "DateTime";
|
||||
return type_name;
|
||||
|
||||
WriteBufferFromOwnString out;
|
||||
out << "DateTime(" << quote << time_zone.getTimeZone() << ")";
|
||||
out << type_name << "(" << quote << time_zone.getTimeZone() << ")";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
@ -194,10 +194,10 @@ namespace ErrorCodes
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
}
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & type_name, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments)
|
||||
return std::make_shared<DataTypeDateTime>();
|
||||
return std::make_shared<DataTypeDateTime>("", type_name);
|
||||
|
||||
if (arguments->children.size() != 1)
|
||||
throw Exception("DateTime data type can optionally have only one argument - time zone name", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
@ -206,7 +206,7 @@ static DataTypePtr create(const ASTPtr & arguments)
|
||||
if (!arg || arg->value.getType() != Field::Types::String)
|
||||
throw Exception("Parameter for DateTime data type must be string literal", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
return std::make_shared<DataTypeDateTime>(arg->value.get<String>());
|
||||
return std::make_shared<DataTypeDateTime>(arg->value.get<String>(), type_name);
|
||||
}
|
||||
|
||||
void registerDataTypeDateTime(DataTypeFactory & factory)
|
||||
|
@ -49,7 +49,7 @@ protected:
|
||||
class DataTypeDateTime final : public DataTypeNumberBase<UInt32>, public TimezoneMixin
|
||||
{
|
||||
public:
|
||||
explicit DataTypeDateTime(const String & time_zone_name = "");
|
||||
explicit DataTypeDateTime(const String & time_zone_name = "", const String & type_name_ = "DateTime");
|
||||
explicit DataTypeDateTime(const TimezoneMixin & time_zone);
|
||||
|
||||
static constexpr auto family_name = "DateTime";
|
||||
@ -75,6 +75,8 @@ public:
|
||||
bool canBeInsideNullable() const override { return true; }
|
||||
|
||||
bool equals(const IDataType & rhs) const override;
|
||||
private:
|
||||
const String type_name;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ getArgument(const ASTPtr & arguments, size_t argument_index, const char * argume
|
||||
return argument->value.get<NearestResultType>();
|
||||
}
|
||||
|
||||
static DataTypePtr create64(const ASTPtr & arguments)
|
||||
static DataTypePtr create64(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->size() == 0)
|
||||
return std::make_shared<DataTypeDateTime64>(DataTypeDateTime64::default_scale);
|
||||
|
@ -195,7 +195,7 @@ const DecimalType<U> decimalResultType(const DataTypeNumber<T> &, const DecimalT
|
||||
}
|
||||
|
||||
template <template <typename> typename DecimalType>
|
||||
DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value)
|
||||
DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value, const String & type_name = "Decimal", bool only_scale = false)
|
||||
{
|
||||
if (precision_value < DecimalUtils::minPrecision() || precision_value > DecimalUtils::maxPrecision<Decimal128>())
|
||||
throw Exception("Wrong precision", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
@ -204,10 +204,10 @@ DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value)
|
||||
throw Exception("Negative scales and scales larger than precision are not supported", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
|
||||
if (precision_value <= DecimalUtils::maxPrecision<Decimal32>())
|
||||
return std::make_shared<DecimalType<Decimal32>>(precision_value, scale_value);
|
||||
return std::make_shared<DecimalType<Decimal32>>(precision_value, scale_value, type_name, only_scale);
|
||||
else if (precision_value <= DecimalUtils::maxPrecision<Decimal64>())
|
||||
return std::make_shared<DecimalType<Decimal64>>(precision_value, scale_value);
|
||||
return std::make_shared<DecimalType<Decimal128>>(precision_value, scale_value);
|
||||
return std::make_shared<DecimalType<Decimal64>>(precision_value, scale_value, type_name, only_scale);
|
||||
return std::make_shared<DecimalType<Decimal128>>(precision_value, scale_value, type_name, only_scale);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ static void checkASTStructure(const ASTPtr & child)
|
||||
}
|
||||
|
||||
template <typename DataTypeEnum>
|
||||
static DataTypePtr createExact(const ASTPtr & arguments)
|
||||
static DataTypePtr createExact(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.empty())
|
||||
throw Exception("Enum data type cannot be empty", ErrorCodes::EMPTY_DATA_PASSED);
|
||||
@ -403,7 +403,7 @@ static DataTypePtr createExact(const ASTPtr & arguments)
|
||||
return std::make_shared<DataTypeEnum>(values);
|
||||
}
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & type_name, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.empty())
|
||||
throw Exception("Enum data type cannot be empty", ErrorCodes::EMPTY_DATA_PASSED);
|
||||
@ -424,10 +424,10 @@ static DataTypePtr create(const ASTPtr & arguments)
|
||||
Int64 value = value_literal->value.get<Int64>();
|
||||
|
||||
if (value > std::numeric_limits<Int8>::max() || value < std::numeric_limits<Int8>::min())
|
||||
return createExact<DataTypeEnum16>(arguments);
|
||||
return createExact<DataTypeEnum16>(type_name, arguments);
|
||||
}
|
||||
|
||||
return createExact<DataTypeEnum8>(arguments);
|
||||
return createExact<DataTypeEnum8>(type_name, arguments);
|
||||
}
|
||||
|
||||
void registerDataTypeEnum(DataTypeFactory & factory)
|
||||
|
@ -74,7 +74,7 @@ DataTypePtr DataTypeFactory::get(const String & family_name_param, const ASTPtr
|
||||
return get("LowCardinality", low_cardinality_params);
|
||||
}
|
||||
|
||||
return findCreatorByName(family_name)(parameters);
|
||||
return findCreatorByName(family_name)(family_name_param, parameters);
|
||||
}
|
||||
|
||||
|
||||
@ -107,19 +107,19 @@ void DataTypeFactory::registerSimpleDataType(const String & name, SimpleCreator
|
||||
throw Exception("DataTypeFactory: the data type " + name + " has been provided "
|
||||
" a null constructor", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
registerDataType(name, [name, creator](const ASTPtr & ast)
|
||||
registerDataType(name, [name, creator](const String & type_name, const ASTPtr & ast)
|
||||
{
|
||||
if (ast)
|
||||
throw Exception("Data type " + name + " cannot have arguments", ErrorCodes::DATA_TYPE_CANNOT_HAVE_ARGUMENTS);
|
||||
return creator();
|
||||
return creator(type_name);
|
||||
}, case_sensitiveness);
|
||||
}
|
||||
|
||||
void DataTypeFactory::registerDataTypeCustom(const String & family_name, CreatorWithCustom creator, CaseSensitiveness case_sensitiveness)
|
||||
{
|
||||
registerDataType(family_name, [creator](const ASTPtr & ast)
|
||||
registerDataType(family_name, [creator](const String & type_name, const ASTPtr & ast)
|
||||
{
|
||||
auto res = creator(ast);
|
||||
auto res = creator(type_name, ast);
|
||||
res.first->setCustomization(std::move(res.second));
|
||||
|
||||
return res.first;
|
||||
@ -128,9 +128,9 @@ void DataTypeFactory::registerDataTypeCustom(const String & family_name, Creator
|
||||
|
||||
void DataTypeFactory::registerSimpleDataTypeCustom(const String & name, SimpleCreatorWithCustom creator, CaseSensitiveness case_sensitiveness)
|
||||
{
|
||||
registerDataTypeCustom(name, [creator](const ASTPtr & /*ast*/)
|
||||
registerDataTypeCustom(name, [creator](const String & type_name, const ASTPtr & /*ast*/)
|
||||
{
|
||||
return creator();
|
||||
return creator(type_name);
|
||||
}, case_sensitiveness);
|
||||
}
|
||||
|
||||
|
@ -16,16 +16,15 @@ namespace DB
|
||||
class IDataType;
|
||||
using DataTypePtr = std::shared_ptr<const IDataType>;
|
||||
|
||||
|
||||
/** Creates a data type by name of data type family and parameters.
|
||||
*/
|
||||
class DataTypeFactory final : private boost::noncopyable, public IFactoryWithAliases<std::function<DataTypePtr(const ASTPtr & parameters)>>
|
||||
class DataTypeFactory final : private boost::noncopyable, public IFactoryWithAliases<std::function<DataTypePtr(const String & type_name, const ASTPtr & parameters)>>
|
||||
{
|
||||
private:
|
||||
using SimpleCreator = std::function<DataTypePtr()>;
|
||||
using SimpleCreator = std::function<DataTypePtr(const String & type_name)>;
|
||||
using DataTypesDictionary = std::unordered_map<String, Creator>;
|
||||
using CreatorWithCustom = std::function<std::pair<DataTypePtr,DataTypeCustomDescPtr>(const ASTPtr & parameters)>;
|
||||
using SimpleCreatorWithCustom = std::function<std::pair<DataTypePtr,DataTypeCustomDescPtr>()>;
|
||||
using CreatorWithCustom = std::function<std::pair<DataTypePtr, DataTypeCustomDescPtr>(const String & type_name, const ASTPtr & parameters)>;
|
||||
using SimpleCreatorWithCustom = std::function<std::pair<DataTypePtr, DataTypeCustomDescPtr>(const String & type_name)>;
|
||||
|
||||
public:
|
||||
static DataTypeFactory & instance();
|
||||
|
@ -34,7 +34,7 @@ namespace ErrorCodes
|
||||
|
||||
std::string DataTypeFixedString::doGetName() const
|
||||
{
|
||||
return "FixedString(" + toString(n) + ")";
|
||||
return type_name + "(" + toString(n) + ")";
|
||||
}
|
||||
|
||||
|
||||
@ -279,7 +279,7 @@ bool DataTypeFixedString::equals(const IDataType & rhs) const
|
||||
}
|
||||
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & type_name, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 1)
|
||||
throw Exception("FixedString data type family must have exactly one argument - size in bytes", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
@ -288,7 +288,7 @@ static DataTypePtr create(const ASTPtr & arguments)
|
||||
if (!argument || argument->value.getType() != Field::Types::UInt64 || argument->value.get<UInt64>() == 0)
|
||||
throw Exception("FixedString data type family must have a number (positive integer) as its argument", ErrorCodes::UNEXPECTED_AST_STRUCTURE);
|
||||
|
||||
return std::make_shared<DataTypeFixedString>(argument->value.get<UInt64>());
|
||||
return std::make_shared<DataTypeFixedString>(argument->value.get<UInt64>(), type_name);
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ private:
|
||||
public:
|
||||
static constexpr bool is_parametric = true;
|
||||
|
||||
DataTypeFixedString(size_t n_) : n(n_)
|
||||
DataTypeFixedString(size_t n_, const String & type_name_ = "FixedString") : n(n_), type_name(type_name_)
|
||||
{
|
||||
if (n == 0)
|
||||
throw Exception("FixedString size must be positive", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
||||
@ -85,6 +85,9 @@ public:
|
||||
bool isCategorial() const override { return true; }
|
||||
bool canBeInsideNullable() const override { return true; }
|
||||
bool canBeInsideLowCardinality() const override { return true; }
|
||||
|
||||
private:
|
||||
const String type_name;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -10,17 +10,22 @@ bool DataTypeInterval::equals(const IDataType & rhs) const
|
||||
return typeid(rhs) == typeid(*this) && kind == static_cast<const DataTypeInterval &>(rhs).kind;
|
||||
}
|
||||
|
||||
template <IntervalKind::Kind kind>
|
||||
static DataTypePtr create(const String & /*type_name*/)
|
||||
{
|
||||
return DataTypePtr(std::make_shared<DataTypeInterval>(kind));
|
||||
}
|
||||
|
||||
void registerDataTypeInterval(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerSimpleDataType("IntervalSecond", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Second)); });
|
||||
factory.registerSimpleDataType("IntervalMinute", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Minute)); });
|
||||
factory.registerSimpleDataType("IntervalHour", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Hour)); });
|
||||
factory.registerSimpleDataType("IntervalDay", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Day)); });
|
||||
factory.registerSimpleDataType("IntervalWeek", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Week)); });
|
||||
factory.registerSimpleDataType("IntervalMonth", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Month)); });
|
||||
factory.registerSimpleDataType("IntervalQuarter", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Quarter)); });
|
||||
factory.registerSimpleDataType("IntervalYear", [] { return DataTypePtr(std::make_shared<DataTypeInterval>(IntervalKind::Year)); });
|
||||
factory.registerSimpleDataType("IntervalSecond", create<IntervalKind::Second>);
|
||||
factory.registerSimpleDataType("IntervalMinute", create<IntervalKind::Minute>);
|
||||
factory.registerSimpleDataType("IntervalHour", create<IntervalKind::Hour>);
|
||||
factory.registerSimpleDataType("IntervalDay", create<IntervalKind::Day>);
|
||||
factory.registerSimpleDataType("IntervalWeek", create<IntervalKind::Week>);
|
||||
factory.registerSimpleDataType("IntervalMonth", create<IntervalKind::Month>);
|
||||
factory.registerSimpleDataType("IntervalQuarter", create<IntervalKind::Quarter>);
|
||||
factory.registerSimpleDataType("IntervalYear", create<IntervalKind::Year>);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -949,7 +949,7 @@ bool DataTypeLowCardinality::equals(const IDataType & rhs) const
|
||||
}
|
||||
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 1)
|
||||
throw Exception("LowCardinality data type family must have single argument - type of elements",
|
||||
|
@ -38,7 +38,9 @@ bool DataTypeNothing::equals(const IDataType & rhs) const
|
||||
|
||||
void registerDataTypeNothing(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerSimpleDataType("Nothing", [] { return DataTypePtr(std::make_shared<DataTypeNothing>()); });
|
||||
const auto & creator = [&](const String & /*type_name*/) { return DataTypePtr(std::make_shared<DataTypeNothing>()); };
|
||||
|
||||
factory.registerSimpleDataType("Nothing", creator);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ bool DataTypeNullable::equals(const IDataType & rhs) const
|
||||
}
|
||||
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 1)
|
||||
throw Exception("Nullable data type family must have exactly one argument - nested type", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
@ -369,7 +369,7 @@ bool DataTypeString::equals(const IDataType & rhs) const
|
||||
|
||||
void registerDataTypeString(DataTypeFactory & factory)
|
||||
{
|
||||
auto creator = static_cast<DataTypePtr(*)()>([] { return DataTypePtr(std::make_shared<DataTypeString>()); });
|
||||
const auto & creator = [&] (const String & type_name) { return std::make_shared<DataTypeString>(type_name); };
|
||||
|
||||
factory.registerSimpleDataType("String", creator);
|
||||
|
||||
|
@ -14,6 +14,10 @@ public:
|
||||
using FieldType = String;
|
||||
static constexpr bool is_parametric = false;
|
||||
|
||||
DataTypeString(const String & type_name_ = "String") : type_name(type_name_) {}
|
||||
|
||||
String doGetName() const override { return type_name; }
|
||||
|
||||
const char * getFamilyName() const override
|
||||
{
|
||||
return "String";
|
||||
@ -63,6 +67,9 @@ public:
|
||||
bool isCategorial() const override { return true; }
|
||||
bool canBeInsideNullable() const override { return true; }
|
||||
bool canBeInsideLowCardinality() const override { return true; }
|
||||
|
||||
private:
|
||||
const String type_name;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -529,7 +529,7 @@ size_t DataTypeTuple::getSizeOfValueInMemory() const
|
||||
}
|
||||
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
static DataTypePtr create(const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.empty())
|
||||
throw Exception("Tuple cannot be empty", ErrorCodes::EMPTY_DATA_PASSED);
|
||||
@ -568,7 +568,7 @@ void registerDataTypeTuple(DataTypeFactory & factory)
|
||||
void registerDataTypeNested(DataTypeFactory & factory)
|
||||
{
|
||||
/// Nested(...) data type is just a sugar for Array(Tuple(...))
|
||||
factory.registerDataType("Nested", [&factory](const ASTPtr & arguments)
|
||||
factory.registerDataType("Nested", [&factory](const String & /*type_name*/, const ASTPtr & arguments)
|
||||
{
|
||||
return std::make_shared<DataTypeArray>(factory.get("Tuple", arguments));
|
||||
});
|
||||
|
@ -106,7 +106,9 @@ bool DataTypeUUID::equals(const IDataType & rhs) const
|
||||
|
||||
void registerDataTypeUUID(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerSimpleDataType("UUID", [] { return DataTypePtr(std::make_shared<DataTypeUUID>()); });
|
||||
const auto & creator = [&] (const String & /*type_name*/) { return std::make_shared<DataTypeUUID>(); };
|
||||
|
||||
factory.registerSimpleDataType("UUID", creator);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include <Parsers/IAST.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include "DataTypesDecimal.h"
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -31,7 +33,12 @@ template <typename T>
|
||||
std::string DataTypeDecimal<T>::doGetName() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Decimal(" << this->precision << ", " << this->scale << ")";
|
||||
ss << type_name << "(";
|
||||
|
||||
if (!only_scale)
|
||||
ss << this->precision << ", ";
|
||||
|
||||
ss << this->scale << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -135,8 +142,14 @@ void DataTypeDecimal<T>::deserializeProtobuf(IColumn & column, ProtobufReader &
|
||||
container.back() = decimal;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DataTypeDecimal<T>::DataTypeDecimal(UInt32 precision_, UInt32 scale_, const String & type_name_, bool only_scale_)
|
||||
: Base(precision_, scale_), type_name(type_name_), only_scale(only_scale_)
|
||||
{
|
||||
}
|
||||
|
||||
static DataTypePtr create(const ASTPtr & arguments)
|
||||
|
||||
static DataTypePtr create(const String & type_name, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 2)
|
||||
throw Exception("Decimal data type family must have exactly two arguments: precision and scale",
|
||||
@ -152,11 +165,11 @@ static DataTypePtr create(const ASTPtr & arguments)
|
||||
UInt64 precision_value = precision->value.get<UInt64>();
|
||||
UInt64 scale_value = scale->value.get<UInt64>();
|
||||
|
||||
return createDecimal<DataTypeDecimal>(precision_value, scale_value);
|
||||
return createDecimal<DataTypeDecimal>(precision_value, scale_value, type_name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static DataTypePtr createExact(const ASTPtr & arguments)
|
||||
static DataTypePtr createExact(const String & type_name, const ASTPtr & arguments)
|
||||
{
|
||||
if (!arguments || arguments->children.size() != 1)
|
||||
throw Exception("Decimal data type family must have exactly two arguments: precision and scale",
|
||||
@ -170,7 +183,7 @@ static DataTypePtr createExact(const ASTPtr & arguments)
|
||||
UInt64 precision = DecimalUtils::maxPrecision<T>();
|
||||
UInt64 scale = scale_arg->value.get<UInt64>();
|
||||
|
||||
return createDecimal<DataTypeDecimal>(precision, scale);
|
||||
return createDecimal<DataTypeDecimal>(precision, scale, type_name, true);
|
||||
}
|
||||
|
||||
void registerDataTypeDecimal(DataTypeFactory & factory)
|
||||
|
@ -35,6 +35,8 @@ public:
|
||||
using typename Base::ColumnType;
|
||||
using Base::Base;
|
||||
|
||||
DataTypeDecimal(UInt32 precision_, UInt32 scale_, const String & type_name_ = "Decimal", bool only_scale_ = false);
|
||||
|
||||
static constexpr auto family_name = "Decimal";
|
||||
|
||||
const char * getFamilyName() const override { return family_name; }
|
||||
@ -57,6 +59,12 @@ public:
|
||||
|
||||
static void readText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_, bool csv = false);
|
||||
static bool tryReadText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_);
|
||||
|
||||
private:
|
||||
/// The name of data type how the user specified it. A single data type may be referenced by various synonims.
|
||||
const String type_name;
|
||||
/// If the user specified it only with scale parameter but without precision.
|
||||
bool only_scale = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -5,20 +5,26 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
template <typename NumberType>
|
||||
static DataTypePtr create(const String & type_name)
|
||||
{
|
||||
return DataTypePtr(std::make_shared<NumberType>(type_name));
|
||||
}
|
||||
|
||||
void registerDataTypeNumbers(DataTypeFactory & factory)
|
||||
{
|
||||
factory.registerSimpleDataType("UInt8", [] { return DataTypePtr(std::make_shared<DataTypeUInt8>()); });
|
||||
factory.registerSimpleDataType("UInt16", [] { return DataTypePtr(std::make_shared<DataTypeUInt16>()); });
|
||||
factory.registerSimpleDataType("UInt32", [] { return DataTypePtr(std::make_shared<DataTypeUInt32>()); });
|
||||
factory.registerSimpleDataType("UInt64", [] { return DataTypePtr(std::make_shared<DataTypeUInt64>()); });
|
||||
factory.registerSimpleDataType("UInt8", create<DataTypeUInt8>);
|
||||
factory.registerSimpleDataType("UInt16", create<DataTypeUInt16>);
|
||||
factory.registerSimpleDataType("UInt32", create<DataTypeUInt32>);
|
||||
factory.registerSimpleDataType("UInt64", create<DataTypeUInt64>);
|
||||
|
||||
factory.registerSimpleDataType("Int8", [] { return DataTypePtr(std::make_shared<DataTypeInt8>()); });
|
||||
factory.registerSimpleDataType("Int16", [] { return DataTypePtr(std::make_shared<DataTypeInt16>()); });
|
||||
factory.registerSimpleDataType("Int32", [] { return DataTypePtr(std::make_shared<DataTypeInt32>()); });
|
||||
factory.registerSimpleDataType("Int64", [] { return DataTypePtr(std::make_shared<DataTypeInt64>()); });
|
||||
factory.registerSimpleDataType("Int8", create<DataTypeInt8>);
|
||||
factory.registerSimpleDataType("Int16", create<DataTypeInt16>);
|
||||
factory.registerSimpleDataType("Int32", create<DataTypeInt32>);
|
||||
factory.registerSimpleDataType("Int64", create<DataTypeInt64>);
|
||||
|
||||
factory.registerSimpleDataType("Float32", [] { return DataTypePtr(std::make_shared<DataTypeFloat32>()); });
|
||||
factory.registerSimpleDataType("Float64", [] { return DataTypePtr(std::make_shared<DataTypeFloat64>()); });
|
||||
factory.registerSimpleDataType("Float32", create<DataTypeFloat32>);
|
||||
factory.registerSimpleDataType("Float64", create<DataTypeFloat64>);
|
||||
|
||||
/// These synonyms are added for compatibility.
|
||||
|
||||
|
@ -25,6 +25,13 @@ class DataTypeNumber final : public DataTypeNumberBase<T>
|
||||
using PromotedType = DataTypeNumber<NearestFieldType<T>>;
|
||||
return std::make_shared<PromotedType>();
|
||||
}
|
||||
|
||||
public:
|
||||
DataTypeNumber(const String & type_name_ = TypeName<T>::get()) : type_name(type_name_) {}
|
||||
|
||||
String doGetName() const override { return type_name; }
|
||||
private:
|
||||
const String type_name;
|
||||
};
|
||||
|
||||
using DataTypeUInt8 = DataTypeNumber<UInt8>;
|
||||
|
@ -70,7 +70,7 @@ void StorageJoin::truncate(const ASTPtr &, const Context &, TableStructureWriteL
|
||||
|
||||
HashJoinPtr StorageJoin::getJoin(std::shared_ptr<AnalyzedJoin> analyzed_join) const
|
||||
{
|
||||
if (!(kind == analyzed_join->kind() && strictness == analyzed_join->strictness()))
|
||||
if (kind != analyzed_join->kind() || strictness != analyzed_join->strictness())
|
||||
throw Exception("Table " + table_name + " has incompatible type of JOIN.", ErrorCodes::INCOMPATIBLE_TYPE_OF_JOIN);
|
||||
|
||||
/// TODO: check key columns
|
||||
@ -96,58 +96,14 @@ void registerStorageJoin(StorageFactory & factory)
|
||||
|
||||
ASTs & engine_args = args.engine_args;
|
||||
|
||||
if (engine_args.size() < 3)
|
||||
throw Exception(
|
||||
"Storage Join requires at least 3 parameters: Join(ANY|ALL, LEFT|INNER, keys...).",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
auto opt_strictness_id = tryGetIdentifierName(engine_args[0]);
|
||||
if (!opt_strictness_id)
|
||||
throw Exception("First parameter of storage Join must be ANY or ALL (without quotes).", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const String strictness_str = Poco::toLower(*opt_strictness_id);
|
||||
ASTTableJoin::Strictness strictness;
|
||||
if (strictness_str == "any")
|
||||
strictness = ASTTableJoin::Strictness::RightAny;
|
||||
else if (strictness_str == "all")
|
||||
strictness = ASTTableJoin::Strictness::All;
|
||||
else
|
||||
throw Exception("First parameter of storage Join must be ANY or ALL (without quotes).", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
auto opt_kind_id = tryGetIdentifierName(engine_args[1]);
|
||||
if (!opt_kind_id)
|
||||
throw Exception("Second parameter of storage Join must be LEFT or INNER (without quotes).", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const String kind_str = Poco::toLower(*opt_kind_id);
|
||||
ASTTableJoin::Kind kind;
|
||||
if (kind_str == "left")
|
||||
kind = ASTTableJoin::Kind::Left;
|
||||
else if (kind_str == "inner")
|
||||
kind = ASTTableJoin::Kind::Inner;
|
||||
else if (kind_str == "right")
|
||||
kind = ASTTableJoin::Kind::Right;
|
||||
else if (kind_str == "full")
|
||||
kind = ASTTableJoin::Kind::Full;
|
||||
else
|
||||
throw Exception("Second parameter of storage Join must be LEFT or INNER or RIGHT or FULL (without quotes).", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
Names key_names;
|
||||
key_names.reserve(engine_args.size() - 2);
|
||||
for (size_t i = 2, size = engine_args.size(); i < size; ++i)
|
||||
{
|
||||
auto opt_key = tryGetIdentifierName(engine_args[i]);
|
||||
if (!opt_key)
|
||||
throw Exception("Parameter №" + toString(i + 1) + " of storage Join don't look like column name.", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
key_names.push_back(*opt_key);
|
||||
}
|
||||
|
||||
auto & settings = args.context.getSettingsRef();
|
||||
|
||||
auto join_use_nulls = settings.join_use_nulls;
|
||||
auto max_rows_in_join = settings.max_rows_in_join;
|
||||
auto max_bytes_in_join = settings.max_bytes_in_join;
|
||||
auto join_overflow_mode = settings.join_overflow_mode;
|
||||
auto join_any_take_last_row = settings.join_any_take_last_row;
|
||||
auto old_any_join = settings.any_join_distinct_right_table_keys;
|
||||
|
||||
if (args.storage_def && args.storage_def->settings)
|
||||
{
|
||||
@ -163,6 +119,8 @@ void registerStorageJoin(StorageFactory & factory)
|
||||
join_overflow_mode.set(setting.value);
|
||||
else if (setting.name == "join_any_take_last_row")
|
||||
join_any_take_last_row.set(setting.value);
|
||||
else if (setting.name == "any_join_distinct_right_table_keys")
|
||||
old_any_join.set(setting.value);
|
||||
else
|
||||
throw Exception(
|
||||
"Unknown setting " + setting.name + " for storage " + args.engine_name,
|
||||
@ -170,6 +128,68 @@ void registerStorageJoin(StorageFactory & factory)
|
||||
}
|
||||
}
|
||||
|
||||
if (engine_args.size() < 3)
|
||||
throw Exception(
|
||||
"Storage Join requires at least 3 parameters: Join(ANY|ALL|SEMI|ANTI, LEFT|INNER|RIGHT, keys...).",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
ASTTableJoin::Strictness strictness = ASTTableJoin::Strictness::Unspecified;
|
||||
ASTTableJoin::Kind kind = ASTTableJoin::Kind::Comma;
|
||||
|
||||
if (auto opt_strictness_id = tryGetIdentifierName(engine_args[0]))
|
||||
{
|
||||
const String strictness_str = Poco::toLower(*opt_strictness_id);
|
||||
|
||||
if (strictness_str == "any" || strictness_str == "\'any\'")
|
||||
{
|
||||
if (old_any_join)
|
||||
strictness = ASTTableJoin::Strictness::RightAny;
|
||||
else
|
||||
strictness = ASTTableJoin::Strictness::Any;
|
||||
}
|
||||
else if (strictness_str == "all" || strictness_str == "\'all\'")
|
||||
strictness = ASTTableJoin::Strictness::All;
|
||||
else if (strictness_str == "semi" || strictness_str == "\'semi\'")
|
||||
strictness = ASTTableJoin::Strictness::Semi;
|
||||
else if (strictness_str == "anti" || strictness_str == "\'anti\'")
|
||||
strictness = ASTTableJoin::Strictness::Anti;
|
||||
}
|
||||
|
||||
if (strictness == ASTTableJoin::Strictness::Unspecified)
|
||||
throw Exception("First parameter of storage Join must be ANY or ALL or SEMI or ANTI.", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (auto opt_kind_id = tryGetIdentifierName(engine_args[1]))
|
||||
{
|
||||
const String kind_str = Poco::toLower(*opt_kind_id);
|
||||
|
||||
if (kind_str == "left" || kind_str == "\'left\'")
|
||||
kind = ASTTableJoin::Kind::Left;
|
||||
else if (kind_str == "inner" || kind_str == "\'inner\'")
|
||||
kind = ASTTableJoin::Kind::Inner;
|
||||
else if (kind_str == "right" || kind_str == "\'right\'")
|
||||
kind = ASTTableJoin::Kind::Right;
|
||||
else if (kind_str == "full" || kind_str == "\'full\'")
|
||||
{
|
||||
if (strictness == ASTTableJoin::Strictness::Any)
|
||||
strictness = ASTTableJoin::Strictness::RightAny;
|
||||
kind = ASTTableJoin::Kind::Full;
|
||||
}
|
||||
}
|
||||
|
||||
if (kind == ASTTableJoin::Kind::Comma)
|
||||
throw Exception("Second parameter of storage Join must be LEFT or INNER or RIGHT or FULL.", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
Names key_names;
|
||||
key_names.reserve(engine_args.size() - 2);
|
||||
for (size_t i = 2, size = engine_args.size(); i < size; ++i)
|
||||
{
|
||||
auto opt_key = tryGetIdentifierName(engine_args[i]);
|
||||
if (!opt_key)
|
||||
throw Exception("Parameter №" + toString(i + 1) + " of storage Join don't look like column name.", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
key_names.push_back(*opt_key);
|
||||
}
|
||||
|
||||
return StorageJoin::create(
|
||||
args.relative_data_path,
|
||||
args.database_name,
|
||||
@ -246,8 +266,8 @@ protected:
|
||||
|
||||
Block block;
|
||||
if (!joinDispatch(parent.kind, parent.strictness, parent.data->maps,
|
||||
[&](auto, auto strictness, auto & map) { block = createBlock<strictness>(map); }))
|
||||
throw Exception("Logical error: unknown JOIN strictness (must be ANY or ALL)", ErrorCodes::LOGICAL_ERROR);
|
||||
[&](auto kind, auto strictness, auto & map) { block = createBlock<kind, strictness>(map); }))
|
||||
throw Exception("Logical error: unknown JOIN strictness", ErrorCodes::LOGICAL_ERROR);
|
||||
return block;
|
||||
}
|
||||
|
||||
@ -265,7 +285,7 @@ private:
|
||||
std::unique_ptr<void, std::function<void(void *)>> position; /// type erasure
|
||||
|
||||
|
||||
template <ASTTableJoin::Strictness STRICTNESS, typename Maps>
|
||||
template <ASTTableJoin::Kind KIND, ASTTableJoin::Strictness STRICTNESS, typename Maps>
|
||||
Block createBlock(const Maps & maps)
|
||||
{
|
||||
for (size_t i = 0; i < sample_block.columns(); ++i)
|
||||
@ -292,7 +312,7 @@ private:
|
||||
{
|
||||
#define M(TYPE) \
|
||||
case Join::Type::TYPE: \
|
||||
rows_added = fillColumns<STRICTNESS>(*maps.TYPE); \
|
||||
rows_added = fillColumns<KIND, STRICTNESS>(*maps.TYPE); \
|
||||
break;
|
||||
APPLY_FOR_JOIN_VARIANTS_LIMITED(M)
|
||||
#undef M
|
||||
@ -323,8 +343,7 @@ private:
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
template <ASTTableJoin::Strictness STRICTNESS, typename Map>
|
||||
template <ASTTableJoin::Kind KIND, ASTTableJoin::Strictness STRICTNESS, typename Map>
|
||||
size_t fillColumns(const Map & map)
|
||||
{
|
||||
size_t rows_added = 0;
|
||||
@ -341,34 +360,35 @@ private:
|
||||
{
|
||||
if constexpr (STRICTNESS == ASTTableJoin::Strictness::RightAny)
|
||||
{
|
||||
for (size_t j = 0; j < columns.size(); ++j)
|
||||
if (j == key_pos)
|
||||
columns[j]->insertData(rawData(it->getKey()), rawSize(it->getKey()));
|
||||
else
|
||||
columns[j]->insertFrom(*it->getMapped().block->getByPosition(column_indices[j]).column.get(), it->getMapped().row_num);
|
||||
++rows_added;
|
||||
fillOne<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
}
|
||||
else if constexpr (STRICTNESS == ASTTableJoin::Strictness::All)
|
||||
{
|
||||
fillAll<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
}
|
||||
else if constexpr (STRICTNESS == ASTTableJoin::Strictness::Any)
|
||||
{
|
||||
throw Exception("New ANY join storage is not implemented yet (set any_join_distinct_right_table_keys=1 to use old one)",
|
||||
ErrorCodes::NOT_IMPLEMENTED);
|
||||
if constexpr (KIND == ASTTableJoin::Kind::Left || KIND == ASTTableJoin::Kind::Inner)
|
||||
fillOne<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
else if constexpr (KIND == ASTTableJoin::Kind::Right)
|
||||
fillAll<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
}
|
||||
else if constexpr (STRICTNESS == ASTTableJoin::Strictness::Asof ||
|
||||
STRICTNESS == ASTTableJoin::Strictness::Semi ||
|
||||
STRICTNESS == ASTTableJoin::Strictness::Anti)
|
||||
else if constexpr (STRICTNESS == ASTTableJoin::Strictness::Semi)
|
||||
{
|
||||
throw Exception("ASOF|SEMI|ANTI join storage is not implemented yet", ErrorCodes::NOT_IMPLEMENTED);
|
||||
if constexpr (KIND == ASTTableJoin::Kind::Left)
|
||||
fillOne<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
else if constexpr (KIND == ASTTableJoin::Kind::Right)
|
||||
fillAll<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
}
|
||||
else if constexpr (STRICTNESS == ASTTableJoin::Strictness::Anti)
|
||||
{
|
||||
if constexpr (KIND == ASTTableJoin::Kind::Left)
|
||||
fillOne<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
else if constexpr (KIND == ASTTableJoin::Kind::Right)
|
||||
fillAll<Map>(columns, column_indices, it, key_pos, rows_added);
|
||||
}
|
||||
else
|
||||
for (auto ref_it = it->getMapped().begin(); ref_it.ok(); ++ref_it)
|
||||
{
|
||||
for (size_t j = 0; j < columns.size(); ++j)
|
||||
if (j == key_pos)
|
||||
columns[j]->insertData(rawData(it->getKey()), rawSize(it->getKey()));
|
||||
else
|
||||
columns[j]->insertFrom(*ref_it->block->getByPosition(column_indices[j]).column.get(), ref_it->row_num);
|
||||
++rows_added;
|
||||
}
|
||||
throw Exception("This JOIN is not implemented yet", ErrorCodes::NOT_IMPLEMENTED);
|
||||
|
||||
if (rows_added >= max_block_size)
|
||||
{
|
||||
@ -379,6 +399,33 @@ private:
|
||||
|
||||
return rows_added;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
static void fillOne(MutableColumns & columns, const ColumnNumbers & column_indices, typename Map::const_iterator & it,
|
||||
const std::optional<size_t> & key_pos, size_t & rows_added)
|
||||
{
|
||||
for (size_t j = 0; j < columns.size(); ++j)
|
||||
if (j == key_pos)
|
||||
columns[j]->insertData(rawData(it->getKey()), rawSize(it->getKey()));
|
||||
else
|
||||
columns[j]->insertFrom(*it->getMapped().block->getByPosition(column_indices[j]).column.get(), it->getMapped().row_num);
|
||||
++rows_added;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
static void fillAll(MutableColumns & columns, const ColumnNumbers & column_indices, typename Map::const_iterator & it,
|
||||
const std::optional<size_t> & key_pos, size_t & rows_added)
|
||||
{
|
||||
for (auto ref_it = it->getMapped().begin(); ref_it.ok(); ++ref_it)
|
||||
{
|
||||
for (size_t j = 0; j < columns.size(); ++j)
|
||||
if (j == key_pos)
|
||||
columns[j]->insertData(rawData(it->getKey()), rawSize(it->getKey()));
|
||||
else
|
||||
columns[j]->insertFrom(*ref_it->block->getByPosition(column_indices[j]).column.get(), ref_it->row_num);
|
||||
++rows_added;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -440,3 +440,42 @@ def test_moves_after_merges_work(started_cluster, name, engine, positive):
|
||||
|
||||
finally:
|
||||
node1.query("DROP TABLE IF EXISTS {}".format(name))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name,engine,positive", [
|
||||
("mt_test_moves_after_merges_do_not_work","MergeTree()",0),
|
||||
("replicated_mt_test_moves_after_merges_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_do_not_work', '1')",0),
|
||||
("mt_test_moves_after_merges_work","MergeTree()",1),
|
||||
("replicated_mt_test_moves_after_merges_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_work', '1')",1),
|
||||
])
|
||||
def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive):
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
s1 String,
|
||||
d1 DateTime
|
||||
) ENGINE = {engine}
|
||||
ORDER BY tuple()
|
||||
TTL d1 TO DISK 'external'
|
||||
SETTINGS storage_policy='small_jbod_with_external'
|
||||
""".format(name=name, engine=engine))
|
||||
|
||||
if positive:
|
||||
node1.query("""
|
||||
ALTER TABLE {name}
|
||||
MODIFY TTL
|
||||
d1 + INTERVAL 15 MINUTE
|
||||
""".format(name=name)) # That shall disable TTL.
|
||||
|
||||
data = [] # 10MB in total
|
||||
for i in range(10):
|
||||
data.append(("'{}'".format(get_random_string(1024 * 1024)), "toDateTime({})".format(time.time()-1))) # 1MB row
|
||||
node1.query("INSERT INTO {} (s1, d1) VALUES {}".format(name, ",".join(["(" + ",".join(x) + ")" for x in data])))
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"jbod1" if positive else "external"}
|
||||
|
||||
assert node1.query("SELECT count() FROM {name}".format(name=name)).strip() == "10"
|
||||
|
||||
finally:
|
||||
node1.query("DROP TABLE IF EXISTS {}".format(name))
|
||||
|
@ -15,8 +15,8 @@
|
||||
1.0000 1.0000 0.33333333 0.33333333 0.20000000 0.20000000
|
||||
50.0000 50.0000 16.66666666 16.66666666 10.00000000 10.00000000
|
||||
-1.0000 -1.0000 -0.33333333 -0.33333333 -0.20000000 -0.20000000
|
||||
0.0000 0.00000000 0.00000000 Decimal(38, 8)
|
||||
-25.5000 -8.49999999 -5.10000000 Decimal(38, 8)
|
||||
0.0000 0.00000000 0.00000000 Decimal128(8)
|
||||
-25.5000 -8.49999999 -5.10000000 Decimal128(8)
|
||||
0.0000 0.00000000 0.00000000
|
||||
10.0000 3.33333333 2.00000000
|
||||
20.0000 6.66666666 4.00000000
|
||||
@ -26,8 +26,8 @@
|
||||
[-50.0000,-40.0000,-30.0000,-20.0000,-10.0000,0.0000,10.0000,20.0000,30.0000,40.0000,50.0000]
|
||||
[-16.66666666,-13.33333333,-10.00000000,-6.66666666,-3.33333333,0.00000000,3.33333333,6.66666666,10.00000000,13.33333333,16.66666666]
|
||||
[-10.00000000,-8.00000000,-6.00000000,-4.00000000,-2.00000000,0.00000000,2.00000000,4.00000000,6.00000000,8.00000000,10.00000000]
|
||||
0.0000 0.00000000 0.00000000 Decimal(38, 8)
|
||||
-25.0000 -8.33333333 -5.00000000 Decimal(38, 8)
|
||||
0.0000 0.00000000 0.00000000 Decimal128(8)
|
||||
-25.0000 -8.33333333 -5.00000000 Decimal128(8)
|
||||
0.0000 0.00000000 0.00000000
|
||||
10.0000 3.33333333 2.00000000
|
||||
20.0000 6.66666666 4.00000000
|
||||
@ -37,8 +37,8 @@
|
||||
[-50.0000,-40.0000,-30.0000,-20.0000,-10.0000,0.0000,10.0000,20.0000,30.0000,40.0000,50.0000]
|
||||
[-16.66666666,-13.33333333,-10.00000000,-6.66666666,-3.33333333,0.00000000,3.33333333,6.66666666,10.00000000,13.33333333,16.66666666]
|
||||
[-10.00000000,-8.00000000,-6.00000000,-4.00000000,-2.00000000,0.00000000,2.00000000,4.00000000,6.00000000,8.00000000,10.00000000]
|
||||
0.0000 0.00000000 0.00000000 Decimal(38, 8)
|
||||
-26.0000 -8.66666666 -5.20000000 Decimal(38, 8)
|
||||
0.0000 0.00000000 0.00000000 Decimal128(8)
|
||||
-26.0000 -8.66666666 -5.20000000 Decimal128(8)
|
||||
0.0000 0.00000000 0.00000000
|
||||
10.0000 3.33333333 2.00000000
|
||||
20.0000 6.66666666 4.00000000
|
||||
|
@ -1,8 +1,8 @@
|
||||
Array(Decimal(9, 3)) Array(Decimal(18, 3)) Array(Decimal(38, 3))
|
||||
Array(Decimal32(3)) Array(Decimal64(3)) Array(Decimal128(3))
|
||||
Array(Decimal(9, 2)) Array(Decimal(18, 2)) Array(Decimal(38, 2))
|
||||
Decimal(9, 3) Decimal(18, 3) Decimal(38, 3)
|
||||
Decimal32(3) Decimal64(3) Decimal128(3)
|
||||
Decimal(9, 2) Decimal(18, 2) Decimal(38, 2)
|
||||
Tuple(Decimal(9, 1), Decimal(18, 1), Decimal(38, 1)) Decimal(9, 1) Decimal(18, 1) Decimal(38, 1)
|
||||
Tuple(Decimal32(1), Decimal64(1), Decimal128(1)) Decimal32(1) Decimal64(1) Decimal128(1)
|
||||
0.100
|
||||
0.200
|
||||
0.300
|
||||
|
@ -1,6 +1,6 @@
|
||||
a Decimal(9, 4) DEFAULT CAST(0, \'Decimal(9, 4)\')
|
||||
b Decimal(18, 4) DEFAULT CAST(a / 2, \'Decimal(18, 4)\')
|
||||
c Decimal(38, 4) DEFAULT CAST(b / 3, \'Decimal(38, 4)\')
|
||||
a DECIMAL(9, 4) DEFAULT CAST(0, \'DECIMAL(9, 4)\')
|
||||
b DECIMAL(18, 4) DEFAULT CAST(a / 2, \'DECIMAL(18, 4)\')
|
||||
c DECIMAL(38, 4) DEFAULT CAST(b / 3, \'DECIMAL(38, 4)\')
|
||||
d Decimal(9, 4) MATERIALIZED a + toDecimal32(\'0.2\', 1)
|
||||
e Decimal(18, 4) ALIAS b * 2
|
||||
f Decimal(38, 4) ALIAS c * 6
|
||||
|
@ -9,7 +9,7 @@
|
||||
10003
|
||||
274972506.6
|
||||
9175437371954010821
|
||||
CREATE TABLE default.compression_codec_multiple_more_types (`id` Decimal(38, 13) CODEC(ZSTD(1), LZ4, ZSTD(1), ZSTD(1), Delta(2), Delta(4), Delta(1), LZ4HC(0)), `data` FixedString(12) CODEC(ZSTD(1), ZSTD(1), Delta(1), Delta(1), Delta(1), NONE, NONE, NONE, LZ4HC(0)), `ddd.age` Array(UInt8) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8)), `ddd.Name` Array(String) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8))) ENGINE = MergeTree() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
CREATE TABLE default.compression_codec_multiple_more_types (`id` Decimal128(13) CODEC(ZSTD(1), LZ4, ZSTD(1), ZSTD(1), Delta(2), Delta(4), Delta(1), LZ4HC(0)), `data` FixedString(12) CODEC(ZSTD(1), ZSTD(1), Delta(1), Delta(1), Delta(1), NONE, NONE, NONE, LZ4HC(0)), `ddd.age` Array(UInt8) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8)), `ddd.Name` Array(String) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8))) ENGINE = MergeTree() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
1.5555555555555 hello world! [77] ['John']
|
||||
7.1000000000000 xxxxxxxxxxxx [127] ['Henry']
|
||||
!
|
||||
|
@ -20,7 +20,7 @@
|
||||
274972506.6
|
||||
9175437371954010821
|
||||
9175437371954010821
|
||||
CREATE TABLE test.compression_codec_multiple_more_types_replicated (`id` Decimal(38, 13) CODEC(ZSTD(1), LZ4, ZSTD(1), ZSTD(1), Delta(2), Delta(4), Delta(1), LZ4HC(0)), `data` FixedString(12) CODEC(ZSTD(1), ZSTD(1), Delta(1), Delta(1), Delta(1), NONE, NONE, NONE, LZ4HC(0)), `ddd.age` Array(UInt8) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8)), `ddd.Name` Array(String) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8))) ENGINE = ReplicatedMergeTree(\'/clickhouse/tables/test/compression_codec_multiple_more_types_replicated\', \'1\') ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
CREATE TABLE test.compression_codec_multiple_more_types_replicated (`id` Decimal128(13) CODEC(ZSTD(1), LZ4, ZSTD(1), ZSTD(1), Delta(2), Delta(4), Delta(1), LZ4HC(0)), `data` FixedString(12) CODEC(ZSTD(1), ZSTD(1), Delta(1), Delta(1), Delta(1), NONE, NONE, NONE, LZ4HC(0)), `ddd.age` Array(UInt8) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8)), `ddd.Name` Array(String) CODEC(LZ4, LZ4HC(0), NONE, NONE, NONE, ZSTD(1), Delta(8))) ENGINE = ReplicatedMergeTree(\'/clickhouse/tables/test/compression_codec_multiple_more_types_replicated\', \'1\') ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
1.5555555555555 hello world! [77] ['John']
|
||||
7.1000000000000 xxxxxxxxxxxx [127] ['Henry']
|
||||
!
|
||||
|
@ -18,7 +18,7 @@
|
||||
7 7
|
||||
8 8
|
||||
9 9
|
||||
SimpleAggregateFunction(sum, Float64)
|
||||
SimpleAggregateFunction(sum, Double)
|
||||
0 0
|
||||
1 2
|
||||
2 4
|
||||
|
@ -1,5 +1,5 @@
|
||||
CREATE TABLE default.ttl (`d` Date, `a` Int32) ENGINE = MergeTree PARTITION BY toDayOfMonth(d) ORDER BY a TTL d + toIntervalDay(1) SETTINGS index_granularity = 8192
|
||||
CREATE TABLE default.ttl (`d` Date, `a` Int) ENGINE = MergeTree PARTITION BY toDayOfMonth(d) ORDER BY a TTL d + toIntervalDay(1) SETTINGS index_granularity = 8192
|
||||
2100-10-10 3
|
||||
2100-10-10 4
|
||||
d Date
|
||||
a Int32 d + toIntervalDay(1)
|
||||
a Int d + toIntervalDay(1)
|
||||
|
@ -6,11 +6,11 @@
|
||||
2000-10-10 00:00:00 0
|
||||
2100-10-10 00:00:00 3
|
||||
2100-10-10 2
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int32, `a` Int32 TTL now() - 1000) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int, `a` Int TTL now() - 1000) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
1 0
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int32, `a` Int32 TTL now() + 1000) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int, `a` Int TTL now() + 1000) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
1 1
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int32, `a` Int32 TTL today() - 1) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int, `a` Int TTL today() - 1) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
1 0
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int32, `a` Int32 TTL today() + 1) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
CREATE TABLE default.ttl_00933_1 (`b` Int, `a` Int TTL today() + 1) ENGINE = MergeTree PARTITION BY tuple() ORDER BY tuple() SETTINGS index_granularity = 8192
|
||||
1 1
|
||||
|
@ -1,24 +1,26 @@
|
||||
DROP TABLE IF EXISTS testJoinTable;
|
||||
|
||||
CREATE TABLE testJoinTable (number UInt64, data String) ENGINE = Join(ANY, INNER, number);
|
||||
SET any_join_distinct_right_table_keys = 1;
|
||||
SET enable_optimize_predicate_expression = 0;
|
||||
|
||||
CREATE TABLE testJoinTable (number UInt64, data String) ENGINE = Join(ANY, INNER, number) SETTINGS any_join_distinct_right_table_keys = 1;
|
||||
|
||||
INSERT INTO testJoinTable VALUES (1, '1'), (2, '2'), (3, '3');
|
||||
|
||||
SELECT * FROM (SELECT * FROM numbers(10)) INNER JOIN testJoinTable USING number;
|
||||
SELECT * FROM (SELECT * FROM numbers(10)) INNER JOIN testJoinTable USING number; -- { serverError 264 }
|
||||
SELECT * FROM (SELECT * FROM numbers(10)) INNER JOIN (SELECT * FROM testJoinTable) USING number;
|
||||
SELECT * FROM (SELECT * FROM numbers(10)) ANY INNER JOIN testJoinTable USING number;
|
||||
SELECT * FROM testJoinTable;
|
||||
|
||||
DROP TABLE testJoinTable;
|
||||
|
||||
SELECT '-';
|
||||
|
||||
SET any_join_distinct_right_table_keys = 1;
|
||||
|
||||
DROP TABLE IF EXISTS master;
|
||||
DROP TABLE IF EXISTS transaction;
|
||||
|
||||
CREATE TABLE transaction (id Int32, value Float64, master_id Int32) ENGINE = MergeTree() ORDER BY id;
|
||||
CREATE TABLE master (id Int32, name String) ENGINE = Join (ANY, LEFT, id);
|
||||
CREATE TABLE master (id Int32, name String) ENGINE = Join (ANY, LEFT, id) SETTINGS any_join_distinct_right_table_keys = 1;
|
||||
|
||||
INSERT INTO master VALUES (1, 'ONE');
|
||||
INSERT INTO transaction VALUES (1, 52.5, 1);
|
||||
@ -34,7 +36,7 @@ DROP TABLE IF EXISTS some_join;
|
||||
DROP TABLE IF EXISTS tbl;
|
||||
|
||||
CREATE TABLE tbl (eventDate Date, id String) ENGINE = MergeTree() PARTITION BY tuple() ORDER BY eventDate;
|
||||
CREATE TABLE some_join (id String, value String) ENGINE = Join(ANY, LEFT, id);
|
||||
CREATE TABLE some_join (id String, value String) ENGINE = Join(ANY, LEFT, id) SETTINGS any_join_distinct_right_table_keys = 1;
|
||||
|
||||
SELECT * FROM tbl AS t ANY LEFT JOIN some_join USING (id);
|
||||
SELECT * FROM tbl AS t ANY LEFT JOIN some_join AS d USING (id);
|
||||
|
@ -0,0 +1,7 @@
|
||||
1 1 1 1 1
|
||||
2 2 2 2 2
|
||||
3 3 3 3 3
|
||||
-
|
||||
1 1 1 1 1
|
||||
2 2 2 2 2
|
||||
3 3 3 3 3
|
@ -0,0 +1,23 @@
|
||||
DROP TABLE IF EXISTS a;
|
||||
DROP TABLE IF EXISTS b;
|
||||
DROP TABLE IF EXISTS id1;
|
||||
DROP TABLE IF EXISTS id2;
|
||||
|
||||
CREATE TABLE a(`id1` UInt32, `id2` UInt32, `valA` UInt32) ENGINE = TinyLog;
|
||||
CREATE TABLE id1(`id1` UInt32, `val1` UInt8) ENGINE = Join(ANY, LEFT, id1);
|
||||
CREATE TABLE id2(`id2` UInt32, `val2` UInt8) ENGINE = Join(ANY, LEFT, id2);
|
||||
|
||||
INSERT INTO a VALUES (1,1,1)(2,2,2)(3,3,3);
|
||||
INSERT INTO id1 VALUES (1,1)(2,2)(3,3);
|
||||
INSERT INTO id2 VALUES (1,1)(2,2)(3,3);
|
||||
|
||||
SELECT * from (SELECT * FROM a ANY LEFT OUTER JOIN id1 USING id1) ANY LEFT OUTER JOIN id2 USING id2;
|
||||
|
||||
create view b as (SELECT * from (SELECT * FROM a ANY LEFT OUTER JOIN id1 USING id1) ANY LEFT OUTER JOIN id2 USING id2);
|
||||
SELECT '-';
|
||||
SELECT * FROM b;
|
||||
|
||||
DROP TABLE a;
|
||||
DROP TABLE b;
|
||||
DROP TABLE id1;
|
||||
DROP TABLE id2;
|
@ -0,0 +1,38 @@
|
||||
binary BINARY(1)
|
||||
dec DEC(1, 1)
|
||||
tinyint TINYINT
|
||||
smallint SMALLINT
|
||||
int INT
|
||||
integer INTEGER
|
||||
bigint BIGINT
|
||||
float FLOAT
|
||||
double DOUBLE
|
||||
char CHAR
|
||||
varchar VARCHAR
|
||||
text TEXT
|
||||
tinytext TINYTEXT
|
||||
mediumtext MEDIUMTEXT
|
||||
longtext LONGTEXT
|
||||
blob BLOB
|
||||
tinyblob TINYBLOB
|
||||
mediumblob MEDIUMBLOB
|
||||
longblob LONGBLOB
|
||||
binary BINARY(1)
|
||||
dec DEC(1, 1)
|
||||
tinyint TINYINT
|
||||
smallint SMALLINT
|
||||
int INT
|
||||
integer INTEGER
|
||||
bigint BIGINT
|
||||
float FLOAT
|
||||
double DOUBLE
|
||||
char CHAR
|
||||
varchar VARCHAR
|
||||
text TEXT
|
||||
tinytext TINYTEXT
|
||||
mediumtext MEDIUMTEXT
|
||||
longtext LONGTEXT
|
||||
blob BLOB
|
||||
tinyblob TINYBLOB
|
||||
mediumblob MEDIUMBLOB
|
||||
longblob LONGBLOB
|
@ -0,0 +1,32 @@
|
||||
DROP TABLE IF EXISTS test_type_alias;
|
||||
|
||||
CREATE TABLE test_type_alias(
|
||||
binary BINARY(1),
|
||||
dec DEC(1, 1),
|
||||
tinyint TINYINT,
|
||||
smallint SMALLINT,
|
||||
int INT,
|
||||
integer INTEGER,
|
||||
bigint BIGINT,
|
||||
float FLOAT,
|
||||
double DOUBLE,
|
||||
char CHAR,
|
||||
varchar VARCHAR,
|
||||
text TEXT,
|
||||
tinytext TINYTEXT,
|
||||
mediumtext MEDIUMTEXT,
|
||||
longtext LONGTEXT,
|
||||
blob BLOB,
|
||||
tinyblob TINYBLOB,
|
||||
mediumblob MEDIUMBLOB,
|
||||
longblob LONGBLOB
|
||||
) ENGINE = Memory;
|
||||
|
||||
DESC test_type_alias;
|
||||
|
||||
DETACH TABLE test_type_alias;
|
||||
ATTACH TABLE test_type_alias;
|
||||
|
||||
DESC test_type_alias;
|
||||
|
||||
DROP TABLE IF EXISTS test_type_alias;
|
@ -0,0 +1,32 @@
|
||||
any left
|
||||
0 a1
|
||||
1 a2
|
||||
2 a3 b1
|
||||
3 a4
|
||||
4 a5 b3
|
||||
any inner
|
||||
2 a3 b1
|
||||
4 a5 b3
|
||||
any right
|
||||
2 a3 b1
|
||||
2 a3 b2
|
||||
4 a5 b3
|
||||
4 a5 b4
|
||||
4 a5 b5
|
||||
5 b6
|
||||
semi left
|
||||
2 a3 b1
|
||||
2 a6 b1
|
||||
4 a5 b3
|
||||
semi right
|
||||
2 a3 b1
|
||||
2 a3 b2
|
||||
4 a5 b3
|
||||
4 a5 b4
|
||||
4 a5 b5
|
||||
anti left
|
||||
0 a1
|
||||
1 a2
|
||||
3 a4
|
||||
anti right
|
||||
5 b6
|
72
dbms/tests/queries/0_stateless/01051_new_any_join_engine.sql
Normal file
72
dbms/tests/queries/0_stateless/01051_new_any_join_engine.sql
Normal file
@ -0,0 +1,72 @@
|
||||
DROP TABLE IF EXISTS t1;
|
||||
|
||||
DROP TABLE IF EXISTS any_left_join;
|
||||
DROP TABLE IF EXISTS any_inner_join;
|
||||
DROP TABLE IF EXISTS any_right_join;
|
||||
DROP TABLE IF EXISTS any_full_join;
|
||||
|
||||
DROP TABLE IF EXISTS semi_left_join;
|
||||
DROP TABLE IF EXISTS semi_right_join;
|
||||
DROP TABLE IF EXISTS anti_left_join;
|
||||
DROP TABLE IF EXISTS anti_right_join;
|
||||
|
||||
CREATE TABLE t1 (x UInt32, str String) engine = Memory;
|
||||
|
||||
CREATE TABLE any_left_join (x UInt32, s String) engine = Join(ANY, LEFT, x);
|
||||
CREATE TABLE any_inner_join (x UInt32, s String) engine = Join(ANY, INNER, x);
|
||||
CREATE TABLE any_right_join (x UInt32, s String) engine = Join(ANY, RIGHT, x);
|
||||
|
||||
CREATE TABLE semi_left_join (x UInt32, s String) engine = Join(SEMI, LEFT, x);
|
||||
CREATE TABLE semi_right_join (x UInt32, s String) engine = Join(SEMI, RIGHT, x);
|
||||
|
||||
CREATE TABLE anti_left_join (x UInt32, s String) engine = Join(ANTI, LEFT, x);
|
||||
CREATE TABLE anti_right_join (x UInt32, s String) engine = Join(ANTI, RIGHT, x);
|
||||
|
||||
INSERT INTO t1 (x, str) VALUES (0, 'a1'), (1, 'a2'), (2, 'a3'), (3, 'a4'), (4, 'a5');
|
||||
|
||||
INSERT INTO any_left_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
INSERT INTO any_inner_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
INSERT INTO any_right_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
|
||||
INSERT INTO semi_left_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
INSERT INTO semi_right_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
INSERT INTO anti_left_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
INSERT INTO anti_right_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
|
||||
|
||||
SET join_use_nulls = 0;
|
||||
SET any_join_distinct_right_table_keys = 0;
|
||||
|
||||
SELECT 'any left';
|
||||
SELECT * FROM t1 ANY LEFT JOIN any_left_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
SELECT 'any inner';
|
||||
SELECT * FROM t1 ANY INNER JOIN any_inner_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
SELECT 'any right';
|
||||
SELECT * FROM t1 ANY RIGHT JOIN any_right_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
|
||||
INSERT INTO t1 (x, str) VALUES (2, 'a6');
|
||||
|
||||
SELECT 'semi left';
|
||||
SELECT * FROM t1 SEMI LEFT JOIN semi_left_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
SELECT 'semi right';
|
||||
SELECT * FROM t1 SEMI RIGHT JOIN semi_right_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
SELECT 'anti left';
|
||||
SELECT * FROM t1 ANTI LEFT JOIN anti_left_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
SELECT 'anti right';
|
||||
SELECT * FROM t1 ANTI RIGHT JOIN anti_right_join j USING(x) ORDER BY x, str, s;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
DROP TABLE any_left_join;
|
||||
DROP TABLE any_inner_join;
|
||||
DROP TABLE any_right_join;
|
||||
|
||||
DROP TABLE semi_left_join;
|
||||
DROP TABLE semi_right_join;
|
||||
DROP TABLE anti_left_join;
|
||||
DROP TABLE anti_right_join;
|
97
docker/test/performance-comparison/compare.sh
Executable file
97
docker/test/performance-comparison/compare.sh
Executable file
@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
set -o pipefail
|
||||
trap "exit" INT TERM
|
||||
trap "kill 0" EXIT
|
||||
|
||||
mkdir left ||:
|
||||
mkdir right ||:
|
||||
mkdir db0 ||:
|
||||
|
||||
left_pr=$1
|
||||
left_sha=$2
|
||||
|
||||
right_pr=$3
|
||||
right_sha=$4
|
||||
|
||||
function download
|
||||
{
|
||||
la="$left_pr-$left_sha.tgz"
|
||||
ra="$right_pr-$right_sha.tgz"
|
||||
wget -nd -c "https://clickhouse-builds.s3.yandex.net/$left_pr/$left_sha/performance/performance.tgz" -O "$la" && tar -C left --strip-components=1 -zxvf "$la" &
|
||||
wget -nd -c "https://clickhouse-builds.s3.yandex.net/$right_pr/$right_sha/performance/performance.tgz" -O "$ra" && tar -C right --strip-components=1 -zxvf "$ra" &
|
||||
cd db0 && wget -nd -c "https://s3.mds.yandex.net/clickhouse-private-datasets/hits_10m_single/partitions/hits_10m_single.tar" && tar -xvf hits_10m_single.tar &
|
||||
cd db0 && wget -nd -c "https://s3.mds.yandex.net/clickhouse-private-datasets/hits_100m_single/partitions/hits_100m_single.tar" && tar -xvf hits_100m_single.tar &
|
||||
#cd db0 && wget -nd -c "https://clickhouse-datasets.s3.yandex.net/hits/partitions/hits_v1.tar" && tar -xvf hits_v1.tar &
|
||||
wait
|
||||
|
||||
# Use hardlinks instead of copying
|
||||
rm -r left/db ||:
|
||||
rm -r right/db ||:
|
||||
cp -al db0/ left/db/
|
||||
cp -al db0/ right/db/
|
||||
}
|
||||
|
||||
#download
|
||||
|
||||
function configure
|
||||
{
|
||||
sed -i 's/<tcp_port>9000/<tcp_port>9001/g' right/config/config.xml
|
||||
|
||||
cat > right/config/config.d/perf-test-tweaks.xml <<EOF
|
||||
<yandex>
|
||||
<logger>
|
||||
<console>true</console>
|
||||
</logger>
|
||||
<text_log remove="remove"/>
|
||||
</yandex>
|
||||
EOF
|
||||
|
||||
cp right/config/config.d/perf-test-tweaks.xml left/config/config.d/perf-test-tweaks.xml
|
||||
}
|
||||
|
||||
configure
|
||||
|
||||
|
||||
function restart
|
||||
{
|
||||
while killall clickhouse ; do echo . ; sleep 1 ; done
|
||||
echo all killed
|
||||
|
||||
# Spawn servers in their own process groups
|
||||
set -m
|
||||
|
||||
left/clickhouse server --config-file=left/config/config.xml -- --path left/db &> left/log.txt &
|
||||
left_pid=$!
|
||||
kill -0 $left_pid
|
||||
disown $left_pid
|
||||
|
||||
right/clickhouse server --config-file=right/config/config.xml -- --path right/db &> right/log.txt &
|
||||
right_pid=$!
|
||||
kill -0 $right_pid
|
||||
disown $right_pid
|
||||
|
||||
set +m
|
||||
|
||||
while ! left/clickhouse client --query "select 1" ; do kill -0 $left_pid ; echo . ; sleep 1 ; done
|
||||
echo left ok
|
||||
|
||||
while ! right/clickhouse client --port 9001 --query "select 1" ; do kill -0 $right_pid ; echo . ; sleep 1 ; done
|
||||
echo right ok
|
||||
}
|
||||
|
||||
restart
|
||||
|
||||
for test in ch/dbms/tests/performance/*.xml
|
||||
do
|
||||
test_name=$(basename $test ".xml")
|
||||
./perf.py "$test" > "$test_name-raw.tsv" || continue
|
||||
right/clickhouse local --file "$test_name-raw.tsv" --structure 'query text, run int, version UInt32, time float' --query "$(cat eqmed.sql)" > "$test_name-report.tsv"
|
||||
done
|
||||
|
||||
#while killall clickhouse ; do echo . ; sleep 1 ; done
|
||||
#echo ok
|
||||
|
||||
|
||||
|
||||
|
41
docker/test/performance-comparison/eqmed.sql
Normal file
41
docker/test/performance-comparison/eqmed.sql
Normal file
@ -0,0 +1,41 @@
|
||||
-- input is table(query text, run UInt32, version int, time float)
|
||||
select
|
||||
abs(diff_percent) > rd_quantiles_percent[3] fail,
|
||||
floor(original_medians_array.time_by_version[1], 4) m1,
|
||||
floor(original_medians_array.time_by_version[2], 4) m2,
|
||||
floor((m1 - m2) / m1, 3) diff_percent,
|
||||
arrayMap(x -> floor(x / m1, 3), rd.rd_quantiles) rd_quantiles_percent,
|
||||
query
|
||||
from
|
||||
(
|
||||
select query, quantiles(0.05, 0.5, 0.95)(abs(time_by_label[1] - time_by_label[2])) rd_quantiles -- quantiles of randomization distribution
|
||||
from
|
||||
(
|
||||
select query, virtual_run, groupArrayInsertAt(median_time, random_label) time_by_label -- make array 'random label' -> 'median time'
|
||||
from (
|
||||
select query, medianExact(time) median_time, virtual_run, random_label -- get median times, grouping by random label
|
||||
from (
|
||||
select *, toUInt32(rowNumberInBlock() % 2) random_label -- randomly relabel measurements
|
||||
from (
|
||||
select query, time, number virtual_run
|
||||
from table, numbers(1, 10000) -- duplicate input measurements into many virtual runs
|
||||
order by query, virtual_run, rand() -- for each virtual run, randomly reorder measurements
|
||||
) virtual_runs
|
||||
) relabeled
|
||||
group by query, virtual_run, random_label
|
||||
) virtual_medians
|
||||
group by query, virtual_run -- aggregate by random_label
|
||||
) virtual_medians_array
|
||||
group by query -- aggregate by virtual_run
|
||||
) rd,
|
||||
(
|
||||
select groupArrayInsertAt(median_time, version) time_by_version, query
|
||||
from
|
||||
(
|
||||
select medianExact(time) median_time, query, version
|
||||
from table group by query, version
|
||||
) original_medians
|
||||
group by query
|
||||
) original_medians_array
|
||||
where rd.query = original_medians_array.query
|
||||
order by fail desc, rd_quantiles_percent[3] asc;
|
87
docker/test/performance-comparison/perf.py
Executable file
87
docker/test/performance-comparison/perf.py
Executable file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import itertools
|
||||
import clickhouse_driver
|
||||
import xml.etree.ElementTree as et
|
||||
import argparse
|
||||
import pprint
|
||||
|
||||
parser = argparse.ArgumentParser(description='Run performance test.')
|
||||
parser.add_argument('file', metavar='FILE', type=argparse.FileType('r'), nargs=1, help='test description file')
|
||||
args = parser.parse_args()
|
||||
|
||||
tree = et.parse(args.file[0])
|
||||
root = tree.getroot()
|
||||
|
||||
# Check main metric
|
||||
main_metric = root.find('main_metric/*').tag
|
||||
if main_metric != 'min_time':
|
||||
raise Exception('Only the min_time main metric is supported. This test uses \'{}\''.format(main_metric))
|
||||
|
||||
# Open connections
|
||||
servers = [{'host': 'localhost', 'port': 9000, 'client_name': 'left'}, {'host': 'localhost', 'port': 9001, 'client_name': 'right'}]
|
||||
connections = [clickhouse_driver.Client(**server) for server in servers]
|
||||
|
||||
# Check tables that should exist
|
||||
tables = [e.text for e in root.findall('preconditions/table_exists')]
|
||||
if tables:
|
||||
for c in connections:
|
||||
tables_list = ", ".join("'{}'".format(t) for t in tables)
|
||||
res = c.execute("select t from values('t text', {}) anti join system.tables on database = currentDatabase() and name = t".format(tables_list))
|
||||
if res:
|
||||
raise Exception('Some tables are not found: {}'.format(res))
|
||||
|
||||
# Process substitutions
|
||||
subst_elems = root.findall('substitutions/substitution')
|
||||
|
||||
parameter_keys = [] # ['table', 'limit' ]
|
||||
parameter_value_arrays = [] # [['hits_100m', 'hits_10m'], ['1', '10']]
|
||||
parameter_combinations = [] # [{table: hits_100m, limit: 1}, ...]
|
||||
for se in subst_elems:
|
||||
parameter_keys.append(se.find('name').text)
|
||||
parameter_value_arrays.append([v.text for v in se.findall('values/value')])
|
||||
parameter_combinations = [dict(zip(parameter_keys, parameter_combination)) for parameter_combination in itertools.product(*parameter_value_arrays)]
|
||||
|
||||
def substitute_parameters(query_templates, parameter_combinations):
|
||||
return list(set([template.format(**parameters) for template, parameters in itertools.product(query_templates, parameter_combinations)]))
|
||||
|
||||
# Run drop queries, ignoring errors
|
||||
drop_query_templates = [q.text for q in root.findall('drop_query')]
|
||||
drop_queries = substitute_parameters(drop_query_templates, parameter_combinations)
|
||||
for c in connections:
|
||||
for q in drop_queries:
|
||||
try:
|
||||
c.execute(q)
|
||||
except:
|
||||
print("Error:", sys.exc_info()[0], file=sys.stderr)
|
||||
|
||||
# Run create queries
|
||||
create_query_templates = [q.text for q in root.findall('create_query')]
|
||||
create_queries = substitute_parameters(create_query_templates, parameter_combinations)
|
||||
for c in connections:
|
||||
for q in create_queries:
|
||||
c.execute(q)
|
||||
|
||||
# Run fill queries
|
||||
fill_query_templates = [q.text for q in root.findall('fill_query')]
|
||||
fill_queries = substitute_parameters(fill_query_templates, parameter_combinations)
|
||||
for c in connections:
|
||||
for q in fill_queries:
|
||||
c.execute(q)
|
||||
|
||||
# Run test queries
|
||||
test_query_templates = [q.text for q in root.findall('query')]
|
||||
test_queries = substitute_parameters(test_query_templates, parameter_combinations)
|
||||
|
||||
for q in test_queries:
|
||||
for run in range(0, 7):
|
||||
for conn_index, c in enumerate(connections):
|
||||
res = c.execute(q)
|
||||
print(q + '\t' + str(run) + '\t' + str(conn_index) + '\t' + str(c.last_query.elapsed))
|
||||
|
||||
# Run drop queries
|
||||
drop_query_templates = [q.text for q in root.findall('drop_query')]
|
||||
drop_queries = substitute_parameters(drop_query_templates, parameter_combinations)
|
||||
for c in connections:
|
||||
for q in drop_queries:
|
||||
c.execute(q)
|
@ -794,14 +794,11 @@ Synonym for ["arrayReverse"](#array_functions-arrayreverse)
|
||||
|
||||
## arrayFlatten {#arrayflatten}
|
||||
|
||||
Converts array of arrays to a flat array.
|
||||
Converts an array of arrays to a flat array.
|
||||
|
||||
Function:
|
||||
|
||||
- Applies for any depth of nested arrays, but all the elements should lay at the same level.
|
||||
|
||||
For example, the `[[[1]], [[2], [3]]]` array can be flattened, but the `[[1], [[2], [3]]]` array can't be flattened.
|
||||
|
||||
- Applies to any depth of nested arrays.
|
||||
- Does not change arrays that are already flat.
|
||||
|
||||
The flattened array contains all the elements from all source arrays.
|
||||
|
@ -152,15 +152,21 @@ SELECT geohashDecode('ezs42') AS res
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## geoToH3
|
||||
## geoToH3 {#geotoh3}
|
||||
|
||||
Calculates [H3](https://uber.github.io/h3/#/documentation/overview/introduction) point index `(lon, lat)` with specified resolution.
|
||||
Returns [H3](https://uber.github.io/h3/#/documentation/overview/introduction) point index `(lon, lat)` with specified resolution.
|
||||
|
||||
[H3](https://uber.github.io/h3/#/documentation/overview/introduction) is a geographical indexing system where Earth's surface divided into even hexagonal tiles. This system is hierarchical, i. e. each hexagon on the top level can be splitted into seven even but smaller ones and so on.
|
||||
|
||||
This index is used primarily for bucketing locations and other geospatial manipulations.
|
||||
|
||||
**Syntax**
|
||||
|
||||
```sql
|
||||
geoToH3(lon, lat, resolution)
|
||||
```
|
||||
|
||||
**Input values**
|
||||
**Parameters**
|
||||
|
||||
- `lon` — Longitude. Type: [Float64](../../data_types/float.md).
|
||||
- `lat` — Latitude. Type: [Float64](../../data_types/float.md).
|
||||
@ -171,13 +177,18 @@ geoToH3(lon, lat, resolution)
|
||||
- Hexagon index number.
|
||||
- 0 in case of error.
|
||||
|
||||
Type: [UInt64](../../data_types/int_uint.md).
|
||||
Type: `UInt64`.
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
```sql
|
||||
SELECT geoToH3(37.79506683, 55.71290588, 15) as h3Index
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
┌────────────h3Index─┐
|
||||
│ 644325524701193974 │
|
||||
|
@ -379,17 +379,84 @@ Returns the ordinal number of the row in the data block. Different data blocks a
|
||||
|
||||
Returns the ordinal number of the row in the data block. This function only considers the affected data blocks.
|
||||
|
||||
## neighbor(column, offset\[, default_value\])
|
||||
## neighbor {#neighbor}
|
||||
|
||||
Returns value for `column`, in `offset` distance from current row.
|
||||
This function is a partial implementation of [window functions](https://en.wikipedia.org/wiki/SQL_window_function) LEAD() and LAG().
|
||||
The window function that provides access to a row at a specified offset which comes before or after the current row of a given column.
|
||||
|
||||
**Syntax**
|
||||
|
||||
```sql
|
||||
neighbor(column, offset[, default_value])
|
||||
```
|
||||
|
||||
The result of the function depends on the affected data blocks and the order of data in the block.
|
||||
If you make a subquery with ORDER BY and call the function from outside the subquery, you can get the expected result.
|
||||
|
||||
If `offset` value is outside block bounds, a default value for `column` returned. If `default_value` is given, then it will be used.
|
||||
**Parameters**
|
||||
|
||||
- `column` — A column name or scalar expression.
|
||||
- `offset` — The number of rows forwards or backwards from the current row of `column`. [Int64](../../data_types/int_uint.md).
|
||||
- `default_value` — Optional. The value to be returned if offset goes beyond the scope of the block. Type of data blocks affected.
|
||||
|
||||
**Returned values**
|
||||
|
||||
- Value for `column` in `offset` distance from current row if `offset` value is not outside block bounds.
|
||||
- Default value for `column` if `offset` value is outside block bounds. If `default_value` is given, then it will be used.
|
||||
|
||||
Type: type of data blocks affected or default value type.
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
```sql
|
||||
SELECT number, neighbor(number, 2) FROM system.numbers LIMIT 10;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
┌─number─┬─neighbor(number, 2)─┐
|
||||
│ 0 │ 2 │
|
||||
│ 1 │ 3 │
|
||||
│ 2 │ 4 │
|
||||
│ 3 │ 5 │
|
||||
│ 4 │ 6 │
|
||||
│ 5 │ 7 │
|
||||
│ 6 │ 8 │
|
||||
│ 7 │ 9 │
|
||||
│ 8 │ 0 │
|
||||
│ 9 │ 0 │
|
||||
└────────┴─────────────────────┘
|
||||
```
|
||||
|
||||
Query:
|
||||
|
||||
```sql
|
||||
SELECT number, neighbor(number, 2, 999) FROM system.numbers LIMIT 10;
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
┌─number─┬─neighbor(number, 2, 999)─┐
|
||||
│ 0 │ 2 │
|
||||
│ 1 │ 3 │
|
||||
│ 2 │ 4 │
|
||||
│ 3 │ 5 │
|
||||
│ 4 │ 6 │
|
||||
│ 5 │ 7 │
|
||||
│ 6 │ 8 │
|
||||
│ 7 │ 9 │
|
||||
│ 8 │ 999 │
|
||||
│ 9 │ 999 │
|
||||
└────────┴──────────────────────────┘
|
||||
```
|
||||
|
||||
This function can be used to compute year-over-year metric value:
|
||||
|
||||
Query:
|
||||
|
||||
```sql
|
||||
WITH toDate('2018-01-01') AS start_date
|
||||
SELECT
|
||||
@ -400,6 +467,8 @@ SELECT
|
||||
FROM numbers(16)
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
┌──────month─┬─money─┬─prev_year─┬─year_over_year─┐
|
||||
│ 2018-01-01 │ 32 │ 0 │ 0 │
|
||||
@ -421,7 +490,6 @@ FROM numbers(16)
|
||||
└────────────┴───────┴───────────┴────────────────┘
|
||||
```
|
||||
|
||||
|
||||
## runningDifference(x) {#other_functions-runningdifference}
|
||||
|
||||
Calculates the difference between successive row values in the data block.
|
||||
|
@ -778,22 +778,6 @@ SELECT arrayReduce('uniqUpTo(3)', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## arrayFlatten(arr) {#array_functions-arrayflatten}
|
||||
|
||||
Функция `arrayFlatten` (или `flatten`) соеденит вложенные массивы и вернет массив из их элементов.
|
||||
|
||||
Пример:
|
||||
|
||||
```sql
|
||||
SELECT arrayFlatten([[1, 2, 3], [4, 5]])
|
||||
```
|
||||
|
||||
```text
|
||||
┌─arrayFlatten([[1, 2, 3], [4, 5]])─┐
|
||||
│ [1,2,3,4,5] │
|
||||
└───────────────────────────────────┘
|
||||
```
|
||||
|
||||
## arrayReverse(arr) {#array_functions-arrayreverse}
|
||||
|
||||
Возвращает массив того же размера, что и исходный массив, содержащий элементы в обратном порядке.
|
||||
@ -814,6 +798,41 @@ SELECT arrayReverse([1, 2, 3])
|
||||
Синоним для ["arrayReverse"](#array_functions-arrayreverse)
|
||||
|
||||
|
||||
## arrayFlatten {#arrayflatten}
|
||||
|
||||
Преобразует массив массивов в плоский массив.
|
||||
|
||||
Функция:
|
||||
|
||||
- Оперирует с массивами любой вложенности.
|
||||
- Не изменяет массив, если он уже плоский.
|
||||
|
||||
Результирующий массив содержит все элементы исходных массивов.
|
||||
|
||||
**Синтаксис**
|
||||
|
||||
```sql
|
||||
flatten(array_of_arrays)
|
||||
```
|
||||
|
||||
Синоним: `flatten`.
|
||||
|
||||
|
||||
**Параметры**
|
||||
|
||||
- `array_of_arrays` — [Массивов](../../data_types/array.md) массивов. Например, `[[1,2,3], [4,5]]`.
|
||||
|
||||
**Примеры**
|
||||
|
||||
```sql
|
||||
SELECT flatten([[[1]], [[2], [3]]])
|
||||
```
|
||||
```text
|
||||
┌─flatten(array(array([1]), array([2], [3])))─┐
|
||||
│ [1,2,3] │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## arrayCompact {#arraycompact}
|
||||
|
||||
Удаляет дубликаты из массива. Порядок результирующих значений определяется порядком в исходном массиве.
|
||||
|
@ -304,15 +304,21 @@ SELECT h3EdgeLengthM(15) as edgeLengthM
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## geoToH3
|
||||
## geoToH3 {#geotoh3}
|
||||
|
||||
Получает H3 индекс точки `(lon, lat)` с заданным разрешением
|
||||
Возвращает H3 индекс точки `(lon, lat)` с заданным разрешением.
|
||||
|
||||
[H3](https://uber.github.io/h3/#/documentation/overview/introduction) - это географическая система индексации, в которой поверхность Земли разделена на ровные шестиугольные плитки. Эта система иерархична, то есть каждый шестиугольник на верхнем уровне может быть разбит на семь еще более мелких и так далее.
|
||||
|
||||
H3 индекс используется в основном для определения местоположения с помощью карт и других геопространственных манипуляций.
|
||||
|
||||
**Синтаксис**
|
||||
|
||||
```sql
|
||||
geoToH3(lon, lat, resolution)
|
||||
```
|
||||
|
||||
**Входные значения**
|
||||
**Параметры**
|
||||
|
||||
- `lon` — географическая долгота. Тип данных — [Float64](../../data_types/float.md).
|
||||
- `lat` — географическая широта. Тип данных — [Float64](../../data_types/float.md).
|
||||
@ -327,9 +333,14 @@ geoToH3(lon, lat, resolution)
|
||||
|
||||
**Пример**
|
||||
|
||||
Запрос:
|
||||
|
||||
```sql
|
||||
SELECT geoToH3(37.79506683, 55.71290588, 15) as h3Index
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
```text
|
||||
┌────────────h3Index─┐
|
||||
│ 644325524701193974 │
|
||||
|
@ -357,16 +357,82 @@ SELECT
|
||||
## rowNumberInAllBlocks()
|
||||
Возвращает порядковый номер строки в блоке данных. Функция учитывает только задействованные блоки данных.
|
||||
|
||||
## neighbor(column, offset\[, default_value\])
|
||||
## neighbor {#neighbor}
|
||||
|
||||
Функция позволяет получить доступ к значению в колонке `column`, находящемуся на смещении `offset` относительно текущей строки.
|
||||
Является частичной реализацией [оконных функций](https://en.wikipedia.org/wiki/SQL_window_function) LEAD() и LAG().
|
||||
Функция позволяет получить доступ к значению в колонке `column`, находящемуся на смещении `offset` относительно текущей строки. Является частичной реализацией [оконных функций](https://en.wikipedia.org/wiki/SQL_window_function) `LEAD()` и `LAG()`.
|
||||
|
||||
Результат функции зависит от затронутых блоков данных и порядка данных в блоке.
|
||||
Если сделать подзапрос с ORDER BY и вызывать функцию извне подзапроса, можно будет получить ожидаемый результат.
|
||||
**Синтаксис**
|
||||
|
||||
Если значение `offset` выходит за пределы блока данных, то берётся значение по-умолчанию для колонки `column`. Если передан параметр `default_value`, то значение берётся из него.
|
||||
Например, эта функция может использоваться чтобы оценить year-over-year значение показателя:
|
||||
```sql
|
||||
neighbor(column, offset[, default_value])
|
||||
```
|
||||
|
||||
Результат функции зависит от затронутых блоков данных и порядка данных в блоке. Если сделать подзапрос с ORDER BY и вызывать функцию извне подзапроса, можно будет получить ожидаемый результат.
|
||||
|
||||
**Параметры**
|
||||
|
||||
- `column` — Имя столбца или скалярное выражение.
|
||||
- `offset` - Смещение от текущей строки `column`. [Int64](../../data_types/int_uint.md).
|
||||
- `default_value` - Опциональный параметр. Значение, которое будет возвращено, если смещение выходит за пределы блока данных.
|
||||
|
||||
**Возвращаемое значение**
|
||||
|
||||
- Значение `column` в смещении от текущей строки, если значение `offset` не выходит за пределы блока.
|
||||
- Значение по умолчанию для `column`, если значение `offset` выходит за пределы блока данных. Если передан параметр `default_value`, то значение берется из него.
|
||||
|
||||
Тип: зависит от данных в `column` или переданного значения по умолчанию в `default_value`.
|
||||
|
||||
**Пример**
|
||||
|
||||
Запрос:
|
||||
|
||||
```sql
|
||||
SELECT number, neighbor(number, 2) FROM system.numbers LIMIT 10;
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
```text
|
||||
┌─number─┬─neighbor(number, 2)─┐
|
||||
│ 0 │ 2 │
|
||||
│ 1 │ 3 │
|
||||
│ 2 │ 4 │
|
||||
│ 3 │ 5 │
|
||||
│ 4 │ 6 │
|
||||
│ 5 │ 7 │
|
||||
│ 6 │ 8 │
|
||||
│ 7 │ 9 │
|
||||
│ 8 │ 0 │
|
||||
│ 9 │ 0 │
|
||||
└────────┴─────────────────────┘
|
||||
```
|
||||
|
||||
Запрос:
|
||||
|
||||
```sql
|
||||
SELECT number, neighbor(number, 2, 999) FROM system.numbers LIMIT 10;
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
```text
|
||||
┌─number─┬─neighbor(number, 2, 999)─┐
|
||||
│ 0 │ 2 │
|
||||
│ 1 │ 3 │
|
||||
│ 2 │ 4 │
|
||||
│ 3 │ 5 │
|
||||
│ 4 │ 6 │
|
||||
│ 5 │ 7 │
|
||||
│ 6 │ 8 │
|
||||
│ 7 │ 9 │
|
||||
│ 8 │ 999 │
|
||||
│ 9 │ 999 │
|
||||
└────────┴──────────────────────────┘
|
||||
```
|
||||
|
||||
Эта функция может использоваться для оценки year-over-year значение показателя:
|
||||
|
||||
Запрос:
|
||||
|
||||
```sql
|
||||
WITH toDate('2018-01-01') AS start_date
|
||||
@ -378,6 +444,8 @@ SELECT
|
||||
FROM numbers(16)
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
```text
|
||||
┌──────month─┬─money─┬─prev_year─┬─year_over_year─┐
|
||||
│ 2018-01-01 │ 32 │ 0 │ 0 │
|
||||
|
Loading…
Reference in New Issue
Block a user