This commit is contained in:
Andrey Mironov 2014-08-26 19:16:35 +04:00
commit f1b94e8235
174 changed files with 6721 additions and 2697 deletions

View File

@ -1,97 +0,0 @@
#pragma once
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/ReadHelpers.h>
#include <DB/AggregateFunctions/IUnaryAggregateFunction.h>
namespace DB
{
struct AggregateFunctionAnyData
{
Field value;
};
/// Берёт первое попавшееся значение
class AggregateFunctionAny final : public IUnaryAggregateFunction<AggregateFunctionAnyData, AggregateFunctionAny>
{
private:
DataTypePtr type;
public:
String getName() const { return "any"; }
DataTypePtr getReturnType() const
{
return type;
}
void setArgument(const DataTypePtr & argument)
{
type = argument;
}
void addOne(AggregateDataPtr place, const IColumn & column, size_t row_num) const
{
Data & d = data(place);
if (!d.value.isNull())
return;
column.get(row_num, d.value);
}
void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs) const
{
Data & d = data(place);
if (d.value.isNull())
d.value = data(rhs).value;
}
void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const
{
const Data & d = data(place);
if (unlikely(d.value.isNull()))
{
writeBinary(false, buf);
}
else
{
writeBinary(true, buf);
type->serializeBinary(data(place).value, buf);
}
}
void deserializeMerge(AggregateDataPtr place, ReadBuffer & buf) const
{
Data & d = data(place);
bool is_not_null = false;
readBinary(is_not_null, buf);
if (is_not_null)
{
Field tmp;
type->deserializeBinary(tmp, buf);
if (d.value.isNull())
d.value = tmp;
}
}
void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const
{
if (unlikely(data(place).value.isNull()))
to.insertDefault();
else
to.insert(data(place).value);
}
};
}

View File

@ -1,83 +0,0 @@
#pragma once
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/ReadHelpers.h>
#include <DB/AggregateFunctions/IUnaryAggregateFunction.h>
namespace DB
{
struct AggregateFunctionAnyLastData
{
Field value;
};
/// Берёт последнее попавшееся значение
class AggregateFunctionAnyLast final : public IUnaryAggregateFunction<AggregateFunctionAnyLastData, AggregateFunctionAnyLast>
{
private:
DataTypePtr type;
public:
String getName() const { return "anyLast"; }
DataTypePtr getReturnType() const
{
return type;
}
void setArgument(const DataTypePtr & argument)
{
type = argument;
}
void addOne(AggregateDataPtr place, const IColumn & column, size_t row_num) const
{
column.get(row_num, data(place).value);
}
void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs) const
{
if (!data(rhs).value.isNull())
data(place).value = data(rhs).value;
}
void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const
{
const Data & d = data(place);
if (unlikely(d.value.isNull()))
{
writeBinary(false, buf);
}
else
{
writeBinary(true, buf);
type->serializeBinary(data(place).value, buf);
}
}
void deserializeMerge(AggregateDataPtr place, ReadBuffer & buf) const
{
bool is_not_null = false;
readBinary(is_not_null, buf);
if (is_not_null)
type->deserializeBinary(data(place).value, buf);
}
void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const
{
if (unlikely(data(place).value.isNull()))
to.insertDefault();
else
to.insert(data(place).value);
}
};
}

View File

@ -19,8 +19,15 @@ namespace DB
template <typename T>
struct __attribute__((__packed__)) AggregateFunctionUniqUpToData
{
/** Если count == threshold + 1 - это значит, что "переполнилось" (значений больше threshold).
* В этом случае (например, после вызова функции merge), массив data не обязательно содержит инициализированные значения
* - пример: объединяем состояние, в котором мало значений, с другим состоянием, которое переполнилось;
* тогда выставляем count в threshold + 1, а значения из другого состояния не копируем.
*/
UInt8 count = 0;
T data[0]; /// Данные идут после конца структуры. При вставке, делается линейный поиск.
/// Данные идут после конца структуры. При вставке, делается линейный поиск.
T data[0];
size_t size() const
@ -31,17 +38,20 @@ struct __attribute__((__packed__)) AggregateFunctionUniqUpToData
/// threshold - для скольки элементов есть место в data.
void insert(T x, UInt8 threshold)
{
/// Состояние уже переполнено - ничего делать не нужно.
if (count > threshold)
return;
size_t limit = std::min(count, threshold);
for (size_t i = 0; i < limit; ++i)
/// Линейный поиск совпадающего элемента.
for (size_t i = 0; i < count; ++i)
if (data[i] == x)
return;
/// Не нашли совпадающий элемент. Если есть место ещё для одного элемента - вставляем его.
if (count < threshold)
data[count] = x;
/// После увеличения count, состояние может оказаться переполненным.
++count;
}
@ -52,19 +62,22 @@ struct __attribute__((__packed__)) AggregateFunctionUniqUpToData
if (rhs.count > threshold)
{
/// Если rhs переполнено, то выставляем у текущего состояния count тоже переполненным.
count = rhs.count;
return;
}
size_t limit = std::min(rhs.count, threshold);
for (size_t i = 0; i < limit; ++i)
for (size_t i = 0; i < rhs.count; ++i)
insert(rhs.data[i], threshold);
}
void write(WriteBuffer & wb, UInt8 threshold) const
{
size_t limit = std::min(count, threshold);
wb.write(reinterpret_cast<const char *>(this), sizeof(*this) + limit * sizeof(data[0]));
writeBinary(count, wb);
/// Пишем значения, только если состояние не переполнено. Иначе они не нужны, а важен только факт того, что состояние переполнено.
if (count <= threshold)
wb.write(reinterpret_cast<const char *>(this), count * sizeof(data[0]));
}
void readAndMerge(ReadBuffer & rb, UInt8 threshold)
@ -72,11 +85,14 @@ struct __attribute__((__packed__)) AggregateFunctionUniqUpToData
UInt8 rhs_count;
readBinary(rhs_count, rb);
if (rhs_count > threshold + 1)
throw Poco::Exception("Cannot read AggregateFunctionUniqUpToData: too large count.");
if (rhs_count > threshold)
{
/// Если rhs переполнено, то выставляем у текущего состояния count тоже переполненным.
count = rhs_count;
return;
}
size_t limit = std::min(rhs_count, threshold);
for (size_t i = 0; i < limit; ++i)
for (size_t i = 0; i < rhs_count; ++i)
{
T x;
readBinary(x, rb);
@ -135,11 +151,13 @@ public:
if (params.size() != 1)
throw Exception("Aggregate function " + getName() + " requires exactly one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
threshold = apply_visitor(FieldVisitorConvertToNumber<UInt64>(), params[0]);
UInt64 threshold_param = apply_visitor(FieldVisitorConvertToNumber<UInt64>(), params[0]);
if (threshold > uniq_upto_max_threshold)
if (threshold_param > uniq_upto_max_threshold)
throw Exception("Too large parameter for aggregate function " + getName() + ". Maximum: " + toString(uniq_upto_max_threshold),
ErrorCodes::ARGUMENT_OUT_OF_BOUND);
threshold = threshold_param;
}
void addOne(AggregateDataPtr place, const IColumn & column, size_t row_num) const

View File

@ -1,132 +0,0 @@
#pragma once
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/ReadHelpers.h>
#include <DB/AggregateFunctions/IUnaryAggregateFunction.h>
namespace DB
{
struct AggregateFunctionMinTraits
{
static bool better(const Field & lhs, const Field & rhs) { return lhs < rhs; }
static String name() { return "min"; }
};
struct AggregateFunctionMaxTraits
{
static bool better(const Field & lhs, const Field & rhs) { return lhs > rhs; }
static String name() { return "max"; }
};
struct AggregateFunctionsMinMaxData
{
Field value;
};
/// Берёт минимальное (или максимальное) значение. Если таких много - то первое попавшееся из них.
template <typename Traits>
class AggregateFunctionsMinMax final : public IUnaryAggregateFunction<AggregateFunctionsMinMaxData, AggregateFunctionsMinMax<Traits> >
{
private:
typedef typename IAggregateFunctionHelper<AggregateFunctionsMinMaxData>::Data Data;
DataTypePtr type;
public:
String getName() const { return Traits::name(); }
DataTypePtr getReturnType() const
{
return type;
}
void setArgument(const DataTypePtr & argument)
{
type = argument;
}
void addOne(AggregateDataPtr place, const IColumn & column, size_t row_num) const
{
Field value;
column.get(row_num, value);
Data & d = this->data(place);
if (!d.value.isNull())
{
if (Traits::better(value, d.value))
d.value = value;
}
else
d.value = value;
}
void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs) const
{
Data & d = this->data(place);
const Data & d_rhs = this->data(rhs);
if (!d.value.isNull())
{
if (Traits::better(d_rhs.value, d.value))
d.value = d_rhs.value;
}
else
d.value = d_rhs.value;
}
void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const
{
const Data & d = this->data(place);
if (unlikely(d.value.isNull()))
{
writeBinary(false, buf);
}
else
{
writeBinary(true, buf);
type->serializeBinary(this->data(place).value, buf);
}
}
void deserializeMerge(AggregateDataPtr place, ReadBuffer & buf) const
{
Data & d = this->data(place);
bool is_not_null = false;
readBinary(is_not_null, buf);
if (is_not_null)
{
if (!d.value.isNull())
{
Field value_;
type->deserializeBinary(value_, buf);
if (Traits::better(value_, d.value))
d.value = value_;
}
else
type->deserializeBinary(d.value, buf);
}
}
void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const
{
if (unlikely(this->data(place).value.isNull()))
to.insertDefault();
else
to.insert(this->data(place).value);
}
};
typedef AggregateFunctionsMinMax<AggregateFunctionMinTraits> AggregateFunctionMin;
typedef AggregateFunctionsMinMax<AggregateFunctionMaxTraits> AggregateFunctionMax;
}

View File

@ -0,0 +1,480 @@
#pragma once
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/ReadHelpers.h>
#include <DB/Columns/ColumnVector.h>
#include <DB/Columns/ColumnString.h>
#include <DB/AggregateFunctions/IUnaryAggregateFunction.h>
namespace DB
{
/** Агрегатные функции, запоминающие одно какое-либо переданное значение.
* Например, min, max, any, anyLast.
*/
/// Для числовых значений.
template <typename T>
struct SingleValueDataFixed
{
typedef SingleValueDataFixed<T> Self;
bool has_value = false; /// Надо запомнить, было ли передано хотя бы одно значение. Это нужно для AggregateFunctionIf.
T value;
bool has() const
{
return has_value;
}
void insertResultInto(IColumn & to) const
{
if (has())
static_cast<ColumnVector<T> &>(to).getData().push_back(value);
else
static_cast<ColumnVector<T> &>(to).insertDefault();
}
void write(WriteBuffer & buf, const IDataType & data_type) const
{
writeBinary(has(), buf);
if (has())
writeBinary(value, buf);
}
void read(ReadBuffer & buf, const IDataType & data_type)
{
readBinary(has_value, buf);
if (has())
readBinary(value, buf);
}
void change(const IColumn & column, size_t row_num)
{
has_value = true;
value = static_cast<const ColumnVector<T> &>(column).getData()[row_num];
}
void change(const Self & to)
{
has_value = true;
value = to.value;
}
void changeFirstTime(const IColumn & column, size_t row_num)
{
if (!has())
change(column, row_num);
}
void changeFirstTime(const Self & to)
{
if (!has())
change(to);
}
void changeIfLess(const IColumn & column, size_t row_num)
{
if (!has() || static_cast<const ColumnVector<T> &>(column).getData()[row_num] < value)
change(column, row_num);
}
void changeIfLess(const Self & to)
{
if (!has() || to.value < value)
change(to);
}
void changeIfGreater(const IColumn & column, size_t row_num)
{
if (!has() || static_cast<const ColumnVector<T> &>(column).getData()[row_num] > value)
change(column, row_num);
}
void changeIfGreater(const Self & to)
{
if (!has() || to.value > value)
change(to);
}
};
/** Для строк. Короткие строки хранятся в самой структуре, а длинные выделяются отдельно.
* NOTE Могло бы подойти также для массивов чисел.
*/
struct __attribute__((__packed__)) SingleValueDataString
{
typedef SingleValueDataString Self;
Int32 size = -1; /// -1 обозначает, что значения нет.
static constexpr Int32 AUTOMATIC_STORAGE_SIZE = 64;
static constexpr Int32 MAX_SMALL_STRING_SIZE = AUTOMATIC_STORAGE_SIZE - sizeof(size);
union
{
char small_data[MAX_SMALL_STRING_SIZE]; /// Включая завершающий ноль.
char * large_data;
};
~SingleValueDataString()
{
if (size > MAX_SMALL_STRING_SIZE)
free(large_data);
}
bool has() const
{
return size >= 0;
}
const char * getData() const
{
return size <= MAX_SMALL_STRING_SIZE ? small_data : large_data;
}
StringRef getStringRef() const
{
return StringRef(getData(), size);
}
void insertResultInto(IColumn & to) const
{
if (has())
static_cast<ColumnString &>(to).insertDataWithTerminatingZero(getData(), size);
else
static_cast<ColumnString &>(to).insertDefault();
}
void write(WriteBuffer & buf, const IDataType & data_type) const
{
writeBinary(size, buf);
if (has())
buf.write(getData(), size);
}
void read(ReadBuffer & buf, const IDataType & data_type)
{
Int32 rhs_size;
readBinary(rhs_size, buf);
if (rhs_size >= 0)
{
if (rhs_size <= MAX_SMALL_STRING_SIZE)
{
if (size > MAX_SMALL_STRING_SIZE)
free(large_data);
size = rhs_size;
if (size > 0)
buf.read(small_data, size);
}
else
{
if (size < rhs_size)
{
if (size > MAX_SMALL_STRING_SIZE)
free(large_data);
large_data = reinterpret_cast<char *>(malloc(rhs_size));
}
size = rhs_size;
buf.read(large_data, size);
}
}
else
{
if (size > MAX_SMALL_STRING_SIZE)
free(large_data);
size = rhs_size;
}
}
void changeImpl(StringRef value)
{
Int32 value_size = value.size;
if (value_size <= MAX_SMALL_STRING_SIZE)
{
if (size > MAX_SMALL_STRING_SIZE)
free(large_data);
size = value_size;
if (size > 0)
memcpy(small_data, value.data, size);
}
else
{
if (size < value_size)
{
if (size > MAX_SMALL_STRING_SIZE)
free(large_data);
large_data = reinterpret_cast<char *>(malloc(value.size));
}
size = value_size;
memcpy(large_data, value.data, size);
}
}
void change(const IColumn & column, size_t row_num)
{
changeImpl(static_cast<const ColumnString &>(column).getDataAtWithTerminatingZero(row_num));
}
void change(const Self & to)
{
changeImpl(to.getStringRef());
}
void changeFirstTime(const IColumn & column, size_t row_num)
{
if (!has())
change(column, row_num);
}
void changeFirstTime(const Self & to)
{
if (!has())
change(to);
}
void changeIfLess(const IColumn & column, size_t row_num)
{
if (!has() || static_cast<const ColumnString &>(column).getDataAtWithTerminatingZero(row_num) < getStringRef())
change(column, row_num);
}
void changeIfLess(const Self & to)
{
if (!has() || to.getStringRef() < getStringRef())
change(to);
}
void changeIfGreater(const IColumn & column, size_t row_num)
{
if (!has() || static_cast<const ColumnString &>(column).getDataAtWithTerminatingZero(row_num) > getStringRef())
change(column, row_num);
}
void changeIfGreater(const Self & to)
{
if (!has() || to.getStringRef() > getStringRef())
change(to);
}
};
/// Для любых других типов значений.
struct SingleValueDataGeneric
{
typedef SingleValueDataGeneric Self;
Field value;
bool has() const
{
return !value.isNull();
}
void insertResultInto(IColumn & to) const
{
if (has())
to.insert(value);
else
to.insertDefault();
}
void write(WriteBuffer & buf, const IDataType & data_type) const
{
if (!value.isNull())
{
writeBinary(true, buf);
data_type.serializeBinary(value, buf);
}
else
writeBinary(false, buf);
}
void read(ReadBuffer & buf, const IDataType & data_type)
{
bool is_not_null;
readBinary(is_not_null, buf);
if (is_not_null)
data_type.deserializeBinary(value, buf);
}
void change(const IColumn & column, size_t row_num)
{
column.get(row_num, value);
}
void change(const Self & to)
{
value = to.value;
}
void changeFirstTime(const IColumn & column, size_t row_num)
{
if (!has())
change(column, row_num);
}
void changeFirstTime(const Self & to)
{
if (!has())
change(to);
}
void changeIfLess(const IColumn & column, size_t row_num)
{
if (!has())
change(column, row_num);
else
{
Field new_value;
column.get(row_num, new_value);
if (new_value < value)
value = new_value;
}
}
void changeIfLess(const Self & to)
{
if (!has() || to.value < value)
change(to);
}
void changeIfGreater(const IColumn & column, size_t row_num)
{
if (!has())
change(column, row_num);
else
{
Field new_value;
column.get(row_num, new_value);
if (new_value > value)
value = new_value;
}
}
void changeIfGreater(const Self & to)
{
if (!has() || to.value > value)
change(to);
}
};
/** То, чем отличаются друг от другая агрегатные функции min, max, any, anyLast
* (условием, при котором сохранённое значение заменяется на новое,
* а также, конечно, именем).
*/
template <typename Data>
struct AggregateFunctionMinData : Data
{
typedef AggregateFunctionMinData<Data> Self;
void changeIfBetter(const IColumn & column, size_t row_num) { this->changeIfLess(column, row_num); }
void changeIfBetter(const Self & to) { this->changeIfLess(to); }
static const char * name() { return "min"; }
};
template <typename Data>
struct AggregateFunctionMaxData : Data
{
typedef AggregateFunctionMaxData<Data> Self;
void changeIfBetter(const IColumn & column, size_t row_num) { this->changeIfGreater(column, row_num); }
void changeIfBetter(const Self & to) { this->changeIfGreater(to); }
static const char * name() { return "max"; }
};
template <typename Data>
struct AggregateFunctionAnyData : Data
{
typedef AggregateFunctionAnyData<Data> Self;
void changeIfBetter(const IColumn & column, size_t row_num) { this->changeFirstTime(column, row_num); }
void changeIfBetter(const Self & to) { this->changeFirstTime(to); }
static const char * name() { return "any"; }
};
template <typename Data>
struct AggregateFunctionAnyLastData : Data
{
typedef AggregateFunctionAnyLastData<Data> Self;
void changeIfBetter(const IColumn & column, size_t row_num) { this->change(column, row_num); }
void changeIfBetter(const Self & to) { this->change(to); }
static const char * name() { return "anyLast"; }
};
template <typename Data>
class AggregateFunctionsSingleValue final : public IUnaryAggregateFunction<Data, AggregateFunctionsSingleValue<Data> >
{
private:
DataTypePtr type;
public:
String getName() const { return Data::name(); }
DataTypePtr getReturnType() const
{
return type;
}
void setArgument(const DataTypePtr & argument)
{
type = argument;
}
void addOne(AggregateDataPtr place, const IColumn & column, size_t row_num) const
{
this->data(place).changeIfBetter(column, row_num);
}
void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs) const
{
this->data(place).changeIfBetter(this->data(rhs));
}
void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const
{
this->data(place).write(buf, *type.get());
}
void deserializeMerge(AggregateDataPtr place, ReadBuffer & buf) const
{
Data rhs; /// Для строчек не очень оптимально, так как может делаться одна лишняя аллокация.
rhs.read(buf, *type.get());
this->data(place).changeIfBetter(rhs);
}
void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const
{
this->data(place).insertResultInto(to);
}
};
}

View File

@ -97,7 +97,8 @@ public:
void sendExternalTablesData(ExternalTablesData & data);
/// Отправить блок данных, который уже был заранее сериализован (и, если надо, сжат), который следует прочитать из input-а.
void sendPreparedData(ReadBuffer & input, const String & name = "");
/// можно передать размер сериализованного/сжатого блока.
void sendPreparedData(ReadBuffer & input, size_t size, const String & name = "");
/// Проверить, есть ли данные, которые можно прочитать.
bool poll(size_t timeout_microseconds = 0);

View File

