Merge remote-tracking branch 'upstream/master' into fix27

This commit is contained in:
proller 2019-12-26 18:21:04 +00:00
commit 08a6f33511
52 changed files with 962 additions and 212 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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>()));

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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>);
}
}

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
};
}

View File

@ -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));
});

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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>

View File

@ -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.

View File

@ -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>;

View File

@ -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;
}
}
};

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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']
!

View File

@ -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']
!

View File

@ -18,7 +18,7 @@
7 7
8 8
9 9
SimpleAggregateFunction(sum, Float64)
SimpleAggregateFunction(sum, Double)
0 0
1 2
2 4

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View 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;

View 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

View 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;

View 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)

View File

@ -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.

View File

@ -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 │

View File

@ -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.

View File

@ -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}
Удаляет дубликаты из массива. Порядок результирующих значений определяется порядком в исходном массиве.

View File

@ -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 │

View File

@ -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 │