@ -161,8 +161,7 @@ public:
void insertDefault()
{
data->insertDefault();
getOffsets().push_back(getOffsets().size() == 0 ? 1 : (getOffsets().back() + 1));
getOffsets().push_back(getOffsets().size() == 0 ? 0 : getOffsets().back());
}
ColumnPtr filter(const Filter & filt) const
@ -387,6 +386,10 @@ private:
throw Exception("Size of offsets doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
ColumnPtr res = cloneEmpty();
if (0 == col_size)
return res;
ColumnArray & res_ = typeid_cast<ColumnArray &>(*res);
const typename ColumnVector<T>::Container_t & cur_data = typeid_cast<const ColumnVector<T> &>(*data).getData();
@ -431,6 +434,10 @@ private:
throw Exception("Size of offsets doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
ColumnPtr res = cloneEmpty();
if (0 == col_size)
return res;
ColumnArray & res_ = typeid_cast<ColumnArray &>(*res);
const ColumnString & cur_string = typeid_cast<const ColumnString &>(*data);

View File

@ -79,12 +79,7 @@ public:
if (s != filt.size())
throw Exception("Size of filter doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
size_t new_size = 0;
for (Filter::const_iterator it = filt.begin(); it != filt.end(); ++it)
if (*it)
++new_size;
return new ColumnConst<T>(new_size, data, data_type);
return new ColumnConst<T>(countBytesInFilter(filt), data, data_type);
}
ColumnPtr replicate(const Offsets_t & offsets) const
@ -92,7 +87,8 @@ public:
if (s != offsets.size())
throw Exception("Size of offsets doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
return new ColumnConst<T>(offsets.back(), data, data_type);
size_t replicated_size = 0 == s ? 0 : offsets.back();
return new ColumnConst<T>(replicated_size, data, data_type);
}
size_t byteSize() const { return sizeof(data) + sizeof(s); }

View File

@ -223,6 +223,9 @@ public:
ColumnFixedString * res_ = new ColumnFixedString(n);
ColumnPtr res = res_;
if (0 == col_size)
return res;
Chars_t & res_chars = res_->chars;
res_chars.reserve(n * offsets.back());

View File

@ -152,36 +152,105 @@ public:
ColumnPtr filter(const Filter & filt) const
{
size_t size = offsets.size();
const size_t size = offsets.size();
if (size != filt.size())
throw Exception("Size of filter doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
if (size == 0)
return new ColumnString;
ColumnString * res_ = new ColumnString;
ColumnPtr res = res_;
auto res = new ColumnString;
Chars_t & res_chars = res_->chars;
Offsets_t & res_offsets = res_->offsets;
Chars_t & res_chars = res->chars;
Offsets_t & res_offsets = res->offsets;
res_chars.reserve(chars.size());
res_offsets.reserve(size);
Offset_t current_new_offset = 0;
Offset_t current_offset = 0;
for (size_t i = 0; i < size; ++i)
auto filt_pos = &filt[0];
const auto filt_end = filt_pos + size;
const auto filt_end_aligned = filt_pos + size / 16 * 16;
auto offsets_pos = &offsets[0];
const auto offsets_begin = offsets_pos;
const auto zero16 = _mm_set1_epi8(0);
/// copy string ending at *end_offset_ptr
const auto copy_string = [&] (const Offset_t * offset_ptr) {
const auto offset = offset_ptr == offsets_begin ? 0 : offset_ptr[-1];
const auto size = *offset_ptr - offset;
current_offset += size;
res_offsets.push_back(current_offset);
const auto chars_size_old = res_chars.size();
res_chars.resize(chars_size_old + size);
memcpy(&res_chars[chars_size_old], &chars[offset], size);
};
while (filt_pos < filt_end_aligned)
{
if (!filt[i])
continue;
const auto mask = _mm_movemask_epi8(_mm_cmpgt_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(filt_pos)),
zero16));
size_t string_offset = i == 0 ? 0 : offsets[i - 1];
size_t string_size = offsets[i] - string_offset;
if (mask == 0)
{
/// 16 consecutive rows do not pass the filter
}
else if (mask == 0xffff)
{
/// 16 consecutive rows pass the filter
const auto first = offsets_pos == offsets_begin;
current_new_offset += string_size;
res_offsets.push_back(current_new_offset);
const auto chunk_offset = first ? 0 : offsets_pos[-1];
const auto chunk_size = offsets_pos[16 - 1] - chunk_offset;
res_chars.resize(res_chars.size() + string_size);
memcpy(&res_chars[res_chars.size() - string_size], &chars[string_offset], string_size);
const auto offsets_size_old = res_offsets.size();
res_offsets.resize(offsets_size_old + 16);
memcpy(&res_offsets[offsets_size_old], offsets_pos, 16 * sizeof(Offset_t));
if (!first)
{
/// difference between current and actual offset
const auto diff_offset = chunk_offset - current_offset;
if (diff_offset > 0)
{
const auto res_offsets_pos = &res_offsets[offsets_size_old];
/// adjust offsets
for (size_t i = 0; i < 16; ++i)
res_offsets_pos[i] -= diff_offset;
}
}
current_offset += chunk_size;
/// copy characters for 16 strings at once
const auto chars_size_old = res_chars.size();
res_chars.resize(chars_size_old + chunk_size);
memcpy(&res_chars[chars_size_old], &chars[chunk_offset], chunk_size);
}
else
{
for (size_t i = 0; i < 16; ++i)
if (filt_pos[i])
copy_string(offsets_pos + i);
}
filt_pos += 16;
offsets_pos += 16;
}
while (filt_pos < filt_end)
{
if (*filt_pos)
copy_string(offsets_pos);
++filt_pos;
++offsets_pos;
}
return res;
@ -354,6 +423,9 @@ public:
ColumnString * res_ = new ColumnString;
ColumnPtr res = res_;
if (0 == col_size)
return res;
Chars_t & res_chars = res_->chars;
Offsets_t & res_offsets = res_->offsets;
res_chars.reserve(chars.size() / col_size * replicate_offsets.back());

View File

@ -222,9 +222,49 @@ public:
typename Self::Container_t & res_data = res_->getData();
res_data.reserve(size);
for (size_t i = 0; i < size; ++i)
if (filt[i])
res_data.push_back(data[i]);
/** Чуть более оптимизированная версия.
* Исходит из допущения, что часто куски последовательно идущих значений
* полностью проходят или полностью не проходят фильтр.
* Поэтому, будем оптимистично проверять куски по 16 значений.
*/
const UInt8 * filt_pos = &filt[0];
const UInt8 * filt_end = filt_pos + size;
const UInt8 * filt_end_sse = filt_pos + size / 16 * 16;
const T * data_pos = &data[0];
const __m128i zero16 = _mm_set1_epi8(0);
while (filt_pos < filt_end_sse)
{
int mask = _mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast<const __m128i *>(filt_pos)), zero16));
if (0 == mask)
{
/// Ничего не вставляем.
}
else if (0xFFFF == mask)
{
res_data.insert_assume_reserved(data_pos, data_pos + 16);
}
else
{
for (size_t i = 0; i < 16; ++i)
if (filt_pos[i])
res_data.push_back(data_pos[i]);
}
filt_pos += 16;
data_pos += 16;
}
while (filt_pos < filt_end)
{
if (*filt_pos)
res_data.push_back(*data_pos);
++filt_pos;
++data_pos;
}
return res;
}
@ -256,6 +296,9 @@ public:
if (size != offsets.size())
throw Exception("Size of offsets doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
if (0 == size)
return new Self;
Self * res_ = new Self;
ColumnPtr res = res_;
typename Self::Container_t & res_data = res_->getData();

View File

@ -198,4 +198,8 @@ public:
};
/// Считает, сколько байт в filt больше нуля.
size_t countBytesInFilter(const IColumn::Filter & filt);
}

View File

@ -70,7 +70,7 @@ public:
if (s != offsets.size())
throw Exception("Size of offsets doesn't match size of column.", ErrorCodes::SIZES_OF_COLUMNS_DOESNT_MATCH);
return cloneDummy(offsets.back());
return cloneDummy(s == 0 ? 0 : offsets.back());
}
private:

View File

@ -0,0 +1,95 @@
#pragma once
#include <Yandex/logger_useful.h>
#include <DB/Columns/IColumn.h>
#include <Poco/AutoPtr.h>
#include <Poco/Util/XMLConfiguration.h>
#include <string>
#include <Poco/File.h>
#include <DB/Common/escapeForFileName.h>
#include <jsonxx.h>
#include <fstream>
namespace DB
{
/// хранит размеры всех столбцов, и может проверять не побились ли столбцы
template <class Storage>
class FileChecker
{
public:
FileChecker(const std::string &file_info_path_, Storage & storage_) :
files_info_path(file_info_path_), storage(storage_), log(&Logger::get("FileChecker"))
{
std::ifstream istr(files_info_path);
files_info.parse(istr);
}
void setPath(const std::string & file_info_path_)
{
files_info_path = file_info_path_;
}
using Files = std::vector<Poco::File>;
void update(const Poco::File & file)
{
updateTree(file);
saveTree();
}
void update(const Files::iterator & begin, const Files::iterator & end)
{
for (auto it = begin; it != end; ++it)
updateTree(*it);
saveTree();
}
/// Проверяем файлы, параметры которых указаны в sizes.json
bool check() const
{
bool correct = true;
for (auto & node : files_info.kv_map())
{
std::string filename = unescapeForFileName(node.first);
size_t expected_size = std::stoull(node.second->get<jsonxx::Object>().get<std::string>("size"));
Poco::File file(Poco::Path(files_info_path).parent().toString() + "/" + filename);
if (!file.exists())
{
LOG_ERROR(log, "File " << file.path() << " doesn't exists");
correct = false;
continue;
}
size_t real_size = file.getSize();
if (real_size != expected_size)
{
LOG_ERROR(log, "Size of " << file.path() << " is wrong. Size is " << real_size << " but should be " << expected_size);
correct = false;
}
}
return correct;
}
private:
void updateTree(const Poco::File & file)
{
files_info.import(escapeForFileName(Poco::Path(file.path()).getFileName()),
jsonxx::Object("size", std::to_string(file.getSize())));
}
void saveTree()
{
std::ofstream file(files_info_path, std::ofstream::trunc);
file << files_info.write(jsonxx::JSON);
}
std::string files_info_path;
jsonxx::Object files_info;
Storage & storage;
Logger * log;
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <DB/Core/Types.h>
#include <Poco/Util/AbstractConfiguration.h>
#include <map>
namespace DB
{
/** Раскрывает в строке макросы из конфига.
*/
class Macros
{
public:
Macros();
Macros(const Poco::Util::AbstractConfiguration & config, const String & key);
/// Заменить в строке подстроки вида {macro_name} на значение для macro_name, полученное из конфига.
String expand(const String & s) const;
private:
typedef std::map<String, String> MacroMap;
MacroMap macros;
};
}

View File

@ -248,6 +248,12 @@ public:
if (required_capacity > capacity())
reserve(round_up_to_power_of_two(required_capacity));
insert_assume_reserved(from_begin, from_end);
}
template <typename It1, typename It2>
void insert_assume_reserved(It1 from_begin, It2 from_end)
{
size_t bytes_to_copy = byte_size(from_end - from_begin);
memcpy(c_end, reinterpret_cast<const void *>(&*from_begin), bytes_to_copy);
c_end += bytes_to_copy;

View File

@ -35,7 +35,7 @@ private:
const char * needle_end;
size_t step; /// Насколько двигаемся, если n-грамма из haystack не нашлась в хэш-таблице.
static const size_t hash_size = 64 * 1024; /// Обычно помещается в L1-кэш, хотя занимает его целиком.
static const size_t hash_size = 64 * 1024; /// Помещается в L2-кэш.
offset_t hash[hash_size]; /// Хэш-таблица.
bool fallback; /// Нужно ли использовать fallback алгоритм.

View File

@ -64,3 +64,5 @@
#define DBMS_MIN_REVISION_WITH_TOTALS_EXTREMES 35265
#define DBMS_MIN_REVISION_WITH_STRING_QUERY_ID 39002
#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50264
#define DBMS_DISTRIBUTED_DIRECTORY_MONITOR_SLEEP_TIME_MS 100

View File

@ -255,6 +255,10 @@ namespace ErrorCodes
INVALID_NESTED_NAME,
CORRUPTED_DATA,
INCORRECT_MARK,
INVALID_PARTITION_NAME,
NOT_LEADER,
NOT_ENOUGH_BLOCK_NUMBERS,
NO_SUCH_REPLICA,
POCO_EXCEPTION = 1000,
STD_EXCEPTION,

View File

@ -582,11 +582,39 @@ private:
writeQuoted(x, wb);
return res;
}
/** В отличие от writeFloatText (и writeQuoted), если число после форматирования выглядит целым, всё равно добавляет десятичную точку.
* - для того, чтобы это число могло обратно распарситься как Float64 парсером запроса (иначе распарсится как целое).
*
* При этом, не оставляет завершающие нули справа.
*
* NOTE: При таком roundtrip-е, точность может теряться.
*/
static inline String formatFloat(Float64 x)
{
char tmp[24];
int res = std::snprintf(tmp, 23, "%.*g", WRITE_HELPERS_DEFAULT_FLOAT_PRECISION, x);
if (res >= 23 || res <= 0)
throw Exception("Cannot print float or double number", ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER);
size_t string_size = res;
tmp[23] = '\0';
if (string_size == strspn(tmp, "-0123456789"))
{
tmp[string_size] = '.';
++string_size;
}
return {tmp, string_size};
}
public:
String operator() (const Null & x) const { return "NULL"; }
String operator() (const UInt64 & x) const { return formatQuoted(x); }
String operator() (const Int64 & x) const { return formatQuoted(x); }
String operator() (const Float64 & x) const { return formatQuoted(x); }
String operator() (const Float64 & x) const { return formatFloat(x); }
String operator() (const String & x) const { return formatQuoted(x); }
String operator() (const Array & x) const

View File

@ -26,7 +26,7 @@ struct StringRef
typedef std::vector<StringRef> StringRefs;
inline bool operator==(StringRef lhs, StringRef rhs)
inline bool operator== (StringRef lhs, StringRef rhs)
{
/// Так почему-то быстрее, чем return lhs.size == rhs.size && 0 == memcmp(lhs.data, rhs.data, lhs.size);
@ -40,18 +40,21 @@ inline bool operator==(StringRef lhs, StringRef rhs)
return true;
}
inline bool operator!=(StringRef lhs, StringRef rhs)
inline bool operator!= (StringRef lhs, StringRef rhs)
{
return !(lhs == rhs);
}
inline bool operator<(StringRef lhs, StringRef rhs)
inline bool operator< (StringRef lhs, StringRef rhs)
{
int cmp = memcmp(lhs.data, rhs.data, std::min(lhs.size, rhs.size));
if (cmp == 0)
return lhs.size < rhs.size;
else
return cmp < 0;
return cmp < 0 || (cmp == 0 && lhs.size < rhs.size);
}
inline bool operator> (StringRef lhs, StringRef rhs)
{
int cmp = memcmp(lhs.data, rhs.data, std::min(lhs.size, rhs.size));
return cmp > 0 || (cmp == 0 && lhs.size > rhs.size);
}

View File

@ -33,6 +33,8 @@ public:
output->write(res);
}
void flush() { output->flush(); }
private:
BlockOutputStreamPtr output;
NamesAndTypesListPtr required_columns;

View File

@ -20,6 +20,8 @@ public:
void writeField(const Field & field);
void writeRowEndDelimiter();
void flush() { ostr.next(); }
protected:
WriteBuffer & ostr;
const Block sample;

View File

@ -18,6 +18,8 @@ public:
void writePrefix() { row_output->writePrefix(); }
void writeSuffix() { row_output->writeSuffix(); }
void flush() { row_output->flush(); }
void setRowsBeforeLimit(size_t rows_before_limit);
void setTotals(const Block & totals);
void setExtremes(const Block & extremes);

View File

@ -63,7 +63,6 @@ public:
BlockInputStreams & getChildren() { return children; }
void dumpTree(std::ostream & ostr, size_t indent = 0, size_t multiplier = 1);
void dumpTreeWithProfile(std::ostream & ostr, size_t indent = 0);
/// Получить листовые источники (не считая этот).
BlockInputStreams getLeaves();

View File

@ -32,6 +32,10 @@ public:
virtual void writePrefix() {}
virtual void writeSuffix() {}
/** Сбросить имеющиеся буферы для записи.
*/
virtual void flush() {}
/** Методы для установки дополнительной информации для вывода в поддерживающих её форматах.
*/
virtual void setRowsBeforeLimit(size_t rows_before_limit) {}

View File

@ -21,8 +21,7 @@ namespace DB
struct BlockStreamProfileInfo
{
bool started = false;
Stopwatch work_stopwatch; /// Время вычислений (выполнения функции read())
Stopwatch total_stopwatch; /// Время с учётом ожидания
Stopwatch total_stopwatch {CLOCK_MONOTONIC_COARSE}; /// Время с учётом ожидания
String stream_name; /// Короткое имя потока, для которого собирается информация
@ -48,7 +47,6 @@ struct BlockStreamProfileInfo
bool hasAppliedLimit() const;
void update(Block & block);
void print(std::ostream & ostr) const;
/// Методы для бинарной [де]сериализации
void read(ReadBuffer & in);

View File

@ -32,6 +32,9 @@ public:
virtual void writePrefix() {}; /// разделитель перед началом результата
virtual void writeSuffix() {}; /// разделитель после конца результата
/** Сбросить имеющиеся буферы для записи. */
virtual void flush() {}
/** Методы для установки дополнительной информации для вывода в поддерживающих её форматах.
*/
virtual void setRowsBeforeLimit(size_t rows_before_limit) {}

View File

@ -26,6 +26,8 @@ public:
void writePrefix();
void writeSuffix();
void flush() { ostr.next(); dst_ostr.next(); }
void setRowsBeforeLimit(size_t rows_before_limit_)
{
applied_limit = true;
@ -41,7 +43,8 @@ protected:
virtual void writeTotals();
virtual void writeExtremes();
WriteBufferValidUTF8 ostr;
WriteBuffer & dst_ostr;
WriteBufferValidUTF8 ostr; /// Валидирует и пишет в dst_ostr.
size_t field_number;
size_t row_count;
bool applied_limit;

View File

@ -15,6 +15,8 @@ public:
NativeBlockOutputStream(WriteBuffer & ostr_) : ostr(ostr_) {}
void write(const Block & block);
void flush() { ostr.next(); }
private:
WriteBuffer & ostr;
};

View File

@ -18,6 +18,8 @@ public:
void write(const Block & block);
void writeSuffix();
void flush() { ostr.next(); }
void setTotals(const Block & totals_) { totals = totals_; }
void setExtremes(const Block & extremes_) { extremes = extremes_; }

View File

@ -18,8 +18,8 @@ namespace DB
class PushingToViewsBlockOutputStream : public IBlockOutputStream
{
public:
PushingToViewsBlockOutputStream(String database_, String table_, const Context &context_, ASTPtr query_ptr_)
:database(database_), table(table_), context(context_), query_ptr(query_ptr_)
PushingToViewsBlockOutputStream(String database_, String table_, const Context & context_, ASTPtr query_ptr_)
: database(database_), table(table_), context(context_), query_ptr(query_ptr_)
{
if (database.empty())
database = context.getCurrentDatabase();

View File

@ -14,8 +14,8 @@ namespace DB
class RemoteBlockOutputStream : public IBlockOutputStream
{
public:
RemoteBlockOutputStream(Connection & connection_, const String & query_)
: connection(connection_), query(query_)
RemoteBlockOutputStream(Connection & connection_, const String & query_, Settings * settings_ = nullptr)
: connection(connection_), query(query_), settings(settings_)
{
}
@ -26,7 +26,7 @@ public:
*/
Block sendQueryAndGetSampleBlock()
{
connection.sendQuery(query);
connection.sendQuery(query, "", QueryProcessingStage::Complete, settings);
sent_query = true;
Connection::Packet packet = connection.receivePacket();
@ -64,12 +64,12 @@ public:
/// Отправить блок данных, который уже был заранее сериализован (и, если надо, сжат), который следует прочитать из input-а.
void writePrepared(ReadBuffer & input)
void writePrepared(ReadBuffer & input, size_t size = 0)
{
if (!sent_query)
sendQueryAndGetSampleBlock(); /// Никак не можем использовать sample_block.
connection.sendPreparedData(input);
connection.sendPreparedData(input, size);
}
@ -95,6 +95,7 @@ public:
private:
Connection & connection;
String query;
Settings * settings;
Block sample_block;
bool sent_query = false;

View File

@ -16,6 +16,8 @@ public:
TabSeparatedBlockOutputStream(WriteBuffer & ostr_) : ostr(ostr_) {}
void write(const Block & block);
void flush() { ostr.next(); }
private:
WriteBuffer & ostr;
};

View File

@ -26,6 +26,8 @@ public:
void writePrefix();
void writeSuffix();
void flush() { ostr.next(); }
void setTotals(const Block & totals_) { totals = totals_; }
void setExtremes(const Block & extremes_) { extremes = extremes_; }

View File

@ -26,6 +26,8 @@ public:
void writeRowEndDelimiter();
void writeRowBetweenDelimiter();
void flush() { ostr.next(); }
private:
WriteBuffer & ostr;
const Block sample;

View File

@ -25,6 +25,8 @@ public:
void writeRowStartDelimiter();
void writeRowBetweenDelimiter();
void flush() { ostr.next(); }
private:
WriteBuffer & ostr;
const Block sample;

View File

@ -1,7 +1,6 @@
#pragma once
#include <Poco/SharedPtr.h>
#include <DB/Functions/IFunction.h>
@ -17,10 +16,19 @@ class Context;
*/
class FunctionFactory
{
private:
typedef IFunction* (*Creator)(const Context & context); /// Не std::function, так как меньше indirection и размер объекта.
std::unordered_map<String, Creator> functions;
public:
FunctionPtr get(
const String & name,
const Context & context) const;
FunctionFactory();
FunctionPtr get(const String & name, const Context & context) const;
void registerFunction(const String & name, Creator creator)
{
functions[name] = creator;
}
};
}

View File

@ -14,7 +14,7 @@ namespace DB
*/
template<typename A, typename B, typename Op>
struct BinaryOperationImpl
struct BinaryOperationImplBase
{
typedef typename Op::ResultType ResultType;
@ -45,6 +45,11 @@ struct BinaryOperationImpl
}
};
template<typename A, typename B, typename Op>
struct BinaryOperationImpl : BinaryOperationImplBase<A, B, Op>
{
};
template<typename A, typename Op>
struct UnaryOperationImpl
{
@ -221,7 +226,7 @@ struct NegateImpl
static inline ResultType apply(A a)
{
return -a;
return -static_cast<ResultType>(a);
}
};
@ -570,4 +575,140 @@ typedef FunctionBinaryArithmetic<BitShiftRightImpl, NameBitShiftRight> Functi
/// Оптимизации для целочисленного деления на константу.
#define LIBDIVIDE_USE_SSE2 1
#include <libdivide.h>
template <typename A, typename B>
struct DivideIntegralByConstantImpl
: BinaryOperationImplBase<A, B, DivideIntegralImpl<A, B>>
{
typedef typename DivideIntegralImpl<A, B>::ResultType ResultType;
static void vector_constant(const PODArray<A> & a, B b, PODArray<ResultType> & c)
{
if (unlikely(b == 0))
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
if (unlikely(std::is_signed<B>::value && b == -1))
{
size_t size = a.size();
for (size_t i = 0; i < size; ++i)
c[i] = -c[i];
return;
}
#pragma GCC diagnostic pop
libdivide::divider<A> divider(b);
size_t size = a.size();
const A * a_pos = &a[0];
const A * a_end = a_pos + size;
ResultType * c_pos = &c[0];
static constexpr size_t values_per_sse_register = 16 / sizeof(A);
const A * a_end_sse = a_pos + size / values_per_sse_register * values_per_sse_register;
while (a_pos < a_end_sse)
{
_mm_storeu_si128(reinterpret_cast<__m128i *>(c_pos),
_mm_loadu_si128(reinterpret_cast<const __m128i *>(a_pos)) / divider);
a_pos += values_per_sse_register;
c_pos += values_per_sse_register;
}
while (a_pos < a_end)
{
*c_pos = *a_pos / divider;
++a_pos;
++c_pos;
}
}
};
template <typename A, typename B>
struct ModuloByConstantImpl
: BinaryOperationImplBase<A, B, ModuloImpl<A, B>>
{
typedef typename ModuloImpl<A, B>::ResultType ResultType;
static void vector_constant(const PODArray<A> & a, B b, PODArray<ResultType> & c)
{
if (unlikely(b == 0))
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
if (unlikely((std::is_signed<B>::value && b == -1) || b == 1))
{
size_t size = a.size();
for (size_t i = 0; i < size; ++i)
c[i] = 0;
return;
}
#pragma GCC diagnostic pop
libdivide::divider<A> divider(b);
/// Тут не удалось сделать так, чтобы SSE вариант из libdivide давал преимущество.
size_t size = a.size();
for (size_t i = 0; i < size; ++i)
c[i] = a[i] - (a[i] / divider) * b; /// NOTE: возможно, не сохраняется семантика деления с остатком отрицательных чисел.
}
};
/** Прописаны специализации для деления чисел типа UInt64 и UInt32 на числа той же знаковости.
* Можно дополнить до всех возможных комбинаций, но потребуется больше кода.
*/
template <> struct BinaryOperationImpl<UInt64, UInt8, DivideIntegralImpl<UInt64, UInt8>> : DivideIntegralByConstantImpl<UInt64, UInt8> {};
template <> struct BinaryOperationImpl<UInt64, UInt16, DivideIntegralImpl<UInt64, UInt16>> : DivideIntegralByConstantImpl<UInt64, UInt16> {};
template <> struct BinaryOperationImpl<UInt64, UInt32, DivideIntegralImpl<UInt64, UInt32>> : DivideIntegralByConstantImpl<UInt64, UInt32> {};
template <> struct BinaryOperationImpl<UInt64, UInt64, DivideIntegralImpl<UInt64, UInt64>> : DivideIntegralByConstantImpl<UInt64, UInt64> {};
template <> struct BinaryOperationImpl<UInt32, UInt8, DivideIntegralImpl<UInt32, UInt8>> : DivideIntegralByConstantImpl<UInt32, UInt8> {};
template <> struct BinaryOperationImpl<UInt32, UInt16, DivideIntegralImpl<UInt32, UInt16>> : DivideIntegralByConstantImpl<UInt32, UInt16> {};
template <> struct BinaryOperationImpl<UInt32, UInt32, DivideIntegralImpl<UInt32, UInt32>> : DivideIntegralByConstantImpl<UInt32, UInt32> {};
template <> struct BinaryOperationImpl<UInt32, UInt64, DivideIntegralImpl<UInt32, UInt64>> : DivideIntegralByConstantImpl<UInt32, UInt64> {};
template <> struct BinaryOperationImpl<Int64, Int8, DivideIntegralImpl<Int64, Int8>> : DivideIntegralByConstantImpl<Int64, Int8> {};
template <> struct BinaryOperationImpl<Int64, Int16, DivideIntegralImpl<Int64, Int16>> : DivideIntegralByConstantImpl<Int64, Int16> {};
template <> struct BinaryOperationImpl<Int64, Int32, DivideIntegralImpl<Int64, Int32>> : DivideIntegralByConstantImpl<Int64, Int32> {};
template <> struct BinaryOperationImpl<Int64, Int64, DivideIntegralImpl<Int64, Int64>> : DivideIntegralByConstantImpl<Int64, Int64> {};
template <> struct BinaryOperationImpl<Int32, Int8, DivideIntegralImpl<Int32, Int8>> : DivideIntegralByConstantImpl<Int32, Int8> {};
template <> struct BinaryOperationImpl<Int32, Int16, DivideIntegralImpl<Int32, Int16>> : DivideIntegralByConstantImpl<Int32, Int16> {};
template <> struct BinaryOperationImpl<Int32, Int32, DivideIntegralImpl<Int32, Int32>> : DivideIntegralByConstantImpl<Int32, Int32> {};
template <> struct BinaryOperationImpl<Int32, Int64, DivideIntegralImpl<Int32, Int64>> : DivideIntegralByConstantImpl<Int32, Int64> {};
template <> struct BinaryOperationImpl<UInt64, UInt8, ModuloImpl<UInt64, UInt8>> : ModuloByConstantImpl<UInt64, UInt8> {};
template <> struct BinaryOperationImpl<UInt64, UInt16, ModuloImpl<UInt64, UInt16>> : ModuloByConstantImpl<UInt64, UInt16> {};
template <> struct BinaryOperationImpl<UInt64, UInt32, ModuloImpl<UInt64, UInt32>> : ModuloByConstantImpl<UInt64, UInt32> {};
template <> struct BinaryOperationImpl<UInt64, UInt64, ModuloImpl<UInt64, UInt64>> : ModuloByConstantImpl<UInt64, UInt64> {};
template <> struct BinaryOperationImpl<UInt32, UInt8, ModuloImpl<UInt32, UInt8>> : ModuloByConstantImpl<UInt32, UInt8> {};
template <> struct BinaryOperationImpl<UInt32, UInt16, ModuloImpl<UInt32, UInt16>> : ModuloByConstantImpl<UInt32, UInt16> {};
template <> struct BinaryOperationImpl<UInt32, UInt32, ModuloImpl<UInt32, UInt32>> : ModuloByConstantImpl<UInt32, UInt32> {};
template <> struct BinaryOperationImpl<UInt32, UInt64, ModuloImpl<UInt32, UInt64>> : ModuloByConstantImpl<UInt32, UInt64> {};
template <> struct BinaryOperationImpl<Int64, Int8, ModuloImpl<Int64, Int8>> : ModuloByConstantImpl<Int64, Int8> {};
template <> struct BinaryOperationImpl<Int64, Int16, ModuloImpl<Int64, Int16>> : ModuloByConstantImpl<Int64, Int16> {};
template <> struct BinaryOperationImpl<Int64, Int32, ModuloImpl<Int64, Int32>> : ModuloByConstantImpl<Int64, Int32> {};
template <> struct BinaryOperationImpl<Int64, Int64, ModuloImpl<Int64, Int64>> : ModuloByConstantImpl<Int64, Int64> {};
template <> struct BinaryOperationImpl<Int32, Int8, ModuloImpl<Int32, Int8>> : ModuloByConstantImpl<Int32, Int8> {};
template <> struct BinaryOperationImpl<Int32, Int16, ModuloImpl<Int32, Int16>> : ModuloByConstantImpl<Int32, Int16> {};
template <> struct BinaryOperationImpl<Int32, Int32, ModuloImpl<Int32, Int32>> : ModuloByConstantImpl<Int32, Int32> {};
template <> struct BinaryOperationImpl<Int32, Int64, ModuloImpl<Int32, Int64>> : ModuloByConstantImpl<Int32, Int64> {};
}

View File

@ -4,10 +4,12 @@
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/DataTypes/DataTypeString.h>
#include <DB/DataTypes/DataTypeFixedString.h>
#include <DB/DataTypes/DataTypeArray.h>
#include <DB/DataTypes/DataTypeDate.h>
#include <DB/DataTypes/DataTypeDateTime.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/Columns/ColumnArray.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Functions/IFunction.h>
@ -375,7 +377,7 @@ public:
prev_offset = new_offset;
}
if (out_offsets.back() != out_vec.size())
if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR);
return true;
@ -436,7 +438,7 @@ public:
prev_offset = new_offset;
}
if (out_offsets.back() != out_vec.size())
if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR);
return true;
@ -742,7 +744,7 @@ public:
}
out_vec.resize(pos - begin);
if (out_offsets.back() != out_vec.size())
if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR);
return true;
@ -797,7 +799,7 @@ public:
}
out_vec.resize(pos - begin);
if (out_offsets.back() != out_vec.size())
if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR);
return true;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
#include <DB/Functions/IFunction.h>
#include "NumberTraits.h"
#include <DB/Columns/ColumnString.h>
#include <DB/DataTypes/DataTypeString.h>
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/IO/WriteBufferFromVector.h>
#include <DB/IO/WriteBufferFromString.h>

View File

@ -50,6 +50,8 @@ namespace DB
* не предназначена для пользователя, а используется только как prerequisites для функций высшего порядка.
*
* sleep(n) - спит n секунд каждый блок.
*
* bar(x, min, max, width) - рисует полосу из количества символов, пропорционального (x - min) и равного width при x == max.
*/
@ -274,7 +276,7 @@ public:
{
const IColumn & argument = *block.getByPosition(arguments[0]).column;
if (!argument.isConst())
throw Exception("Argument for function 'materialize' must be constant.", ErrorCodes::ILLEGAL_COLUMN);
throw Exception("Argument for function " + getName() + " must be constant.", ErrorCodes::ILLEGAL_COLUMN);
block.getByPosition(result).column = dynamic_cast<const IColumnConst &>(argument).convertToFullColumn();
}
@ -354,7 +356,7 @@ public:
DataTypePtr getReturnType(const DataTypes & arguments) const
{
if (arguments.size() < 2)
throw Exception("Function tuple requires at least two arguments.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
throw Exception("Function " + getName() + " requires at least two arguments.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
return new DataTypeTuple(arguments);
}
@ -386,18 +388,18 @@ public:
ExpressionActions::Actions & out_prerequisites)
{
if (arguments.size() != 2)
throw Exception("Function tupleElement requires exactly two arguments: tuple and element index.",
throw Exception("Function " + getName() + " requires exactly two arguments: tuple and element index.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
const ColumnConstUInt8 * index_col = typeid_cast<const ColumnConstUInt8 *>(&*arguments[1].column);
if (!index_col)
throw Exception("Second argument to tupleElement must be a constant UInt8", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
throw Exception("Second argument to " + getName() + " must be a constant UInt8", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
size_t index = index_col->getData();
const DataTypeTuple * tuple = typeid_cast<const DataTypeTuple *>(&*arguments[0].type);
if (!tuple)
throw Exception("First argument for function tupleElement must be tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
if (index == 0)
throw Exception("Indices in tuples are 1-based.", ErrorCodes::ILLEGAL_INDEX);
@ -417,10 +419,10 @@ public:
const ColumnConstUInt8 * index_col = typeid_cast<const ColumnConstUInt8 *>(&*block.getByPosition(arguments[1]).column);
if (!tuple_col)
throw Exception("First argument for function tupleElement must be tuple.", ErrorCodes::ILLEGAL_COLUMN);
throw Exception("First argument for function " + getName() + " must be tuple.", ErrorCodes::ILLEGAL_COLUMN);
if (!index_col)
throw Exception("Second argument for function tupleElement must be UInt8 constant literal.", ErrorCodes::ILLEGAL_COLUMN);
throw Exception("Second argument for function " + getName() + " must be UInt8 constant literal.", ErrorCodes::ILLEGAL_COLUMN);
size_t index = index_col->getData();
if (index == 0)
@ -472,11 +474,11 @@ public:
DataTypePtr getReturnType(const DataTypes & arguments) const
{
if (arguments.size() != 1)
throw Exception("Function arrayJoin requires exactly one argument.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
throw Exception("Function " + getName() + " requires exactly one argument.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
const DataTypeArray * arr = typeid_cast<const DataTypeArray *>(&*arguments[0]);
if (!arr)
throw Exception("Argument for function arrayJoin must be Array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
throw Exception("Argument for function " + getName() + " must be Array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return arr->getNestedType()->clone();
}
@ -484,7 +486,7 @@ public:
/// Выполнить функцию над блоком.
void execute(Block & block, const ColumnNumbers & arguments, size_t result)
{
throw Exception("Function arrayJoin must not be executed directly.", ErrorCodes::FUNCTION_IS_SPECIAL);
throw Exception("Function " + getName() + " must not be executed directly.", ErrorCodes::FUNCTION_IS_SPECIAL);
}
};
@ -539,4 +541,202 @@ class FunctionReplicate : public IFunction
}
};
class FunctionBar : public IFunction
{
public:
/// Получить имя функции.
String getName() const
{
return "bar";
}
/// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
DataTypePtr getReturnType(const DataTypes & arguments) const
{
if (arguments.size() != 3 && arguments.size() != 4)
throw Exception("Function " + getName() + " requires from 3 or 4 parameters: value, min_value, max_value, [max_width_of_bar = 80]. Passed "
+ toString(arguments.size()) + ".",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
if (!arguments[0]->isNumeric() || !arguments[1]->isNumeric() || !arguments[2]->isNumeric()
|| (arguments.size() == 4 && !arguments[3]->isNumeric()))
throw Exception("All arguments for function " + getName() + " must be numeric.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return new DataTypeString;
}
/// Выполнить функцию над блоком.
void execute(Block & block, const ColumnNumbers & arguments, size_t result)
{
Int64 min = extractConstant<Int64>(block, arguments, 1, "Second"); /// Уровень значения, при котором полоска имеет нулевую длину.
Int64 max = extractConstant<Int64>(block, arguments, 2, "Third"); /// Уровень значения, при котором полоска имеет максимальную длину.
/// Максимальная ширина полоски в символах, по-умолчанию.
Float64 max_width = arguments.size() == 4
? extractConstant<Float64>(block, arguments, 3, "Fourth")
: 80;
if (max_width < 1)
throw Exception("Max_width argument must be >= 1.", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
if (max_width > 1000)
throw Exception("Too large max_width.", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
const auto & src = *block.getByPosition(arguments[0]).column;
if (src.isConst())
{
auto res_column = new ColumnConstString(block.rowsInFirstColumn(), "");
block.getByPosition(result).column = res_column;
if ( executeConstNumber<UInt8> (src, *res_column, min, max, max_width)
|| executeConstNumber<UInt16> (src, *res_column, min, max, max_width)
|| executeConstNumber<UInt32> (src, *res_column, min, max, max_width)
|| executeConstNumber<UInt64> (src, *res_column, min, max, max_width)
|| executeConstNumber<Int8> (src, *res_column, min, max, max_width)
|| executeConstNumber<Int16> (src, *res_column, min, max, max_width)
|| executeConstNumber<Int32> (src, *res_column, min, max, max_width)
|| executeConstNumber<Int64> (src, *res_column, min, max, max_width)
|| executeConstNumber<Float32> (src, *res_column, min, max, max_width)
|| executeConstNumber<Float64> (src, *res_column, min, max, max_width))
{
}
else
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
}
else
{
auto res_column = new ColumnString;
block.getByPosition(result).column = res_column;
if ( executeNumber<UInt8> (src, *res_column, min, max, max_width)
|| executeNumber<UInt16> (src, *res_column, min, max, max_width)
|| executeNumber<UInt32> (src, *res_column, min, max, max_width)
|| executeNumber<UInt64> (src, *res_column, min, max, max_width)
|| executeNumber<Int8> (src, *res_column, min, max, max_width)
|| executeNumber<Int16> (src, *res_column, min, max, max_width)
|| executeNumber<Int32> (src, *res_column, min, max, max_width)
|| executeNumber<Int64> (src, *res_column, min, max, max_width)
|| executeNumber<Float32> (src, *res_column, min, max, max_width)
|| executeNumber<Float64> (src, *res_column, min, max, max_width))
{
}
else
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
}
}
private:
template <typename T>
T extractConstant(Block & block, const ColumnNumbers & arguments, size_t argument_pos, const char * which_argument) const
{
const auto & column = *block.getByPosition(arguments[argument_pos]).column;
if (!column.isConst())
throw Exception(which_argument + String(" argument for function ") + getName() + " must be constant.", ErrorCodes::ILLEGAL_COLUMN);
return apply_visitor(FieldVisitorConvertToNumber<T>(), column[0]);
}
static constexpr size_t BAR_CHAR_SIZE = strlen("");
template <typename T>
static Float64 barWidth(T x, Int64 min, Int64 max, Float64 max_width)
{
if (x <= min)
return 0;
if (x >= max)
return max_width;
return (x - min) * max_width / (max - min);
}
static size_t barWidthInBytes(Float64 width)
{
return ceil(width - 1.0 / 8) * BAR_CHAR_SIZE;
}
/// В dst должно быть место для barWidthInBytes(width) символов и завершающего нуля.
static void renderBar(Float64 width, char * dst)
{
size_t floor_width = floor(width);
for (size_t i = 0; i < floor_width; ++i)
{
memcpy(dst, "", BAR_CHAR_SIZE);
dst += BAR_CHAR_SIZE;
}
size_t remainder = floor((width - floor_width) * 8);
if (remainder)
{
memcpy(dst, &"▏▎▍▌▋▋▊▉"[(remainder - 1) * BAR_CHAR_SIZE], BAR_CHAR_SIZE);
dst += BAR_CHAR_SIZE;
}
*dst = 0;
}
template <typename T>
static void fill(const PODArray<T> & src, ColumnString::Chars_t & dst_chars, ColumnString::Offsets_t & dst_offsets,
Int64 min, Int64 max, Float64 max_width)
{
size_t size = src.size();
size_t current_offset = 0;
dst_offsets.resize(size);
dst_chars.reserve(size * (barWidthInBytes(max_width) + 1)); /// строки 0-terminated.
for (size_t i = 0; i < size; ++i)
{
Float64 width = barWidth(src[i], min, max, max_width);
size_t next_size = current_offset + barWidthInBytes(width) + 1;
dst_chars.resize(next_size);
renderBar(width, reinterpret_cast<char *>(&dst_chars[current_offset]));
current_offset = next_size;
dst_offsets[i] = current_offset;
}
}
template <typename T>
static void fill(T src, String & dst_chars,
Int64 min, Int64 max, Float64 max_width)
{
Float64 width = barWidth(src, min, max, max_width);
dst_chars.resize(barWidthInBytes(width));
renderBar(width, &dst_chars[0]);
}
template <typename T>
static bool executeNumber(const IColumn & src, ColumnString & dst, Int64 min, Int64 max, Float64 max_width)
{
if (const ColumnVector<T> * col = typeid_cast<const ColumnVector<T> *>(&src))
{
fill(col->getData(), dst.getChars(), dst.getOffsets(), min, max, max_width);
return true;
}
else
return false;
}
template <typename T>
static bool executeConstNumber(const IColumn & src, ColumnConstString & dst, Int64 min, Int64 max, Float64 max_width)
{
if (const ColumnConst<T> * col = typeid_cast<const ColumnConst<T> *>(&src))
{
fill(col->getData(), dst.getData(), min, max, max_width);
return true;
}
else
return false;
}
};
}

View File

@ -4,6 +4,7 @@
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/Functions/IFunction.h>
#include <DB/Common/HashTable/Hash.h>
#include <stats/IntHash.h>
@ -20,7 +21,7 @@ namespace DB
*
* Некриптографические генераторы:
*
* rand - linear congruental generator 0 .. 2^31 - 1.
* rand - linear congruental generator 0 .. 2^32 - 1.
* rand64 - комбинирует несколько значений rand, чтобы получить значения из диапазона 0 .. 2^64 - 1.
*
* В качестве затравки используют время.
@ -30,13 +31,37 @@ namespace DB
namespace detail
{
void seed(drand48_data & rand_state, intptr_t additional_seed)
struct LinearCongruentialGenerator
{
/// Константы из man lrand48_r.
static constexpr UInt64 a = 0x5DEECE66D;
static constexpr UInt64 c = 0xB;
/// А эта - из head -c8 /dev/urandom | xxd -p
UInt64 current = 0x09826f4a081cee35ULL;
LinearCongruentialGenerator() {}
LinearCongruentialGenerator(UInt64 value) : current(value) {}
void seed(UInt64 value)
{
current = value;
}
UInt32 next()
{
current = current * a + c;
return current >> 16;
}
};
void seed(LinearCongruentialGenerator & generator, intptr_t additional_seed)
{
struct timespec times;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &times))
throwFromErrno("Cannot clock_gettime.", ErrorCodes::CANNOT_CLOCK_GETTIME);
srand48_r(intHash32<0>(times.tv_nsec ^ intHash32<0>(additional_seed)), &rand_state);
generator.seed(intHash64(times.tv_nsec ^ intHash64(additional_seed)));
}
}
@ -46,15 +71,34 @@ struct RandImpl
static void execute(PODArray<ReturnType> & res)
{
drand48_data rand_state;
detail::seed(rand_state, reinterpret_cast<intptr_t>(&res[0]));
detail::LinearCongruentialGenerator generator0;
detail::LinearCongruentialGenerator generator1;
detail::LinearCongruentialGenerator generator2;
detail::LinearCongruentialGenerator generator3;
detail::seed(generator0, 0xfb4121280b2ab902ULL + reinterpret_cast<intptr_t>(&res[0]));
detail::seed(generator1, 0x0121cf76df39c673ULL + reinterpret_cast<intptr_t>(&res[0]));
detail::seed(generator2, 0x17ae86e3a19a602fULL + reinterpret_cast<intptr_t>(&res[0]));
detail::seed(generator3, 0x8b6e16da7e06d622ULL + reinterpret_cast<intptr_t>(&res[0]));
size_t size = res.size();
for (size_t i = 0; i < size; ++i)
ReturnType * pos = &res[0];
ReturnType * end = pos + size;
ReturnType * end4 = pos + size / 4 * 4;
while (pos < end4)
{
long rand_res;
lrand48_r(&rand_state, &rand_res);
res[i] = rand_res;
pos[0] = generator0.next();
pos[1] = generator1.next();
pos[2] = generator2.next();
pos[3] = generator3.next();
pos += 4;
}
while (pos < end)
{
pos[0] = generator0.next();
++pos;
}
}
};
@ -65,21 +109,32 @@ struct Rand64Impl
static void execute(PODArray<ReturnType> & res)
{
drand48_data rand_state;
detail::seed(rand_state, reinterpret_cast<intptr_t>(&res[0]));
detail::LinearCongruentialGenerator generator0;
detail::LinearCongruentialGenerator generator1;
detail::LinearCongruentialGenerator generator2;
detail::LinearCongruentialGenerator generator3;
detail::seed(generator0, 0xfb4121280b2ab902ULL + reinterpret_cast<intptr_t>(&res[0]));
detail::seed(generator1, 0x0121cf76df39c673ULL + reinterpret_cast<intptr_t>(&res[0]));
detail::seed(generator2, 0x17ae86e3a19a602fULL + reinterpret_cast<intptr_t>(&res[0]));
detail::seed(generator3, 0x8b6e16da7e06d622ULL + reinterpret_cast<intptr_t>(&res[0]));
size_t size = res.size();
for (size_t i = 0; i < size; ++i)
ReturnType * pos = &res[0];
ReturnType * end = pos + size;
ReturnType * end2 = pos + size / 2 * 2;
while (pos < end2)
{
long rand_res1;
long rand_res2;
long rand_res3;
pos[0] = (static_cast<UInt64>(generator0.next()) << 32) | generator1.next();
pos[1] = (static_cast<UInt64>(generator2.next()) << 32) | generator3.next();
pos += 2;
}
lrand48_r(&rand_state, &rand_res1);
lrand48_r(&rand_state, &rand_res2);
lrand48_r(&rand_state, &rand_res3);
res[i] = rand_res1 ^ (rand_res2 << 18) ^ (rand_res3 << 33);
while (pos < end)
{
pos[0] = (static_cast<UInt64>(generator0.next()) << 32) | generator1.next();
++pos;
}
}
};

View File

@ -1,8 +1,8 @@
#pragma once
#include <DB/DataTypes/DataTypeString.h>
#include <DB/DataTypes/DataTypeArray.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Columns/ColumnArray.h>
#include <DB/Functions/IFunction.h>

View File

@ -7,7 +7,9 @@
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/DataTypes/DataTypeString.h>
#include <DB/DataTypes/DataTypeFixedString.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Common/Volnitsky.h>
#include <DB/Functions/IFunction.h>

View File

@ -282,7 +282,6 @@ inline void writeQuotedString(const String & s, WriteBuffer & buf)
writeAnyQuotedString<'\''>(s, buf);
}
/// Совместимо с JSON.
inline void writeDoubleQuotedString(const String & s, WriteBuffer & buf)
{
writeAnyQuotedString<'"'>(s, buf);

View File

@ -91,7 +91,7 @@ struct AggregationMethodKey64
/** Разместить дополнительные данные, если это необходимо, в случае, когда в хэш-таблицу был вставлен новый ключ.
*/
void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
static void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
{
}
@ -139,7 +139,7 @@ struct AggregationMethodString
static AggregateDataPtr & getAggregateData(Mapped & value) { return value; }
static const AggregateDataPtr & getAggregateData(const Mapped & value) { return value; }
void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
static void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
{
it->first.data = pool.insert(it->first.data, it->first.size);
}
@ -186,7 +186,7 @@ struct AggregationMethodFixedString
static AggregateDataPtr & getAggregateData(Mapped & value) { return value; }
static const AggregateDataPtr & getAggregateData(const Mapped & value) { return value; }
void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
static void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
{
it->first.data = pool.insert(it->first.data, it->first.size);
}
@ -226,7 +226,7 @@ struct AggregationMethodKeys128
static AggregateDataPtr & getAggregateData(Mapped & value) { return value; }
static const AggregateDataPtr & getAggregateData(const Mapped & value) { return value; }
void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
static void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
{
}
@ -271,7 +271,7 @@ struct AggregationMethodHashed
static AggregateDataPtr & getAggregateData(Mapped & value) { return value.second; }
static const AggregateDataPtr & getAggregateData(const Mapped & value) { return value.second; }
void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
static void onNewKey(iterator & it, size_t keys_size, size_t i, StringRefs & keys, Arena & pool)
{
it->second.first = placeKeysInPool(i, keys_size, keys, pool);
}

View File

@ -29,10 +29,19 @@ public:
/// Соединения с удалёнными серверами.
ConnectionPools pools;
struct ShardInfo
{
/// contains names of directories for asynchronous write to StorageDistributed
std::vector<std::string> dir_names;
int weight;
size_t num_local_nodes;
};
std::vector<ShardInfo> shard_info_vec;
std::vector<size_t> slot_to_shard;
/// используеться для выставления ограничения на размер таймаута
static Poco::Timespan saturate(const Poco::Timespan & v, const Poco::Timespan & limit);
private:
struct Address
{
/** В конфиге адреса либо находятся в узлах <node>:
@ -59,6 +68,7 @@ private:
Address(const String & host_port_, const String & user_, const String & password_);
};
private:
static bool isLocal(const Address & address);
/// Массив шардов. Каждый шард - адреса одного сервера.

View File

@ -9,6 +9,7 @@
#include <Yandex/logger_useful.h>
#include <DB/Core/NamesAndTypes.h>
#include <DB/Common/Macros.h>
#include <DB/IO/UncompressedCache.h>
#include <DB/Storages/MarkCache.h>
#include <DB/DataStreams/FormatFactory.h>
@ -56,7 +57,7 @@ typedef std::vector<DatabaseAndTableName> Dependencies;
*/
struct ContextShared
{
Logger * log; /// Логгер.
Logger * log = &Logger::get("Context"); /// Логгер.
struct AfterDestroy
{
@ -69,7 +70,7 @@ struct ContextShared
LOG_INFO(log, "Uninitialized shared context.");
#endif
}
} after_destroy;
} after_destroy {log};
mutable Poco::Mutex mutex; /// Для доступа и модификации разделяемых объектов.
@ -95,8 +96,8 @@ struct ContextShared
ViewDependencies view_dependencies; /// Текущие зависимости
ConfigurationPtr users_config; /// Конфиг с секциями users, profiles и quotas.
InterserverIOHandler interserver_io_handler; /// Обработчик для межсерверной передачи данных.
String default_replica_name; /// Имя реплики из конфига.
BackgroundProcessingPoolPtr background_pool; /// Пул потоков для фоновой работы, выполняемой таблицами.
Macros macros; /// Подстановки из конфига.
/// Кластеры для distributed таблиц
/// Создаются при создании Distributed таблиц, так как нужно дождаться пока будут выставлены Settings
@ -105,8 +106,6 @@ struct ContextShared
bool shutdown_called = false;
ContextShared() : log(&Logger::get("Context")), after_destroy(log) {};
~ContextShared()
{
#ifndef DBMS_CLIENT
@ -238,9 +237,8 @@ public:
String getDefaultFormat() const; /// Если default_format не задан - возвращается некоторый глобальный формат по-умолчанию.
void setDefaultFormat(const String & name);
/// Имя этой реплики из конфига.
String getDefaultReplicaName() const;
void setDefaultReplicaName(const String & name);
const Macros & getMacros() const;
void setMacros(Macros && macros);
Settings getSettings() const;
void setSettings(const Settings & settings_);

View File

@ -22,11 +22,41 @@ public:
/** Изменяет список столбцов в метаданных таблицы на диске. Нужно вызывать под TableStructureLock соответствующей таблицы.
*/
static void updateMetadata(const String & database, const String & table, const NamesAndTypesList & columns, Context & context);
static AlterCommands parseAlter(const ASTAlterQuery::ParameterContainer & params, const DataTypeFactory & data_type_factory);
private:
struct PartitionCommand
{
enum Type
{
DROP_PARTITION,
ATTACH_PARTITION,
};
Type type;
Field partition;
bool detach; /// true для DETACH PARTITION.
bool unreplicated;
bool part;
static PartitionCommand dropPartition(const Field & partition, bool detach)
{
return {DROP_PARTITION, partition, detach};
}
static PartitionCommand attachPartition(const Field & partition, bool unreplicated, bool part)
{
return {ATTACH_PARTITION, partition, false, unreplicated, part};
}
};
typedef std::vector<PartitionCommand> PartitionCommands;
ASTPtr query_ptr;
Context context;
static void parseAlter(const ASTAlterQuery::ParameterContainer & params, const DataTypeFactory & data_type_factory,
AlterCommands & out_alter_commands, PartitionCommands & out_partition_commands);
};
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <DB/Interpreters/Context.h>
#include <DB/Parsers/ASTIdentifier.h>
namespace DB
{
class InterpreterCheckQuery
{
public:
InterpreterCheckQuery(ASTPtr query_ptr_, Context & context_);
BlockInputStreamPtr execute();
DB::Block getSampleBlock();
private:
ASTPtr query_ptr;
Context context;
DB::Block result;
};
}

View File

@ -1,7 +1,5 @@
#pragma once
#include <statdaemons/Stopwatch.h>
#include <Yandex/logger_useful.h>
#include <DB/Parsers/ASTJoin.h>

View File

@ -2,8 +2,6 @@
#include <set>
#include <statdaemons/Stopwatch.h>
#include <Yandex/logger_useful.h>
#include <DB/Core/ColumnNumbers.h>

View File

@ -70,6 +70,9 @@ struct Settings
* TODO: Сейчас применяется только при запуске сервера. Можно сделать изменяемым динамически. */ \
M(SettingUInt64, background_pool_size, DBMS_DEFAULT_BACKGROUND_POOL_SIZE) \
\
/** Sleep time for StorageDistributed DirectoryMonitors in case there is no work or exception has been thrown */ \
M(SettingMilliseconds, distributed_directory_monitor_sleep_time_ms, DBMS_DISTRIBUTED_DIRECTORY_MONITOR_SLEEP_TIME_MS) \
\
M(SettingLoadBalancing, load_balancing, LoadBalancing::RANDOM) \
\
M(SettingTotalsMode, totals_mode, TotalsMode::BEFORE_HAVING) \

View File

@ -9,7 +9,8 @@ namespace DB
* ALTER TABLE [db.]name_type
* ADD COLUMN col_name type [AFTER col_after],
* DROP COLUMN col_drop,
* MODIFY COLUMN col_name type
* MODIFY COLUMN col_name type,
* DROP PARTITION partition
* ...
*/
@ -18,16 +19,18 @@ class ASTAlterQuery : public IAST
public:
enum ParameterType
{
ADD,
DROP,
MODIFY,
ADD_COLUMN,
DROP_COLUMN,
MODIFY_COLUMN,
DROP_PARTITION,
ATTACH_PARTITION,
NO_TYPE
};
struct Parameters
{
Parameters() : type(NO_TYPE) {}
int type;
int type = NO_TYPE;
/** В запросе ADD COLUMN здесь хранится имя и тип добавляемого столбца
* В запросе DROP это поле не используется
@ -40,12 +43,21 @@ public:
*/
ASTPtr column;
/** В запросе DROP PARTITION здесь хранится имя partition'а.
*/
ASTPtr partition;
bool detach = false; /// true для DETACH PARTITION.
bool part = false; /// true для ATTACH [UNREPLICATED] PART
bool unreplicated = false; /// true для ATTACH UNREPLICATED ...
/// deep copy
void clone(Parameters & p) const
{
p.type = type;
p.column = column->clone();
p = *this;
p.name_type = name_type->clone();
p.column = column->clone();
p.partition = partition->clone();
}
};
typedef std::vector<Parameters> ParameterContainer;
@ -54,6 +66,18 @@ public:
String table;
void addParameters(const Parameters & params)
{
parameters.push_back(params);
if (params.name_type)
children.push_back(params.name_type);
if (params.column)
children.push_back(params.column);
if (params.partition)
children.push_back(params.partition);
}
ASTAlterQuery(StringRange range_ = StringRange()) : IAST(range_) {};
/** Получить текст, который идентифицирует этот элемент. */

View File

@ -0,0 +1,25 @@
#pragma once
#include <DB/Parsers/IAST.h>
namespace DB
{
struct ASTCheckQuery : public IAST
{
ASTCheckQuery(StringRange range_ = StringRange()) : IAST(range_) {};
ASTCheckQuery(const ASTCheckQuery & ast) = default;
/** Получить текст, который идентифицирует этот элемент. */
String getID() const { return ("CheckQuery_" + database + "_" + table); };
ASTPtr clone() const
{
return new ASTCheckQuery(*this);
}
std::string database;
std::string table;
};
}

View File

@ -10,6 +10,7 @@ namespace DB
* [ADD COLUMN col_name type [AFTER col_after],]
* [DROP COLUMN col_drop, ...]
* [MODIFY COLUMN col_modify type, ...]
* [DROP PARTITION partition, ...]
*/
class ParserAlterQuery : public IParserBase
{

View File

@ -0,0 +1,17 @@
#pragma once
#include <DB/Parsers/IParserBase.h>
namespace DB
{
/** Запрос вида
* CHECK [TABLE] [database.]table
*/
class ParserCheckQuery : public IParserBase
{
protected:
const char * getName() const { return "ALTER query"; }
bool parseImpl(Pos & pos, Pos end, ASTPtr & node, Expected & expected);
};
}

View File

@ -24,6 +24,7 @@
#include <DB/Parsers/ASTAlterQuery.h>
#include <DB/Parsers/ASTShowProcesslistQuery.h>
#include <DB/Parsers/ASTJoin.h>
#include <DB/Parsers/ASTCheckQuery.h>
//#include <DB/Parsers/ASTMultiQuery.h>
@ -57,6 +58,7 @@ void formatAST(const ASTOrderByElement & ast, std::ostream & s, size_t indent =
void formatAST(const ASTSubquery & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false);
void formatAST(const ASTAlterQuery & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false);
void formatAST(const ASTJoin & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false);
void formatAST(const ASTCheckQuery & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false);
//void formatAST(const ASTMultiQuery & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false);
void formatAST(const ASTQueryWithTableAndOutput & ast, std::string name, std::ostream & s,

View File

@ -6,7 +6,7 @@
namespace DB
{
/// Операция из запроса ALTER. Добавление столбцов типа Nested не развернуто в добавление отдельных столбцов.
/// Операция из запроса ALTER (кроме DROP PARTITION). Добавление столбцов типа Nested не развернуто в добавление отдельных столбцов.
struct AlterCommand
{
enum Type

View File

@ -0,0 +1,207 @@
#pragma once
#include <DB/DataStreams/RemoteBlockOutputStream.h>
#include <DB/Common/escapeForFileName.h>
#include <DB/Storages/StorageDistributed.h>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <thread>
#include <mutex>
namespace DB
{
namespace
{
template <typename PoolFactory>
ConnectionPools createPoolsForAddresses(const std::string & name, PoolFactory && factory)
{
ConnectionPools pools;
for (auto it = boost::make_split_iterator(name, boost::first_finder(",")); it != decltype(it){}; ++it)
{
const auto address = boost::copy_range<std::string>(*it);
const auto user_pw_end = strchr(address.data(), '@');
const auto colon = strchr(address.data(), ':');
if (!user_pw_end || !colon)
throw Exception{
"Shard address '" + address + "' does not match to 'user[:password]@host:port' pattern",
ErrorCodes::INCORRECT_FILE_NAME
};
const auto has_pw = colon < user_pw_end;
const auto host_end = has_pw ? strchr(user_pw_end + 1, ':') : colon;
if (!host_end)
throw Exception{
"Shard address '" + address + "' does not contain port",
ErrorCodes::INCORRECT_FILE_NAME
};
const auto user = unescapeForFileName({address.data(), has_pw ? colon : user_pw_end});
const auto password = has_pw ? unescapeForFileName({colon + 1, user_pw_end}) : std::string{};
const auto host = unescapeForFileName({user_pw_end + 1, host_end});
const auto port = parse<UInt16>(host_end + 1);
pools.emplace_back(factory(host, port, user, password));
}
return pools;
}
}
/** Implementation for StorageDistributed::DirectoryMonitor nested class.
* This type is not designed for standalone use. */
class StorageDistributed::DirectoryMonitor
{
public:
DirectoryMonitor(StorageDistributed & storage, const std::string & name)
: storage(storage), pool{createPool(name)}, path{storage.path + name + '/'}
, sleep_time{storage.context.getSettingsRef().distributed_directory_monitor_sleep_time_ms.totalMilliseconds()}
, log{&Logger::get(getLoggerName())}
{
}
~DirectoryMonitor()
{
{
quit = true;
std::lock_guard<std::mutex> lock{mutex};
}
cond.notify_one();
thread.join();
}
private:
void run()
{
std::unique_lock<std::mutex> lock{mutex};
const auto quit_requested = [this] { return quit; };
while (!quit_requested())
{
auto do_sleep = true;
try
{
do_sleep = !findFiles();
}
catch (...)
{
do_sleep = true;
tryLogCurrentException(getLoggerName().data());
}
if (do_sleep)
cond.wait_for(lock, sleep_time, quit_requested);
}
}
ConnectionPoolPtr createPool(const std::string & name)
{
const auto pool_factory = [this, &name] (const std::string & host, const UInt16 port,
const std::string & user, const std::string & password) {
return new ConnectionPool{
1, host, port, "",
user, password, storage.context.getDataTypeFactory(),
storage.getName() + '_' + name};
};
auto pools = createPoolsForAddresses(name, pool_factory);
return pools.size() == 1 ? pools.front() : new ConnectionPoolWithFailover(pools, LoadBalancing::RANDOM);
}
bool findFiles()
{
std::map<UInt64, std::string> files;
Poco::DirectoryIterator end;
for (Poco::DirectoryIterator it{path}; it != end; ++it)
{
const auto & file_path_str = it->path();
Poco::Path file_path{file_path_str};
if (!it->isDirectory() && 0 == strncmp(file_path.getExtension().data(), "bin", strlen("bin")))
files[parse<UInt64>(file_path.getBaseName())] = file_path_str;
}
if (files.empty())
return false;
for (const auto & file : files)
{
if (quit)
return true;
processFile(file.second);
}
return true;
}
void processFile(const std::string & file_path)
{
LOG_TRACE(log, "Started processing `" << file_path << '`');
auto connection = pool->get();
try
{
ReadBufferFromFile in{file_path};
std::string insert_query;
readStringBinary(insert_query, in);
RemoteBlockOutputStream remote{*connection, insert_query};
remote.writePrefix();
remote.writePrepared(in);
remote.writeSuffix();
}
catch (const Exception & e)
{
const auto code = e.code();
/// mark file as broken if necessary
if (code == ErrorCodes::CHECKSUM_DOESNT_MATCH ||
code == ErrorCodes::TOO_LARGE_SIZE_COMPRESSED ||
code == ErrorCodes::CANNOT_READ_ALL_DATA)
{
const auto last_path_separator_pos = file_path.rfind('/');
const auto & path = file_path.substr(0, last_path_separator_pos + 1);
const auto & file_name = file_path.substr(last_path_separator_pos + 1);
const auto & broken_path = path + "broken/";
const auto & broken_file_path = broken_path + file_name;
Poco::File{broken_path}.createDirectory();
Poco::File{file_path}.renameTo(broken_file_path);
LOG_ERROR(log, "Renamed `" << file_path << "` to `" << broken_file_path << '`');
}
throw;
}
Poco::File{file_path}.remove();
LOG_TRACE(log, "Finished processing `" << file_path << '`');
}
std::string getLoggerName() const
{
return storage.name + '.' + storage.getName() + ".DirectoryMonitor";
}
StorageDistributed & storage;
ConnectionPoolPtr pool;
std::string path;
std::chrono::milliseconds sleep_time;
bool quit{false};
std::mutex mutex;
std::condition_variable cond;
Logger * log;
std::thread thread{&DirectoryMonitor::run, this};
};
}

View File

@ -0,0 +1,217 @@
#pragma once
#include <DB/Storages/StorageDistributed.h>
#include <DB/Storages/Distributed/queryToString.h>
#include <DB/IO/WriteBufferFromFile.h>
#include <DB/IO/CompressedWriteBuffer.h>
#include <DB/DataStreams/NativeBlockOutputStream.h>
#include <DB/Interpreters/InterpreterInsertQuery.h>
#include <statdaemons/Increment.h>
#include <statdaemons/stdext.h>
#include <iostream>
#include <type_traits>
namespace DB
{
/** Запись асинхронная - данные сначала записываются на локальную файловую систему, а потом отправляются на удалённые серверы.
* Если Distributed таблица использует более одного шарда, то для того, чтобы поддерживалась запись,
* при создании таблицы должен быть указан дополнительный параметр у ENGINE - ключ шардирования.
* Ключ шардирования - произвольное выражение от столбцов. Например, rand() или UserID.
* При записи блок данных разбивается по остатку от деления ключа шардирования на суммарный вес шардов,
* и полученные блоки пишутся в сжатом Native формате в отдельные директории для отправки.
* Для каждого адреса назначения (каждой директории с данными для отправки), в StorageDistributed создаётся отдельный поток,
* который следит за директорией и отправляет данные. */
class DistributedBlockOutputStream : public IBlockOutputStream
{
public:
DistributedBlockOutputStream(StorageDistributed & storage, const ASTPtr & query_ast)
: storage(storage), query_ast(query_ast)
{
}
void write(const Block & block) override
{
if (storage.getShardingKeyExpr() && storage.cluster.shard_info_vec.size() > 1)
return writeSplit(block);
writeImpl(block);
}
private:
template <typename T>
static std::vector<IColumn::Filter> createFiltersImpl(const size_t num_rows, const IColumn * column, const Cluster & cluster)
{
const auto total_weight = cluster.slot_to_shard.size();
const auto num_shards = cluster.shard_info_vec.size();
std::vector<IColumn::Filter> filters(num_shards);
/** Деление отрицательного числа с остатком на положительное, в C++ даёт отрицательный остаток.
* Для данной задачи это не подходит. Поэтому, будем обрабатывать знаковые типы как беззнаковые.
* Это даёт уже что-то совсем не похожее на деление с остатком, но подходящее для данной задачи.
*/
using UnsignedT = typename std::make_unsigned<T>::type;
/// const columns contain only one value, therefore we do not need to read it at every iteration
if (column->isConst())
{
const auto data = typeid_cast<const ColumnConst<T> *>(column)->getData();
const auto shard_num = cluster.slot_to_shard[static_cast<UnsignedT>(data) % total_weight];
for (size_t i = 0; i < num_shards; ++i)
filters[i].assign(num_rows, static_cast<UInt8>(shard_num == i));
}
else
{
const auto & data = typeid_cast<const ColumnVector<T> *>(column)->getData();
for (size_t i = 0; i < num_shards; ++i)
{
filters[i].resize(num_rows);
for (size_t j = 0; j < num_rows; ++j)
filters[i][j] = cluster.slot_to_shard[static_cast<UnsignedT>(data[j]) % total_weight] == i;
}
}
return filters;
}
std::vector<IColumn::Filter> createFilters(Block block)
{
using create_filters_sig = std::vector<IColumn::Filter>(size_t, const IColumn *, const Cluster &);
/// hashmap of pointers to functions corresponding to each integral type
static std::unordered_map<std::string, create_filters_sig *> creators{
{ TypeName<UInt8>::get(), &createFiltersImpl<UInt8> },
{ TypeName<UInt16>::get(), &createFiltersImpl<UInt16> },
{ TypeName<UInt32>::get(), &createFiltersImpl<UInt32> },
{ TypeName<UInt64>::get(), &createFiltersImpl<UInt64> },
{ TypeName<Int8>::get(), &createFiltersImpl<Int8> },
{ TypeName<Int16>::get(), &createFiltersImpl<Int16> },
{ TypeName<Int32>::get(), &createFiltersImpl<Int32> },
{ TypeName<Int64>::get(), &createFiltersImpl<Int64> },
};
storage.getShardingKeyExpr()->execute(block);
const auto & key_column = block.getByName(storage.getShardingKeyColumnName());
/// check that key column has valid type
const auto it = creators.find(key_column.type->getName());
return it != std::end(creators)
? (*it->second)(block.rowsInFirstColumn(), key_column.column.get(), storage.cluster)
: throw Exception{
"Sharding key expression does not evaluate to an integer type",
ErrorCodes::TYPE_MISMATCH
};
}
void writeSplit(const Block & block)
{
const auto num_cols = block.columns();
/// cache column pointers for later reuse
std::vector<const IColumn*> columns(num_cols);
for (size_t i = 0; i < columns.size(); ++i)
columns[i] = block.getByPosition(i).column;
auto filters = createFilters(block);
const auto num_shards = storage.cluster.shard_info_vec.size();
for (size_t i = 0; i < num_shards; ++i)
{
auto target_block = block.cloneEmpty();
for (size_t col = 0; col < num_cols; ++col)
target_block.getByPosition(col).column = columns[col]->filter(filters[i]);
if (target_block.rowsInFirstColumn())
writeImpl(target_block, i);
}
}
void writeImpl(const Block & block, const size_t shard_id = 0)
{
const auto & shard_info = storage.cluster.shard_info_vec[shard_id];
if (shard_info.num_local_nodes)
writeToLocal(block, shard_info.num_local_nodes);
/// dir_names is empty if shard has only local addresses
if (!shard_info.dir_names.empty())
writeToShard(block, shard_info.dir_names);
}
void writeToLocal(const Block & block, const size_t repeats)
{
InterpreterInsertQuery interp{query_ast, storage.context};
auto block_io = interp.execute();
block_io.out->writePrefix();
for (size_t i = 0; i < repeats; ++i)
block_io.out->write(block);
block_io.out->writeSuffix();
}
void writeToShard(const Block & block, const std::vector<std::string> & dir_names)
{
/** tmp directory is used to ensure atomicity of transactions
* and keep monitor thread out from reading incomplete data
*/
std::string first_file_tmp_path{};
auto first = true;
const auto & query_string = queryToString(query_ast);
/// write first file, hardlink the others
for (const auto & dir_name : dir_names)
{
const auto & path = storage.getPath() + dir_name + '/';
/// ensure shard subdirectory creation and notify storage
if (Poco::File(path).createDirectory())
storage.requireDirectoryMonitor(dir_name);
const auto & file_name = toString(Increment{path + "increment.txt"}.get(true)) + ".bin";
const auto & block_file_path = path + file_name;
/** on first iteration write block to a temporary directory for subsequent hardlinking to ensure
* the inode is not freed until we're done */
if (first)
{
first = false;
const auto & tmp_path = path + "tmp/";
Poco::File(tmp_path).createDirectory();
const auto & block_file_tmp_path = tmp_path + file_name;
first_file_tmp_path = block_file_tmp_path;
WriteBufferFromFile out{block_file_tmp_path};
CompressedWriteBuffer compress{out};
NativeBlockOutputStream stream{compress};
writeStringBinary(query_string, out);
stream.writePrefix();
stream.write(block);
stream.writeSuffix();
}
if (link(first_file_tmp_path.data(), block_file_path.data()))
throwFromErrno("Could not link " + block_file_path + " to " + first_file_tmp_path);
}
/** remove the temporary file, enabling the OS to reclaim inode after all threads
* have removed their corresponding files */
Poco::File(first_file_tmp_path).remove();
}
StorageDistributed & storage;
ASTPtr query_ast;
};
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <DB/Parsers/formatAST.h>
namespace DB
{
inline std::string queryToString(const ASTPtr & query)
{
std::ostringstream s;
formatAST(*query, s, 0, false, true);
return s.str();
}
}

View File

@ -205,6 +205,20 @@ public:
throw Exception("Method alter is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
}
/** Выполнить запрос (DROP|DETACH) PARTITION.
*/
virtual void dropPartition(const Field & partition, bool detach)
{
throw Exception("Method dropPartition is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
}
/** Выполнить запрос ATTACH [UNREPLICATED] (PART|PARTITION).
*/
virtual void attachPartition(const Field & partition, bool unreplicated, bool part)
{
throw Exception("Method attachPartition is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
}
/** Выполнить какую-либо фоновую работу. Например, объединение кусков в таблице типа MergeTree.
* Возвращает - была ли выполнена какая-либо работа.
*/
@ -248,6 +262,9 @@ public:
/// Поддерживается ли индекс в секции IN
virtual bool supportsIndexForIn() const { return false; };
/// проверяет валидность данных
virtual bool checkData() const { throw DB::Exception("Check query is not supported for " + getName() + " storage"); }
protected:
IStorage() : is_dropped(false) {}

View File

@ -13,7 +13,7 @@ namespace DB
* При вызове деструктора или завершении сессии в ZooKeeper, переходит в состояние ABANDONED.
* (В том числе при падении программы)
*/
class AbandonableLockInZooKeeper
class AbandonableLockInZooKeeper : private boost::noncopyable
{
public:
enum State
@ -34,6 +34,14 @@ public:
path = zookeeper.create(path_prefix, holder_path, zkutil::CreateMode::PersistentSequential);
}
AbandonableLockInZooKeeper(AbandonableLockInZooKeeper && rhs)
: zookeeper(rhs.zookeeper)
{
std::swap(path_prefix, rhs.path_prefix);
std::swap(path, rhs.path);
std::swap(holder_path, rhs.holder_path);
}
String getPath()
{
return path;
@ -49,6 +57,7 @@ public:
{
zookeeper.remove(path);
zookeeper.remove(holder_path);
holder_path = "";
}
/// Добавляет в список действия, эквивалентные unlock().
@ -60,6 +69,9 @@ public:
~AbandonableLockInZooKeeper()
{
if (holder_path.empty())
return;
try
{
zookeeper.tryRemove(holder_path);

View File

@ -63,12 +63,14 @@ public:
void add(const String & name);
String getContainingPart(const String & name) const;
Strings getParts() const;
Strings getParts() const; /// В порядке возрастания месяца и номера блока.
size_t size() const;
static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level);
/// Возвращает true если имя директории совпадает с форматом имени директории кусочков
static bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches);
static bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec * out_matches = nullptr);
/// Кладет в DataPart данные из имени кусочка.
static void parsePartName(const String & file_name, Part & part, const Poco::RegularExpression::MatchVec * matches = nullptr);

View File

@ -16,15 +16,21 @@
namespace DB
{
/** В нескольких потоках в бесконечном цикле выполняет указанные функции.
/** Используя фиксированное количество потоков, выполнять произвольное количество задач в бесконечном цикле.
* Предназначена для задач, выполняющих постоянную фоновую работу (например, слияния).
* Задача - функция, возвращающая bool - сделала ли она какую-либо работу.
* Если сделала - надо выполнить ещё раз. Если нет - надо подождать несколько секунд, или до события wake, и выполнить ещё раз.
*
* Также, задача во время выполнения может временно увеличить какой-либо счётчик, относящийся ко всем задачам
* - например, число одновременно идующих слияний.
*/
class BackgroundProcessingPool
{
public:
typedef std::map<String, int> Counters;
/** Используется изнутри таски. Позволяет инкрементировать какие-нибудь счетчики.
* После завершения таски, все изменения откатятся.
/** Используется изнутри задачи. Позволяет инкрементировать какие-нибудь счетчики.
* После завершения задачи, все изменения откатятся.
* Например, чтобы можно было узнавать количество потоков, выполняющих большое слияние,
* можно в таске, выполняющей большое слияние, инкрементировать счетчик. Декрементировать обратно его не нужно.
*/
@ -57,10 +63,14 @@ public:
/// Переставить таск в начало очереди и разбудить какой-нибудь поток.
void wake()
{
Poco::ScopedReadRWLock rlock(rwlock);
if (removed)
return;
std::unique_lock<std::mutex> lock(pool.mutex);
pool.tasks.splice(pool.tasks.begin(), pool.tasks, iterator);
/// Не очень надежно: если все потоки сейчас выполняют работу, этот вызов никого не разбудит,
/// Не очень надёжно: если все потоки сейчас выполняют работу, этот вызов никого не разбудит,
/// и все будут спать в конце итерации.
pool.wake_event.notify_one();
}
@ -70,50 +80,32 @@ public:
BackgroundProcessingPool & pool;
Task function;
Poco::RWLock lock;
volatile bool removed;
/// При выполнении задачи, держится read lock. Переменная removed меняется под write lock-ом.
Poco::RWLock rwlock;
volatile bool removed = false;
std::list<std::shared_ptr<TaskInfo>>::iterator iterator;
TaskInfo(BackgroundProcessingPool & pool_, const Task & function_) : pool(pool_), function(function_), removed(false) {}
TaskInfo(BackgroundProcessingPool & pool_, const Task & function_) : pool(pool_), function(function_) {}
};
typedef std::shared_ptr<TaskInfo> TaskHandle;
BackgroundProcessingPool(int size_) : size(size_), sleep_seconds(10), shutdown(false) {}
void setNumberOfThreads(int size_)
BackgroundProcessingPool(int size_) : size(size_)
{
if (size_ <= 0)
throw Exception("Invalid number of threads: " + toString(size_), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
std::unique_lock<std::mutex> tlock(threads_mutex);
std::unique_lock<std::mutex> lock(mutex);
if (size_ == size)
return;
if (threads.empty())
{
size = size_;
return;
threads.resize(size);
for (auto & thread : threads)
thread = std::thread([this] { threadFunction(); });
}
throw Exception("setNumberOfThreads is not implemented for non-empty pool", ErrorCodes::NOT_IMPLEMENTED);
}
int getNumberOfThreads()
int getNumberOfThreads() const
{
std::unique_lock<std::mutex> lock(mutex);
return size;
}
void setSleepTime(double seconds)
{
std::unique_lock<std::mutex> lock(mutex);
sleep_seconds = seconds;
}
int getCounter(const String & name)
{
std::unique_lock<std::mutex> lock(mutex);
@ -122,8 +114,6 @@ public:
TaskHandle addTask(const Task & task)
{
std::unique_lock<std::mutex> lock(threads_mutex);
TaskHandle res(new TaskInfo(*this, task));
{
@ -132,44 +122,22 @@ public:
res->iterator = --tasks.end();
}
if (threads.empty())
{
shutdown = false;
counters.clear();
threads.resize(size);
for (std::thread & thread : threads)
thread = std::thread(std::bind(&BackgroundProcessingPool::threadFunction, this));
}
wake_event.notify_all();
return res;
}
void removeTask(const TaskHandle & task)
{
std::unique_lock<std::mutex> tlock(threads_mutex);
/// Дождемся завершения всех выполнений этой задачи.
/// Дождёмся завершения всех выполнений этой задачи.
{
Poco::ScopedWriteRWLock wlock(task->lock);
Poco::ScopedWriteRWLock wlock(task->rwlock);
task->removed = true;
}
{
std::unique_lock<std::mutex> lock(mutex);
auto it = std::find(tasks.begin(), tasks.end(), task);
if (it == tasks.end())
throw Exception("Task not found", ErrorCodes::LOGICAL_ERROR);
tasks.erase(it);
}
if (tasks.empty())
{
shutdown = true;
wake_event.notify_all();
for (std::thread & thread : threads)
thread.join();
threads.clear();
counters.clear();
tasks.erase(task->iterator);
}
}
@ -177,16 +145,11 @@ public:
{
try
{
std::unique_lock<std::mutex> lock(threads_mutex);
if (!threads.empty())
{
LOG_ERROR(&Logger::get("~BackgroundProcessingPool"), "Destroying non-empty BackgroundProcessingPool");
shutdown = true;
wake_event.notify_all();
for (std::thread & thread : threads)
thread.join();
}
}
catch (...)
{
tryLogCurrentException(__PRETTY_FUNCTION__);
@ -197,24 +160,25 @@ private:
typedef std::list<TaskHandle> Tasks;
typedef std::vector<std::thread> Threads;
std::mutex threads_mutex;
std::mutex mutex;
int size;
Tasks tasks; /// Таски в порядке, в котором мы планируем их выполнять.
Threads threads;
Counters counters;
double sleep_seconds;
const size_t size;
static constexpr double sleep_seconds = 10;
volatile bool shutdown;
Tasks tasks; /// Задачи в порядке, в котором мы планируем их выполнять.
Counters counters;
std::mutex mutex; /// Для работы со списком tasks, а также с counters (когда threads не пустой).
Threads threads;
volatile bool shutdown = false;
std::condition_variable wake_event;
void threadFunction()
{
while (!shutdown)
{
Counters counters_diff;
bool need_sleep = false;
size_t tasks_count = 1;
try
{
@ -236,11 +200,12 @@ private:
if (!task)
{
std::this_thread::sleep_for(std::chrono::duration<double>(sleep_seconds));
std::unique_lock<std::mutex> lock(mutex);
wake_event.wait_for(lock, std::chrono::duration<double>(sleep_seconds));
continue;
}
Poco::ScopedReadRWLock rlock(task->lock);
Poco::ScopedReadRWLock rlock(task->rwlock);
if (task->removed)
continue;
@ -248,15 +213,11 @@ private:
if (task->function(context))
{
/// Если у таска получилось выполнить какую-то работу, запустим его снова без паузы.
std::unique_lock<std::mutex> lock(mutex);
auto it = std::find(tasks.begin(), tasks.end(), task);
if (it != tasks.end())
{
/// Если у задачи получилось выполнить какую-то работу, запустим её снова без паузы.
need_sleep = false;
tasks.splice(tasks.begin(), tasks, it);
}
std::unique_lock<std::mutex> lock(mutex);
tasks.splice(tasks.begin(), tasks, task->iterator);
}
}
catch (...)
@ -265,15 +226,13 @@ private:
tryLogCurrentException(__PRETTY_FUNCTION__);
}
/// Вычтем все счетчики обратно.
/// Вычтем все счётчики обратно.
if (!counters_diff.empty())
{
std::unique_lock<std::mutex> lock(mutex);
for (const auto & it : counters_diff)
{
counters[it.first] -= it.second;
}
}
if (shutdown)
break;
@ -281,7 +240,7 @@ private:
if (need_sleep)
{
std::unique_lock<std::mutex> lock(mutex);
wake_event.wait_for(lock, std::chrono::duration<double>(sleep_seconds / tasks_count));
wake_event.wait_for(lock, std::chrono::duration<double>(sleep_seconds));
}
}
}

View File

@ -26,27 +26,51 @@ public:
{
try
{
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::reserved_bytes_mutex);
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::mutex);
if (DiskSpaceMonitor::reserved_bytes < size)
{
DiskSpaceMonitor::reserved_bytes = 0;
LOG_ERROR(&Logger::get("DiskSpaceMonitor"), "Unbalanced reservations; it's a bug");
LOG_ERROR(&Logger::get("DiskSpaceMonitor"), "Unbalanced reservations size; it's a bug");
}
else
{
DiskSpaceMonitor::reserved_bytes -= size;
}
if (DiskSpaceMonitor::reservation_count == 0)
{
LOG_ERROR(&Logger::get("DiskSpaceMonitor"), "Unbalanced reservation count; it's a bug");
}
else
{
--DiskSpaceMonitor::reservation_count;
}
}
catch (...)
{
tryLogCurrentException("~DiskSpaceMonitor");
}
}
/// Изменить количество зарезервированного места. При увеличении не делается проверка, что места достаточно.
void update(size_t new_size)
{
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::mutex);
DiskSpaceMonitor::reserved_bytes -= size;
size = new_size;
DiskSpaceMonitor::reserved_bytes += size;
}
size_t getSize() const
{
return size;
}
private:
Reservation(size_t size_) : size(size_)
{
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::reserved_bytes_mutex);
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::mutex);
DiskSpaceMonitor::reserved_bytes += size;
++DiskSpaceMonitor::reservation_count;
}
size_t size;
};
@ -65,7 +89,7 @@ public:
/// Зарезервируем дополнительно 30 МБ. Когда я тестировал, statvfs показывал на несколько мегабайт больше свободного места, чем df.
res -= std::min(res, 30 * (1ul << 20));
Poco::ScopedLock<Poco::FastMutex> lock(reserved_bytes_mutex);
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
if (reserved_bytes > res)
res = 0;
@ -75,6 +99,18 @@ public:
return res;
}
static size_t getReservedSpace()
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
return reserved_bytes;
}
static size_t getReservationCount()
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
return reservation_count;
}
/// Если места (приблизительно) недостаточно, бросает исключение.
static ReservationPtr reserve(const std::string & path, size_t size)
{
@ -87,7 +123,8 @@ public:
private:
static size_t reserved_bytes;
static Poco::FastMutex reserved_bytes_mutex;
static size_t reservation_count;
static Poco::FastMutex mutex;
};
}

View File

@ -100,7 +100,7 @@ struct MergeTreeSettings
size_t max_rows_to_use_cache = 1024 * 1024;
/// Через сколько секунд удалять ненужные куски.
time_t old_parts_lifetime = 5 * 60;
time_t old_parts_lifetime = 8 * 60;
/// Если в таблице хотя бы столько активных кусков, искусственно замедлять вставки в таблицу.
size_t parts_to_delay_insert = 150;
@ -110,11 +110,17 @@ struct MergeTreeSettings
double insert_delay_step = 1.1;
/// Для скольки последних блоков хранить хеши в ZooKeeper.
size_t replicated_deduplication_window = 1000;
size_t replicated_deduplication_window = 100;
/// Хранить примерно столько последних записей в логе в ZooKeeper, даже если они никому уже не нужны.
/// Не влияет на работу таблиц; используется только чтобы успеть посмотреть на лог в ZooKeeper глазами прежде, чем его очистят.
size_t replicated_logs_to_keep = 100;
/// Максимальное количество ошибок при загрузке кусков, при котором ReplicatedMergeTree соглашается запускаться.
size_t replicated_max_unexpected_parts = 3;
size_t replicated_max_unexpectedly_merged_parts = 2;
size_t replicated_max_missing_obsolete_parts = 5;
size_t replicated_max_missing_active_parts = 20;
};
class MergeTreeData : public ITableDeclaration
@ -307,17 +313,22 @@ public:
Poco::File(to).remove(true);
}
/// Переименовывает кусок, дописав к имени префикс.
void renameAddPrefix(const String & prefix) const
void renameTo(const String & new_name) const
{
String from = storage.full_path + name + "/";
String to = storage.full_path + prefix + name + "/";
String to = storage.full_path + new_name + "/";
Poco::File f(from);
f.setLastModified(Poco::Timestamp::fromEpochTime(time(0)));
f.renameTo(to);
}
/// Переименовывает кусок, дописав к имени префикс.
void renameAddPrefix(const String & prefix) const
{
renameTo(prefix + name);
}
/// Загрузить индекс и вычислить размер. Если size=0, вычислить его тоже.
void loadIndex()
{
@ -344,12 +355,12 @@ public:
}
/// Прочитать контрольные суммы, если есть.
void loadChecksums()
void loadChecksums(bool require)
{
String path = storage.full_path + name + "/checksums.txt";
if (!Poco::File(path).exists())
{
if (storage.require_part_metadata)
if (require)
throw Exception("No checksums.txt in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
return;
@ -359,16 +370,21 @@ public:
assertEOF(file);
}
void loadColumns()
void loadColumns(bool require)
{
String path = storage.full_path + name + "/columns.txt";
if (!Poco::File(path).exists())
{
if (storage.require_part_metadata)
if (require)
throw Exception("No columns.txt in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
columns = *storage.columns;
/// Если нет файла со списком столбцов, запишем его.
for (const NameAndTypePair & column : *storage.columns)
{
if (Poco::File(storage.full_path + name + "/" + escapeForFileName(column.name) + ".bin").exists())
columns.push_back(column);
}
{
WriteBufferFromFile out(path + ".tmp", 4096);
columns.writeText(out);
@ -382,7 +398,7 @@ public:
columns.readText(file, storage.context.getDataTypeFactory());
}
void checkNotBroken()
void checkNotBroken(bool require_part_metadata)
{
String path = storage.full_path + name;
@ -391,7 +407,7 @@ public:
if (!checksums.files.count("primary.idx"))
throw Exception("No checksum for primary.idx", ErrorCodes::NO_FILE_IN_DATA_PART);
if (storage.require_part_metadata)
if (require_part_metadata)
{
for (const NameAndTypePair & it : columns)
{
@ -560,6 +576,9 @@ public:
bool require_part_metadata_,
BrokenPartCallback broken_part_callback_ = &MergeTreeData::doNothing);
/// Загрузить множество кусков с данными с диска. Вызывается один раз - сразу после создания объекта.
void loadDataParts(bool skip_sanity_checks);
std::string getModePrefix() const;
bool supportsSampling() const { return !!sampling_expression; }
@ -625,15 +644,23 @@ public:
*/
DataPartsVector renameTempPartAndReplace(MutableDataPartPtr part, Increment * increment = nullptr, Transaction * out_transaction = nullptr);
/** Убирает из рабочего набора куски remove и добавляет куски add.
/** Убирает из рабочего набора куски remove и добавляет куски add. add должны уже быть в all_data_parts.
* Если clear_without_timeout, данные будут удалены при следующем clearOldParts, игнорируя old_parts_lifetime.
*/
void replaceParts(const DataPartsVector & remove, const DataPartsVector & add, bool clear_without_timeout);
/** Переименовывает кусок в prefix_кусок и убирает его из рабочего набора.
/** Добавляет новый кусок в список известных кусков и в рабочий набор.
*/
void attachPart(DataPartPtr part);
/** Переименовывает кусок в detached/prefix_кусок и забывает про него. Данные не будут удалены в clearOldParts.
* Если restore_covered, добавляет в рабочий набор неактивные куски, слиянием которых получен удаляемый кусок.
*/
void renameAndDetachPart(DataPartPtr part, const String & prefix, bool restore_covered = false);
void renameAndDetachPart(DataPartPtr part, const String & prefix = "", bool restore_covered = false, bool move_to_detached = true);
/** Убирает кусок из списка кусков (включая all_data_parts), но не перемещщает директорию.
*/
void detachPartInPlace(DataPartPtr part);
/** Возвращает старые неактуальные куски, которые можно удалить. Одновременно удаляет их из списка кусков, но не с диска.
*/
@ -685,6 +712,9 @@ public:
ExpressionActionsPtr getPrimaryExpression() const { return primary_expr; }
SortDescription getSortDescription() const { return sort_descr; }
/// Проверить, что кусок не сломан и посчитать для него чексуммы, если их нет.
MutableDataPartPtr loadPartAndFixMetadata(const String & relative_path);
const Context & context;
const String date_column_name;
const ASTPtr sampling_expression;
@ -726,9 +756,6 @@ private:
DataParts all_data_parts;
Poco::FastMutex all_data_parts_mutex;
/// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта.
void loadDataParts();
/** Выражение, преобразующее типы столбцов.
* Если преобразований типов нет, out_expression=nullptr.
* out_rename_map отображает файлы-столбцы на выходе выражения в новые файлы таблицы.

View File

@ -1,6 +1,7 @@
#pragma once
#include <DB/Storages/MergeTree/MergeTreeData.h>
#include <DB/Storages/MergeTree/DiskSpaceMonitor.h>
namespace DB
{
@ -34,9 +35,13 @@ public:
bool only_small,
const AllowedMergingPredicate & can_merge);
/// Сливает куски.
/** Сливает куски.
* Если reservation != nullptr, то и дело уменьшает размер зарезервированного места
* приблизительно пропорционально количеству уже выписанных данных.
*/
MergeTreeData::DataPartPtr mergeParts(
const MergeTreeData::DataPartsVector & parts, const String & merged_name, MergeTreeData::Transaction * out_transaction = nullptr);
const MergeTreeData::DataPartsVector & parts, const String & merged_name,
MergeTreeData::Transaction * out_transaction = nullptr, DiskSpaceMonitor::Reservation * disk_reservation = nullptr);
/// Примерное количество места на диске, нужное для мерджа. С запасом.
size_t estimateDiskSpaceForMerge(const MergeTreeData::DataPartsVector & parts);

View File

@ -9,16 +9,27 @@ namespace DB
class MergeTreePartChecker
{
public:
struct Settings
{
bool verbose = false; /// Пишет в stderr прогресс и ошибки, и не останавливается при первой ошибке.
bool require_checksums = false; /// Требует, чтобы был columns.txt.
bool require_column_files = false; /// Требует, чтобы для всех столбцов из columns.txt были файлы.
size_t index_granularity = 8192;
Settings & setVerbose(bool verbose_) { verbose = verbose_; return *this; }
Settings & setRequireChecksums(bool require_checksums_) { require_checksums = require_checksums_; return *this; }
Settings & setRequireColumnFiles(bool require_column_files_) { require_column_files = require_column_files_; return *this; }
Settings & setIndexGranularity(size_t index_granularity_) { index_granularity = index_granularity_; return *this; }
};
/** Полностью проверяет данные кусочка:
* - Вычисляет контрольные суммы и сравнивает с checksums.txt.
* - Для массивов и строк проверяет соответствие размеров и количества данных.
* - Проверяет правильность засечек.
* Бросает исключение, если кусок испорчен или если проверить не получилось (TODO: можно попробовать разделить эти случаи).
* Если strict, требует, чтобы для всех столбцов из columns.txt были файлы.
* Если verbose, пишет в stderr прогресс и ошибки, и не останавливается при первой ошибке.
*/
static void checkDataPart(String path, size_t index_granularity, bool strict, const DataTypeFactory & data_type_factory,
bool verbose = false);
static void checkDataPart(String path, const Settings & settings, const DataTypeFactory & data_type_factory,
MergeTreeData::DataPart::Checksums * out_checksums = nullptr);
};
}

View File

@ -28,30 +28,12 @@ public:
time_t min_date_time = DateLUT::instance().fromDayNum(DayNum_t(current_block.min_date));
String month_name = toString(Date2OrderedIdentifier(min_date_time) / 100);
String month_path = storage.zookeeper_path + "/block_numbers/" + month_name;
if (!storage.zookeeper->exists(month_path))
{
/// Создадим в block_numbers ноду для месяца и пропустим в ней 200 значений инкремента.
/// Нужно, чтобы в будущем при необходимости можно было добавить данные в начало.
zkutil::Ops ops;
auto acl = storage.zookeeper->getDefaultACL();
ops.push_back(new zkutil::Op::Create(month_path, "", acl, zkutil::CreateMode::Persistent));
for (size_t i = 0; i < 200; ++i)
{
ops.push_back(new zkutil::Op::Create(month_path + "/skip_increment", "", acl, zkutil::CreateMode::Persistent));
ops.push_back(new zkutil::Op::Remove(month_path + "/skip_increment", -1));
}
/// Игнорируем ошибки - не получиться могло только если кто-то еще выполнил эту строчку раньше нас.
storage.zookeeper->tryMulti(ops);
}
AbandonableLockInZooKeeper block_number_lock(
storage.zookeeper_path + "/block_numbers/" + month_name + "/block-",
storage.zookeeper_path + "/temp", *storage.zookeeper);
AbandonableLockInZooKeeper block_number_lock = storage.allocateBlockNumber(month_name);
UInt64 part_number = block_number_lock.getNumber();
MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, part_number);
String part_name = ActiveDataPartSet::getPartName(part->left_date, part->right_date, part->left, part->right, part->level);
/// Если в запросе не указан ID, возьмем в качестве ID хеш от данных. То есть, не вставляем одинаковые данные дважды.
/// NOTE: Если такая дедупликация не нужна, можно вместо этого оставлять block_id пустым.
@ -61,13 +43,10 @@ public:
LOG_DEBUG(log, "Wrote block " << part_number << " with ID " << block_id << ", " << current_block.block.rows() << " rows");
MergeTreeData::Transaction transaction; /// Если не получится добавить кусок в ZK, снова уберем его из рабочего набора.
storage.data.renameTempPartAndAdd(part, nullptr, &transaction);
StorageReplicatedMergeTree::LogEntry log_entry;
log_entry.type = StorageReplicatedMergeTree::LogEntry::GET_PART;
log_entry.source_replica = storage.replica_name;
log_entry.new_part_name = part->name;
log_entry.new_part_name = part_name;
/// Одновременно добавим информацию о куске во все нужные места в ZooKeeper и снимем block_number_lock.
zkutil::Ops ops;
@ -94,7 +73,7 @@ public:
storage.zookeeper->getDefaultACL(),
zkutil::CreateMode::Persistent));
}
storage.checkPartAndAddToZooKeeper(part, ops);
storage.checkPartAndAddToZooKeeper(part, ops, part_name);
ops.push_back(new zkutil::Op::Create(
storage.zookeeper_path + "/log/log-",
log_entry.toString(),
@ -102,6 +81,9 @@ public:
zkutil::CreateMode::PersistentSequential));
block_number_lock.getUnlockOps(ops);
MergeTreeData::Transaction transaction; /// Если не получится добавить кусок в ZK, снова уберем его из рабочего набора.
storage.data.renameTempPartAndAdd(part, nullptr, &transaction);
try
{
auto code = storage.zookeeper->tryMulti(ops);

View File

@ -37,6 +37,8 @@ public:
String source_database_name;
String source_table_name;
bool checkData() const override;
private:
String name;
const Context & context;

View File

@ -18,6 +18,9 @@ namespace DB
*/
class StorageDistributed : public IStorage
{
friend class DistributedBlockOutputStream;
friend class DirectoryMonitor;
public:
static StoragePtr create(
const std::string & name_, /// Имя таблицы.
@ -25,7 +28,9 @@ public:
const String & remote_database_, /// БД на удалённых серверах.
const String & remote_table_, /// Имя таблицы на удалённых серверах.
const String & cluster_name,
Context & context_);
Context & context_,
const ASTPtr & sharding_key_,
const String & data_path_);
static StoragePtr create(
const std::string & name_, /// Имя таблицы.
@ -57,12 +62,21 @@ public:
size_t max_block_size = DEFAULT_BLOCK_SIZE,
unsigned threads = 1);
BlockOutputStreamPtr write(ASTPtr query) override;
void drop() override {}
void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) { name = new_table_name; }
/// в подтаблицах добавлять и удалять столбы нужно вручную
/// структура подтаблиц не проверяется
void alter(const AlterCommands & params, const String & database_name, const String & table_name, Context & context);
void shutdown() override;
const ExpressionActionsPtr & getShardingKeyExpr() const { return sharding_key_expr; }
const String & getShardingKeyColumnName() const { return sharding_key_column_name; }
const String & getPath() const { return path; }
private:
StorageDistributed(
const std::string & name_,
@ -70,17 +84,24 @@ private:
const String & remote_database_,
const String & remote_table_,
Cluster & cluster_,
const Context & context_);
Context & context_,
const ASTPtr & sharding_key_ = nullptr,
const String & data_path_ = String{});
/// Создает копию запроса, меняет имена базы данных и таблицы.
ASTPtr rewriteQuery(ASTPtr query);
/// create directory monitor thread by subdirectory name
void createDirectoryMonitor(const std::string & name);
/// create directory monitors for each existing subdirectory
void createDirectoryMonitors();
/// ensure directory monitor creation
void requireDirectoryMonitor(const std::string & name);
String name;
NamesAndTypesListPtr columns;
String remote_database;
String remote_table;
const Context & context;
Context & context;
/// Временные таблицы, которые необходимо отправить на сервер. Переменная очищается после каждого вызова метода read
/// Для подготовки к отправке нужно использовтаь метод storeExternalTables
@ -91,6 +112,14 @@ private:
/// Соединения с удалёнными серверами.
Cluster & cluster;
ExpressionActionsPtr sharding_key_expr;
String sharding_key_column_name;
bool write_enabled;
String path;
class DirectoryMonitor;
std::unordered_map<std::string, std::unique_ptr<DirectoryMonitor>> directory_monitors;
};
}

View File

@ -13,6 +13,7 @@
#include <DB/Storages/IStorage.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
#include <DB/DataStreams/IBlockOutputStream.h>
#include <DB/Common/FileChecker.h>
namespace DB
@ -80,6 +81,8 @@ class LogBlockOutputStream : public IBlockOutputStream
{
public:
LogBlockOutputStream(StorageLog & storage_);
~LogBlockOutputStream() { writeSuffix(); }
void write(const Block & block);
void writeSuffix();
private:
@ -156,6 +159,22 @@ public:
void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name);
/// Данные столбца
struct ColumnData
{
/// Задает номер столбца в файле с засечками.
/// Не обязательно совпадает с номером столбца среди столбцов таблицы: здесь нумеруются также столбцы с длинами массивов.
size_t column_index;
Poco::File data_file;
Marks marks;
};
typedef std::map<String, ColumnData> Files_t;
Files_t & getFiles() { return files; }
bool checkData() const override;
protected:
String path;
String name;
@ -195,18 +214,8 @@ protected:
unsigned threads = 1);
private:
/// Данные столбца
struct ColumnData
{
/// Задает номер столбца в файле с засечками.
/// Не обязательно совпадает с номером столбца среди столбцов таблицы: здесь нумеруются также столбцы с длинами массивов.
size_t column_index;
Poco::File data_file;
Marks marks;
};
typedef std::map<String, ColumnData> Files_t;
Files_t files; /// name -> data
Names column_names; /// column_index -> name
Poco::File marks_file;
@ -218,6 +227,10 @@ private:
size_t max_compress_block_size;
protected:
FileChecker<StorageLog> file_checker;
private:
/** Для обычных столбцов, в засечках указано количество строчек в блоке.
* Для столбцов-массивов и вложенных структур, есть более одной группы засечек, соответствующих разным файлам:
* - для внутренностей (файла name.bin) - указано суммарное количество элементов массивов в блоке,

View File

@ -15,6 +15,9 @@ public:
std::string getName() const { return "MaterializedView"; }
std::string getInnerTableName() const { return ".inner." + table_name; }
NameAndTypePair getColumn(const String &column_name) const;
bool hasColumn(const String &column_name) const;
BlockOutputStreamPtr write(ASTPtr query);
void drop() override;
bool optimize();

View File

@ -6,6 +6,7 @@
#include <DB/Storages/MergeTree/MergeTreeDataWriter.h>
#include <DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h>
#include <DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h>
#include "MergeTree/AbandonableLockInZooKeeper.h"
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <zkutil/ZooKeeper.h>
#include <zkutil/LeaderElection.h>
@ -77,6 +78,9 @@ public:
void alter(const AlterCommands & params, const String & database_name, const String & table_name, Context & context) override;
void dropPartition(const Field & partition, bool detach) override;
void attachPartition(const Field & partition, bool unreplicated, bool part) override;
/** Удаляет реплику из ZooKeeper. Если других реплик нет, удаляет всю таблицу из ZooKeeper.
*/
void drop() override;
@ -111,7 +115,7 @@ private:
{
try
{
Poco::ScopedLock<Poco::FastMutex> lock(storage.queue_mutex);
std::unique_lock<std::mutex> lock(storage.queue_mutex);
if (!storage.future_parts.erase(part))
throw Exception("Untagging already untagged future part " + part + ". This is a bug.", ErrorCodes::LOGICAL_ERROR);
}
@ -126,30 +130,48 @@ private:
struct LogEntry
{
typedef Poco::SharedPtr<LogEntry> Ptr;
enum Type
{
GET_PART,
MERGE_PARTS,
GET_PART, /// Получить кусок с другой реплики.
MERGE_PARTS, /// Слить куски.
DROP_RANGE, /// Удалить куски в указанном месяце в указанном диапазоне номеров.
ATTACH_PART, /// Перенести кусок из директории detached или unreplicated.
};
String znode_name;
Type type;
String source_replica; /// Пустая строка значит, что эта запись была добавлена сразу в очередь, а не скопирована из лога.
/// Имя куска, получающегося в результате.
/// Для DROP_RANGE имя несуществующего куска. Нужно удалить все куски, покрытые им.
String new_part_name;
Strings parts_to_merge;
/// Для DROP_RANGE, true значит, что куски нужно не удалить, а перенести в директорию detached.
bool detach = false;
/// Для ATTACH_PART имя куска в директории detached или unreplicated.
String source_part_name;
/// Нужно переносить из директории unreplicated, а не detached.
bool attach_unreplicated;
FuturePartTaggerPtr future_part_tagger;
bool currently_executing = false; /// Доступ под queue_mutex.
std::condition_variable execution_complete; /// Пробуждается когда currently_executing становится false.
void addResultToVirtualParts(StorageReplicatedMergeTree & storage)
{
if (type == MERGE_PARTS || type == GET_PART)
if (type == MERGE_PARTS || type == GET_PART || type == DROP_RANGE || type == ATTACH_PART)
storage.virtual_parts.add(new_part_name);
}
void tagPartAsFuture(StorageReplicatedMergeTree & storage)
{
if (type == MERGE_PARTS || type == GET_PART)
if (type == MERGE_PARTS || type == GET_PART || type == ATTACH_PART)
future_part_tagger = new FuturePartTagger(new_part_name, storage);
}
@ -166,17 +188,19 @@ private:
return s;
}
static LogEntry parse(const String & s)
static Ptr parse(const String & s)
{
ReadBufferFromString in(s);
LogEntry res;
res.readText(in);
Ptr res = new LogEntry;
res->readText(in);
assertEOF(in);
return res;
}
};
typedef std::list<LogEntry> LogEntries;
typedef LogEntry::Ptr LogEntryPtr;
typedef std::list<LogEntryPtr> LogEntries;
typedef std::set<String> StringSet;
typedef std::list<String> StringList;
@ -194,7 +218,7 @@ private:
* В ZK записи в хронологическом порядке. Здесь - не обязательно.
*/
LogEntries queue;
Poco::FastMutex queue_mutex;
std::mutex queue_mutex;
/** Куски, которые появятся в результате действий, выполняемых прямо сейчас фоновыми потоками (этих действий нет в очереди).
* Использовать под залоченным queue_mutex.
@ -248,6 +272,7 @@ private:
std::unique_ptr<MergeTreeData> unreplicated_data;
std::unique_ptr<MergeTreeDataSelectExecutor> unreplicated_reader;
std::unique_ptr<MergeTreeDataMerger> unreplicated_merger;
Poco::FastMutex unreplicated_mutex; /// Для мерджей и удаления нереплицируемых кусков.
/// Потоки:
@ -261,12 +286,14 @@ private:
/// Поток, выбирающий куски для слияния.
std::thread merge_selecting_thread;
Poco::Event merge_selecting_event;
std::mutex merge_selecting_mutex; /// Берется на каждую итерацию выбора кусков для слияния.
/// Поток, удаляющий старые куски, записи в логе и блоки.
std::thread cleanup_thread;
/// Поток, обрабатывающий переподключение к ZooKeeper при истечении сессии (очень маловероятное событие).
std::thread restarting_thread;
Poco::Event restarting_event;
/// Поток, следящий за изменениями списка столбцов в ZooKeeper и обновляющий куски в соответствии с этими изменениями.
std::thread alter_thread;
@ -285,7 +312,6 @@ private:
Poco::Event shutdown_event;
/// Нужно ли завершить restarting_thread.
volatile bool permanent_shutdown_called = false;
Poco::Event permanent_shutdown_event;
StorageReplicatedMergeTree(
const String & zookeeper_path_,
@ -306,7 +332,7 @@ private:
/** Создает минимальный набор нод в ZooKeeper.
*/
void createTable();
void createTableIfNotExists();
/** Создает реплику в ZooKeeper и добавляет в очередь все, что нужно, чтобы догнать остальные реплики.
*/
@ -319,7 +345,7 @@ private:
/** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata).
* Если нет - бросить исключение.
*/
void checkTableStructure(bool skip_sanity_checks);
void checkTableStructure(bool skip_sanity_checks, bool allow_alter);
/** Проверить, что множество кусков соответствует тому, что в ZK (/replicas/me/parts/).
* Если каких-то кусков, описанных в ZK нет локально, бросить исключение.
@ -332,11 +358,11 @@ private:
void initVirtualParts();
/// Запустить или остановить фоновые потоки. Используется для частичной переинициализации при пересоздании сессии в ZooKeeper.
void startup();
bool tryStartup(); /// Возвращает false, если недоступен ZooKeeper.
void partialShutdown();
/// Запретить запись в таблицу и завершить все фоновые потоки.
void goReadOnly();
void goReadOnlyPermanently();
/** Проверить, что чексумма куска совпадает с чексуммой того же куска на какой-нибудь другой реплике.
@ -345,7 +371,7 @@ private:
* Кладет в ops действия, добавляющие данные о куске в ZooKeeper.
* Вызывать под TableStructureLock.
*/
void checkPartAndAddToZooKeeper(MergeTreeData::DataPartPtr part, zkutil::Ops & ops);
void checkPartAndAddToZooKeeper(MergeTreeData::DataPartPtr part, zkutil::Ops & ops, String name_override = "");
/// Убирает кусок из ZooKeeper и добавляет в очередь задание скачать его. Предполагается это делать с битыми кусками.
void removePartAndEnqueueFetch(const String & part_name);
@ -379,6 +405,9 @@ private:
*/
bool executeLogEntry(const LogEntry & entry, BackgroundProcessingPool::Context & pool_context);
void executeDropRange(const LogEntry & entry);
bool executeAttachPart(const LogEntry & entry); /// Возвращает false, если куска нет, и его нужно забрать с другой реплики.
/** Обновляет очередь.
*/
void queueUpdatingThread();
@ -423,6 +452,15 @@ private:
/** Скачать указанный кусок с указанной реплики.
*/
void fetchPart(const String & part_name, const String & replica_name);
///
AbandonableLockInZooKeeper allocateBlockNumber(const String & month_name);
/** Дождаться, пока все реплики, включая эту, выполнят указанное действие из лога.
* Если одновременно с этим добавляются реплики, может не дождаться добавленную реплику.
*/
void waitForAllReplicasToProcessLogEntry(const LogEntry & entry);
};
}

View File

@ -3,7 +3,6 @@
#include <Poco/SharedPtr.h>
#include <DB/Storages/IStorage.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
namespace DB
@ -12,20 +11,6 @@ namespace DB
using Poco::SharedPtr;
class NumbersBlockInputStream : public IProfilingBlockInputStream
{
public:
NumbersBlockInputStream(size_t block_size_);
String getName() const { return "NumbersBlockInputStream"; }
String getID() const { return "Numbers"; }
protected:
Block readImpl();
private:
size_t block_size;
UInt64 next;
};
/** Реализует хранилище для системной таблицы Numbers.
* Таблица содержит единственный столбец number UInt64.
* Из этой таблицы можно прочитать все натуральные числа, начиная с 0 (до 2^64 - 1, а потом заново).
@ -33,7 +18,7 @@ private:
class StorageSystemNumbers : public IStorage
{
public:
static StoragePtr create(const std::string & name_);
static StoragePtr create(const std::string & name_, bool multithreaded_ = false);
std::string getName() const { return "SystemNumbers"; }
std::string getTableName() const { return name; }
@ -51,8 +36,9 @@ public:
private:
const std::string name;
NamesAndTypesList columns;
bool multithreaded;
StorageSystemNumbers(const std::string & name_);
StorageSystemNumbers(const std::string & name_, bool multithreaded_);
};
}

View File

@ -12,6 +12,8 @@
#include <DB/Storages/IStorage.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
#include <DB/DataStreams/IBlockOutputStream.h>
#include <DB/Common/FileChecker.h>
#include <Poco/Util/XMLConfiguration.h>
namespace DB
@ -55,11 +57,13 @@ private:
void readData(const String & name, const IDataType & type, IColumn & column, size_t limit, size_t level = 0, bool read_offsets = true);
};
class TinyLogBlockOutputStream : public IBlockOutputStream
{
public:
TinyLogBlockOutputStream(StorageTinyLog & storage_);
~TinyLogBlockOutputStream();
void write(const Block & block);
void writeSuffix();
private:
@ -129,12 +133,7 @@ public:
void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name);
private:
String path;
String name;
NamesAndTypesListPtr columns;
size_t max_compress_block_size;
bool checkData() const override;
/// Данные столбца
struct ColumnData
@ -142,8 +141,22 @@ private:
Poco::File data_file;
};
typedef std::map<String, ColumnData> Files_t;
Files_t & getFiles();
private:
String path;
String name;
NamesAndTypesListPtr columns;
size_t max_compress_block_size;
Files_t files;
FileChecker<StorageTinyLog> file_checker;
Logger * log;
StorageTinyLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach, size_t max_compress_block_size_);
void addFile(const String & column_name, const IDataType & type, size_t level = 0);

View File

@ -1,9 +1,7 @@
#include <DB/AggregateFunctions/AggregateFunctionCount.h>
#include <DB/AggregateFunctions/AggregateFunctionSum.h>
#include <DB/AggregateFunctions/AggregateFunctionAvg.h>
#include <DB/AggregateFunctions/AggregateFunctionAny.h>
#include <DB/AggregateFunctions/AggregateFunctionAnyLast.h>
#include <DB/AggregateFunctions/AggregateFunctionsMinMax.h>
#include <DB/AggregateFunctions/AggregateFunctionsMinMaxAny.h>
#include <DB/AggregateFunctions/AggregateFunctionsArgMinMax.h>
#include <DB/AggregateFunctions/AggregateFunctionUniq.h>
#include <DB/AggregateFunctions/AggregateFunctionUniqUpTo.h>
@ -69,6 +67,7 @@ static IAggregateFunction * createWithNumericType(const IDataType & argument_typ
return nullptr;
}
template<template <typename, typename> class AggregateFunctionTemplate, template <typename> class Data>
static IAggregateFunction * createWithNumericType(const IDataType & argument_type)
{
@ -87,18 +86,48 @@ static IAggregateFunction * createWithNumericType(const IDataType & argument_typ
}
/// min, max, any, anyLast
template<template <typename> class AggregateFunctionTemplate, template <typename> class Data>
static IAggregateFunction * createAggregateFunctionSingleValue(const String & name, const DataTypes & argument_types)
{
if (argument_types.size() != 1)
throw Exception("Incorrect number of arguments for aggregate function " + name, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
const IDataType & argument_type = *argument_types[0];
if (typeid_cast<const DataTypeUInt8 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<UInt8>>>;
else if (typeid_cast<const DataTypeUInt16 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<UInt16>>>;
else if (typeid_cast<const DataTypeUInt32 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<UInt32>>>;
else if (typeid_cast<const DataTypeUInt64 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<UInt64>>>;
else if (typeid_cast<const DataTypeInt8 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Int8>>>;
else if (typeid_cast<const DataTypeInt16 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Int16>>>;
else if (typeid_cast<const DataTypeInt32 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Int32>>>;
else if (typeid_cast<const DataTypeInt64 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Int64>>>;
else if (typeid_cast<const DataTypeFloat32 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Float32>>>;
else if (typeid_cast<const DataTypeFloat64 *>(&argument_type)) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Float64>>>;
else if (typeid_cast<const DataTypeDate *>(&argument_type))
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DataTypeDate::FieldType>>>;
else if (typeid_cast<const DataTypeDateTime*>(&argument_type))
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DataTypeDateTime::FieldType>>>;
else if (typeid_cast<const DataTypeString*>(&argument_type))
return new AggregateFunctionTemplate<Data<SingleValueDataString>>;
else
return new AggregateFunctionTemplate<Data<SingleValueDataGeneric>>;
}
AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const DataTypes & argument_types, int recursion_level) const
{
if (name == "count")
return new AggregateFunctionCount;
else if (name == "any")
return new AggregateFunctionAny;
return createAggregateFunctionSingleValue<AggregateFunctionsSingleValue, AggregateFunctionAnyData>(name, argument_types);
else if (name == "anyLast")
return new AggregateFunctionAnyLast;
return createAggregateFunctionSingleValue<AggregateFunctionsSingleValue, AggregateFunctionAnyLastData>(name, argument_types);
else if (name == "min")
return new AggregateFunctionMin;
return createAggregateFunctionSingleValue<AggregateFunctionsSingleValue, AggregateFunctionMinData>(name, argument_types);
else if (name == "max")
return new AggregateFunctionMax;
return createAggregateFunctionSingleValue<AggregateFunctionsSingleValue, AggregateFunctionMaxData>(name, argument_types);
else if (name == "argMin")
return new AggregateFunctionArgMin;
else if (name == "argMax")

View File

@ -49,6 +49,16 @@
#include <DB/Common/ExternalTable.h>
/// http://en.wikipedia.org/wiki/ANSI_escape_code
#define SAVE_CURSOR_POSITION "\033[s"
#define RESTORE_CURSOR_POSITION "\033[u"
#define CLEAR_TO_END_OF_LINE "\033[K"
/// Эти коды, возможно, поддерживаются не везде.
#define DISABLE_LINE_WRAPPING "\033[?7l"
#define ENABLE_LINE_WRAPPING "\033[?7h"
/** Клиент командной строки СУБД ClickHouse.
*/
@ -61,11 +71,7 @@ using Poco::SharedPtr;
class Client : public Poco::Util::Application
{
public:
Client() : is_interactive(true), stdin_is_not_tty(false),
format_max_block_size(0), std_in(STDIN_FILENO), std_out(STDOUT_FILENO), processed_rows(0),
rows_read_on_server(0), bytes_read_on_server(0), written_progress_chars(0), written_first_block(false)
{
}
Client() {}
private:
typedef std::unordered_set<String> StringSet;
@ -77,24 +83,24 @@ private:
"q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй"
};
bool is_interactive; /// Использовать readline интерфейс или batch режим.
bool stdin_is_not_tty; /// stdin - не терминал.
bool is_interactive = true; /// Использовать readline интерфейс или batch режим.
bool stdin_is_not_tty = false; /// stdin - не терминал.
SharedPtr<Connection> connection; /// Соединение с БД.
String query; /// Текущий запрос.
String format; /// Формат вывода результата в консоль.
size_t format_max_block_size; /// Максимальный размер блока при выводе в консоль.
size_t format_max_block_size = 0; /// Максимальный размер блока при выводе в консоль.
String insert_format; /// Формат данных для INSERT-а при чтении их из stdin в batch режиме
size_t insert_format_max_block_size; /// Максимальный размер блока при чтении данных INSERT-а.
size_t insert_format_max_block_size = 0; /// Максимальный размер блока при чтении данных INSERT-а.
Context context;
/// Чтение из stdin для batch режима
ReadBufferFromFileDescriptor std_in;
ReadBufferFromFileDescriptor std_in {STDIN_FILENO};
/// Вывод в консоль
WriteBufferFromFileDescriptor std_out;
WriteBufferFromFileDescriptor std_out {STDOUT_FILENO};
BlockOutputStreamPtr block_std_out;
String home_path;
@ -105,7 +111,7 @@ private:
String history_file;
/// Строк прочитано или записано.
size_t processed_rows;
size_t processed_rows = 0;
/// Распарсенный запрос. Оттуда берутся некоторые настройки (формат).
ASTPtr parsed_query;
@ -115,10 +121,10 @@ private:
Stopwatch watch;
size_t rows_read_on_server;
size_t bytes_read_on_server;
size_t written_progress_chars;
bool written_first_block;
size_t rows_read_on_server = 0;
size_t bytes_read_on_server = 0;
size_t written_progress_chars = 0;
bool written_first_block = false;
/// Информация о внешних таблицах
std::list<ExternalTable> external_tables;
@ -441,7 +447,7 @@ private:
if (exit_strings.end() != exit_strings.find(line))
return false;
block_std_out = nullptr;
resetOutput();
watch.restart();
@ -642,6 +648,14 @@ private:
}
/** Сбросить все данные, что ещё остались в буферах. */
void resetOutput()
{
block_std_out = nullptr;
std_out.next();
}
/** Получает и обрабатывает пакеты из сервера.
* Также следит, не требуется ли прервать выполнение запроса.
*/
@ -747,12 +761,7 @@ private:
void onData(Block & block)
{
if (written_progress_chars)
{
for (size_t i = 0; i < written_progress_chars; ++i)
std::cerr << "\b \b";
written_progress_chars = 0;
}
clearProgress();
if (!block)
return;
@ -780,7 +789,8 @@ private:
written_first_block = true;
}
std_out.next();
/// Полученный блок данных сразу выводится клиенту.
block_std_out->flush();
}
@ -804,8 +814,18 @@ private:
}
void clearProgress()
{
std::cerr << RESTORE_CURSOR_POSITION CLEAR_TO_END_OF_LINE;
written_progress_chars = 0;
}
void writeProgress()
{
if (!is_interactive)
return;
static size_t increment = 0;
static const char * indicators[8] =
{
@ -816,12 +836,13 @@ private:
"\033[1;34m←\033[0m",
"\033[1;35m↖\033[0m",
"\033[1;36m↑\033[0m",
"\033[1;37m↗\033[0m",
"\033[1m↗\033[0m",
};
if (is_interactive)
{
std::cerr << std::string(written_progress_chars, '\b');
if (written_progress_chars)
clearProgress();
else
std::cerr << SAVE_CURSOR_POSITION;
std::stringstream message;
message << indicators[increment % 8]
@ -837,10 +858,9 @@ private:
message << ". ";
written_progress_chars = message.str().size() - 13;
std::cerr << message.rdbuf();
std::cerr << DISABLE_LINE_WRAPPING << message.rdbuf() << ENABLE_LINE_WRAPPING;
++increment;
}
}
void writeFinalProgress()
@ -859,6 +879,8 @@ private:
void onException(const Exception & e)
{
resetOutput();
std::cerr << "Received exception from server:" << std::endl
<< "Code: " << e.code() << ". " << e.displayText();
}
@ -876,7 +898,7 @@ private:
if (block_std_out)
block_std_out->writeSuffix();
std_out.next();
resetOutput();
if (is_interactive && !written_first_block)
std::cout << "Ok." << std::endl;

View File

@ -284,14 +284,17 @@ void Connection::sendData(const Block & block, const String & name)
}
void Connection::sendPreparedData(ReadBuffer & input, const String & name)
void Connection::sendPreparedData(ReadBuffer & input, size_t size, const String & name)
{
writeVarUInt(Protocol::Client::Data, *out);
if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES)
writeStringBinary(name, *out);
if (0 == size)
copyData(input, *out);
else
copyData(input, *out, size);
out->next();
}

View File

@ -0,0 +1,45 @@
#include <emmintrin.h>
#include <DB/Columns/IColumn.h>
namespace DB
{
size_t countBytesInFilter(const IColumn::Filter & filt)
{
size_t count = 0;
/** NOTE: По идее, filt должен содержать только нолики и единички.
* Но, на всякий случай, здесь используется условие > 0 (на знаковые байты).
* Лучше было бы использовать != 0, то это не позволяет SSE2.
*/
const __m128i zero16 = _mm_set1_epi8(0);
const Int8 * pos = reinterpret_cast<const Int8 *>(&filt[0]);
const Int8 * end = pos + filt.size();
const Int8 * end64 = pos + filt.size() / 64 * 64;
for (; pos < end64; pos += 64)
count += __builtin_popcountll(
static_cast<UInt64>(_mm_movemask_epi8(_mm_cmpgt_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pos)),
zero16)))
| (static_cast<UInt64>(_mm_movemask_epi8(_mm_cmpgt_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pos + 16)),
zero16))) << 16)
| (static_cast<UInt64>(_mm_movemask_epi8(_mm_cmpgt_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pos + 32)),
zero16))) << 32)
| (static_cast<UInt64>(_mm_movemask_epi8(_mm_cmpgt_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pos + 48)),
zero16))) << 48));
for (; pos < end; ++pos)
count += *pos > 0;
return count;
}
}

View File

@ -0,0 +1,60 @@
#include <DB/Common/Macros.h>
#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>
namespace DB
{
Macros::Macros() {}
Macros::Macros(const Poco::Util::AbstractConfiguration & config, const String & root_key)
{
Poco::Util::AbstractConfiguration::Keys keys;
config.keys(root_key, keys);
for (const String & key : keys)
{
macros[key] = config.getString(root_key + "." + key);
}
}
String Macros::expand(const String & s) const
{
if (s.find('{') == String::npos)
return s;
String res;
size_t pos = 0;
while (true)
{
size_t begin = s.find('{', pos);
if (begin == String::npos)
{
res.append(s, pos, String::npos);
break;
}
else
{
res.append(s, pos, begin - pos);
}
++begin;
size_t end = s.find('}', begin);
if (end == String::npos)
throw Exception("Unbalanced { and } in string with macros: \"" + s + "\"", ErrorCodes::SYNTAX_ERROR);
String macro_name = s.substr(begin, end - begin);
auto it = macros.find(macro_name);
if (it == macros.end())
throw Exception("No macro " + macro_name + " in config", ErrorCodes::SYNTAX_ERROR);
res += it->second;
pos = end + 1;
}
return res;
}
}

View File

@ -108,19 +108,16 @@ static bool isValidFunction(ASTPtr expression, const NameSet & columns)
/// Извлечь все подфункции главной конъюнкции, но зависящие только от заданных столбцов
static void extractFunctions(ASTPtr expression, const NameSet & columns, std::vector<ASTPtr> & result)
{
if (const ASTFunction * function = typeid_cast<const ASTFunction *>(&* expression))
{
if (function->name == "and")
const ASTFunction * function = typeid_cast<const ASTFunction *>(&* expression);
if (function && function->name == "and")
{
for (size_t i = 0; i < function->arguments->children.size(); ++i)
extractFunctions(function->arguments->children[i], columns, result);
}
else
else if (isValidFunction(expression, columns))
{
if (isValidFunction(expression, columns))
result.push_back(expression->clone());
}
}
}
/// Построить конъюнкцию из заданных функций

View File

@ -111,6 +111,8 @@ void CreatingSetsBlockInputStream::create(SubqueryForSet & subquery)
size_t rows = 0;
size_t bytes = 0;
watch.stop();
subquery.source->getLeafRowsBytes(rows, bytes);
size_t head_rows = 0;

View File

@ -41,7 +41,7 @@ Block FilterBlockInputStream::readImpl()
/** Если фильтр - константа (например, написано WHERE 1),
* то либо вернём пустой блок, либо вернём блок без изменений.
*/
ColumnConstUInt8 * column_const = typeid_cast<ColumnConstUInt8 *>(&*column);
const ColumnConstUInt8 * column_const = typeid_cast<const ColumnConstUInt8 *>(&*column);
if (column_const)
{
if (!column_const->getData())
@ -50,52 +50,73 @@ Block FilterBlockInputStream::readImpl()
return res;
}
ColumnUInt8 * column_vec = typeid_cast<ColumnUInt8 *>(&*column);
const ColumnUInt8 * column_vec = typeid_cast<const ColumnUInt8 *>(&*column);
if (!column_vec)
throw Exception("Illegal type " + column->getName() + " of column for filter. Must be ColumnUInt8 or ColumnConstUInt8.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER);
IColumn::Filter & filter = column_vec->getData();
const IColumn::Filter & filter = column_vec->getData();
/// Если кроме столбца с фильтром ничего нет.
if (columns == 1)
/** Выясним, сколько строк будет в результате.
* Для этого отфильтруем первый попавшийся неконстантный столбец
* или же посчитаем количество выставленных байт в фильтре.
*/
size_t first_non_constant_column = 0;
for (size_t i = 0; i < columns; ++i)
{
/// То посчитаем в нём количество единичек.
if (!res.getByPosition(i).column->isConst())
{
first_non_constant_column = i;
if (first_non_constant_column != static_cast<size_t>(filter_column))
break;
}
}
size_t filtered_rows = 0;
for (size_t i = 0, size = filter.size(); i < size; ++i)
if (filter[i])
++filtered_rows;
if (first_non_constant_column != static_cast<size_t>(filter_column))
{
ColumnWithNameAndType & current_column = res.getByPosition(first_non_constant_column);
current_column.column = current_column.column->filter(filter);
filtered_rows = current_column.column->size();
}
else
{
filtered_rows = countBytesInFilter(filter);
}
/// Если текущий блок полностью отфильтровался - перейдём к следующему.
if (filtered_rows == 0)
continue;
/// Заменяем этот столбец на столбец с константой 1, нужного размера.
/// Если через фильтр проходят все строчки.
if (filtered_rows == filter.size())
{
/// Заменим столбец с фильтром на константу.
res.getByPosition(filter_column).column = new ColumnConstUInt8(filtered_rows, 1);
/// Остальные столбцы трогать не нужно.
return res;
}
/// Общий случай - фильтруем остальные столбцы.
/// Фильтруем остальные столбцы.
for (size_t i = 0; i < columns; ++i)
{
if (i != static_cast<size_t>(filter_column))
{
ColumnWithNameAndType & current_column = res.getByPosition(i);
current_column.column = current_column.column->filter(filter);
if (current_column.column->empty())
break;
}
if (i == static_cast<size_t>(filter_column))
{
/// Сам столбец с фильтром заменяем на столбец с константой 1, так как после фильтрации в нём ничего другого не останется.
current_column.column = new ColumnConstUInt8(filtered_rows, 1);
continue;
}
/// Любой столбец - не являющийся фильтром.
IColumn & any_not_filter_column = *res.getByPosition(filter_column == 0 ? 1 : 0).column;
/// Если текущий блок полностью отфильтровался - перейдём к следующему.
if (any_not_filter_column.empty())
if (i == first_non_constant_column)
continue;
/// Сам столбец с фильтром заменяем на столбец с константой 1, так как после фильтрации в нём ничего другого не останется.
res.getByPosition(filter_column).column = new ColumnConstUInt8(any_not_filter_column.size(), 1);
if (current_column.column->isConst())
current_column.column = current_column.column->cut(0, filtered_rows);
else
current_column.column = current_column.column->filter(filter);
}
return res;
}

View File

@ -91,31 +91,6 @@ void IBlockInputStream::dumpTree(std::ostream & ostr, size_t indent, size_t mult
}
void IBlockInputStream::dumpTreeWithProfile(std::ostream & ostr, size_t indent)
{
ostr << indent + 1 << ". " << getShortName() << "." << std::endl;
/// Для красоты
size_t width = log10(indent + 1) + 4 + getShortName().size();
for (size_t i = 0; i < width; ++i)
ostr << "";
ostr << std::endl;
/// Информация профайлинга, если есть
if (IProfilingBlockInputStream * profiling = dynamic_cast<IProfilingBlockInputStream *>(this))
{
if (profiling->getInfo().blocks != 0)
{
profiling->getInfo().print(ostr);
ostr << std::endl;
}
}
for (BlockInputStreams::iterator it = children.begin(); it != children.end(); ++it)
(*it)->dumpTreeWithProfile(ostr, indent + 1);
}
String IBlockInputStream::getShortName() const
{
String res = getName();

View File

@ -52,7 +52,7 @@ bool BlockStreamProfileInfo::hasAppliedLimit() const
void BlockStreamProfileInfo::update(Block & block)
{
++blocks;
rows += block.rows();
rows += block.rowsInFirstColumn();
bytes += block.bytes();
if (column_names.empty())
@ -99,63 +99,6 @@ void BlockStreamProfileInfo::calculateRowsBeforeLimit() const
}
void BlockStreamProfileInfo::print(std::ostream & ostr) const
{
UInt64 elapsed = work_stopwatch.elapsed();
UInt64 nested_elapsed = 0;
double elapsed_seconds = work_stopwatch.elapsedSeconds();
double nested_elapsed_seconds = 0;
UInt64 nested_rows = 0;
UInt64 nested_blocks = 0;
UInt64 nested_bytes = 0;
if (!nested_infos.empty())
{
for (BlockStreamProfileInfos::const_iterator it = nested_infos.begin(); it != nested_infos.end(); ++it)
{
if ((*it)->work_stopwatch.elapsed() > nested_elapsed)
{
nested_elapsed = (*it)->work_stopwatch.elapsed();
nested_elapsed_seconds = (*it)->work_stopwatch.elapsedSeconds();
}
nested_rows += (*it)->rows;
nested_blocks += (*it)->blocks;
nested_bytes += (*it)->bytes;
}
}
ostr << std::fixed << std::setprecision(2)
<< "Columns: " << column_names << std::endl
<< "Elapsed: " << elapsed_seconds << " sec. "
<< "(" << elapsed * 100.0 / total_stopwatch.elapsed() << "%), " << std::endl;
if (!nested_infos.empty())
{
double self_percents = (elapsed - nested_elapsed) * 100.0 / total_stopwatch.elapsed();
ostr<< "Elapsed (self): " << (elapsed_seconds - nested_elapsed_seconds) << " sec. "
<< "(" << (self_percents >= 50 ? "\033[1;31m" : (self_percents >= 10 ? "\033[1;33m" : "")) /// Раскраска больших значений
<< self_percents << "%"
<< (self_percents >= 10 ? "\033[0m" : "") << "), " << std::endl
<< "Rows (in): " << nested_rows << ", per second: " << nested_rows / elapsed_seconds << ", " << std::endl
<< "Blocks (in): " << nested_blocks << ", per second: " << nested_blocks / elapsed_seconds << ", " << std::endl
<< " " << nested_bytes / 1000000.0 << " MB (memory), "
<< nested_bytes * 1000 / elapsed << " MB/s (memory), " << std::endl;
if (self_percents > 0.1)
ostr << "Rows per second (in, self): " << (nested_rows / (elapsed_seconds - nested_elapsed_seconds))
<< ", " << (elapsed - nested_elapsed) / nested_rows << " ns/row, " << std::endl;
}
ostr << "Rows (out): " << rows << ", per second: " << rows / elapsed_seconds << ", " << std::endl
<< "Blocks (out): " << blocks << ", per second: " << blocks / elapsed_seconds << ", " << std::endl
<< " " << bytes / 1000000.0 << " MB (memory), " << bytes * 1000 / elapsed << " MB/s (memory), " << std::endl
<< "Average block size (out): " << rows / blocks << "." << std::endl;
}
Block IProfilingBlockInputStream::read()
{
if (!info.started)
@ -175,9 +118,7 @@ Block IProfilingBlockInputStream::read()
if (is_cancelled)
return res;
info.work_stopwatch.start();
res = readImpl();
info.work_stopwatch.stop();
/* if (res)
{
@ -225,7 +166,7 @@ Block IProfilingBlockInputStream::read()
cancel();
}
progress(res.rows(), res.bytes());
progress(res.rowsInFirstColumn(), res.bytes());
return res;
}
@ -330,9 +271,6 @@ bool IProfilingBlockInputStream::checkLimits()
void IProfilingBlockInputStream::checkQuota(Block & block)
{
time_t current_time = time(0);
double total_elapsed = info.total_stopwatch.elapsedSeconds();
switch (limits.mode)
{
case LIMITS_TOTAL:
@ -340,15 +278,20 @@ void IProfilingBlockInputStream::checkQuota(Block & block)
break;
case LIMITS_CURRENT:
quota->checkAndAddResultRowsBytes(current_time, block.rows(), block.bytes());
{
time_t current_time = time(0);
double total_elapsed = info.total_stopwatch.elapsedSeconds();
quota->checkAndAddResultRowsBytes(current_time, block.rowsInFirstColumn(), block.bytes());
quota->checkAndAddExecutionTime(current_time, Poco::Timespan((total_elapsed - prev_elapsed) * 1000000.0));
prev_elapsed = total_elapsed;
break;
}
default:
throw Exception("Logical error: unknown limits mode.", ErrorCodes::LOGICAL_ERROR);
}
prev_elapsed = total_elapsed;
}
@ -369,7 +312,6 @@ void IProfilingBlockInputStream::progressImpl(size_t rows, size_t bytes)
size_t total_rows = process_list_elem->rows_processed;
size_t total_bytes = process_list_elem->bytes_processed;
double total_elapsed = info.total_stopwatch.elapsedSeconds();
/** Проверяем ограничения на объём данных для чтения, скорость выполнения запроса, квоту на объём данных для чтения.
* NOTE: Может быть, имеет смысл сделать, чтобы они проверялись прямо в ProcessList?
@ -389,14 +331,18 @@ void IProfilingBlockInputStream::progressImpl(size_t rows, size_t bytes)
throw Exception("Logical error: unknown overflow mode", ErrorCodes::LOGICAL_ERROR);
}
if (limits.min_execution_speed
&& total_elapsed > limits.timeout_before_checking_execution_speed.totalMicroseconds() / 1000000.0
if (limits.min_execution_speed)
{
double total_elapsed = info.total_stopwatch.elapsedSeconds();
if (total_elapsed > limits.timeout_before_checking_execution_speed.totalMicroseconds() / 1000000.0
&& total_rows / total_elapsed < limits.min_execution_speed)
{
throw Exception("Query is executing too slow: " + toString(total_rows / total_elapsed)
+ " rows/sec., minimum: " + toString(limits.min_execution_speed),
ErrorCodes::TOO_SLOW);
}
}
if (quota != nullptr && limits.mode == LIMITS_TOTAL)
{

View File

@ -10,7 +10,7 @@ using Poco::SharedPtr;
JSONRowOutputStream::JSONRowOutputStream(WriteBuffer & ostr_, const Block & sample_)
: ostr(ostr_), field_number(0), row_count(0), applied_limit(false), rows_before_limit(0)
: dst_ostr(ostr_), ostr(dst_ostr), field_number(0), row_count(0), applied_limit(false), rows_before_limit(0)
{
NamesAndTypesList columns(sample_.getColumnsList());
fields.assign(columns.begin(), columns.end());
@ -44,8 +44,6 @@ void JSONRowOutputStream::writePrefix()
writeChar('\n', ostr);
writeCString("\t\"data\":\n", ostr);
writeCString("\t[\n", ostr);
ostr.next();
}

View File

@ -64,6 +64,8 @@ Block MergeSortingBlockInputStream::merge(Blocks & blocks)
else
merged = mergeImpl<SortCursor>(blocks, cursors);
watch.stop();
LOG_DEBUG(log, std::fixed << std::setprecision(2)
<< "Merge sorted " << blocks.size() << " blocks, " << merged.rows() << " rows"
<< " in " << watch.elapsedSeconds() << " sec., "

View File

@ -184,7 +184,7 @@ void MergingSortedBlockInputStream::fetchNextBlock(const TSortCursor & current,
void MergingSortedBlockInputStream::readSuffixImpl()
{
const BlockStreamProfileInfo & profile_info = getInfo();
double seconds = profile_info.work_stopwatch.elapsedSeconds();
double seconds = profile_info.total_stopwatch.elapsedSeconds();
LOG_DEBUG(log, std::fixed << std::setprecision(2)
<< "Merge sorted " << profile_info.blocks << " blocks, " << profile_info.rows << " rows"
<< " in " << seconds << " sec., "

View File

@ -140,7 +140,7 @@ void PrettyBlockOutputStream::write(const Block & block_)
const ColumnWithNameAndType & col = block.getByPosition(i);
if (!no_escapes)
writeCString("\033[1;37m", ostr);
writeCString("\033[1m", ostr);
if (col.type->isNumeric())
{

View File

@ -29,7 +29,7 @@ void PrettyCompactBlockOutputStream::writeHeader(
writeCString("", ostr);
if (!no_escapes)
writeCString("\033[1;37m", ostr);
writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr);
if (!no_escapes)
writeCString("\033[0m", ostr);
@ -37,7 +37,7 @@ void PrettyCompactBlockOutputStream::writeHeader(
else
{
if (!no_escapes)
writeCString("\033[1;37m", ostr);
writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr);
if (!no_escapes)
writeCString("\033[0m", ostr);

View File

@ -48,7 +48,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_)
writeChar(' ', ostr);
if (!no_escapes)
writeCString("\033[1;37m", ostr);
writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr);
if (!no_escapes)
writeCString("\033[0m", ostr);
@ -56,7 +56,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_)
else
{
if (!no_escapes)
writeCString("\033[1;37m", ostr);
writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr);
if (!no_escapes)
writeCString("\033[0m", ostr);

View File

@ -1,247 +1,69 @@
#include <DB/Functions/FunctionsArithmetic.h>
#include <DB/Functions/FunctionsComparison.h>
#include <DB/Functions/FunctionsLogical.h>
#include <DB/Functions/FunctionsString.h>
#include <DB/Functions/FunctionsConversion.h>
#include <DB/Functions/FunctionsDateTime.h>
#include <DB/Functions/FunctionsStringSearch.h>
#include <DB/Functions/FunctionsHashing.h>
#include <DB/Functions/FunctionsRandom.h>
#include <DB/Functions/FunctionsURL.h>
#include <DB/Functions/FunctionsArray.h>
#include <DB/Functions/FunctionsStringArray.h>
#include <DB/Functions/FunctionsConditional.h>
#include <DB/Functions/FunctionsDictionaries.h>
#include <DB/Functions/FunctionsMiscellaneous.h>
#include <DB/Functions/FunctionsRound.h>
#include <DB/Functions/FunctionsReinterpret.h>
#include <DB/Functions/FunctionsFormatting.h>
#include <DB/Functions/FunctionsCoding.h>
#include <DB/Functions/FunctionsHigherOrder.h>
#include <DB/Functions/FunctionsVisitParam.h>
#include <DB/Functions/FunctionFactory.h>
namespace DB
{
/** Эти функции определены в отдельных translation unit-ах.
* Это сделано для того, чтобы уменьшить потребление оперативки при сборке, и ускорить параллельную сборку.
*/
void registerFunctionsArithmetic(FunctionFactory &);
void registerFunctionsArray(FunctionFactory &);
void registerFunctionsCoding(FunctionFactory &);
void registerFunctionsComparison(FunctionFactory &);
void registerFunctionsConditional(FunctionFactory &);
void registerFunctionsConversion(FunctionFactory &);
void registerFunctionsDateTime(FunctionFactory &);
void registerFunctionsDictionaries(FunctionFactory &);
void registerFunctionsFormatting(FunctionFactory &);
void registerFunctionsHashing(FunctionFactory &);
void registerFunctionsHigherOrder(FunctionFactory &);
void registerFunctionsLogical(FunctionFactory &);
void registerFunctionsMiscellaneous(FunctionFactory &);
void registerFunctionsRandom(FunctionFactory &);
void registerFunctionsReinterpret(FunctionFactory &);
void registerFunctionsRound(FunctionFactory &);
void registerFunctionsString(FunctionFactory &);
void registerFunctionsStringArray(FunctionFactory &);
void registerFunctionsStringSearch(FunctionFactory &);
void registerFunctionsURL(FunctionFactory &);
void registerFunctionsVisitParam(FunctionFactory &);
FunctionFactory::FunctionFactory()
{
registerFunctionsArithmetic(*this);
registerFunctionsArray(*this);
registerFunctionsCoding(*this);
registerFunctionsComparison(*this);
registerFunctionsConditional(*this);
registerFunctionsConversion(*this);
registerFunctionsDateTime(*this);
registerFunctionsDictionaries(*this);
registerFunctionsFormatting(*this);
registerFunctionsHashing(*this);
registerFunctionsHigherOrder(*this);
registerFunctionsLogical(*this);
registerFunctionsMiscellaneous(*this);
registerFunctionsRandom(*this);
registerFunctionsReinterpret(*this);
registerFunctionsRound(*this);
registerFunctionsString(*this);
registerFunctionsStringArray(*this);
registerFunctionsStringSearch(*this);
registerFunctionsURL(*this);
registerFunctionsVisitParam(*this);
}
FunctionPtr FunctionFactory::get(
const String & name,
const Context & context) const
{
/// Немного неоптимально.
if (name == "plus") return new FunctionPlus;
else if (name == "minus") return new FunctionMinus;
else if (name == "multiply") return new FunctionMultiply;
else if (name == "divide") return new FunctionDivideFloating;
else if (name == "intDiv") return new FunctionDivideIntegral;
else if (name == "modulo") return new FunctionModulo;
else if (name == "negate") return new FunctionNegate;
else if (name == "bitAnd") return new FunctionBitAnd;
else if (name == "bitOr") return new FunctionBitOr;
else if (name == "bitXor") return new FunctionBitXor;
else if (name == "bitNot") return new FunctionBitNot;
else if (name == "bitShiftLeft") return new FunctionBitShiftLeft;
else if (name == "bitShiftRight") return new FunctionBitShiftRight;
else if (name == "equals") return new FunctionEquals;
else if (name == "notEquals") return new FunctionNotEquals;
else if (name == "less") return new FunctionLess;
else if (name == "greater") return new FunctionGreater;
else if (name == "lessOrEquals") return new FunctionLessOrEquals;
else if (name == "greaterOrEquals") return new FunctionGreaterOrEquals;
else if (name == "and") return new FunctionAnd;
else if (name == "or") return new FunctionOr;
else if (name == "xor") return new FunctionXor;
else if (name == "not") return new FunctionNot;
else if (name == "roundToExp2") return new FunctionRoundToExp2;
else if (name == "roundDuration") return new FunctionRoundDuration;
else if (name == "roundAge") return new FunctionRoundAge;
else if (name == "empty") return new FunctionEmpty;
else if (name == "notEmpty") return new FunctionNotEmpty;
else if (name == "length") return new FunctionLength;
else if (name == "lengthUTF8") return new FunctionLengthUTF8;
else if (name == "lower") return new FunctionLower;
else if (name == "upper") return new FunctionUpper;
else if (name == "lowerUTF8") return new FunctionLowerUTF8;
else if (name == "upperUTF8") return new FunctionUpperUTF8;
else if (name == "reverse") return new FunctionReverse;
else if (name == "reverseUTF8") return new FunctionReverseUTF8;
else if (name == "concat") return new FunctionConcat;
else if (name == "substring") return new FunctionSubstring;
else if (name == "replaceOne") return new FunctionReplaceOne;
else if (name == "replaceAll") return new FunctionReplaceAll;
else if (name == "replaceRegexpOne") return new FunctionReplaceRegexpOne;
else if (name == "replaceRegexpAll") return new FunctionReplaceRegexpAll;
else if (name == "substringUTF8") return new FunctionSubstringUTF8;
else if (name == "toUInt8") return new FunctionToUInt8;
else if (name == "toUInt16") return new FunctionToUInt16;
else if (name == "toUInt32") return new FunctionToUInt32;
else if (name == "toUInt64") return new FunctionToUInt64;
else if (name == "toInt8") return new FunctionToInt8;
else if (name == "toInt16") return new FunctionToInt16;
else if (name == "toInt32") return new FunctionToInt32;
else if (name == "toInt64") return new FunctionToInt64;
else if (name == "toFloat32") return new FunctionToFloat32;
else if (name == "toFloat64") return new FunctionToFloat64;
else if (name == "toDate") return new FunctionToDate;
else if (name == "toDateTime") return new FunctionToDateTime;
else if (name == "toString") return new FunctionToString;
else if (name == "toFixedString") return new FunctionToFixedString;
else if (name == "toStringCutToZero") return new FunctionToStringCutToZero;
else if (name == "reinterpretAsUInt8") return new FunctionReinterpretAsUInt8;
else if (name == "reinterpretAsUInt16") return new FunctionReinterpretAsUInt16;
else if (name == "reinterpretAsUInt32") return new FunctionReinterpretAsUInt32;
else if (name == "reinterpretAsUInt64") return new FunctionReinterpretAsUInt64;
else if (name == "reinterpretAsInt8") return new FunctionReinterpretAsInt8;
else if (name == "reinterpretAsInt16") return new FunctionReinterpretAsInt16;
else if (name == "reinterpretAsInt32") return new FunctionReinterpretAsInt32;
else if (name == "reinterpretAsInt64") return new FunctionReinterpretAsInt64;
else if (name == "reinterpretAsFloat32") return new FunctionReinterpretAsFloat32;
else if (name == "reinterpretAsFloat64") return new FunctionReinterpretAsFloat64;
else if (name == "reinterpretAsDate") return new FunctionReinterpretAsDate;
else if (name == "reinterpretAsDateTime") return new FunctionReinterpretAsDateTime;
else if (name == "reinterpretAsString") return new FunctionReinterpretAsString;
else if (name == "toYear") return new FunctionToYear;
else if (name == "toMonth") return new FunctionToMonth;
else if (name == "toDayOfMonth") return new FunctionToDayOfMonth;
else if (name == "toDayOfWeek") return new FunctionToDayOfWeek;
else if (name == "toHour") return new FunctionToHour;
else if (name == "toMinute") return new FunctionToMinute;
else if (name == "toSecond") return new FunctionToSecond;
else if (name == "toMonday") return new FunctionToMonday;
else if (name == "toStartOfMonth") return new FunctionToStartOfMonth;
else if (name == "toStartOfQuarter") return new FunctionToStartOfQuarter;
else if (name == "toStartOfYear") return new FunctionToStartOfYear;
else if (name == "toStartOfMinute") return new FunctionToStartOfMinute;
else if (name == "toStartOfHour") return new FunctionToStartOfHour;
else if (name == "toRelativeYearNum") return new FunctionToRelativeYearNum;
else if (name == "toRelativeMonthNum") return new FunctionToRelativeMonthNum;
else if (name == "toRelativeWeekNum") return new FunctionToRelativeWeekNum;
else if (name == "toRelativeDayNum") return new FunctionToRelativeDayNum;
else if (name == "toRelativeHourNum") return new FunctionToRelativeHourNum;
else if (name == "toRelativeMinuteNum") return new FunctionToRelativeMinuteNum;
else if (name == "toRelativeSecondNum") return new FunctionToRelativeSecondNum;
else if (name == "toTime") return new FunctionToTime;
else if (name == "now") return new FunctionNow;
else if (name == "timeSlot") return new FunctionTimeSlot;
else if (name == "timeSlots") return new FunctionTimeSlots;
else if (name == "position") return new FunctionPosition;
else if (name == "positionUTF8") return new FunctionPositionUTF8;
else if (name == "match") return new FunctionMatch;
else if (name == "like") return new FunctionLike;
else if (name == "notLike") return new FunctionNotLike;
else if (name == "extract") return new FunctionExtract;
else if (name == "extractAll") return new FunctionExtractAll;
else if (name == "halfMD5") return new FunctionHalfMD5;
else if (name == "sipHash64") return new FunctionSipHash64;
else if (name == "cityHash64") return new FunctionCityHash64;
else if (name == "intHash32") return new FunctionIntHash32;
else if (name == "intHash64") return new FunctionIntHash64;
else if (name == "IPv4NumToString") return new FunctionIPv4NumToString;
else if (name == "IPv4StringToNum") return new FunctionIPv4StringToNum;
else if (name == "hex") return new FunctionHex;
else if (name == "unhex") return new FunctionUnhex;
else if (name == "bitmaskToList") return new FunctionBitmaskToList;
else if (name == "bitmaskToArray") return new FunctionBitmaskToArray;
else if (name == "rand") return new FunctionRand;
else if (name == "rand64") return new FunctionRand64;
else if (name == "protocol") return new FunctionProtocol;
else if (name == "domain") return new FunctionDomain;
else if (name == "domainWithoutWWW") return new FunctionDomainWithoutWWW;
else if (name == "topLevelDomain") return new FunctionTopLevelDomain;
else if (name == "path") return new FunctionPath;
else if (name == "queryString") return new FunctionQueryString;
else if (name == "fragment") return new FunctionFragment;
else if (name == "queryStringAndFragment") return new FunctionQueryStringAndFragment;
else if (name == "extractURLParameter") return new FunctionExtractURLParameter;
else if (name == "extractURLParameters") return new FunctionExtractURLParameters;
else if (name == "extractURLParameterNames") return new FunctionExtractURLParameterNames;
else if (name == "URLHierarchy") return new FunctionURLHierarchy;
else if (name == "URLPathHierarchy") return new FunctionURLPathHierarchy;
else if (name == "cutWWW") return new FunctionCutWWW;
else if (name == "cutQueryString") return new FunctionCutQueryString;
else if (name == "cutFragment") return new FunctionCutFragment;
else if (name == "cutQueryStringAndFragment") return new FunctionCutQueryStringAndFragment;
else if (name == "cutURLParameter") return new FunctionCutURLParameter;
else if (name == "hostName") return new FunctionHostName;
else if (name == "visibleWidth") return new FunctionVisibleWidth;
else if (name == "toTypeName") return new FunctionToTypeName;
else if (name == "blockSize") return new FunctionBlockSize;
else if (name == "sleep") return new FunctionSleep;
else if (name == "materialize") return new FunctionMaterialize;
else if (name == "ignore") return new FunctionIgnore;
else if (name == "arrayJoin") return new FunctionArrayJoin;
else if (name == "tuple") return new FunctionTuple;
else if (name == "tupleElement") return new FunctionTupleElement;
else if (name == "in") return new FunctionIn(false, false);
else if (name == "notIn") return new FunctionIn(true, false);
else if (name == "globalIn") return new FunctionIn(false, true);
else if (name == "globalNotIn") return new FunctionIn(true, true);
else if (name == "array") return new FunctionArray;
else if (name == "arrayElement") return new FunctionArrayElement;
else if (name == "has") return new FunctionHas;
else if (name == "indexOf") return new FunctionIndexOf;
else if (name == "countEqual") return new FunctionCountEqual;
else if (name == "arrayEnumerate") return new FunctionArrayEnumerate;
else if (name == "arrayEnumerateUniq") return new FunctionArrayEnumerateUniq;
else if (name == "arrayMap") return new FunctionArrayMap;
else if (name == "arrayFilter") return new FunctionArrayFilter;
else if (name == "arrayCount") return new FunctionArrayCount;
else if (name == "arrayExists") return new FunctionArrayExists;
else if (name == "arrayAll") return new FunctionArrayAll;
else if (name == "arraySum") return new FunctionArraySum;
else if (name == "alphaTokens") return new FunctionAlphaTokens;
else if (name == "splitByChar") return new FunctionSplitByChar;
else if (name == "splitByString") return new FunctionSplitByString;
else if (name == "if") return new FunctionIf;
else if (name == "regionToCity") return new FunctionRegionToCity(context.getDictionaries().getRegionsHierarchies());
else if (name == "regionToArea") return new FunctionRegionToArea(context.getDictionaries().getRegionsHierarchies());
else if (name == "regionToCountry") return new FunctionRegionToCountry(context.getDictionaries().getRegionsHierarchies());
else if (name == "regionToContinent") return new FunctionRegionToContinent(context.getDictionaries().getRegionsHierarchies());
else if (name == "OSToRoot") return new FunctionOSToRoot(context.getDictionaries().getTechDataHierarchy());
else if (name == "SEToRoot") return new FunctionSEToRoot(context.getDictionaries().getTechDataHierarchy());
else if (name == "categoryToRoot") return new FunctionCategoryToRoot(context.getDictionaries().getCategoriesHierarchy());
else if (name == "categoryToSecondLevel") return new FunctionCategoryToSecondLevel(context.getDictionaries().getCategoriesHierarchy());
else if (name == "regionIn") return new FunctionRegionIn(context.getDictionaries().getRegionsHierarchies());
else if (name == "OSIn") return new FunctionOSIn(context.getDictionaries().getTechDataHierarchy());
else if (name == "SEIn") return new FunctionSEIn(context.getDictionaries().getTechDataHierarchy());
else if (name == "categoryIn") return new FunctionCategoryIn(context.getDictionaries().getCategoriesHierarchy());
else if (name == "regionHierarchy") return new FunctionRegionHierarchy(context.getDictionaries().getRegionsHierarchies());
else if (name == "OSHierarchy") return new FunctionOSHierarchy(context.getDictionaries().getTechDataHierarchy());
else if (name == "SEHierarchy") return new FunctionSEHierarchy(context.getDictionaries().getTechDataHierarchy());
else if (name == "categoryHierarchy") return new FunctionCategoryHierarchy(context.getDictionaries().getCategoriesHierarchy());
else if (name == "regionToName") return new FunctionRegionToName(context.getDictionaries().getRegionsNames());
else if (name == "visitParamHas") return new FunctionVisitParamHas;
else if (name == "visitParamExtractUInt") return new FunctionVisitParamExtractUInt;
else if (name == "visitParamExtractInt") return new FunctionVisitParamExtractInt;
else if (name == "visitParamExtractFloat") return new FunctionVisitParamExtractFloat;
else if (name == "visitParamExtractBool") return new FunctionVisitParamExtractBool;
else if (name == "visitParamExtractRaw") return new FunctionVisitParamExtractRaw;
else if (name == "visitParamExtractString") return new FunctionVisitParamExtractString;
auto it = functions.find(name);
if (functions.end() != it)
return it->second(context);
else
throw Exception("Unknown function " + name, ErrorCodes::UNKNOWN_FUNCTION);
}

View File

@ -0,0 +1,28 @@
#include <DB/Functions/FunctionFactory.h>
#include <DB/Functions/FunctionsArithmetic.h>
namespace DB
{
void registerFunctionsArithmetic(FunctionFactory & factory)
{
#define F [](const Context & context) -> IFunction*
factory.registerFunction("plus", F { return new FunctionPlus; });
factory.registerFunction("minus", F { return new FunctionMinus; });
factory.registerFunction("multiply", F { return new FunctionMultiply; });
factory.registerFunction("divide", F { return new FunctionDivideFloating; });
factory.registerFunction("intDiv", F { return new FunctionDivideIntegral; });
factory.registerFunction("modulo", F { return new FunctionModulo; });
factory.registerFunction("negate", F { return new FunctionNegate; });
factory.registerFunction("bitAnd", F { return new FunctionBitAnd; });
factory.registerFunction("bitOr", F { return new FunctionBitOr; });
factory.registerFunction("bitXor", F { return new FunctionBitXor; });
factory.registerFunction("bitNot", F { return new FunctionBitNot; });
factory.registerFunction("bitShiftLeft", F { return new FunctionBitShiftLeft; });
factory.registerFunction("bitShiftRight", F { return new FunctionBitShiftRight; });
#undef F
}
}

View File

@ -0,0 +1,22 @@
#include <DB/Functions/FunctionFactory.h>
#include <DB/Functions/FunctionsArray.h>
namespace DB
{
void registerFunctionsArray(FunctionFactory & factory)
{
#define F [](const Context & context) -> IFunction*
factory.registerFunction("array", F { return new FunctionArray; });
factory.registerFunction("arrayElement", F { return new FunctionArrayElement; });
factory.registerFunction("has", F { return new FunctionHas; });
factory.registerFunction("indexOf", F { return new FunctionIndexOf; });
factory.registerFunction("countEqual", F { return new FunctionCountEqual; });
factory.registerFunction("arrayEnumerate", F { return new FunctionArrayEnumerate; });
factory.registerFunction("arrayEnumerateUniq", F { return new FunctionArrayEnumerateUniq; });
#undef F
}
}

View File

@ -0,0 +1,21 @@
#include <DB/Functions/FunctionFactory.h>
#include <DB/Functions/FunctionsCoding.h>
namespace DB
{
void registerFunctionsCoding(FunctionFactory & factory)
{
#define F [](const Context & context) -> IFunction*
factory.registerFunction("toStringCutToZero", F { return new FunctionToStringCutToZero; });
factory.registerFunction("IPv4NumToString", F { return new FunctionIPv4NumToString; });
factory.registerFunction("IPv4StringToNum", F { return new FunctionIPv4StringToNum; });
factory.registerFunction("hex", F { return new FunctionHex; });
factory.registerFunction("unhex", F { return new FunctionUnhex; });
factory.registerFunction("bitmaskToArray", F { return new FunctionBitmaskToArray; });
#undef F
}
}

Some files were not shown because too many files have changed in this diff Show More