This commit is contained in:
Pavel Kartavyy 2015-08-20 15:12:15 +03:00
commit c9e318a2c4
146 changed files with 4128 additions and 1692 deletions

View File

@ -120,6 +120,10 @@ struct AggregateFunctionSequenceMatchData final
} }
}; };
/// Max number of iterations to match the pattern against a sequence, exception thrown when exceeded
constexpr auto sequence_match_max_iterations = 1000000;
class AggregateFunctionSequenceMatch final : public IAggregateFunctionHelper<AggregateFunctionSequenceMatchData> class AggregateFunctionSequenceMatch final : public IAggregateFunctionHelper<AggregateFunctionSequenceMatchData>
{ {
public: public:
@ -385,6 +389,7 @@ private:
return false; return false;
}; };
std::size_t i = 0;
while (action_it != action_end && events_it != events_end) while (action_it != action_end && events_it != events_end)
{ {
// std::cout << "start_timestamp " << base_it->first << "; "; // std::cout << "start_timestamp " << base_it->first << "; ";
@ -465,6 +470,12 @@ private:
"Unknown PatternActionType", "Unknown PatternActionType",
ErrorCodes::LOGICAL_ERROR ErrorCodes::LOGICAL_ERROR
}; };
if (++i > sequence_match_max_iterations)
throw Exception{
"Pattern application proves too difficult, exceeding max iterations (" + toString(sequence_match_max_iterations) + ")",
ErrorCodes::TOO_SLOW
};
} }
/// if there are some actions remaining /// if there are some actions remaining

View File

@ -25,32 +25,7 @@
namespace DB namespace DB
{ {
/// uniq
template <typename T> struct AggregateFunctionUniqTraits
{
static UInt64 hash(T x) { return x; }
};
template <> struct AggregateFunctionUniqTraits<Float32>
{
static UInt64 hash(Float32 x)
{
UInt64 res = 0;
memcpy(reinterpret_cast<char *>(&res), reinterpret_cast<char *>(&x), sizeof(x));
return res;
}
};
template <> struct AggregateFunctionUniqTraits<Float64>
{
static UInt64 hash(Float64 x)
{
UInt64 res = 0;
memcpy(reinterpret_cast<char *>(&res), reinterpret_cast<char *>(&x), sizeof(x));
return res;
}
};
struct AggregateFunctionUniqUniquesHashSetData struct AggregateFunctionUniqUniquesHashSetData
{ {
@ -60,6 +35,7 @@ struct AggregateFunctionUniqUniquesHashSetData
static String getName() { return "uniq"; } static String getName() { return "uniq"; }
}; };
/// uniqHLL12
template <typename T> template <typename T>
struct AggregateFunctionUniqHLL12Data struct AggregateFunctionUniqHLL12Data
@ -79,6 +55,7 @@ struct AggregateFunctionUniqHLL12Data<String>
static String getName() { return "uniqHLL12"; } static String getName() { return "uniqHLL12"; }
}; };
/// uniqExact
template <typename T> template <typename T>
struct AggregateFunctionUniqExactData struct AggregateFunctionUniqExactData
@ -121,8 +98,8 @@ struct AggregateFunctionUniqExactData<String>
template <typename T> template <typename T>
struct AggregateFunctionUniqCombinedData struct AggregateFunctionUniqCombinedData
{ {
using Key = T; using Key = UInt32;
using Set = CombinedCardinalityEstimator<Key, HashSet<Key, DefaultHash<Key>, HashTableGrower<4> >, 16, 16, 19>; using Set = CombinedCardinalityEstimator<Key, HashSet<Key, TrivialHash, HashTableGrower<> >, 16, 14, 17, TrivialHash>;
Set set; Set set;
static String getName() { return "uniqCombined"; } static String getName() { return "uniqCombined"; }
@ -132,7 +109,7 @@ template <>
struct AggregateFunctionUniqCombinedData<String> struct AggregateFunctionUniqCombinedData<String>
{ {
using Key = UInt64; using Key = UInt64;
using Set = CombinedCardinalityEstimator<Key, HashSet<Key, DefaultHash<Key>, HashTableGrower<4> >, 16, 16, 19>; using Set = CombinedCardinalityEstimator<Key, HashSet<Key, TrivialHash, HashTableGrower<> >, 16, 14, 17, TrivialHash>;
Set set; Set set;
static String getName() { return "uniqCombined"; } static String getName() { return "uniqCombined"; }
@ -140,6 +117,87 @@ struct AggregateFunctionUniqCombinedData<String>
namespace detail namespace detail
{ {
/** Хэш-функция для uniqCombined.
*/
template<typename T, typename Enable = void>
struct CombinedCardinalityTraits
{
static UInt32 hash(T key)
{
return key;
}
};
template<typename T>
struct CombinedCardinalityTraits<T, typename std::enable_if<std::is_same<T, Int64>::value>::type>
{
using U = typename std::make_unsigned<T>::type;
static UInt32 hash(T key)
{
return intHash32<0>(static_cast<U>(key));
};
};
template<typename T>
struct CombinedCardinalityTraits<T, typename std::enable_if<std::is_same<T, UInt64>::value>::type>
{
static UInt32 hash(T key)
{
return intHash32<0>(key);
};
};
template<typename T>
struct CombinedCardinalityTraits<T, typename std::enable_if<std::is_same<T, Float64>::value>::type>
{
static UInt32 hash(T key)
{
UInt64 res = 0;
memcpy(reinterpret_cast<char *>(&res), reinterpret_cast<char *>(&key), sizeof(key));
return intHash32<0>(res);
}
};
template<typename T>
struct CombinedCardinalityTraits<T, typename std::enable_if<std::is_same<T, Float32>::value>::type>
{
static UInt32 hash(T key)
{
UInt32 res = 0;
memcpy(reinterpret_cast<char *>(&res), reinterpret_cast<char *>(&key), sizeof(key));
return res;
}
};
/** Хэш-функция для uniq.
*/
template <typename T> struct AggregateFunctionUniqTraits
{
static UInt64 hash(T x) { return x; }
};
template <> struct AggregateFunctionUniqTraits<Float32>
{
static UInt64 hash(Float32 x)
{
UInt64 res = 0;
memcpy(reinterpret_cast<char *>(&res), reinterpret_cast<char *>(&x), sizeof(x));
return res;
}
};
template <> struct AggregateFunctionUniqTraits<Float64>
{
static UInt64 hash(Float64 x)
{
UInt64 res = 0;
memcpy(reinterpret_cast<char *>(&res), reinterpret_cast<char *>(&x), sizeof(x));
return res;
}
};
/** Структура для делегации работы по добавлению одного элемента в агрегатные функции uniq. /** Структура для делегации работы по добавлению одного элемента в агрегатные функции uniq.
* Используется для частичной специализации для добавления строк. * Используется для частичной специализации для добавления строк.
*/ */
@ -193,10 +251,8 @@ namespace detail
{ {
static void addOne(AggregateFunctionUniqCombinedData<T> & data, const IColumn & column, size_t row_num) static void addOne(AggregateFunctionUniqCombinedData<T> & data, const IColumn & column, size_t row_num)
{ {
if (data.set.isMedium()) const auto & value = static_cast<const ColumnVector<T> &>(column).getData()[row_num];
data.set.insert(static_cast<const ColumnVector<T> &>(column).getData()[row_num]); data.set.insert(CombinedCardinalityTraits<T>::hash(value));
else
data.set.insert(AggregateFunctionUniqTraits<T>::hash(static_cast<const ColumnVector<T> &>(column).getData()[row_num]));
} }
}; };
@ -209,6 +265,7 @@ namespace detail
data.set.insert(CityHash64(value.data, value.size)); data.set.insert(CityHash64(value.data, value.size));
} }
}; };
} }

View File

@ -0,0 +1,124 @@
#pragma once
#include <malloc.h>
#include <string.h>
#include <sys/mman.h>
#include <DB/Common/MemoryTracker.h>
#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>
/** Отвечает за выделение/освобождение памяти. Используется, например, в PODArray, Arena.
* Интерфейс отличается от std::allocator
* - наличием метода realloc, который для больших кусков памяти использует mremap;
* - передачей размера в метод free;
* - наличием аргумента alignment;
*/
class Allocator
{
private:
/** См. комментарий в HashTableAllocator.h
*/
static constexpr size_t MMAP_THRESHOLD = 64 * (1 << 20);
static constexpr size_t HUGE_PAGE_SIZE = 2 * (1 << 20);
static constexpr size_t MMAP_MIN_ALIGNMENT = 4096;
static constexpr size_t MALLOC_MIN_ALIGNMENT = 8;
public:
/// Выделить кусок памяти.
void * alloc(size_t size, size_t alignment = 0)
{
if (current_memory_tracker)
current_memory_tracker->alloc(size);
void * buf;
if (size >= MMAP_THRESHOLD)
{
if (alignment > MMAP_MIN_ALIGNMENT)
throw DB::Exception("Too large alignment: more than page size.", DB::ErrorCodes::BAD_ARGUMENTS);
buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (MAP_FAILED == buf)
DB::throwFromErrno("Allocator: Cannot mmap.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY);
/// См. комментарий в HashTableAllocator.h
if (size >= HUGE_PAGE_SIZE && 0 != madvise(buf, size, MADV_HUGEPAGE))
DB::throwFromErrno("HashTableAllocator: Cannot madvise with MADV_HUGEPAGE.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY);
}
else
{
if (alignment <= MALLOC_MIN_ALIGNMENT)
{
buf = ::malloc(size);
if (nullptr == buf)
DB::throwFromErrno("Allocator: Cannot malloc.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY);
}
else
{
buf = nullptr;
int res = posix_memalign(&buf, alignment, size);
if (0 != res)
DB::throwFromErrno("Cannot allocate memory (posix_memalign)", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY, res);
}
}
return buf;
}
/// Освободить память.
void free(void * buf, size_t size)
{
if (size >= MMAP_THRESHOLD)
{
if (0 != munmap(buf, size))
DB::throwFromErrno("Allocator: Cannot munmap.", DB::ErrorCodes::CANNOT_MUNMAP);
}
else
{
::free(buf);
}
if (current_memory_tracker)
current_memory_tracker->free(size);
}
/** Увеличить размер куска памяти.
* Содержимое старого куска памяти переезжает в начало нового.
* Положение куска памяти может измениться.
*/
void * realloc(void * buf, size_t old_size, size_t new_size, size_t alignment = 0)
{
if (old_size < MMAP_THRESHOLD && new_size < MMAP_THRESHOLD && alignment <= MALLOC_MIN_ALIGNMENT)
{
if (current_memory_tracker)
current_memory_tracker->realloc(old_size, new_size);
buf = ::realloc(buf, new_size);
if (nullptr == buf)
DB::throwFromErrno("Allocator: Cannot realloc.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY);
}
else if (old_size >= MMAP_THRESHOLD && new_size >= MMAP_THRESHOLD)
{
if (current_memory_tracker)
current_memory_tracker->realloc(old_size, new_size);
buf = mremap(buf, old_size, new_size, MREMAP_MAYMOVE);
if (MAP_FAILED == buf)
DB::throwFromErrno("Allocator: Cannot mremap.", DB::ErrorCodes::CANNOT_MREMAP);
}
else
{
void * new_buf = alloc(new_size, alignment);
memcpy(new_buf, buf, old_size);
free(buf, old_size);
buf = new_buf;
}
return buf;
}
};

View File

@ -6,7 +6,7 @@
#include <Poco/SharedPtr.h> #include <Poco/SharedPtr.h>
#include <Yandex/likely.h> #include <Yandex/likely.h>
#include <DB/Common/ProfileEvents.h> #include <DB/Common/ProfileEvents.h>
#include <DB/Common/MemoryTracker.h> #include <DB/Common/Allocator.h>
namespace DB namespace DB
@ -25,7 +25,7 @@ class Arena
{ {
private: private:
/// Непрерывный кусок памяти и указатель на свободное место в нём. Односвязный список. /// Непрерывный кусок памяти и указатель на свободное место в нём. Односвязный список.
struct Chunk : private std::allocator<char> /// empty base optimization struct Chunk : private Allocator /// empty base optimization
{ {
char * begin; char * begin;
char * pos; char * pos;
@ -38,10 +38,7 @@ private:
ProfileEvents::increment(ProfileEvents::ArenaAllocChunks); ProfileEvents::increment(ProfileEvents::ArenaAllocChunks);
ProfileEvents::increment(ProfileEvents::ArenaAllocBytes, size_); ProfileEvents::increment(ProfileEvents::ArenaAllocBytes, size_);
if (current_memory_tracker) begin = reinterpret_cast<char *>(Allocator::alloc(size_));
current_memory_tracker->alloc(size_);
begin = allocate(size_);
pos = begin; pos = begin;
end = begin + size_; end = begin + size_;
prev = prev_; prev = prev_;
@ -49,10 +46,7 @@ private:
~Chunk() ~Chunk()
{ {
deallocate(begin, size()); Allocator::free(begin, size());
if (current_memory_tracker)
current_memory_tracker->free(size());
if (prev) if (prev)
delete prev; delete prev;

View File

@ -2,7 +2,8 @@
#include <DB/Common/HashTable/SmallTable.h> #include <DB/Common/HashTable/SmallTable.h>
#include <DB/Common/HashTable/HashSet.h> #include <DB/Common/HashTable/HashSet.h>
#include <DB/Common/HyperLogLogWithSmallSetOptimization.h> #include <statdaemons/HyperLogLogCounter.h>
#include <DB/Core/Defines.h>
namespace DB namespace DB
@ -11,11 +12,11 @@ namespace DB
namespace details namespace details
{ {
enum class ContainerType { SMALL, MEDIUM, LARGE }; enum class ContainerType : UInt8 { SMALL = 1, MEDIUM = 2, LARGE = 3 };
ContainerType max(const ContainerType & lhs, const ContainerType & rhs) static inline ContainerType max(const ContainerType & lhs, const ContainerType & rhs)
{ {
unsigned int res = std::max(static_cast<unsigned int>(lhs), static_cast<unsigned int>(rhs)); UInt8 res = std::max(static_cast<UInt8>(lhs), static_cast<UInt8>(rhs));
return static_cast<ContainerType>(res); return static_cast<ContainerType>(res);
} }
@ -25,38 +26,41 @@ ContainerType max(const ContainerType & lhs, const ContainerType & rhs)
* Для среднего - выделяется HashSet. * Для среднего - выделяется HashSet.
* Для большого - выделяется HyperLogLog. * Для большого - выделяется HyperLogLog.
*/ */
template <typename Key, typename HashContainer, UInt8 small_set_size_max, UInt8 medium_set_power2_max, UInt8 K> template
<
typename Key,
typename HashContainer,
UInt8 small_set_size_max,
UInt8 medium_set_power2_max,
UInt8 K,
typename Hash = IntHash32<Key>,
typename DenominatorType = double
>
class CombinedCardinalityEstimator class CombinedCardinalityEstimator
{ {
public: public:
using Self = CombinedCardinalityEstimator<Key, HashContainer, small_set_size_max, medium_set_power2_max, K>; using Self = CombinedCardinalityEstimator<Key, HashContainer, small_set_size_max, medium_set_power2_max, K, Hash, DenominatorType>;
private: private:
using Small = SmallSet<Key, small_set_size_max>; using Small = SmallSet<Key, small_set_size_max>;
using Medium = HashContainer; using Medium = HashContainer;
using Large = HyperLogLogWithSmallSetOptimization<Key, small_set_size_max, K>; using Large = HyperLogLogCounter<K, Hash, DenominatorType>;
public: public:
CombinedCardinalityEstimator()
{
setContainerType(details::ContainerType::SMALL);
}
~CombinedCardinalityEstimator() ~CombinedCardinalityEstimator()
{ {
if (container_type == details::ContainerType::MEDIUM) destroy();
{
delete medium;
if (current_memory_tracker)
current_memory_tracker->free(sizeof(medium));
}
else if (container_type == details::ContainerType::LARGE)
{
delete large;
if (current_memory_tracker)
current_memory_tracker->free(sizeof(large));
}
} }
void insert(Key value) void insert(Key value)
{ {
auto container_type = getContainerType();
if (container_type == details::ContainerType::SMALL) if (container_type == details::ContainerType::SMALL)
{ {
if (small.find(value) == small.end()) if (small.find(value) == small.end())
@ -66,41 +70,43 @@ public:
else else
{ {
toMedium(); toMedium();
medium->insert(value); getContainer<Medium>().insert(value);
} }
} }
} }
else if (container_type == details::ContainerType::MEDIUM) else if (container_type == details::ContainerType::MEDIUM)
{ {
if (medium->size() < medium_set_size_max) auto & container = getContainer<Medium>();
medium->insert(value); if (container.size() < medium_set_size_max)
container.insert(value);
else else
{ {
toLarge(); toLarge();
large->insert(value); getContainer<Large>().insert(value);
} }
} }
else if (container_type == details::ContainerType::LARGE) else if (container_type == details::ContainerType::LARGE)
large->insert(value); getContainer<Large>().insert(value);
else
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
} }
UInt32 size() const UInt32 size() const
{ {
auto container_type = getContainerType();
if (container_type == details::ContainerType::SMALL) if (container_type == details::ContainerType::SMALL)
return small.size(); return small.size();
else if (container_type == details::ContainerType::MEDIUM) else if (container_type == details::ContainerType::MEDIUM)
return medium->size(); return getContainer<Medium>().size();
else if (container_type == details::ContainerType::LARGE) else if (container_type == details::ContainerType::LARGE)
return large->size(); return getContainer<Large>().size();
else else
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR); throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
} }
void merge(const Self & rhs) void merge(const Self & rhs)
{ {
details::ContainerType max_container_type = details::max(container_type, rhs.container_type); auto container_type = getContainerType();
auto max_container_type = details::max(container_type, rhs.getContainerType());
if (container_type != max_container_type) if (container_type != max_container_type)
{ {
@ -110,41 +116,18 @@ public:
toLarge(); toLarge();
} }
if (container_type == details::ContainerType::SMALL) if (rhs.getContainerType() == details::ContainerType::SMALL)
{ {
for (const auto & x : rhs.small) for (const auto & x : rhs.small)
insert(x); insert(x);
} }
else if (container_type == details::ContainerType::MEDIUM) else if (rhs.getContainerType() == details::ContainerType::MEDIUM)
{ {
if (rhs.container_type == details::ContainerType::SMALL) for (const auto & x : rhs.getContainer<Medium>())
{
for (const auto & x : rhs.small)
insert(x); insert(x);
} }
else if (rhs.container_type == details::ContainerType::MEDIUM) else if (rhs.getContainerType() == details::ContainerType::LARGE)
{ getContainer<Large>().merge(rhs.getContainer<Large>());
for (const auto & x : *rhs.medium)
insert(x);
}
}
else if (container_type == details::ContainerType::LARGE)
{
if (rhs.container_type == details::ContainerType::SMALL)
{
for (const auto & x : rhs.small)
insert(x);
}
else if (rhs.container_type == details::ContainerType::MEDIUM)
{
for (const auto & x : *rhs.medium)
insert(x);
}
else if (rhs.container_type == details::ContainerType::LARGE)
large->merge(*rhs.large);
}
else
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
} }
/// Можно вызывать только для пустого объекта. /// Можно вызывать только для пустого объекта.
@ -152,79 +135,95 @@ public:
{ {
UInt8 v; UInt8 v;
readBinary(v, in); readBinary(v, in);
details::ContainerType t = static_cast<details::ContainerType>(v); auto container_type = static_cast<details::ContainerType>(v);
if (t == details::ContainerType::SMALL) if (container_type == details::ContainerType::SMALL)
small.read(in); small.read(in);
else if (t == details::ContainerType::MEDIUM) else if (container_type == details::ContainerType::MEDIUM)
{ {
toMedium(); toMedium();
medium->read(in); getContainer<Medium>().read(in);
} }
else if (t == details::ContainerType::LARGE) else if (container_type == details::ContainerType::LARGE)
{ {
toLarge(); toLarge();
large->read(in); getContainer<Large>().read(in);
} }
else
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
} }
void readAndMerge(DB::ReadBuffer & in) void readAndMerge(DB::ReadBuffer & in)
{ {
Self other; auto container_type = getContainerType();
other.read(in);
merge(other); UInt8 v;
readBinary(v, in);
auto rhs_container_type = static_cast<details::ContainerType>(v);
auto max_container_type = details::max(container_type, rhs_container_type);
if (container_type != max_container_type)
{
if (max_container_type == details::ContainerType::MEDIUM)
toMedium();
else if (max_container_type == details::ContainerType::LARGE)
toLarge();
}
if (rhs_container_type == details::ContainerType::SMALL)
{
typename Small::Reader reader(in);
while (reader.next())
insert(reader.get());
}
else if (rhs_container_type == details::ContainerType::MEDIUM)
{
typename Medium::Reader reader(in);
while (reader.next())
insert(reader.get());
}
else if (rhs_container_type == details::ContainerType::LARGE)
getContainer<Large>().readAndMerge(in);
} }
void write(DB::WriteBuffer & out) const void write(DB::WriteBuffer & out) const
{ {
UInt8 v = static_cast<UInt8>(container_type); auto container_type = getContainerType();
writeBinary(v, out); writeBinary(static_cast<UInt8>(container_type), out);
if (container_type == details::ContainerType::SMALL) if (container_type == details::ContainerType::SMALL)
small.write(out); small.write(out);
else if (container_type == details::ContainerType::MEDIUM) else if (container_type == details::ContainerType::MEDIUM)
medium->write(out); getContainer<Medium>().write(out);
else if (container_type == details::ContainerType::LARGE) else if (container_type == details::ContainerType::LARGE)
large->write(out); getContainer<Large>().write(out);
else
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
}
bool isMedium() const
{
return container_type == details::ContainerType::MEDIUM;
} }
private: private:
void toMedium() void toMedium()
{ {
if (container_type != details::ContainerType::SMALL) if (getContainerType() != details::ContainerType::SMALL)
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR); throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
if (current_memory_tracker) auto tmp_medium = std::make_unique<Medium>();
current_memory_tracker->alloc(sizeof(medium));
Medium * tmp_medium = new Medium;
for (const auto & x : small) for (const auto & x : small)
tmp_medium->insert(x); tmp_medium->insert(x);
medium = tmp_medium; medium = tmp_medium.release();
setContainerType(details::ContainerType::MEDIUM);
container_type = details::ContainerType::MEDIUM; if (current_memory_tracker)
current_memory_tracker->alloc(sizeof(medium));
} }
void toLarge() void toLarge()
{ {
auto container_type = getContainerType();
if ((container_type != details::ContainerType::SMALL) && (container_type != details::ContainerType::MEDIUM)) if ((container_type != details::ContainerType::SMALL) && (container_type != details::ContainerType::MEDIUM))
throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR); throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR);
if (current_memory_tracker) auto tmp_large = std::make_unique<Large>();
current_memory_tracker->alloc(sizeof(large));
Large * tmp_large = new Large;
if (container_type == details::ContainerType::SMALL) if (container_type == details::ContainerType::SMALL)
{ {
@ -233,11 +232,25 @@ private:
} }
else if (container_type == details::ContainerType::MEDIUM) else if (container_type == details::ContainerType::MEDIUM)
{ {
for (const auto & x : *medium) for (const auto & x : getContainer<Medium>())
tmp_large->insert(x); tmp_large->insert(x);
destroy();
} }
large = tmp_large; large = tmp_large.release();
setContainerType(details::ContainerType::LARGE);
if (current_memory_tracker)
current_memory_tracker->alloc(sizeof(large));
}
void NO_INLINE destroy()
{
auto container_type = getContainerType();
clearContainerType();
if (container_type == details::ContainerType::MEDIUM) if (container_type == details::ContainerType::MEDIUM)
{ {
@ -247,16 +260,54 @@ private:
if (current_memory_tracker) if (current_memory_tracker)
current_memory_tracker->free(sizeof(medium)); current_memory_tracker->free(sizeof(medium));
} }
else if (container_type == details::ContainerType::LARGE)
{
delete large;
large = nullptr;
container_type = details::ContainerType::LARGE; if (current_memory_tracker)
current_memory_tracker->free(sizeof(large));
}
}
template<typename T>
inline T & getContainer()
{
return *reinterpret_cast<T *>(address & mask);
}
template<typename T>
inline const T & getContainer() const
{
return *reinterpret_cast<T *>(address & mask);
}
void setContainerType(details::ContainerType t)
{
address &= mask;
address |= static_cast<UInt8>(t);
}
inline details::ContainerType getContainerType() const
{
return static_cast<details::ContainerType>(address & ~mask);
}
void clearContainerType()
{
address &= mask;
} }
private: private:
Small small; Small small;
Medium * medium = nullptr; union
Large * large = nullptr; {
const UInt32 medium_set_size_max = 1UL << medium_set_power2_max; Medium * medium;
details::ContainerType container_type = details::ContainerType::SMALL; Large * large;
UInt64 address = 0;
};
static const UInt64 mask = 0xFFFFFFFFFFFFFFFC;
static const UInt32 medium_set_size_max = 1UL << medium_set_power2_max;
}; };
} }

View File

@ -16,12 +16,11 @@ namespace DB
{ {
/// хранит размеры всех столбцов, и может проверять не побились ли столбцы /// хранит размеры всех столбцов, и может проверять не побились ли столбцы
template <class Storage>
class FileChecker class FileChecker
{ {
public: public:
FileChecker(const std::string &file_info_path_, Storage & storage_) : FileChecker(const std::string & file_info_path_) :
files_info_path(file_info_path_), files_info(), storage(storage_), log(&Logger::get("FileChecker")) files_info_path(file_info_path_), files_info(), log(&Logger::get("FileChecker"))
{ {
Poco::Path path(files_info_path); Poco::Path path(files_info_path);
tmp_files_info_path = path.parent().toString() + "tmp_" + path.getFileName(); tmp_files_info_path = path.parent().toString() + "tmp_" + path.getFileName();
@ -107,7 +106,6 @@ private:
using PropertyTree = boost::property_tree::ptree; using PropertyTree = boost::property_tree::ptree;
PropertyTree files_info; PropertyTree files_info;
Storage & storage;
Logger * log; Logger * log;
}; };
} }

View File

@ -251,6 +251,7 @@ class HashTable :
protected: protected:
friend class const_iterator; friend class const_iterator;
friend class iterator; friend class iterator;
friend class Reader;
template <typename, typename, typename, typename, typename, typename, size_t> template <typename, typename, typename, typename, typename, typename, size_t>
friend class TwoLevelHashTable; friend class TwoLevelHashTable;
@ -429,6 +430,51 @@ public:
free(); free();
} }
class Reader final : private Cell::State
{
public:
Reader(DB::ReadBuffer & in_)
: in(in_)
{
}
Reader(const Reader &) = delete;
Reader & operator=(const Reader &) = delete;
bool next()
{
if (read_count == size)
{
is_eof = true;
return false;
}
else if (read_count == 0)
{
Cell::State::read(in);
DB::readVarUInt(size, in);
}
cell.read(in);
++read_count;
return true;
}
inline const value_type & get() const
{
if ((read_count == 0) || is_eof)
throw DB::Exception("No available data", DB::ErrorCodes::NO_AVAILABLE_DATA);
return cell.getValue();
}
private:
DB::ReadBuffer in;
Cell cell;
size_t read_count = 0;
size_t size;
bool is_eof = false;
};
class iterator class iterator
{ {
@ -757,7 +803,7 @@ public:
{ {
Cell x; Cell x;
x.read(rb); x.read(rb);
insert(Cell::getKey(x.getValue())); insert(x);
} }
} }
@ -781,7 +827,7 @@ public:
Cell x; Cell x;
DB::assertString(",", rb); DB::assertString(",", rb);
x.readText(rb); x.readText(rb);
insert(Cell::getKey(x.getValue())); insert(x);
} }
} }

View File

@ -19,6 +19,7 @@
/** Общая часть разных хэш-таблиц, отвечающая за выделение/освобождение памяти. /** Общая часть разных хэш-таблиц, отвечающая за выделение/освобождение памяти.
* Отличается от Allocator тем, что зануляет память.
* Используется в качестве параметра шаблона (есть несколько реализаций с таким же интерфейсом). * Используется в качестве параметра шаблона (есть несколько реализаций с таким же интерфейсом).
*/ */
class HashTableAllocator class HashTableAllocator
@ -33,9 +34,9 @@ private:
* Рассчитываем, что набор операций mmap/что-то сделать/mremap может выполняться всего лишь около 1000 раз в секунду. * Рассчитываем, что набор операций mmap/что-то сделать/mremap может выполняться всего лишь около 1000 раз в секунду.
* *
* PS. Также это требуется, потому что tcmalloc не может выделить кусок памяти больше 16 GB. * PS. Также это требуется, потому что tcmalloc не может выделить кусок памяти больше 16 GB.
* NOTE Можно попробовать MAP_HUGETLB, но придётся самостоятельно управлять количеством доступных страниц.
*/ */
static constexpr size_t MMAP_THRESHOLD = 64 * (1 << 20); static constexpr size_t MMAP_THRESHOLD = 64 * (1 << 20);
static constexpr size_t HUGE_PAGE_SIZE = 2 * (1 << 20);
public: public:
/// Выделить кусок памяти и заполнить его нулями. /// Выделить кусок памяти и заполнить его нулями.
@ -52,6 +53,14 @@ public:
if (MAP_FAILED == buf) if (MAP_FAILED == buf)
DB::throwFromErrno("HashTableAllocator: Cannot mmap.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY); DB::throwFromErrno("HashTableAllocator: Cannot mmap.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY);
/** Использование huge pages позволяет увеличить производительность более чем в три раза
* в запросе SELECT number % 1000000 AS k, count() FROM system.numbers GROUP BY k,
* (хэш-таблица на 1 000 000 элементов)
* и примерно на 15% в случае хэш-таблицы на 100 000 000 элементов.
*/
if (size >= HUGE_PAGE_SIZE && 0 != madvise(buf, size, MADV_HUGEPAGE))
DB::throwFromErrno("HashTableAllocator: Cannot madvise with MADV_HUGEPAGE.", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY);
/// Заполнение нулями не нужно - mmap сам это делает. /// Заполнение нулями не нужно - mmap сам это делает.
} }
else else
@ -108,6 +117,10 @@ public:
if (MAP_FAILED == buf) if (MAP_FAILED == buf)
DB::throwFromErrno("HashTableAllocator: Cannot mremap.", DB::ErrorCodes::CANNOT_MREMAP); DB::throwFromErrno("HashTableAllocator: Cannot mremap.", DB::ErrorCodes::CANNOT_MREMAP);
/** Здесь не получается сделать madvise с MADV_HUGEPAGE.
* Похоже, что при mremap, huge pages сами расширяются на новую область.
*/
/// Заполнение нулями не нужно. /// Заполнение нулями не нужно.
} }
else else

View File

@ -27,6 +27,7 @@ class SmallTable :
protected: protected:
friend class const_iterator; friend class const_iterator;
friend class iterator; friend class iterator;
friend class Reader;
typedef SmallTable<Key, Cell, capacity> Self; typedef SmallTable<Key, Cell, capacity> Self;
typedef Cell cell_type; typedef Cell cell_type;
@ -66,6 +67,55 @@ public:
typedef typename Cell::value_type value_type; typedef typename Cell::value_type value_type;
class Reader final : private Cell::State
{
public:
Reader(DB::ReadBuffer & in_)
: in(in_)
{
}
Reader(const Reader &) = delete;
Reader & operator=(const Reader &) = delete;
bool next()
{
if (read_count == size)
{
is_eof = true;
return false;
}
else if (read_count == 0)
{
Cell::State::read(in);
DB::readVarUInt(size, in);
if (size > capacity)
throw DB::Exception("Illegal size");
}
cell.read(in);
++read_count;
return true;
}
inline const value_type & get() const
{
if ((read_count == 0) || is_eof)
throw DB::Exception("No available data", DB::ErrorCodes::NO_AVAILABLE_DATA);
return cell.getValue();
}
private:
DB::ReadBuffer in;
Cell cell;
size_t read_count = 0;
size_t size;
bool is_eof = false;
};
class iterator class iterator
{ {
Self * container; Self * container;

View File

@ -16,7 +16,7 @@ template <
UInt8 small_set_size, UInt8 small_set_size,
UInt8 K, UInt8 K,
typename Hash = IntHash32<Key>, typename Hash = IntHash32<Key>,
typename DenominatorType = float> typename DenominatorType = double>
class HyperLogLogWithSmallSetOptimization class HyperLogLogWithSmallSetOptimization
{ {
private: private:
@ -114,10 +114,20 @@ public:
void readAndMerge(DB::ReadBuffer & in) void readAndMerge(DB::ReadBuffer & in)
{ {
/// Немного не оптимально. bool is_rhs_large;
HyperLogLogWithSmallSetOptimization other; readBinary(is_rhs_large, in);
other.read(in);
merge(other); if (!isLarge() && is_rhs_large)
toLarge();
if (!is_rhs_large)
{
typename Small::Reader reader(in);
while (reader.next())
insert(reader.get());
}
else
large->readAndMerge(in);
} }
void write(DB::WriteBuffer & out) const void write(DB::WriteBuffer & out) const

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <string.h> #include <string.h>
#include <malloc.h>
#include <cstddef> #include <cstddef>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
@ -12,7 +11,7 @@
#include <Yandex/likely.h> #include <Yandex/likely.h>
#include <Yandex/strong_typedef.h> #include <Yandex/strong_typedef.h>
#include <DB/Common/MemoryTracker.h> #include <DB/Common/Allocator.h>
#include <DB/Core/Exception.h> #include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h> #include <DB/Core/ErrorCodes.h>
@ -32,28 +31,18 @@ namespace DB
* Конструктор по-умолчанию создаёт пустой объект, который не выделяет память. * Конструктор по-умолчанию создаёт пустой объект, который не выделяет память.
* Затем выделяется память минимум под POD_ARRAY_INITIAL_SIZE элементов. * Затем выделяется память минимум под POD_ARRAY_INITIAL_SIZE элементов.
* *
* При первом выделении памяти использует std::allocator.
* В реализации из libstdc++ он кэширует куски памяти несколько больше, чем обычный malloc.
*
* При изменении размера, использует realloc, который может (но не обязан) использовать mremap для больших кусков памяти.
* По факту, mremap используется при использовании аллокатора из glibc, но не используется, например, в tcmalloc.
*
* Если вставлять элементы push_back-ом, не делая reserve, то PODArray примерно в 2.5 раза быстрее std::vector. * Если вставлять элементы push_back-ом, не делая reserve, то PODArray примерно в 2.5 раза быстрее std::vector.
*/ */
#define POD_ARRAY_INITIAL_SIZE 4096UL #define POD_ARRAY_INITIAL_SIZE 4096UL
template <typename T> template <typename T>
class PODArray : private boost::noncopyable, private std::allocator<char> /// empty base optimization class PODArray : private boost::noncopyable, private Allocator /// empty base optimization
{ {
private: private:
typedef std::allocator<char> Allocator;
char * c_start; char * c_start;
char * c_end; char * c_end;
char * c_end_of_storage; char * c_end_of_storage;
bool use_libc_realloc = false;
T * t_start() { return reinterpret_cast<T *>(c_start); } T * t_start() { return reinterpret_cast<T *>(c_start); }
T * t_end() { return reinterpret_cast<T *>(c_end); } T * t_end() { return reinterpret_cast<T *>(c_end); }
T * t_end_of_storage() { return reinterpret_cast<T *>(c_end_of_storage); } T * t_end_of_storage() { return reinterpret_cast<T *>(c_end_of_storage); }
@ -90,10 +79,7 @@ private:
size_t bytes_to_alloc = to_size(n); size_t bytes_to_alloc = to_size(n);
if (current_memory_tracker) c_start = c_end = reinterpret_cast<char *>(Allocator::alloc(bytes_to_alloc));
current_memory_tracker->alloc(bytes_to_alloc);
c_start = c_end = Allocator::allocate(bytes_to_alloc);
c_end_of_storage = c_start + bytes_to_alloc; c_end_of_storage = c_start + bytes_to_alloc;
} }
@ -102,13 +88,7 @@ private:
if (c_start == nullptr) if (c_start == nullptr)
return; return;
if (use_libc_realloc) Allocator::free(c_start, storage_size());
::free(c_start);
else
Allocator::deallocate(c_start, storage_size());
if (current_memory_tracker)
current_memory_tracker->free(storage_size());
} }
void realloc(size_t n) void realloc(size_t n)
@ -122,38 +102,10 @@ private:
ptrdiff_t end_diff = c_end - c_start; ptrdiff_t end_diff = c_end - c_start;
size_t bytes_to_alloc = to_size(n); size_t bytes_to_alloc = to_size(n);
char * old_c_start = c_start; c_start = reinterpret_cast<char *>(Allocator::realloc(c_start, storage_size(), bytes_to_alloc));
char * old_c_end_of_storage = c_end_of_storage;
if (current_memory_tracker)
current_memory_tracker->realloc(storage_size(), bytes_to_alloc);
if (use_libc_realloc)
{
auto new_c_start = reinterpret_cast<char *>(::realloc(c_start, bytes_to_alloc));
if (nullptr == new_c_start)
throwFromErrno("PODArray: cannot realloc", ErrorCodes::CANNOT_ALLOCATE_MEMORY);
c_start = new_c_start;
}
else
{
auto new_c_start = reinterpret_cast<char *>(malloc(bytes_to_alloc));
if (nullptr == new_c_start)
throwFromErrno("PODArray: cannot realloc", ErrorCodes::CANNOT_ALLOCATE_MEMORY);
c_start = new_c_start;
memcpy(c_start, old_c_start, std::min(bytes_to_alloc, static_cast<size_t>(end_diff)));
Allocator::deallocate(old_c_start, old_c_end_of_storage - old_c_start);
}
c_end = c_start + end_diff; c_end = c_start + end_diff;
c_end_of_storage = c_start + bytes_to_alloc; c_end_of_storage = c_start + bytes_to_alloc;
use_libc_realloc = true;
} }
public: public:
@ -187,7 +139,6 @@ public:
std::swap(c_start, other.c_start); std::swap(c_start, other.c_start);
std::swap(c_end, other.c_end); std::swap(c_end, other.c_end);
std::swap(c_end_of_storage, other.c_end_of_storage); std::swap(c_end_of_storage, other.c_end_of_storage);
std::swap(use_libc_realloc, other.use_libc_realloc);
return *this; return *this;
} }

View File

@ -283,6 +283,9 @@ namespace ErrorCodes
INDEX_NOT_USED = 277, INDEX_NOT_USED = 277,
LEADERSHIP_LOST = 278, LEADERSHIP_LOST = 278,
ALL_CONNECTION_TRIES_FAILED = 279, ALL_CONNECTION_TRIES_FAILED = 279,
NO_AVAILABLE_DATA = 280,
DICTIONARY_IS_EMPTY = 281,
INCORRECT_INDEX = 282,
KEEPER_EXCEPTION = 999, KEEPER_EXCEPTION = 999,
POCO_EXCEPTION = 1000, POCO_EXCEPTION = 1000,

View File

@ -28,8 +28,8 @@ ExceptionPtr cloneCurrentException();
/** Попробовать записать исключение в лог (и забыть про него). /** Попробовать записать исключение в лог (и забыть про него).
* Можно использовать в деструкторах в блоке catch (...). * Можно использовать в деструкторах в блоке catch (...).
*/ */
void tryLogCurrentException(const char * log_name); void tryLogCurrentException(const char * log_name, const std::string & start_of_message = "");
void tryLogCurrentException(Poco::Logger * logger); void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_message = "");
std::string getCurrentExceptionMessage(bool with_stacktrace); std::string getCurrentExceptionMessage(bool with_stacktrace);

View File

@ -14,10 +14,10 @@ class FormatFactory
{ {
public: public:
BlockInputStreamPtr getInput(const String & name, ReadBuffer & buf, BlockInputStreamPtr getInput(const String & name, ReadBuffer & buf,
Block & sample, size_t max_block_size) const; const Block & sample, size_t max_block_size) const;
BlockOutputStreamPtr getOutput(const String & name, WriteBuffer & buf, BlockOutputStreamPtr getOutput(const String & name, WriteBuffer & buf,
Block & sample) const; const Block & sample) const;
}; };
} }

View File

@ -0,0 +1,38 @@
#pragma once
#include <tuple>
#include <DB/Core/Types.h>
#include <DB/IO/WriteHelpers.h>
namespace DB
{
/** Засечка - позиция в сжатом файле. Сжатый файл состоит из уложенных подряд сжатых блоков.
* Засечка представляют собой пару - смещение в файле до начала сжатого блока, смещение в разжатом блоке до начала данных.
*/
struct MarkInCompressedFile
{
size_t offset_in_compressed_file;
size_t offset_in_decompressed_block;
bool operator==(const MarkInCompressedFile & rhs) const
{
return std::tie(offset_in_compressed_file, offset_in_decompressed_block)
== std::tie(rhs.offset_in_compressed_file, rhs.offset_in_decompressed_block);
}
bool operator!=(const MarkInCompressedFile & rhs) const
{
return !(*this == rhs);
}
String toString() const
{
return "(" + DB::toString(offset_in_compressed_file) + "," + DB::toString(offset_in_decompressed_block) + ")";
}
};
using MarksInCompressedFile = std::vector<MarkInCompressedFile>;
}

View File

@ -0,0 +1,110 @@
#pragma once
#include <DB/Interpreters/Aggregator.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
namespace DB
{
/** Доагрегирует потоки блоков, держа в оперативной памяти только по одному блоку из каждого потока.
* Это экономит оперативку в случае использования двухуровневой агрегации, где в каждом потоке будет до 256 блоков с частями результата.
*
* Агрегатные функции в блоках не должны быть финализированы, чтобы их состояния можно было объединить.
*/
class MergingAggregatedMemoryEfficientBlockInputStream : public IProfilingBlockInputStream
{
public:
MergingAggregatedMemoryEfficientBlockInputStream(BlockInputStreams inputs_, const Names & keys_names_,
const AggregateDescriptions & aggregates_, bool overflow_row_, bool final_)
: aggregator(keys_names_, aggregates_, overflow_row_, 0, OverflowMode::THROW, nullptr, 0, 0),
final(final_)
{
children = inputs_;
current_blocks.resize(children.size());
overflow_blocks.resize(children.size());
is_exhausted.resize(children.size());
}
String getName() const override { return "MergingAggregatedMemorySavvy"; }
String getID() const override
{
std::stringstream res;
res << "MergingAggregatedMemorySavvy(" << aggregator.getID();
for (size_t i = 0, size = children.size(); i < size; ++i)
res << ", " << children.back()->getID();
res << ")";
return res.str();
}
protected:
Block readImpl() override
{
/// Если child - RemoteBlockInputStream, то отправляет запрос на все удалённые серверы, инициируя вычисления.
if (current_bucket_num == -1)
for (auto & child : children)
child->readPrefix();
/// Всё прочитали.
if (current_bucket_num > 255)
return {};
/// Читаем следующие блоки для current_bucket_num
for (size_t i = 0, size = children.size(); i < size; ++i)
{
while (!is_exhausted[i] && (!current_blocks[i] || current_blocks[i].info.bucket_num < current_bucket_num))
{
current_blocks[i] = children[i]->read();
if (!current_blocks[i])
{
is_exhausted[i] = true;
}
else if (current_blocks[i].info.is_overflows)
{
overflow_blocks[i].swap(current_blocks[i]);
}
}
}
/// Может быть, нет блоков для current_bucket_num, а все блоки имеют больший bucket_num.
Int32 min_bucket_num = 256;
for (size_t i = 0, size = children.size(); i < size; ++i)
if (!is_exhausted[i] && current_blocks[i].info.bucket_num < min_bucket_num)
min_bucket_num = current_blocks[i].info.bucket_num;
current_bucket_num = min_bucket_num;
/// Все потоки исчерпаны.
if (current_bucket_num > 255)
return {}; /// TODO overflow_blocks.
/// TODO Если есть single_level и two_level блоки.
/// Объединяем все блоки с current_bucket_num.
BlocksList blocks_to_merge;
for (size_t i = 0, size = children.size(); i < size; ++i)
if (current_blocks[i].info.bucket_num == current_bucket_num)
blocks_to_merge.emplace_back(std::move(current_blocks[i]));
Block res = aggregator.mergeBlocks(blocks_to_merge, final);
++current_bucket_num;
return res;
}
private:
Aggregator aggregator;
bool final;
Int32 current_bucket_num = -1;
std::vector<Block> current_blocks;
std::vector<UInt8> is_exhausted;
std::vector<Block> overflow_blocks;
};
}

View File

@ -1,22 +1,75 @@
#pragma once #pragma once
#include <DB/DataStreams/IProfilingBlockInputStream.h> #include <DB/DataStreams/IProfilingBlockInputStream.h>
#include <DB/DataStreams/MarkInCompressedFile.h>
namespace DB namespace DB
{ {
class CompressedReadBufferFromFile;
/** Формат Native может содержать отдельно расположенный индекс,
* который позволяет понять, где какой столбец расположен,
* и пропускать ненужные столбцы.
*/
/** Позиция одного кусочка одного столбца. */
struct IndexOfOneColumnForNativeFormat
{
String name;
String type;
MarkInCompressedFile location;
};
/** Индекс для блока данных. */
struct IndexOfBlockForNativeFormat
{
using Columns = std::vector<IndexOfOneColumnForNativeFormat>;
size_t num_columns;
size_t num_rows;
Columns columns;
};
/** Весь индекс. */
struct IndexForNativeFormat
{
using Blocks = std::vector<IndexOfBlockForNativeFormat>;
Blocks blocks;
IndexForNativeFormat() {}
IndexForNativeFormat(ReadBuffer & istr, const NameSet & required_columns)
{
read(istr, required_columns);
}
/// Прочитать индекс, только для нужных столбцов.
void read(ReadBuffer & istr, const NameSet & required_columns);
};
/** Десериализует поток блоков из родного бинарного формата (с именами и типами столбцов). /** Десериализует поток блоков из родного бинарного формата (с именами и типами столбцов).
* Предназначено для взаимодействия между серверами. * Предназначено для взаимодействия между серверами.
*
* Также может использоваться для хранения данных на диске.
* В этом случае, может использовать индекс.
*/ */
class NativeBlockInputStream : public IProfilingBlockInputStream class NativeBlockInputStream : public IProfilingBlockInputStream
{ {
public: public:
/** В случае указания ненулевой server_revision, может ожидаться и считываться дополнительная информация о блоке, /** В случае указания ненулевой server_revision, может ожидаться и считываться дополнительная информация о блоке,
* в зависимости от поддерживаемой для указанной ревизии. * в зависимости от поддерживаемой для указанной ревизии.
*
* index - не обязательный параметр. Если задан, то будут читаться только указанные в индексе кусочки столбцов.
*/ */
NativeBlockInputStream(ReadBuffer & istr_, UInt64 server_revision_ = 0) NativeBlockInputStream(
: istr(istr_), server_revision(server_revision_) {} ReadBuffer & istr_, UInt64 server_revision_ = 0,
bool use_index_ = false,
IndexForNativeFormat::Blocks::const_iterator index_block_it_ = IndexForNativeFormat::Blocks::const_iterator{},
IndexForNativeFormat::Blocks::const_iterator index_block_end_ = IndexForNativeFormat::Blocks::const_iterator{});
String getName() const override { return "Native"; } String getName() const override { return "Native"; }
@ -35,6 +88,14 @@ protected:
private: private:
ReadBuffer & istr; ReadBuffer & istr;
UInt64 server_revision; UInt64 server_revision;
bool use_index;
IndexForNativeFormat::Blocks::const_iterator index_block_it;
IndexForNativeFormat::Blocks::const_iterator index_block_end;
IndexOfBlockForNativeFormat::Columns::const_iterator index_column_it;
/// Если задан индекс, то istr должен быть CompressedReadBufferFromFile.
CompressedReadBufferFromFile * istr_concrete;
}; };
} }

View File

@ -6,8 +6,14 @@
namespace DB namespace DB
{ {
class WriteBuffer;
class CompressedWriteBuffer;
/** Сериализует поток блоков в родном бинарном формате (с именами и типами столбцов). /** Сериализует поток блоков в родном бинарном формате (с именами и типами столбцов).
* Предназначено для взаимодействия между серверами. * Предназначено для взаимодействия между серверами.
*
* Может быть указан поток для записи индекса. Индекс содержит смещения до каждого кусочка каждого столбца.
*/ */
class NativeBlockOutputStream : public IBlockOutputStream class NativeBlockOutputStream : public IBlockOutputStream
{ {
@ -15,8 +21,9 @@ public:
/** В случае указания ненулевой client_revision, может записываться дополнительная информация о блоке, /** В случае указания ненулевой client_revision, может записываться дополнительная информация о блоке,
* в зависимости от поддерживаемой для указанной ревизии. * в зависимости от поддерживаемой для указанной ревизии.
*/ */
NativeBlockOutputStream(WriteBuffer & ostr_, UInt64 client_revision_ = 0) NativeBlockOutputStream(
: ostr(ostr_), client_revision(client_revision_) {} WriteBuffer & ostr_, UInt64 client_revision_ = 0,
WriteBuffer * index_ostr_ = nullptr);
void write(const Block & block) override; void write(const Block & block) override;
void flush() override { ostr.next(); } void flush() override { ostr.next(); }
@ -26,6 +33,10 @@ public:
private: private:
WriteBuffer & ostr; WriteBuffer & ostr;
UInt64 client_revision; UInt64 client_revision;
WriteBuffer * index_ostr;
/// Если требуется записывать индекс, то ostr обязан быть CompressedWriteBuffer.
CompressedWriteBuffer * ostr_concrete = nullptr;
}; };
} }

View File

@ -84,6 +84,16 @@ public:
if (!is_cancelled.compare_exchange_strong(old_val, true, std::memory_order_seq_cst, std::memory_order_relaxed)) if (!is_cancelled.compare_exchange_strong(old_val, true, std::memory_order_seq_cst, std::memory_order_relaxed))
return; return;
{
std::lock_guard<std::mutex> lock(external_tables_mutex);
/// Останавливаем отправку внешних данных.
for (auto & vec : external_tables_data)
for (auto & elem : vec)
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(elem.first.get()))
stream->cancel();
}
if (!isQueryPending() || hasThrownException()) if (!isQueryPending() || hasThrownException())
return; return;
@ -101,14 +111,24 @@ public:
parallel_replicas->disconnect(); parallel_replicas->disconnect();
} }
/// Отправляет запрос (инициирует вычисления) раньше, чем read.
void readPrefix() override
{
if (!sent_query)
sendQuery();
}
protected: protected:
/// Отправить на удаленные реплики все временные таблицы /// Отправить на удаленные реплики все временные таблицы
void sendExternalTables() void sendExternalTables()
{ {
size_t count = parallel_replicas->size(); size_t count = parallel_replicas->size();
std::vector<ExternalTablesData> instances; {
instances.reserve(count); std::lock_guard<std::mutex> lock(external_tables_mutex);
external_tables_data.reserve(count);
for (size_t i = 0; i < count; ++i) for (size_t i = 0; i < count; ++i)
{ {
@ -124,29 +144,21 @@ protected:
else else
res.push_back(std::make_pair(input[0], table.first)); res.push_back(std::make_pair(input[0], table.first));
} }
instances.push_back(std::move(res)); external_tables_data.push_back(std::move(res));
}
} }
parallel_replicas->sendExternalTablesData(instances); parallel_replicas->sendExternalTablesData(external_tables_data);
} }
Block readImpl() override Block readImpl() override
{ {
if (!sent_query) if (!sent_query)
{ {
createParallelReplicas(); sendQuery();
if (settings.skip_unavailable_shards && 0 == parallel_replicas->size()) if (settings.skip_unavailable_shards && 0 == parallel_replicas->size())
return Block(); return {};
established = true;
parallel_replicas->sendQuery(query, "", stage, true);
established = false;
sent_query = true;
sendExternalTables();
} }
while (true) while (true)
@ -267,6 +279,23 @@ protected:
} }
private: private:
void sendQuery()
{
createParallelReplicas();
if (settings.skip_unavailable_shards && 0 == parallel_replicas->size())
return;
established = true;
parallel_replicas->sendQuery(query, "", stage, true);
established = false;
sent_query = true;
sendExternalTables();
}
/// ITable::read requires a Context, therefore we should create one if the user can't supply it /// ITable::read requires a Context, therefore we should create one if the user can't supply it
static Context & getDefaultContext() static Context & getDefaultContext()
{ {
@ -302,6 +331,10 @@ private:
QueryProcessingStage::Enum stage; QueryProcessingStage::Enum stage;
Context context; Context context;
/// Потоки для чтения из временных таблиц - для последующей отправки данных на удалённые серверы для GLOBAL-подзапросов.
std::vector<ExternalTablesData> external_tables_data;
std::mutex external_tables_mutex;
/// Установили соединения с репликами, но ещё не отправили запрос. /// Установили соединения с репликами, но ещё не отправили запрос.
std::atomic<bool> established { false }; std::atomic<bool> established { false };

View File

@ -620,9 +620,9 @@ private:
mutable std::mt19937_64 rnd_engine{getSeed()}; mutable std::mt19937_64 rnd_engine{getSeed()};
mutable std::size_t bytes_allocated = 0; mutable std::size_t bytes_allocated = 0;
mutable std::atomic<std::size_t> element_count{}; mutable std::atomic<std::size_t> element_count{0};
mutable std::atomic<std::size_t> hit_count{}; mutable std::atomic<std::size_t> hit_count{0};
mutable std::atomic<std::size_t> query_count{}; mutable std::atomic<std::size_t> query_count{0};
const std::chrono::time_point<std::chrono::system_clock> creation_time = std::chrono::system_clock::now(); const std::chrono::time_point<std::chrono::system_clock> creation_time = std::chrono::system_clock::now();
}; };

View File

@ -20,8 +20,7 @@ class FileDictionarySource final : public IDictionarySource
public: public:
FileDictionarySource(const std::string & filename, const std::string & format, Block & sample_block, FileDictionarySource(const std::string & filename, const std::string & format, Block & sample_block,
const Context & context) const Context & context)
: filename{filename}, format{format}, sample_block{sample_block}, context(context), : filename{filename}, format{format}, sample_block{sample_block}, context(context)
last_modification{getLastModification()}
{} {}
FileDictionarySource(const FileDictionarySource & other) FileDictionarySource(const FileDictionarySource & other)

View File

@ -20,9 +20,10 @@ class FlatDictionary final : public IDictionary
{ {
public: public:
FlatDictionary(const std::string & name, const DictionaryStructure & dict_struct, FlatDictionary(const std::string & name, const DictionaryStructure & dict_struct,
DictionarySourcePtr source_ptr, const DictionaryLifetime dict_lifetime) DictionarySourcePtr source_ptr, const DictionaryLifetime dict_lifetime, bool require_nonempty)
: name{name}, dict_struct(dict_struct), : name{name}, dict_struct(dict_struct),
source_ptr{std::move(source_ptr)}, dict_lifetime(dict_lifetime) source_ptr{std::move(source_ptr)}, dict_lifetime(dict_lifetime),
require_nonempty(require_nonempty)
{ {
createAttributes(); createAttributes();
@ -40,7 +41,7 @@ public:
} }
FlatDictionary(const FlatDictionary & other) FlatDictionary(const FlatDictionary & other)
: FlatDictionary{other.name, other.dict_struct, other.source_ptr->clone(), other.dict_lifetime} : FlatDictionary{other.name, other.dict_struct, other.source_ptr->clone(), other.dict_lifetime, other.require_nonempty}
{} {}
std::exception_ptr getCreationException() const override { return creation_exception; } std::exception_ptr getCreationException() const override { return creation_exception; }
@ -198,6 +199,9 @@ private:
} }
stream->readSuffix(); stream->readSuffix();
if (require_nonempty && 0 == element_count)
throw Exception("Dictionary source is empty and 'require_nonempty' property is set.", ErrorCodes::DICTIONARY_IS_EMPTY);
} }
template <typename T> template <typename T>
@ -348,6 +352,7 @@ private:
const DictionaryStructure dict_struct; const DictionaryStructure dict_struct;
const DictionarySourcePtr source_ptr; const DictionarySourcePtr source_ptr;
const DictionaryLifetime dict_lifetime; const DictionaryLifetime dict_lifetime;
const bool require_nonempty;
std::map<std::string, std::size_t> attribute_index_by_name; std::map<std::string, std::size_t> attribute_index_by_name;
std::vector<attribute_t> attributes; std::vector<attribute_t> attributes;
@ -356,7 +361,7 @@ private:
std::size_t bytes_allocated = 0; std::size_t bytes_allocated = 0;
std::size_t element_count = 0; std::size_t element_count = 0;
std::size_t bucket_count = 0; std::size_t bucket_count = 0;
mutable std::atomic<std::size_t> query_count; mutable std::atomic<std::size_t> query_count{0};
std::chrono::time_point<std::chrono::system_clock> creation_time; std::chrono::time_point<std::chrono::system_clock> creation_time;

View File

@ -18,9 +18,10 @@ class HashedDictionary final : public IDictionary
{ {
public: public:
HashedDictionary(const std::string & name, const DictionaryStructure & dict_struct, HashedDictionary(const std::string & name, const DictionaryStructure & dict_struct,
DictionarySourcePtr source_ptr, const DictionaryLifetime dict_lifetime) DictionarySourcePtr source_ptr, const DictionaryLifetime dict_lifetime, bool require_nonempty)
: name{name}, dict_struct(dict_struct), : name{name}, dict_struct(dict_struct),
source_ptr{std::move(source_ptr)}, dict_lifetime(dict_lifetime) source_ptr{std::move(source_ptr)}, dict_lifetime(dict_lifetime),
require_nonempty(require_nonempty)
{ {
createAttributes(); createAttributes();
@ -38,7 +39,7 @@ public:
} }
HashedDictionary(const HashedDictionary & other) HashedDictionary(const HashedDictionary & other)
: HashedDictionary{other.name, other.dict_struct, other.source_ptr->clone(), other.dict_lifetime} : HashedDictionary{other.name, other.dict_struct, other.source_ptr->clone(), other.dict_lifetime, other.require_nonempty}
{} {}
std::exception_ptr getCreationException() const override { return creation_exception; } std::exception_ptr getCreationException() const override { return creation_exception; }
@ -196,6 +197,9 @@ private:
} }
stream->readSuffix(); stream->readSuffix();
if (require_nonempty && 0 == element_count)
throw Exception("Dictionary source is empty and 'require_nonempty' property is set.", ErrorCodes::DICTIONARY_IS_EMPTY);
} }
template <typename T> template <typename T>
@ -334,6 +338,7 @@ private:
const DictionaryStructure dict_struct; const DictionaryStructure dict_struct;
const DictionarySourcePtr source_ptr; const DictionarySourcePtr source_ptr;
const DictionaryLifetime dict_lifetime; const DictionaryLifetime dict_lifetime;
const bool require_nonempty;
std::map<std::string, std::size_t> attribute_index_by_name; std::map<std::string, std::size_t> attribute_index_by_name;
std::vector<attribute_t> attributes; std::vector<attribute_t> attributes;
@ -342,7 +347,7 @@ private:
std::size_t bytes_allocated = 0; std::size_t bytes_allocated = 0;
std::size_t element_count = 0; std::size_t element_count = 0;
std::size_t bucket_count = 0; std::size_t bucket_count = 0;
mutable std::atomic<std::size_t> query_count{}; mutable std::atomic<std::size_t> query_count{0};
std::chrono::time_point<std::chrono::system_clock> creation_time; std::chrono::time_point<std::chrono::system_clock> creation_time;

View File

@ -23,10 +23,10 @@ public:
db{config.getString(config_prefix + ".db", "")}, db{config.getString(config_prefix + ".db", "")},
table{config.getString(config_prefix + ".table")}, table{config.getString(config_prefix + ".table")},
where{config.getString(config_prefix + ".where", "")}, where{config.getString(config_prefix + ".where", "")},
dont_check_update_time{config.getBool(config_prefix + ".dont_check_update_time", false)},
sample_block{sample_block}, sample_block{sample_block},
pool{config, config_prefix}, pool{config, config_prefix},
load_all_query{composeLoadAllQuery()}, load_all_query{composeLoadAllQuery()}
last_modification{getLastModification()}
{} {}
/// copy-constructor is provided in order to support cloneability /// copy-constructor is provided in order to support cloneability
@ -35,6 +35,7 @@ public:
db{other.db}, db{other.db},
table{other.table}, table{other.table},
where{other.where}, where{other.where},
dont_check_update_time{other.dont_check_update_time},
sample_block{other.sample_block}, sample_block{other.sample_block},
pool{other.pool}, pool{other.pool},
load_all_query{other.load_all_query}, last_modification{other.last_modification} load_all_query{other.load_all_query}, last_modification{other.last_modification}
@ -43,18 +44,27 @@ public:
BlockInputStreamPtr loadAll() override BlockInputStreamPtr loadAll() override
{ {
last_modification = getLastModification(); last_modification = getLastModification();
LOG_TRACE(log, load_all_query);
return new MySQLBlockInputStream{pool.Get(), load_all_query, sample_block, max_block_size}; return new MySQLBlockInputStream{pool.Get(), load_all_query, sample_block, max_block_size};
} }
BlockInputStreamPtr loadIds(const std::vector<std::uint64_t> & ids) override BlockInputStreamPtr loadIds(const std::vector<std::uint64_t> & ids) override
{ {
last_modification = getLastModification(); /// Здесь не логгируем и не обновляем время модификации, так как запрос может быть большим, и часто задаваться.
const auto query = composeLoadIdsQuery(ids);
const auto query = composeLoadIdsQuery(ids);
return new MySQLBlockInputStream{pool.Get(), query, sample_block, max_block_size}; return new MySQLBlockInputStream{pool.Get(), query, sample_block, max_block_size};
} }
bool isModified() const override { return getLastModification() > last_modification; } bool isModified() const override
{
if (dont_check_update_time)
return true;
return getLastModification() > last_modification;
}
bool supportsSelectiveLoad() const override { return true; } bool supportsSelectiveLoad() const override { return true; }
DictionarySourcePtr clone() const override { return std::make_unique<MySQLDictionarySource>(*this); } DictionarySourcePtr clone() const override { return std::make_unique<MySQLDictionarySource>(*this); }
@ -65,27 +75,47 @@ public:
} }
private: private:
Logger * log = &Logger::get("MySQLDictionarySource");
mysqlxx::DateTime getLastModification() const mysqlxx::DateTime getLastModification() const
{ {
const auto Update_time_idx = 12;
mysqlxx::DateTime update_time{std::time(nullptr)}; mysqlxx::DateTime update_time{std::time(nullptr)};
if (dont_check_update_time)
return update_time;
try try
{ {
auto connection = pool.Get(); auto connection = pool.Get();
auto query = connection->query("SHOW TABLE STATUS LIKE '%" + strconvert::escaped_for_like(table) + "%';"); auto query = connection->query("SHOW TABLE STATUS LIKE '" + strconvert::escaped_for_like(table) + "'");
LOG_TRACE(log, query.str());
auto result = query.use(); auto result = query.use();
size_t fetched_rows = 0;
if (auto row = result.fetch()) if (auto row = result.fetch())
{ {
const auto & update_time_value = row[Update_time_idx]; ++fetched_rows;
const auto UPDATE_TIME_IDX = 12;
const auto & update_time_value = row[UPDATE_TIME_IDX];
if (!update_time_value.isNull()) if (!update_time_value.isNull())
{
update_time = update_time_value.getDateTime(); update_time = update_time_value.getDateTime();
LOG_TRACE(log, "Got update time: " << update_time);
}
/// fetch remaining rows to avoid "commands out of sync" error /// fetch remaining rows to avoid "commands out of sync" error
while (auto row = result.fetch()); while (auto row = result.fetch())
++fetched_rows;
} }
if (0 == fetched_rows)
LOG_ERROR(log, "Cannot find table in SHOW TABLE STATUS result.");
if (fetched_rows > 1)
LOG_ERROR(log, "Found more than one table in SHOW TABLE STATUS result.");
} }
catch (...) catch (...)
{ {
@ -209,6 +239,7 @@ private:
const std::string db; const std::string db;
const std::string table; const std::string table;
const std::string where; const std::string where;
const bool dont_check_update_time;
Block sample_block; Block sample_block;
mutable mysqlxx::PoolWithFailover pool; mutable mysqlxx::PoolWithFailover pool;
const std::string load_all_query; const std::string load_all_query;

View File

@ -19,9 +19,10 @@ class RangeHashedDictionary final : public IDictionaryBase
public: public:
RangeHashedDictionary( RangeHashedDictionary(
const std::string & name, const DictionaryStructure & dict_struct, DictionarySourcePtr source_ptr, const std::string & name, const DictionaryStructure & dict_struct, DictionarySourcePtr source_ptr,
const DictionaryLifetime dict_lifetime) const DictionaryLifetime dict_lifetime, bool require_nonempty)
: name{name}, dict_struct(dict_struct), : name{name}, dict_struct(dict_struct),
source_ptr{std::move(source_ptr)}, dict_lifetime(dict_lifetime) source_ptr{std::move(source_ptr)}, dict_lifetime(dict_lifetime),
require_nonempty(require_nonempty)
{ {
createAttributes(); createAttributes();
@ -39,7 +40,7 @@ public:
} }
RangeHashedDictionary(const RangeHashedDictionary & other) RangeHashedDictionary(const RangeHashedDictionary & other)
: RangeHashedDictionary{other.name, other.dict_struct, other.source_ptr->clone(), other.dict_lifetime} : RangeHashedDictionary{other.name, other.dict_struct, other.source_ptr->clone(), other.dict_lifetime, other.require_nonempty}
{} {}
std::exception_ptr getCreationException() const override { return creation_exception; } std::exception_ptr getCreationException() const override { return creation_exception; }
@ -218,6 +219,9 @@ private:
} }
stream->readSuffix(); stream->readSuffix();
if (require_nonempty && 0 == element_count)
throw Exception("Dictionary source is empty and 'require_nonempty' property is set.", ErrorCodes::DICTIONARY_IS_EMPTY);
} }
template <typename T> template <typename T>
@ -410,6 +414,7 @@ private:
const DictionaryStructure dict_struct; const DictionaryStructure dict_struct;
const DictionarySourcePtr source_ptr; const DictionarySourcePtr source_ptr;
const DictionaryLifetime dict_lifetime; const DictionaryLifetime dict_lifetime;
const bool require_nonempty;
std::map<std::string, std::size_t> attribute_index_by_name; std::map<std::string, std::size_t> attribute_index_by_name;
std::vector<attribute_t> attributes; std::vector<attribute_t> attributes;
@ -417,7 +422,7 @@ private:
std::size_t bytes_allocated = 0; std::size_t bytes_allocated = 0;
std::size_t element_count = 0; std::size_t element_count = 0;
std::size_t bucket_count = 0; std::size_t bucket_count = 0;
mutable std::atomic<std::size_t> query_count{}; mutable std::atomic<std::size_t> query_count{0};
std::chrono::time_point<std::chrono::system_clock> creation_time; std::chrono::time_point<std::chrono::system_clock> creation_time;

View File

@ -43,6 +43,8 @@ namespace DB
* Например: arrayEnumerateUniq([10, 20, 10, 30]) = [1, 1, 2, 1] * Например: arrayEnumerateUniq([10, 20, 10, 30]) = [1, 1, 2, 1]
* arrayEnumerateUniq(arr1, arr2...) * arrayEnumerateUniq(arr1, arr2...)
* - для кортежей из элементов на соответствующих позициях в нескольких массивах. * - для кортежей из элементов на соответствующих позициях в нескольких массивах.
*
* emptyArrayToSingle(arr) - заменить пустые массивы на массивы из одного элемента со значением "по-умолчанию".
*/ */
@ -1695,6 +1697,256 @@ private:
}; };
class FunctionEmptyArrayToSingle : public IFunction
{
public:
static constexpr auto name = "emptyArrayToSingle";
static IFunction * create(const Context & context) { return new FunctionEmptyArrayToSingle; }
/// Получить имя функции.
String getName() const
{
return name;
}
/// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
DataTypePtr getReturnType(const DataTypes & arguments) const
{
if (arguments.size() != 1)
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
+ toString(arguments.size()) + ", should be 1.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
const DataTypeArray * array_type = typeid_cast<const DataTypeArray *>(arguments[0].get());
if (!array_type)
throw Exception("Argument for function " + getName() + " must be array.",
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return arguments[0]->clone();
}
/// Выполнить функцию над блоком.
void execute(Block & block, const ColumnNumbers & arguments, size_t result)
{
if (executeConst(block, arguments, result))
return;
const ColumnArray * array = typeid_cast<const ColumnArray *>(block.getByPosition(arguments[0]).column.get());
if (!array)
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of first argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
ColumnPtr res_ptr = array->cloneEmpty();
block.getByPosition(result).column = res_ptr;
ColumnArray & res = static_cast<ColumnArray &>(*res_ptr);
const IColumn & src_data = array->getData();
const ColumnArray::Offsets_t & src_offsets = array->getOffsets();
IColumn & res_data = res.getData();
ColumnArray::Offsets_t & res_offsets = res.getOffsets();
if (!( executeNumber<UInt8> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<UInt16> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<UInt32> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<UInt64> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<Int8> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<Int16> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<Int32> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<Int64> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<Float32> (src_data, src_offsets, res_data, res_offsets)
|| executeNumber<Float64> (src_data, src_offsets, res_data, res_offsets)
|| executeString (src_data, src_offsets, res_data, res_offsets)
|| executeFixedString (src_data, src_offsets, res_data, res_offsets)))
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of first argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
}
private:
bool executeConst(Block & block, const ColumnNumbers & arguments, size_t result)
{
if (const ColumnConstArray * const_array = typeid_cast<const ColumnConstArray *>(block.getByPosition(arguments[0]).column.get()))
{
if (const_array->getData().empty())
{
auto nested_type = typeid_cast<const DataTypeArray &>(*block.getByPosition(arguments[0]).type).getNestedType();
block.getByPosition(result).column = new ColumnConstArray(
block.rowsInFirstColumn(),
{nested_type->getDefault()},
nested_type->clone());
}
else
block.getByPosition(result).column = block.getByPosition(arguments[0]).column;
return true;
}
else
return false;
}
template <typename T>
bool executeNumber(
const IColumn & src_data, const ColumnArray::Offsets_t & src_offsets,
IColumn & res_data_col, ColumnArray::Offsets_t & res_offsets)
{
if (const ColumnVector<T> * src_data_concrete = typeid_cast<const ColumnVector<T> *>(&src_data))
{
const PODArray<T> & src_data = src_data_concrete->getData();
PODArray<T> & res_data = typeid_cast<ColumnVector<T> &>(res_data_col).getData();
size_t size = src_offsets.size();
res_offsets.resize(size);
res_data.reserve(src_data.size());
ColumnArray::Offset_t src_prev_offset = 0;
ColumnArray::Offset_t res_prev_offset = 0;
for (size_t i = 0; i < size; ++i)
{
if (src_offsets[i] != src_prev_offset)
{
size_t size_to_write = src_offsets[i] - src_prev_offset;
size_t prev_res_data_size = res_data.size();
res_data.resize(prev_res_data_size + size_to_write);
memcpy(&res_data[prev_res_data_size], &src_data[src_prev_offset], size_to_write * sizeof(T));
res_prev_offset += size_to_write;
res_offsets[i] = res_prev_offset;
}
else
{
res_data.push_back(T());
++res_prev_offset;
res_offsets[i] = res_prev_offset;
}
src_prev_offset = src_offsets[i];
}
return true;
}
else
return false;
}
bool executeFixedString(
const IColumn & src_data, const ColumnArray::Offsets_t & src_offsets,
IColumn & res_data_col, ColumnArray::Offsets_t & res_offsets)
{
if (const ColumnFixedString * src_data_concrete = typeid_cast<const ColumnFixedString *>(&src_data))
{
const size_t n = src_data_concrete->getN();
const ColumnFixedString::Chars_t & src_data = src_data_concrete->getChars();
ColumnFixedString::Chars_t & res_data = typeid_cast<ColumnFixedString &>(res_data_col).getChars();
size_t size = src_offsets.size();
res_offsets.resize(size);
res_data.reserve(src_data.size());
ColumnArray::Offset_t src_prev_offset = 0;
ColumnArray::Offset_t res_prev_offset = 0;
for (size_t i = 0; i < size; ++i)
{
if (src_offsets[i] != src_prev_offset)
{
size_t size_to_write = src_offsets[i] - src_prev_offset;
size_t prev_res_data_size = res_data.size();
res_data.resize(prev_res_data_size + size_to_write * n);
memcpy(&res_data[prev_res_data_size], &src_data[src_prev_offset], size_to_write * n);
res_prev_offset += size_to_write;
res_offsets[i] = res_prev_offset;
}
else
{
size_t prev_res_data_size = res_data.size();
res_data.resize(prev_res_data_size + n);
memset(&res_data[prev_res_data_size], 0, n);
++res_prev_offset;
res_offsets[i] = res_prev_offset;
}
src_prev_offset = src_offsets[i];
}
return true;
}
else
return false;
}
bool executeString(
const IColumn & src_data, const ColumnArray::Offsets_t & src_array_offsets,
IColumn & res_data_col, ColumnArray::Offsets_t & res_array_offsets)
{
if (const ColumnString * src_data_concrete = typeid_cast<const ColumnString *>(&src_data))
{
const ColumnString::Offsets_t & src_string_offsets = src_data_concrete->getOffsets();
ColumnString::Offsets_t & res_string_offsets = typeid_cast<ColumnString &>(res_data_col).getOffsets();
const ColumnString::Chars_t & src_data = src_data_concrete->getChars();
ColumnString::Chars_t & res_data = typeid_cast<ColumnString &>(res_data_col).getChars();
size_t size = src_array_offsets.size();
res_array_offsets.resize(size);
res_string_offsets.reserve(src_string_offsets.size());
res_data.reserve(src_data.size());
ColumnArray::Offset_t src_array_prev_offset = 0;
ColumnArray::Offset_t res_array_prev_offset = 0;
ColumnString::Offset_t src_string_prev_offset = 0;
ColumnString::Offset_t res_string_prev_offset = 0;
for (size_t i = 0; i < size; ++i)
{
if (src_array_offsets[i] != src_array_prev_offset)
{
size_t array_size = src_array_offsets[i] - src_array_prev_offset;
size_t bytes_to_copy = 0;
size_t from_string_prev_offset_local = src_string_prev_offset;
for (size_t j = 0; j < array_size; ++j)
{
size_t string_size = src_string_offsets[src_array_prev_offset + j] - from_string_prev_offset_local;
res_string_prev_offset += string_size;
res_string_offsets.push_back(res_string_prev_offset);
from_string_prev_offset_local += string_size;
bytes_to_copy += string_size;
}
size_t res_data_old_size = res_data.size();
res_data.resize(res_data_old_size + bytes_to_copy);
memcpy(&res_data[res_data_old_size], &src_data[src_string_prev_offset], bytes_to_copy);
res_array_prev_offset += array_size;
res_array_offsets[i] = res_array_prev_offset;
}
else
{
res_data.push_back(0); /// Пустая строка, включая ноль на конце.
++res_string_prev_offset;
res_string_offsets.push_back(res_string_prev_offset);
++res_array_prev_offset;
res_array_offsets[i] = res_array_prev_offset;
}
src_array_prev_offset = src_array_offsets[i];
if (src_array_prev_offset)
src_string_prev_offset = src_string_offsets[src_array_prev_offset - 1];
}
return true;
}
else
return false;
}
};
struct NameHas { static constexpr auto name = "has"; }; struct NameHas { static constexpr auto name = "has"; };
struct NameIndexOf { static constexpr auto name = "indexOf"; }; struct NameIndexOf { static constexpr auto name = "indexOf"; };
struct NameCountEqual { static constexpr auto name = "countEqual"; }; struct NameCountEqual { static constexpr auto name = "countEqual"; };

View File

@ -862,7 +862,7 @@ private:
dict->getString(attr_name, ids, out.get()); dict->getString(attr_name, ids, out.get());
block.getByPosition(result).column = new ColumnConst<String>{ block.getByPosition(result).column = new ColumnConst<String>{
id_col->size(), out->getDataAtWithTerminatingZero(0).toString() id_col->size(), out->getDataAt(0).toString()
}; };
} }
else else
@ -967,7 +967,7 @@ private:
dictionary->getString(attr_name, ids, dates, out.get()); dictionary->getString(attr_name, ids, dates, out.get());
block.getByPosition(result).column = new ColumnConst<String>{ block.getByPosition(result).column = new ColumnConst<String>{
id_col->size(), out->getDataAtWithTerminatingZero(0).toString() id_col->size(), out->getDataAt(0).toString()
}; };
} }
else else

View File

@ -404,31 +404,36 @@ struct ExtractURLParameterImpl
{ {
size_t cur_offset = offsets[i]; size_t cur_offset = offsets[i];
const char * pos = nullptr;
do
{
const char * str = reinterpret_cast<const char *>(&data[prev_offset]); const char * str = reinterpret_cast<const char *>(&data[prev_offset]);
const char * pos = nullptr;
const char * begin = strchr(str, '?'); const char * begin = strchr(str, '?');
if (begin == nullptr) if (begin != nullptr)
break; {
pos = begin + 1;
while (true)
{
pos = strstr(pos, param_str);
pos = strstr(begin + 1, param_str);
if (pos == nullptr) if (pos == nullptr)
break; break;
if (pos != begin + 1 && *(pos - 1) != ';' && *(pos - 1) != '&')
if (pos[-1] != '?' && pos[-1] != '&')
{ {
pos = nullptr; pos += param_len;
continue;
}
else
{
pos += param_len;
break; break;
} }
}
pos += param_len; }
} while (false);
if (pos != nullptr) if (pos != nullptr)
{ {
const char * end = strpbrk(pos, "&;#"); const char * end = strpbrk(pos, "&#");
if (end == nullptr) if (end == nullptr)
end = pos + strlen(pos); end = pos + strlen(pos);

View File

@ -3,7 +3,7 @@
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <DB/Common/ProfileEvents.h> #include <DB/Common/ProfileEvents.h>
#include <DB/Common/MemoryTracker.h> #include <DB/Common/Allocator.h>
#include <DB/Core/Exception.h> #include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h> #include <DB/Core/ErrorCodes.h>
@ -18,7 +18,7 @@ namespace DB
* Отличается тем, что не делает лишний memset. (И почти ничего не делает.) * Отличается тем, что не делает лишний memset. (И почти ничего не делает.)
* Также можно попросить выделять выровненный кусок памяти. * Также можно попросить выделять выровненный кусок памяти.
*/ */
struct Memory : boost::noncopyable struct Memory : boost::noncopyable, Allocator
{ {
size_t m_capacity = 0; size_t m_capacity = 0;
size_t m_size = 0; size_t m_size = 0;
@ -66,16 +66,22 @@ struct Memory : boost::noncopyable
} }
else else
{ {
dealloc(); new_size = align(new_size);
m_data = reinterpret_cast<char *>(Allocator::realloc(m_data, m_capacity, new_size, alignment));
m_capacity = new_size; m_capacity = new_size;
m_size = m_capacity; m_size = m_capacity;
alloc();
} }
} }
private: private:
size_t align(size_t value) const
{
if (!alignment)
return value;
return (value + alignment - 1) / alignment * alignment;
}
void alloc() void alloc()
{ {
if (!m_capacity) if (!m_capacity)
@ -87,33 +93,10 @@ private:
ProfileEvents::increment(ProfileEvents::IOBufferAllocs); ProfileEvents::increment(ProfileEvents::IOBufferAllocs);
ProfileEvents::increment(ProfileEvents::IOBufferAllocBytes, m_capacity); ProfileEvents::increment(ProfileEvents::IOBufferAllocBytes, m_capacity);
if (current_memory_tracker) size_t new_capacity = align(m_capacity);
current_memory_tracker->alloc(m_capacity); m_data = reinterpret_cast<char *>(Allocator::alloc(new_capacity, alignment));
m_capacity = new_capacity;
char * new_m_data = nullptr;
if (!alignment)
{
new_m_data = reinterpret_cast<char *>(malloc(m_capacity));
if (!new_m_data)
throw Exception("Cannot allocate memory (malloc)", ErrorCodes::CANNOT_ALLOCATE_MEMORY);
m_data = new_m_data;
return;
}
size_t aligned_capacity = (m_capacity + alignment - 1) / alignment * alignment;
m_capacity = aligned_capacity;
m_size = m_capacity; m_size = m_capacity;
int res = posix_memalign(reinterpret_cast<void **>(&new_m_data), alignment, m_capacity);
if (0 != res)
DB::throwFromErrno("Cannot allocate memory (posix_memalign)", ErrorCodes::CANNOT_ALLOCATE_MEMORY, res);
m_data = new_m_data;
} }
void dealloc() void dealloc()
@ -121,11 +104,8 @@ private:
if (!m_data) if (!m_data)
return; return;
free(reinterpret_cast<void *>(m_data)); Allocator::free(reinterpret_cast<void *>(m_data), m_capacity);
m_data = nullptr; /// Чтобы избежать double free, если последующий вызов alloc кинет исключение. m_data = nullptr; /// Чтобы избежать double free, если последующий вызов alloc кинет исключение.
if (current_memory_tracker)
current_memory_tracker->free(m_capacity);
} }
}; };

View File

@ -15,8 +15,8 @@ template <class Buffer>
class IHashingBuffer : public BufferWithOwnMemory<Buffer> class IHashingBuffer : public BufferWithOwnMemory<Buffer>
{ {
public: public:
IHashingBuffer<Buffer>(size_t block_size_ = DBMS_DEFAULT_HASHING_BLOCK_SIZE) : IHashingBuffer<Buffer>(size_t block_size_ = DBMS_DEFAULT_HASHING_BLOCK_SIZE)
block_pos(0), block_size(block_size_), state(0, 0) : BufferWithOwnMemory<Buffer>(block_size_), block_pos(0), block_size(block_size_), state(0, 0)
{ {
} }

View File

@ -674,17 +674,6 @@ typedef SharedPtr<AggregatedDataVariants> AggregatedDataVariantsPtr;
typedef std::vector<AggregatedDataVariantsPtr> ManyAggregatedDataVariants; typedef std::vector<AggregatedDataVariantsPtr> ManyAggregatedDataVariants;
/** Достать вариант агрегации по его типу. */
template <typename Method> Method & getDataVariant(AggregatedDataVariants & variants);
#define M(NAME, IS_TWO_LEVEL) \
template <> inline decltype(AggregatedDataVariants::NAME)::element_type & getDataVariant<decltype(AggregatedDataVariants::NAME)::element_type>(AggregatedDataVariants & variants) { return *variants.NAME; }
APPLY_FOR_AGGREGATED_VARIANTS(M)
#undef M
/** Агрегирует источник блоков. /** Агрегирует источник блоков.
*/ */
class Aggregator class Aggregator
@ -733,11 +722,15 @@ public:
*/ */
AggregatedDataVariantsPtr merge(ManyAggregatedDataVariants & data_variants, size_t max_threads); AggregatedDataVariantsPtr merge(ManyAggregatedDataVariants & data_variants, size_t max_threads);
/** Объединить несколько агрегированных блоков в одну структуру данных. /** Объединить поток частично агрегированных блоков в одну структуру данных.
* (Доагрегировать несколько блоков, которые представляют собой результат независимых агрегаций с удалённых серверов.) * (Доагрегировать несколько блоков, которые представляют собой результат независимых агрегаций с удалённых серверов.)
*/ */
void mergeStream(BlockInputStreamPtr stream, AggregatedDataVariants & result, size_t max_threads); void mergeStream(BlockInputStreamPtr stream, AggregatedDataVariants & result, size_t max_threads);
/** Объединить несколько частично агрегированных блоков в один.
*/
Block mergeBlocks(BlocksList & blocks, bool final);
using CancellationHook = std::function<bool()>; using CancellationHook = std::function<bool()>;
/** Установить функцию, которая проверяет, можно ли прервать текущую задачу. /** Установить функцию, которая проверяет, можно ли прервать текущую задачу.
@ -974,4 +967,15 @@ protected:
}; };
/** Достать вариант агрегации по его типу. */
template <typename Method> Method & getDataVariant(AggregatedDataVariants & variants);
#define M(NAME, IS_TWO_LEVEL) \
template <> inline decltype(AggregatedDataVariants::NAME)::element_type & getDataVariant<decltype(AggregatedDataVariants::NAME)::element_type>(AggregatedDataVariants & variants) { return *variants.NAME; }
APPLY_FOR_AGGREGATED_VARIANTS(M)
#undef M
} }

View File

@ -66,6 +66,7 @@ public:
/// Для ARRAY_JOIN /// Для ARRAY_JOIN
NameSet array_joined_columns; NameSet array_joined_columns;
bool array_join_is_left;
/// Для JOIN /// Для JOIN
const Join * join = nullptr; const Join * join = nullptr;
@ -122,13 +123,14 @@ public:
return a; return a;
} }
static ExpressionAction arrayJoin(const NameSet & array_joined_columns) static ExpressionAction arrayJoin(const NameSet & array_joined_columns, bool array_join_is_left)
{ {
if (array_joined_columns.empty()) if (array_joined_columns.empty())
throw Exception("No arrays to join", ErrorCodes::LOGICAL_ERROR); throw Exception("No arrays to join", ErrorCodes::LOGICAL_ERROR);
ExpressionAction a; ExpressionAction a;
a.type = ARRAY_JOIN; a.type = ARRAY_JOIN;
a.array_joined_columns = array_joined_columns; a.array_joined_columns = array_joined_columns;
a.array_join_is_left = array_join_is_left;
return a; return a;
} }

View File

@ -226,6 +226,10 @@ private:
/// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn. /// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn.
void makeSet(ASTFunction * node, const Block & sample_block); void makeSet(ASTFunction * node, const Block & sample_block);
/// Замена скалярных подзапросов на значения-константы.
void executeScalarSubqueries();
void executeScalarSubqueriesImpl(ASTPtr & ast);
/// Находит глобальные подзапросы в секциях GLOBAL IN/JOIN. Заполняет external_tables. /// Находит глобальные подзапросы в секциях GLOBAL IN/JOIN. Заполняет external_tables.
void initGlobalSubqueriesAndExternalTables(); void initGlobalSubqueriesAndExternalTables();
void initGlobalSubqueries(ASTPtr & ast); void initGlobalSubqueries(ASTPtr & ast);

View File

@ -57,9 +57,19 @@ private:
std::uint64_t error_count; std::uint64_t error_count;
}; };
/** Имя словаря -> словарь.
*/
std::unordered_map<std::string, dictionary_info> dictionaries; std::unordered_map<std::string, dictionary_info> dictionaries;
std::unordered_map<std::string, std::chrono::system_clock::time_point> update_times;
/** Здесь находятся словари, которых ещё ни разу не удалось загрузить.
* В dictionaries они тоже присутствуют, но с нулевым указателем dict.
*/
std::unordered_map<std::string, failed_dictionary_info> failed_dictionaries; std::unordered_map<std::string, failed_dictionary_info> failed_dictionaries;
/** И для обычных и для failed_dictionaries.
*/
std::unordered_map<std::string, std::chrono::system_clock::time_point> update_times;
std::mt19937_64 rnd_engine{getSeed()}; std::mt19937_64 rnd_engine{getSeed()};
Context & context; Context & context;

View File

@ -24,8 +24,7 @@ public:
*/ */
BlockIO execute() override BlockIO execute() override
{ {
executeImpl(false); return executeImpl(false);
return {};
} }
/** assume_metadata_exists - не проверять наличие файла с метаданными и не создавать его /** assume_metadata_exists - не проверять наличие файла с метаданными и не создавать его
@ -45,7 +44,7 @@ public:
const ColumnDefaults & column_defaults); const ColumnDefaults & column_defaults);
private: private:
void executeImpl(bool assume_metadata_exists); BlockIO executeImpl(bool assume_metadata_exists);
/// AST в список столбцов с типами. Столбцы типа Nested развернуты в список настоящих столбцов. /// AST в список столбцов с типами. Столбцы типа Nested развернуты в список настоящих столбцов.
using ColumnsAndDefaults = std::pair<NamesAndTypesList, ColumnDefaults>; using ColumnsAndDefaults = std::pair<NamesAndTypesList, ColumnDefaults>;

View File

@ -91,6 +91,8 @@ struct Settings
M(SettingUInt64, min_count_to_compile, 3) \ M(SettingUInt64, min_count_to_compile, 3) \
/** При каком количестве ключей, начинает использоваться двухуровневая агрегация. 0 - никогда не использовать. */ \ /** При каком количестве ключей, начинает использоваться двухуровневая агрегация. 0 - никогда не использовать. */ \
M(SettingUInt64, group_by_two_level_threshold, 100000) \ M(SettingUInt64, group_by_two_level_threshold, 100000) \
/** Включён ли экономный по памяти режим распределённой агрегации. */ \
M(SettingBool, distributed_aggregation_memory_efficient, false) \
\ \
/** Максимальное количество используемых реплик каждого шарда при выполнении запроса */ \ /** Максимальное количество используемых реплик каждого шарда при выполнении запроса */ \
M(SettingUInt64, max_parallel_replicas, 1) \ M(SettingUInt64, max_parallel_replicas, 1) \

View File

@ -18,4 +18,9 @@ void sortBlock(Block & block, const SortDescription & description, size_t limit
*/ */
void stableSortBlock(Block & block, const SortDescription & description); void stableSortBlock(Block & block, const SortDescription & description);
/** То же, что и stableSortBlock, но не сортировать блок, а только рассчитать перестановку значений,
* чтобы потом можно было переставить значения столбцов самостоятельно.
*/
void stableGetPermutation(const Block & block, const SortDescription & description, IColumn::Permutation & out_permutation);
} }

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include <DB/Parsers/IAST.h> #include <DB/Parsers/IAST.h>
#include <mysqlxx/Manip.h>
namespace DB namespace DB
{ {
@ -98,5 +100,87 @@ public:
} }
return res; return res;
} }
};
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
frame.need_parens = false;
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "ALTER TABLE " << (settings.hilite ? hilite_none : "");
if (!table.empty())
{
if (!database.empty())
{
settings.ostr << indent_str << database;
settings.ostr << ".";
}
settings.ostr << indent_str << table;
}
settings.ostr << settings.nl_or_ws;
for (size_t i = 0; i < parameters.size(); ++i)
{
const ASTAlterQuery::Parameters & p = parameters[i];
if (p.type == ASTAlterQuery::ADD_COLUMN)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "ADD COLUMN " << (settings.hilite ? hilite_none : "");
p.col_decl->formatImpl(settings, state, frame);
/// AFTER
if (p.column)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << " AFTER " << (settings.hilite ? hilite_none : "");
p.column->formatImpl(settings, state, frame);
}
}
else if (p.type == ASTAlterQuery::DROP_COLUMN)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "DROP COLUMN " << (settings.hilite ? hilite_none : "");
p.column->formatImpl(settings, state, frame);
}
else if (p.type == ASTAlterQuery::MODIFY_COLUMN)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "MODIFY COLUMN " << (settings.hilite ? hilite_none : "");
p.col_decl->formatImpl(settings, state, frame);
}
else if (p.type == ASTAlterQuery::DROP_PARTITION)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << (p.detach ? "DETACH" : "DROP") << " PARTITION "
<< (settings.hilite ? hilite_none : "");
p.partition->formatImpl(settings, state, frame);
}
else if (p.type == ASTAlterQuery::ATTACH_PARTITION)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "ATTACH " << (p.unreplicated ? "UNREPLICATED " : "")
<< (p.part ? "PART " : "PARTITION ") << (settings.hilite ? hilite_none : "");
p.partition->formatImpl(settings, state, frame);
}
else if (p.type == ASTAlterQuery::FETCH_PARTITION)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "FETCH " << (p.unreplicated ? "UNREPLICATED " : "")
<< "PARTITION " << (settings.hilite ? hilite_none : "");
p.partition->formatImpl(settings, state, frame);
settings.ostr << (settings.hilite ? hilite_keyword : "")
<< " FROM " << (settings.hilite ? hilite_none : "") << mysqlxx::quote << p.from;
}
else if (p.type == ASTAlterQuery::FREEZE_PARTITION)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "FREEZE PARTITION " << (settings.hilite ? hilite_none : "");
p.partition->formatImpl(settings, state, frame);
}
else
throw Exception("Unexpected type of ALTER", ErrorCodes::UNEXPECTED_AST_STRUCTURE);
std::string comma = (i < (parameters.size() -1) ) ? "," : "";
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << comma << (settings.hilite ? hilite_none : "");
settings.ostr << settings.nl_or_ws;
}
}
};
} }

View File

@ -16,6 +16,12 @@ public:
String getID() const override { return "Asterisk"; } String getID() const override { return "Asterisk"; }
ASTPtr clone() const override { return new ASTAsterisk(*this); } ASTPtr clone() const override { return new ASTAsterisk(*this); }
String getColumnName() const override { return "*"; } String getColumnName() const override { return "*"; }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << "*";
}
}; };
} }

View File

@ -19,6 +19,28 @@ struct ASTCheckQuery : public IAST
std::string database; std::string database;
std::string table; std::string table;
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
std::string nl_or_nothing = settings.one_line ? "" : "\n";
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
std::string nl_or_ws = settings.one_line ? " " : "\n";
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "CHECK TABLE " << (settings.hilite ? hilite_none : "");
if (!table.empty())
{
if (!database.empty())
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << database << (settings.hilite ? hilite_none : "");
settings.ostr << ".";
}
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << table << (settings.hilite ? hilite_none : "");
}
settings.ostr << nl_or_ws;
}
}; };
} }

View File

@ -40,6 +40,26 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
frame.need_parens = false;
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
settings.ostr << settings.nl_or_ws << indent_str << backQuoteIfNeed(name);
if (type)
{
settings.ostr << ' ';
type->formatImpl(settings, state, frame);
}
if (default_expression)
{
settings.ostr << ' ' << (settings.hilite ? hilite_keyword : "") << default_specifier << (settings.hilite ? hilite_none : "") << ' ';
default_expression->formatImpl(settings, state, frame);
}
}
}; };
} }

View File

@ -48,6 +48,74 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
frame.need_parens = false;
if (!database.empty() && table.empty())
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << (attach ? "ATTACH DATABASE " : "CREATE DATABASE ") << (if_not_exists ? "IF NOT EXISTS " : "") << (settings.hilite ? hilite_none : "")
<< backQuoteIfNeed(database);
return;
}
{
std::string what = "TABLE";
if (is_view)
what = "VIEW";
if (is_materialized_view)
what = "MATERIALIZED VIEW";
settings.ostr
<< (settings.hilite ? hilite_keyword : "")
<< (attach ? "ATTACH " : "CREATE ")
<< (is_temporary ? "TEMPORARY " : "")
<< what
<< " " << (if_not_exists ? "IF NOT EXISTS " : "")
<< (settings.hilite ? hilite_none : "")
<< (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table);
}
if (!as_table.empty())
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " AS " << (settings.hilite ? hilite_none : "")
<< (!as_database.empty() ? backQuoteIfNeed(as_database) + "." : "") << backQuoteIfNeed(as_table);
}
if (columns)
{
settings.ostr << (settings.one_line ? " (" : "\n(");
FormatStateStacked frame_nested = frame;
++frame_nested.indent;
columns->formatImpl(settings, state, frame_nested);
settings.ostr << (settings.one_line ? ")" : "\n)");
}
if (storage && !is_materialized_view && !is_view)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ENGINE" << (settings.hilite ? hilite_none : "") << " = ";
storage->formatImpl(settings, state, frame);
}
if (inner_storage)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ENGINE" << (settings.hilite ? hilite_none : "") << " = ";
inner_storage->formatImpl(settings, state, frame);
}
if (is_populate)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " POPULATE" << (settings.hilite ? hilite_none : "");
}
if (select)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " AS" << settings.nl_or_ws << (settings.hilite ? hilite_none : "");
select->formatImpl(settings, state, frame);
}
}
}; };
} }

View File

@ -24,6 +24,25 @@ public:
String getID() const override { return (detach ? "DetachQuery_" : "DropQuery_") + database + "_" + table; }; String getID() const override { return (detach ? "DetachQuery_" : "DropQuery_") + database + "_" + table; };
ASTPtr clone() const override { return new ASTDropQuery(*this); } ASTPtr clone() const override { return new ASTDropQuery(*this); }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
if (table.empty() && !database.empty())
{
settings.ostr << (settings.hilite ? hilite_keyword : "")
<< (detach ? "DETACH DATABASE " : "DROP DATABASE ")
<< (if_exists ? "IF EXISTS " : "")
<< (settings.hilite ? hilite_none : "")
<< backQuoteIfNeed(database);
return;
}
settings.ostr << (settings.hilite ? hilite_keyword : "")
<< (detach ? "DETACH TABLE " : "DROP TABLE ")
<< (if_exists ? "IF EXISTS " : "") << (settings.hilite ? hilite_none : "")
<< (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table);
}
}; };
} }

View File

@ -31,6 +31,40 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
for (ASTs::const_iterator it = children.begin(); it != children.end(); ++it)
{
if (it != children.begin())
settings.ostr << ", ";
(*it)->formatImpl(settings, state, frame);
}
}
friend class ASTSelectQuery;
/** Вывести список выражений в секциях запроса SELECT - по одному выражению на строку.
*/
void formatImplMultiline(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
{
std::string indent_str = "\n" + std::string(4 * (frame.indent + 1), ' ');
++frame.indent;
for (ASTs::const_iterator it = children.begin(); it != children.end(); ++it)
{
if (it != children.begin())
settings.ostr << ", ";
if (children.size() > 1)
settings.ostr << indent_str;
(*it)->formatImpl(settings, state, frame);
}
}
}; };
} }

View File

@ -82,6 +82,9 @@ public:
return ptr; return ptr;
} }
protected:
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
}; };

View File

@ -2,6 +2,7 @@
#include <DB/DataTypes/IDataType.h> #include <DB/DataTypes/IDataType.h>
#include <DB/Parsers/ASTWithAlias.h> #include <DB/Parsers/ASTWithAlias.h>
#include <DB/IO/WriteBufferFromOStream.h>
namespace DB namespace DB
@ -41,6 +42,18 @@ public:
{ {
set.insert(name); set.insert(name);
} }
protected:
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << (settings.hilite ? hilite_identifier : "");
WriteBufferFromOStream wb(settings.ostr, 32);
writeProbablyBackQuotedString(name, wb);
wb.next();
settings.ostr << (settings.hilite ? hilite_none : "");
}
}; };
} }

View File

@ -42,6 +42,43 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
frame.need_parens = false;
settings.ostr << (settings.hilite ? hilite_keyword : "") << "INSERT INTO " << (settings.hilite ? hilite_none : "")
<< (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table);
if (!insert_id.empty())
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ID = " << (settings.hilite ? hilite_none : "")
<< mysqlxx::quote << insert_id;
if (columns)
{
settings.ostr << " (";
columns->formatImpl(settings, state, frame);
settings.ostr << ")";
}
if (select)
{
settings.ostr << " ";
select->formatImpl(settings, state, frame);
}
else
{
if (!format.empty())
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " FORMAT " << (settings.hilite ? hilite_none : "") << format;
}
else
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " VALUES" << (settings.hilite ? hilite_none : "");
}
}
}
}; };
} }

View File

@ -83,6 +83,37 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
frame.need_parens = false;
settings.ostr << (settings.hilite ? hilite_keyword : "");
if (locality == ASTJoin::Global)
settings.ostr << "GLOBAL ";
if (kind != ASTJoin::Cross)
settings.ostr << (strictness == ASTJoin::Any ? "ANY " : "ALL ");
settings.ostr << (kind == ASTJoin::Inner ? "INNER "
: (kind == ASTJoin::Left ? "LEFT "
: (kind == ASTJoin::Right ? "RIGHT "
: (kind == ASTJoin::Cross ? "CROSS "
: "FULL OUTER "))));
settings.ostr << "JOIN "
<< (settings.hilite ? hilite_none : "");
table->formatImpl(settings, state, frame);
if (kind != ASTJoin::Cross)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " USING " << (settings.hilite ? hilite_none : "");
using_expr_list->formatImpl(settings, state, frame);
}
}
}; };
} }

View File

@ -26,6 +26,12 @@ public:
String getID() const override { return "Literal_" + apply_visitor(FieldVisitorDump(), value); } String getID() const override { return "Literal_" + apply_visitor(FieldVisitorDump(), value); }
ASTPtr clone() const override { return new ASTLiteral(*this); } ASTPtr clone() const override { return new ASTLiteral(*this); }
protected:
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << apply_visitor(FieldVisitorToString(), value);
}
}; };
} }

View File

@ -34,6 +34,15 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
settings.ostr << settings.nl_or_ws << indent_str << backQuoteIfNeed(name) << " ";
type->formatImpl(settings, state, frame);
}
}; };
} }

View File

@ -22,6 +22,13 @@ public:
String getID() const override { return "OptimizeQuery_" + database + "_" + table; }; String getID() const override { return "OptimizeQuery_" + database + "_" + table; };
ASTPtr clone() const override { return new ASTOptimizeQuery(*this); } ASTPtr clone() const override { return new ASTOptimizeQuery(*this); }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "OPTIMIZE TABLE " << (settings.hilite ? hilite_none : "")
<< (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table);
}
}; };
} }

View File

@ -29,6 +29,18 @@ public:
String getID() const override { return "OrderByElement"; } String getID() const override { return "OrderByElement"; }
ASTPtr clone() const override { return new ASTOrderByElement(*this); } ASTPtr clone() const override { return new ASTOrderByElement(*this); }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
children.front()->formatImpl(settings, state, frame);
settings.ostr << (settings.hilite ? hilite_keyword : "") << (direction == -1 ? " DESC" : " ASC") << (settings.hilite ? hilite_none : "");
if (!collator.isNull())
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " COLLATE " << (settings.hilite ? hilite_none : "")
<< "'" << collator->getLocale() << "'";
}
}
}; };
} }

View File

@ -24,7 +24,7 @@ public:
/// Объявляет класс-наследник ASTQueryWithOutput с реализованными методами getID и clone. /// Объявляет класс-наследник ASTQueryWithOutput с реализованными методами getID и clone.
#define DEFINE_AST_QUERY_WITH_OUTPUT(Name, ID) \ #define DEFINE_AST_QUERY_WITH_OUTPUT(Name, ID, Query) \
class Name : public ASTQueryWithOutput \ class Name : public ASTQueryWithOutput \
{ \ { \
public: \ public: \
@ -44,6 +44,12 @@ public: \
} \ } \
return ptr; \ return ptr; \
} \ } \
\
protected: \
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override \
{ \
settings.ostr << (settings.hilite ? hilite_keyword : "") << Query << (settings.hilite ? hilite_none : ""); \
} \
}; };
} }

View File

@ -18,11 +18,25 @@ namespace DB
ASTQueryWithTableAndOutput() = default; ASTQueryWithTableAndOutput() = default;
ASTQueryWithTableAndOutput(const StringRange range_) : ASTQueryWithOutput(range_) {} ASTQueryWithTableAndOutput(const StringRange range_) : ASTQueryWithOutput(range_) {}
protected:
void formatHelper(const FormatSettings & settings, FormatState & state, FormatStateStacked frame, const char * name) const
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << name << " " << (settings.hilite ? hilite_none : "")
<< (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table);
if (format)
{
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << indent_str << "FORMAT " << (settings.hilite ? hilite_none : "");
format->formatImpl(settings, state, frame);
}
}
}; };
/// Объявляет класс-наследник ASTQueryWithTableAndOutput с реализованными методами getID и clone. /// Объявляет класс-наследник ASTQueryWithTableAndOutput с реализованными методами getID и clone.
#define DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(Name, ID) \ #define DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(Name, ID, Query) \
class Name : public ASTQueryWithTableAndOutput \ class Name : public ASTQueryWithTableAndOutput \
{ \ { \
public: \ public: \
@ -42,5 +56,11 @@ public: \
} \ } \
return ptr; \ return ptr; \
} \ } \
\
protected: \
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override \
{ \
formatHelper(settings, state, frame, Query); \
} \
}; };
} }

View File

@ -34,6 +34,22 @@ public:
String getID() const override { return "Rename"; }; String getID() const override { return "Rename"; };
ASTPtr clone() const override { return new ASTRenameQuery(*this); } ASTPtr clone() const override { return new ASTRenameQuery(*this); }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "RENAME TABLE " << (settings.hilite ? hilite_none : "");
for (ASTRenameQuery::Elements::const_iterator it = elements.begin(); it != elements.end(); ++it)
{
if (it != elements.begin())
settings.ostr << ", ";
settings.ostr << (!it->from.database.empty() ? backQuoteIfNeed(it->from.database) + "." : "") << backQuoteIfNeed(it->from.table)
<< (settings.hilite ? hilite_keyword : "") << " TO " << (settings.hilite ? hilite_none : "")
<< (!it->to.database.empty() ? backQuoteIfNeed(it->to.database) + "." : "") << backQuoteIfNeed(it->to.table);
}
}
}; };
} }

View File

@ -50,6 +50,7 @@ public:
ASTPtr select_expression_list; ASTPtr select_expression_list;
ASTPtr database; ASTPtr database;
ASTPtr table; /// Идентификатор, табличная функция или подзапрос (рекурсивно ASTSelectQuery) ASTPtr table; /// Идентификатор, табличная функция или подзапрос (рекурсивно ASTSelectQuery)
bool array_join_is_left = false; /// LEFT ARRAY JOIN
ASTPtr array_join_expression_list; /// ARRAY JOIN ASTPtr array_join_expression_list; /// ARRAY JOIN
ASTPtr join; /// Обычный (не ARRAY) JOIN. ASTPtr join; /// Обычный (не ARRAY) JOIN.
bool final = false; bool final = false;
@ -67,6 +68,9 @@ public:
ASTPtr prev_union_all; ASTPtr prev_union_all;
/// Следующий запрос SELECT в цепочке UNION ALL, если такой есть /// Следующий запрос SELECT в цепочке UNION ALL, если такой есть
ASTPtr next_union_all; ASTPtr next_union_all;
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
}; };
} }

View File

@ -22,6 +22,17 @@ public:
String getID() const override { return "Set_" + getColumnName(); } String getID() const override { return "Set_" + getColumnName(); }
ASTPtr clone() const override { return new ASTSet(*this); } ASTPtr clone() const override { return new ASTSet(*this); }
String getColumnName() const override { return column_name; } String getColumnName() const override { return column_name; }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
/** Подготовленное множество. В пользовательских запросах такого не бывает, но такое бывает после промежуточных преобразований запроса.
* Выведем его не по-настоящему (это не будет корректным запросом, но покажет, что здесь было множество).
*/
settings.ostr << (settings.hilite ? hilite_keyword : "")
<< "(...)"
<< (settings.hilite ? hilite_none : "");
}
}; };
} }

View File

@ -31,6 +31,20 @@ public:
String getID() const override { return "Set"; }; String getID() const override { return "Set"; };
ASTPtr clone() const override { return new ASTSetQuery(*this); } ASTPtr clone() const override { return new ASTSetQuery(*this); }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SET " << (global ? "GLOBAL " : "") << (settings.hilite ? hilite_none : "");
for (ASTSetQuery::Changes::const_iterator it = changes.begin(); it != changes.end(); ++it)
{
if (it != changes.begin())
settings.ostr << ", ";
settings.ostr << it->name << " = " << apply_visitor(FieldVisitorToString(), it->value);
}
}
}; };
} }

View File

@ -5,5 +5,7 @@
namespace DB namespace DB
{ {
DEFINE_AST_QUERY_WITH_OUTPUT(ASTShowProcesslistQuery, "ShowProcesslistQuery")
DEFINE_AST_QUERY_WITH_OUTPUT(ASTShowProcesslistQuery, "ShowProcesslistQuery", "SHOW PROCESSLIST")
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <mysqlxx/Manip.h>
#include <DB/Parsers/IAST.h> #include <DB/Parsers/IAST.h>
#include <DB/Parsers/ASTQueryWithOutput.h> #include <DB/Parsers/ASTQueryWithOutput.h>
@ -39,6 +40,34 @@ public:
return ptr; return ptr;
} }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
if (databases)
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW DATABASES" << (settings.hilite ? hilite_none : "");
}
else
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW TABLES" << (settings.hilite ? hilite_none : "");
if (!from.empty())
settings.ostr << (settings.hilite ? hilite_keyword : "") << " FROM " << (settings.hilite ? hilite_none : "")
<< backQuoteIfNeed(from);
if (!like.empty())
settings.ostr << (settings.hilite ? hilite_keyword : "") << " LIKE " << (settings.hilite ? hilite_none : "")
<< mysqlxx::quote << like;
}
if (format)
{
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << indent_str << "FORMAT " << (settings.hilite ? hilite_none : "");
format->formatImpl(settings, state, frame);
}
}
}; };
} }

View File

@ -2,7 +2,7 @@
#include <DB/DataTypes/IDataType.h> #include <DB/DataTypes/IDataType.h>
#include <DB/Parsers/IAST.h> #include <DB/Parsers/ASTWithAlias.h>
namespace DB namespace DB
@ -11,11 +11,11 @@ namespace DB
/** Подзарос SELECT /** Подзарос SELECT
*/ */
class ASTSubquery : public IAST class ASTSubquery : public ASTWithAlias
{ {
public: public:
ASTSubquery() = default; ASTSubquery() = default;
ASTSubquery(const StringRange range_) : IAST(range_) {} ASTSubquery(const StringRange range_) : ASTWithAlias(range_) {}
/** Получить текст, который идентифицирует этот элемент. */ /** Получить текст, который идентифицирует этот элемент. */
String getID() const override { return "Subquery"; } String getID() const override { return "Subquery"; }
@ -34,6 +34,20 @@ public:
} }
String getColumnName() const override { return getTreeID(); } String getColumnName() const override { return getTreeID(); }
protected:
void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' ');
std::string nl_or_nothing = settings.one_line ? "" : "\n";
settings.ostr << nl_or_nothing << indent_str << "(" << nl_or_nothing;
FormatStateStacked frame_nested = frame;
frame_nested.need_parens = false;
++frame_nested.indent;
children[0]->formatImpl(settings, state, frame_nested);
settings.ostr << nl_or_nothing << indent_str << ")";
}
}; };
} }

View File

@ -21,6 +21,13 @@ public:
String getID() const override { return "UseQuery_" + database; }; String getID() const override { return "UseQuery_" + database; };
ASTPtr clone() const override { return new ASTUseQuery(*this); } ASTPtr clone() const override { return new ASTUseQuery(*this); }
protected:
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "USE " << (settings.hilite ? hilite_none : "") << backQuoteIfNeed(database);
return;
}
}; };
} }

View File

@ -1,11 +1,13 @@
#pragma once #pragma once
#include <DB/IO/WriteBufferFromOStream.h>
#include <DB/Parsers/IAST.h> #include <DB/Parsers/IAST.h>
namespace DB namespace DB
{ {
/** Базовый класс для AST, которые могут содержать алиас (идентификаторы, литералы, функции). /** Базовый класс для AST, которые могут содержать алиас (идентификаторы, литералы, функции).
*/ */
class ASTWithAlias : public IAST class ASTWithAlias : public IAST
@ -19,10 +21,16 @@ public:
String getAliasOrColumnName() const override { return alias.empty() ? getColumnName() : alias; } String getAliasOrColumnName() const override { return alias.empty() ? getColumnName() : alias; }
String tryGetAlias() const override { return alias; } String tryGetAlias() const override { return alias; }
void setAlias(const String & to) override { alias = to; } void setAlias(const String & to) override { alias = to; }
/// Вызывает formatImplWithoutAlias, а также выводит алиас. Если надо - заключает всё выражение в скобки.
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override final;
virtual void formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const = 0;
}; };
/// helper for setting aliases and chaining result to other functions /// helper for setting aliases and chaining result to other functions
inline ASTPtr setAlias(ASTPtr ast, const String & alias) { inline ASTPtr setAlias(ASTPtr ast, const String & alias)
{
ast->setAlias(alias); ast->setAlias(alias);
return ast; return ast;
}; };

View File

@ -3,6 +3,8 @@
#include <list> #include <list>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <iostream>
#include <set>
#include <Poco/SharedPtr.h> #include <Poco/SharedPtr.h>
@ -14,8 +16,6 @@
#include <DB/IO/WriteHelpers.h> #include <DB/IO/WriteHelpers.h>
#include <DB/Parsers/StringRange.h> #include <DB/Parsers/StringRange.h>
#include <iostream>
namespace DB namespace DB
{ {
@ -133,6 +133,68 @@ public:
(*it)->collectIdentifierNames(set); (*it)->collectIdentifierNames(set);
} }
/// Преобразовать в строку.
/// Настройки формата.
struct FormatSettings
{
std::ostream & ostr;
bool hilite;
bool one_line;
char nl_or_ws;
FormatSettings(std::ostream & ostr_, bool hilite_, bool one_line_)
: ostr(ostr_), hilite(hilite_), one_line(one_line_)
{
nl_or_ws = one_line ? ' ' : '\n';
}
};
/// Состояние. Например, может запоминаться множество узлов, которых мы уже обошли.
struct FormatState
{
/** Запрос SELECT, в котором найден алиас; идентификатор узла с таким алиасом.
* Нужно, чтобы когда узел встретился повторно, выводить только алиас.
*/
std::set<std::pair<const IAST *, std::string>> printed_asts_with_alias;
};
/// Состояние, которое копируется при форматировании каждого узла. Например, уровень вложенности.
struct FormatStateStacked
{
UInt8 indent = 0;
bool need_parens = false;
const IAST * current_select = nullptr;
};
void format(const FormatSettings & settings) const
{
FormatState state;
formatImpl(settings, state, FormatStateStacked());
}
virtual void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
{
throw Exception("Unknown element in AST: " + getID()
+ ((range.first && (range.second > range.first))
? " '" + std::string(range.first, range.second - range.first) + "'"
: ""),
ErrorCodes::UNKNOWN_ELEMENT_IN_AST);
}
void writeAlias(const String & name, std::ostream & s, bool hilite) const;
protected:
/// Для подсветки синтаксиса.
static const char * hilite_keyword;
static const char * hilite_identifier;
static const char * hilite_function;
static const char * hilite_operator;
static const char * hilite_alias;
static const char * hilite_none;
private: private:
size_t checkDepthImpl(size_t max_depth, size_t level) const size_t checkDepthImpl(size_t max_depth, size_t level) const
{ {
@ -152,4 +214,9 @@ private:
typedef SharedPtr<IAST> ASTPtr; typedef SharedPtr<IAST> ASTPtr;
typedef std::vector<ASTPtr> ASTs; typedef std::vector<ASTPtr> ASTs;
/// Квотировать идентификатор обратными кавычками, если это требуется.
String backQuoteIfNeed(const String & x);
} }

View File

@ -6,16 +6,8 @@
namespace DB namespace DB
{ {
/** EXISTS запрос DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(ASTExistsQuery, "ExistsQuery", "EXISTS TABLE")
*/ DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(ASTShowCreateQuery, "ShowCreateQuery", "SHOW CREATE TABLE")
DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(ASTExistsQuery, "ExistsQuery") DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(ASTDescribeQuery, "DescribeQuery", "DESCRIBE TABLE")
/** SHOW CREATE TABLE запрос
*/
DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(ASTShowCreateQuery, "ShowCreateQuery")
/** DESCRIBE TABLE запрос
*/
DEFINE_AST_QUERY_WITH_TABLE_AND_OUTPUT(ASTDescribeQuery, "DescribeQuery")
} }

View File

@ -12,11 +12,14 @@ namespace DB
/** Берёт синтаксическое дерево и превращает его обратно в текст. /** Берёт синтаксическое дерево и превращает его обратно в текст.
* В случае запроса INSERT, данные будут отсутствовать. * В случае запроса INSERT, данные будут отсутствовать.
*/ */
void formatAST(const IAST & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false); inline void formatAST(const IAST & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false)
{
IAST::FormatSettings settings(s, hilite, one_line);
ast.format(settings);
}
String formatColumnsForCreateQuery(NamesAndTypesList & columns); String formatColumnsForCreateQuery(NamesAndTypesList & columns);
String backQuoteIfNeed(const String & x);
inline std::ostream & operator<<(std::ostream & os, const IAST & ast) { return formatAST(ast, os, 0, false, true), os; } inline std::ostream & operator<<(std::ostream & os, const IAST & ast) { return formatAST(ast, os, 0, false, true), os; }
inline std::ostream & operator<<(std::ostream & os, const ASTPtr & ast) { return formatAST(*ast, os, 0, false, true), os; } inline std::ostream & operator<<(std::ostream & os, const ASTPtr & ast) { return formatAST(*ast, os, 0, false, true), os; }

View File

@ -6,34 +6,13 @@
#include <DB/Common/ProfileEvents.h> #include <DB/Common/ProfileEvents.h>
#include <DB/Common/SipHash.h> #include <DB/Common/SipHash.h>
#include <DB/Interpreters/AggregationCommon.h> #include <DB/Interpreters/AggregationCommon.h>
#include <DB/DataStreams/MarkInCompressedFile.h>
namespace DB namespace DB
{ {
struct MarkInCompressedFile
{
size_t offset_in_compressed_file;
size_t offset_in_decompressed_block;
bool operator==(const MarkInCompressedFile & rhs) const
{
return std::forward_as_tuple(offset_in_compressed_file, offset_in_decompressed_block) ==
std::forward_as_tuple(rhs.offset_in_compressed_file, rhs.offset_in_decompressed_block);
}
bool operator!=(const MarkInCompressedFile & rhs) const
{
return !(*this == rhs);
}
String toString() const
{
return "(" + DB::toString(offset_in_compressed_file) + "," + DB::toString(offset_in_decompressed_block) + ")";
}
};
typedef std::vector<MarkInCompressedFile> MarksInCompressedFile;
/// Оценка количества байтов, занимаемых засечками в кеше. /// Оценка количества байтов, занимаемых засечками в кеше.
struct MarksWeightFunction struct MarksWeightFunction
{ {

View File

@ -23,19 +23,16 @@ public:
{ {
DayNum_t left_date; DayNum_t left_date;
DayNum_t right_date; DayNum_t right_date;
UInt64 left; Int64 left;
UInt64 right; Int64 right;
UInt32 level; UInt32 level;
std::string name; std::string name;
DayNum_t left_month; DayNum_t month;
DayNum_t right_month;
bool operator<(const Part & rhs) const bool operator<(const Part & rhs) const
{ {
if (left_month != rhs.left_month) if (month != rhs.month)
return left_month < rhs.left_month; return month < rhs.month;
if (right_month != rhs.right_month)
return right_month < rhs.right_month;
if (left != rhs.left) if (left != rhs.left)
return left < rhs.left; return left < rhs.left;
@ -48,8 +45,7 @@ public:
/// Содержит другой кусок (получен после объединения другого куска с каким-то ещё) /// Содержит другой кусок (получен после объединения другого куска с каким-то ещё)
bool contains(const Part & rhs) const bool contains(const Part & rhs) const
{ {
return left_month == rhs.left_month /// Куски за разные месяцы не объединяются return month == rhs.month /// Куски за разные месяцы не объединяются
&& right_month == rhs.right_month
&& left_date <= rhs.left_date && left_date <= rhs.left_date
&& right_date >= rhs.right_date && right_date >= rhs.right_date
&& left <= rhs.left && left <= rhs.left
@ -66,7 +62,7 @@ public:
size_t size() const; size_t size() const;
static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level); static String getPartName(DayNum_t left_date, DayNum_t right_date, Int64 left_id, Int64 right_id, UInt64 level);
/// Возвращает true если имя директории совпадает с форматом имени директории кусочков /// Возвращает true если имя директории совпадает с форматом имени директории кусочков
static bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec * out_matches = nullptr); static bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec * out_matches = nullptr);

View File

@ -33,7 +33,7 @@ public:
auto part_blocks = storage.writer.splitBlockIntoParts(block); auto part_blocks = storage.writer.splitBlockIntoParts(block);
for (auto & current_block : part_blocks) for (auto & current_block : part_blocks)
{ {
UInt64 temp_index = storage.increment.get(); Int64 temp_index = storage.increment.get();
MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, temp_index); MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, temp_index);
storage.data.renameTempPartAndAdd(part, &storage.increment); storage.data.renameTempPartAndAdd(part, &storage.increment);

View File

@ -625,7 +625,7 @@ public:
|| mode == Mode::Aggregating; || mode == Mode::Aggregating;
} }
UInt64 getMaxDataPartIndex(); Int64 getMaxDataPartIndex();
std::string getTableName() const override std::string getTableName() const override
{ {
@ -779,6 +779,13 @@ public:
return it == std::end(column_sizes) ? 0 : it->second; return it == std::end(column_sizes) ? 0 : it->second;
} }
using ColumnSizes = std::unordered_map<std::string, size_t>;
ColumnSizes getColumnSizes() const
{
Poco::ScopedLock<Poco::FastMutex> lock{data_parts_mutex};
return column_sizes;
}
/// Для ATTACH/DETACH/DROP PARTITION. /// Для ATTACH/DETACH/DROP PARTITION.
static String getMonthName(const Field & partition); static String getMonthName(const Field & partition);
static DayNum_t getMonthDayNum(const Field & partition); static DayNum_t getMonthDayNum(const Field & partition);
@ -810,7 +817,7 @@ private:
NamesAndTypesListPtr columns; NamesAndTypesListPtr columns;
/// Актуальные размеры столбцов в сжатом виде /// Актуальные размеры столбцов в сжатом виде
std::unordered_map<std::string, size_t> column_sizes; ColumnSizes column_sizes;
BrokenPartCallback broken_part_callback; BrokenPartCallback broken_part_callback;

View File

@ -43,7 +43,7 @@ public:
* temp_index - значение left и right для нового куска. Можно будет изменить при переименовании. * temp_index - значение left и right для нового куска. Можно будет изменить при переименовании.
* Возвращает кусок с именем, начинающимся с tmp_, еще не добавленный в MergeTreeData. * Возвращает кусок с именем, начинающимся с tmp_, еще не добавленный в MergeTreeData.
*/ */
MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithDateInterval & block, UInt64 temp_index); MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithDateInterval & block, Int64 temp_index);
private: private:
MergeTreeData & data; MergeTreeData & data;

View File

@ -247,7 +247,7 @@ private:
++right; ++right;
} }
/// Если правее засечек нет, просто используем DEFAULT_BUFFER_SIZE /// Если правее засечек нет, просто используем max_read_buffer_size
if (right >= (*marks).size() || (right + 1 == (*marks).size() && if (right >= (*marks).size() || (right + 1 == (*marks).size() &&
(*marks)[right].offset_in_compressed_file == (*marks)[all_mark_ranges[i].end].offset_in_compressed_file)) (*marks)[right].offset_in_compressed_file == (*marks)[all_mark_ranges[i].end].offset_in_compressed_file))
{ {

View File

@ -14,6 +14,8 @@
namespace DB namespace DB
{ {
class IMergedBlockOutputStream : public IBlockOutputStream class IMergedBlockOutputStream : public IBlockOutputStream
{ {
public: public:
@ -230,7 +232,9 @@ protected:
CompressionMethod compression_method; CompressionMethod compression_method;
}; };
/** Для записи одного куска. Данные уже отсортированы, относятся к одному месяцу, и пишутся в один кускок.
/** Для записи одного куска.
* Данные относятся к одному месяцу, и пишутся в один кускок.
*/ */
class MergedBlockOutputStream : public IMergedBlockOutputStream class MergedBlockOutputStream : public IMergedBlockOutputStream
{ {
@ -278,45 +282,18 @@ public:
} }
} }
/// Если данные заранее отсортированы.
void write(const Block & block) override void write(const Block & block) override
{ {
size_t rows = block.rows(); writeImpl(block, nullptr);
/// Сначала пишем индекс. Индекс содержит значение Primary Key для каждой index_granularity строки.
typedef std::vector<const ColumnWithTypeAndName *> PrimaryColumns;
PrimaryColumns primary_columns;
for (const auto & descr : storage.getSortDescription())
primary_columns.push_back(
!descr.column_name.empty()
? &block.getByName(descr.column_name)
: &block.getByPosition(descr.column_number));
for (size_t i = index_offset; i < rows; i += storage.index_granularity)
{
for (PrimaryColumns::const_iterator it = primary_columns.begin(); it != primary_columns.end(); ++it)
{
if (storage.mode != MergeTreeData::Unsorted)
index_vec.push_back((*(*it)->column)[i]);
(*it)->type->serializeBinary(index_vec.back(), *index_stream);
} }
++marks_count; /** Если данные не отсортированы, но мы заранее вычислили перестановку, после которой они станут сортированными.
} * Этот метод используется для экономии оперативки, так как не нужно держать одновременно два блока - исходный и отсортированный.
*/
/// Множество записанных столбцов со смещениями, чтобы не писать общие для вложенных структур столбцы несколько раз void writeWithPermutation(const Block & block, const IColumn::Permutation * permutation)
OffsetColumns offset_columns;
/// Теперь пишем данные.
for (const auto & it : columns_list)
{ {
const ColumnWithTypeAndName & column = block.getByName(it.name); writeImpl(block, permutation);
writeData(column.name, *column.type, *column.column, offset_columns);
}
size_t written_for_last_mark = (storage.index_granularity - index_offset + rows) % storage.index_granularity;
index_offset = (storage.index_granularity - written_for_last_mark) % storage.index_granularity;
} }
void writeSuffix() override void writeSuffix() override
@ -391,6 +368,86 @@ private:
} }
} }
/** Если задана permutation, то переставляет значения в столбцах при записи.
* Это нужно, чтобы не держать целый блок в оперативке для его сортировки.
*/
void writeImpl(const Block & block, const IColumn::Permutation * permutation)
{
size_t rows = block.rows();
/// Множество записанных столбцов со смещениями, чтобы не писать общие для вложенных структур столбцы несколько раз
OffsetColumns offset_columns;
auto sort_description = storage.getSortDescription();
/// Сюда будем складывать столбцы, относящиеся к Primary Key, чтобы потом записать индекс.
std::vector<ColumnWithTypeAndName> primary_columns(sort_description.size());
std::map<String, size_t> primary_columns_name_to_position;
for (size_t i = 0, size = sort_description.size(); i < size; ++i)
{
const auto & descr = sort_description[i];
String name = !descr.column_name.empty()
? descr.column_name
: block.getByPosition(descr.column_number).name;
if (!primary_columns_name_to_position.emplace(name, i).second)
throw Exception("Primary key contains duplicate columns", ErrorCodes::BAD_ARGUMENTS);
primary_columns[i] = !descr.column_name.empty()
? block.getByName(descr.column_name)
: block.getByPosition(descr.column_number);
/// Столбцы первичного ключа переупорядочиваем заранее и складываем в primary_columns.
if (permutation)
primary_columns[i].column = primary_columns[i].column->permute(*permutation, 0);
}
/// Теперь пишем данные.
for (const auto & it : columns_list)
{
const ColumnWithTypeAndName & column = block.getByName(it.name);
if (permutation)
{
auto primary_column_it = primary_columns_name_to_position.find(it.name);
if (primary_columns_name_to_position.end() != primary_column_it)
{
writeData(column.name, *column.type, *primary_columns[primary_column_it->second].column, offset_columns);
}
else
{
/// Столбцы, не входящие в первичный ключ, переупорядочиваем здесь; затем результат освобождается - для экономии оперативки.
ColumnPtr permutted_column = column.column->permute(*permutation, 0);
writeData(column.name, *column.type, *permutted_column, offset_columns);
}
}
else
{
writeData(column.name, *column.type, *column.column, offset_columns);
}
}
/// Пишем индекс. Индекс содержит значение Primary Key для каждой index_granularity строки.
for (size_t i = index_offset; i < rows; i += storage.index_granularity)
{
if (storage.mode != MergeTreeData::Unsorted)
{
for (const auto & primary_column : primary_columns)
{
index_vec.push_back((*primary_column.column)[i]);
primary_column.type->serializeBinary(index_vec.back(), *index_stream);
}
}
++marks_count;
}
size_t written_for_last_mark = (storage.index_granularity - index_offset + rows) % storage.index_granularity;
index_offset = (storage.index_granularity - written_for_last_mark) % storage.index_granularity;
}
private: private:
NamesAndTypesList columns_list; NamesAndTypesList columns_list;
String part_path; String part_path;

View File

@ -38,7 +38,7 @@ public:
AbandonableLockInZooKeeper block_number_lock = storage.allocateBlockNumber(month_name); AbandonableLockInZooKeeper block_number_lock = storage.allocateBlockNumber(month_name);
UInt64 part_number = block_number_lock.getNumber(); Int64 part_number = block_number_lock.getNumber();
MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, part_number); 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); String part_name = ActiveDataPartSet::getPartName(part->left_date, part->right_date, part->left, part->right, part->level);

View File

@ -84,8 +84,6 @@ public:
}; };
typedef std::map<String, ColumnData> Files_t; typedef std::map<String, ColumnData> Files_t;
Files_t & getFiles() { return files; }
bool checkData() const override; bool checkData() const override;
protected: protected:
@ -149,7 +147,7 @@ private:
size_t max_compress_block_size; size_t max_compress_block_size;
protected: protected:
FileChecker<StorageLog> file_checker; FileChecker file_checker;
private: private:
/** Для обычных столбцов, в засечках указано количество строчек в блоке. /** Для обычных столбцов, в засечках указано количество строчек в блоке.

View File

@ -393,8 +393,11 @@ private:
*/ */
void waitForReplicaToProcessLogEntry(const String & replica_name, const LogEntry & entry); void waitForReplicaToProcessLogEntry(const String & replica_name, const LogEntry & entry);
/// Преобразовать число в строку формате суффиксов автоинкрементных нод в ZooKeeper. /** Преобразовать число в строку формате суффиксов автоинкрементных нод в ZooKeeper.
static String padIndex(UInt64 index) * Поддерживаются также отрицательные числа - для них имя ноды выглядит несколько глупо
* и не соответствует никакой автоинкрементной ноде в ZK.
*/
static String padIndex(Int64 index)
{ {
String index_str = toString(index); String index_str = toString(index);
return std::string(10 - index_str.size(), '0') + index_str; return std::string(10 - index_str.size(), '0') + index_str;

View File

@ -0,0 +1,89 @@
#pragma once
#include <map>
#include <Poco/File.h>
#include <DB/Storages/IStorage.h>
#include <DB/Common/FileChecker.h>
namespace DB
{
/** Реализует хранилище, подходящее для маленьких кусочков лога.
* При этом, хранит все столбцы в одном файле формата Native, с расположенным рядом индексом.
*/
class StorageStripeLog : public IStorage
{
friend class StripeLogBlockInputStream;
friend class StripeLogBlockOutputStream;
public:
/** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце),
* (корректность имён и путей не проверяется)
* состоящую из указанных столбцов.
* Если не указано attach - создать директорию, если её нет.
*/
static StoragePtr create(
const std::string & path_,
const std::string & name_,
NamesAndTypesListPtr columns_,
const NamesAndTypesList & materialized_columns_,
const NamesAndTypesList & alias_columns_,
const ColumnDefaults & column_defaults_,
bool attach,
size_t max_compress_block_size_ = DEFAULT_MAX_COMPRESS_BLOCK_SIZE);
std::string getName() const override { return "StripeLog"; }
std::string getTableName() const override { return name; }
const NamesAndTypesList & getColumnsListImpl() const override { return *columns; }
BlockInputStreams read(
const Names & column_names,
ASTPtr query,
const Context & context,
const Settings & settings,
QueryProcessingStage::Enum & processed_stage,
size_t max_block_size = DEFAULT_BLOCK_SIZE,
unsigned threads = 1) override;
BlockOutputStreamPtr write(ASTPtr query) override;
void rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name) override;
bool checkData() const override;
/// Данные файла.
struct ColumnData
{
Poco::File data_file;
};
typedef std::map<String, ColumnData> Files_t;
std::string full_path() { return path + escapeForFileName(name) + '/';}
private:
String path;
String name;
NamesAndTypesListPtr columns;
size_t max_compress_block_size;
FileChecker file_checker;
Poco::RWLock rwlock;
Logger * log;
StorageStripeLog(
const std::string & path_,
const std::string & name_,
NamesAndTypesListPtr columns_,
const NamesAndTypesList & materialized_columns_,
const NamesAndTypesList & alias_columns_,
const ColumnDefaults & column_defaults_,
bool attach,
size_t max_compress_block_size_);
};
}

View File

@ -64,8 +64,6 @@ public:
}; };
typedef std::map<String, ColumnData> Files_t; typedef std::map<String, ColumnData> Files_t;
Files_t & getFiles();
std::string full_path() { return path + escapeForFileName(name) + '/';} std::string full_path() { return path + escapeForFileName(name) + '/';}
private: private:
@ -77,7 +75,7 @@ private:
Files_t files; Files_t files;
FileChecker<StorageTinyLog> file_checker; FileChecker file_checker;
Logger * log; Logger * log;

View File

@ -652,7 +652,7 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da
AggregateFunctionPtr nested = get(String(name.data(), name.size() - strlen("State")), argument_types, recursion_level + 1); AggregateFunctionPtr nested = get(String(name.data(), name.size() - strlen("State")), argument_types, recursion_level + 1);
return new AggregateFunctionState(nested); return new AggregateFunctionState(nested);
} }
else if (recursion_level == 0 && name.size() > strlen("Merge") && !(strcmp(name.data() + name.size() - strlen("Merge"), "Merge"))) else if (recursion_level <= 1 && name.size() > strlen("Merge") && !(strcmp(name.data() + name.size() - strlen("Merge"), "Merge")))
{ {
/// Для агрегатных функций вида aggMerge, где agg - имя другой агрегатной функции. /// Для агрегатных функций вида aggMerge, где agg - имя другой агрегатной функции.
if (argument_types.size() != 1) if (argument_types.size() != 1)
@ -668,7 +668,7 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da
return new AggregateFunctionMerge(nested); return new AggregateFunctionMerge(nested);
} }
else if (recursion_level <= 1 && name.size() >= 3 && name[name.size() - 2] == 'I' && name[name.size() - 1] == 'f') else if (recursion_level <= 2 && name.size() >= 3 && name[name.size() - 2] == 'I' && name[name.size() - 1] == 'f')
{ {
if (argument_types.empty()) if (argument_types.empty())
throw Exception{ throw Exception{
@ -682,7 +682,7 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da
AggregateFunctionPtr nested = get(String(name.data(), name.size() - 2), nested_dt, recursion_level + 1); AggregateFunctionPtr nested = get(String(name.data(), name.size() - 2), nested_dt, recursion_level + 1);
return new AggregateFunctionIf(nested); return new AggregateFunctionIf(nested);
} }
else if (recursion_level <= 2 && name.size() > strlen("Array") && !(strcmp(name.data() + name.size() - strlen("Array"), "Array"))) else if (recursion_level <= 3 && name.size() > strlen("Array") && !(strcmp(name.data() + name.size() - strlen("Array"), "Array")))
{ {
/// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции.
size_t num_agruments = argument_types.size(); size_t num_agruments = argument_types.size();
@ -695,7 +695,7 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da
else else
throw Exception("Illegal type " + argument_types[i]->getName() + " of argument #" + toString(i + 1) + " for aggregate function " + name + ". Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); throw Exception("Illegal type " + argument_types[i]->getName() + " of argument #" + toString(i + 1) + " for aggregate function " + name + ". Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
} }
AggregateFunctionPtr nested = get(String(name.data(), name.size() - strlen("Array")), nested_arguments, recursion_level + 2); /// + 2, чтобы ни один другой модификатор не мог идти перед Array AggregateFunctionPtr nested = get(String(name.data(), name.size() - strlen("Array")), nested_arguments, recursion_level + 3); /// + 3, чтобы ни один другой модификатор не мог идти перед Array
return new AggregateFunctionArray(nested); return new AggregateFunctionArray(nested);
} }
else else
@ -765,14 +765,14 @@ bool AggregateFunctionFactory::isAggregateFunctionName(const String & name, int
if (recursion_level <= 0 && name.size() > strlen("State") && !(strcmp(name.data() + name.size() - strlen("State"), "State"))) if (recursion_level <= 0 && name.size() > strlen("State") && !(strcmp(name.data() + name.size() - strlen("State"), "State")))
return isAggregateFunctionName(String(name.data(), name.size() - strlen("State")), recursion_level + 1); return isAggregateFunctionName(String(name.data(), name.size() - strlen("State")), recursion_level + 1);
/// Для агрегатных функций вида aggMerge, где agg - имя другой агрегатной функции. /// Для агрегатных функций вида aggMerge, где agg - имя другой агрегатной функции.
if (recursion_level <= 0 && name.size() > strlen("Merge") && !(strcmp(name.data() + name.size() - strlen("Merge"), "Merge"))) if (recursion_level <= 1 && name.size() > strlen("Merge") && !(strcmp(name.data() + name.size() - strlen("Merge"), "Merge")))
return isAggregateFunctionName(String(name.data(), name.size() - strlen("Merge")), recursion_level + 1); return isAggregateFunctionName(String(name.data(), name.size() - strlen("Merge")), recursion_level + 1);
/// Для агрегатных функций вида aggIf, где agg - имя другой агрегатной функции. /// Для агрегатных функций вида aggIf, где agg - имя другой агрегатной функции.
if (recursion_level <= 1 && name.size() >= 3 && name[name.size() - 2] == 'I' && name[name.size() - 1] == 'f') if (recursion_level <= 2 && name.size() >= 3 && name[name.size() - 2] == 'I' && name[name.size() - 1] == 'f')
return isAggregateFunctionName(String(name.data(), name.size() - 2), recursion_level + 1); return isAggregateFunctionName(String(name.data(), name.size() - 2), recursion_level + 1);
/// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции.
if (recursion_level <= 2 && name.size() > strlen("Array") && !(strcmp(name.data() + name.size() - strlen("Array"), "Array"))) if (recursion_level <= 3 && name.size() > strlen("Array") && !(strcmp(name.data() + name.size() - strlen("Array"), "Array")))
return isAggregateFunctionName(String(name.data(), name.size() - strlen("Array")), recursion_level + 2); /// + 2, чтобы ни один другой модификатор не мог идти перед Array return isAggregateFunctionName(String(name.data(), name.size() - strlen("Array")), recursion_level + 3); /// + 3, чтобы ни один другой модификатор не мог идти перед Array
return false; return false;
} }

View File

@ -369,6 +369,19 @@ private:
} }
/** Проверка для случая, когда в терминал вставляется многострочный запрос из буфера обмена.
* Позволяет не начинать выполнение одной строчки запроса, пока весь запрос не будет вставлен.
*/
static bool hasDataInSTDIN()
{
timeval timeout = { 0, 0 };
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
return select(1, &fds, 0, 0, &timeout) == 1;
}
void loop() void loop()
{ {
String query; String query;
@ -395,7 +408,7 @@ private:
query += line; query += line;
if (!ends_with_backslash && (ends_with_semicolon || has_vertical_output_suffix || !config().has("multiline"))) if (!ends_with_backslash && (ends_with_semicolon || has_vertical_output_suffix || (!config().has("multiline") && !hasDataInSTDIN())))
{ {
if (query != prev_query) if (query != prev_query)
{ {
@ -464,6 +477,12 @@ private:
copyData(in, out); copyData(in, out);
} }
process(line);
}
bool process(const String & line)
{
if (config().has("multiquery")) if (config().has("multiquery"))
{ {
/// Несколько запросов, разделенных ';'. /// Несколько запросов, разделенных ';'.
@ -494,17 +513,20 @@ private:
while (isWhitespace(*begin) || *begin == ';') while (isWhitespace(*begin) || *begin == ';')
++begin; ++begin;
process(query, ast); if (!processSingleQuery(query, ast))
return false;
} }
return true;
} }
else else
{ {
process(line); return processSingleQuery(line);
} }
} }
bool process(const String & line, ASTPtr parsed_query_ = nullptr) bool processSingleQuery(const String & line, ASTPtr parsed_query_ = nullptr)
{ {
if (exit_strings.end() != exit_strings.find(line)) if (exit_strings.end() != exit_strings.find(line))
return false; return false;
@ -838,15 +860,8 @@ private:
} }
void onData(Block & block) void initBlockOutputStream(const Block & block)
{ {
if (written_progress_chars)
clearProgress();
if (!block)
return;
processed_rows += block.rows();
if (!block_std_out) if (!block_std_out)
{ {
String current_format = format; String current_format = format;
@ -869,8 +884,21 @@ private:
block_std_out = context.getFormatFactory().getOutput(current_format, std_out, block); block_std_out = context.getFormatFactory().getOutput(current_format, std_out, block);
block_std_out->writePrefix(); block_std_out->writePrefix();
} }
}
/// Загаловочный блок с нулем строк использовался для инициализации block_std_out,
void onData(Block & block)
{
if (written_progress_chars)
clearProgress();
if (!block)
return;
processed_rows += block.rows();
initBlockOutputStream(block);
/// Заголовочный блок с нулем строк использовался для инициализации block_std_out,
/// выводить его не нужно /// выводить его не нужно
if (block.rows() != 0) if (block.rows() != 0)
{ {
@ -885,11 +913,13 @@ private:
void onTotals(Block & block) void onTotals(Block & block)
{ {
initBlockOutputStream(block);
block_std_out->setTotals(block); block_std_out->setTotals(block);
} }
void onExtremes(Block & block) void onExtremes(Block & block)
{ {
initBlockOutputStream(block);
block_std_out->setExtremes(block); block_std_out->setExtremes(block);
} }

View File

@ -214,7 +214,7 @@ Connection::Packet ParallelReplicas::receivePacketUnlocked()
auto it = getReplicaForReading(); auto it = getReplicaForReading();
if (it == replica_map.end()) if (it == replica_map.end())
throw Exception("No available replica", ErrorCodes::NO_AVAILABLE_REPLICA); throw Exception("Logical error: no available replica", ErrorCodes::NO_AVAILABLE_REPLICA);
Connection * connection = it->second; Connection * connection = it->second;
Connection::Packet packet = connection->receivePacket(); Connection::Packet packet = connection->receivePacket();
@ -263,9 +263,8 @@ ParallelReplicas::ReplicaMap::iterator ParallelReplicas::waitForReadEvent()
Poco::Net::Socket::SocketList read_list; Poco::Net::Socket::SocketList read_list;
read_list.reserve(active_replica_count); read_list.reserve(active_replica_count);
/** Сначала проверяем, есть ли данные, которые уже лежат в буфере /// Сначала проверяем, есть ли данные, которые уже лежат в буфере
* хоть одного соединения. /// хоть одного соединения.
*/
for (auto & e : replica_map) for (auto & e : replica_map)
{ {
Connection * connection = e.second; Connection * connection = e.second;
@ -273,9 +272,8 @@ ParallelReplicas::ReplicaMap::iterator ParallelReplicas::waitForReadEvent()
read_list.push_back(connection->socket); read_list.push_back(connection->socket);
} }
/** Если не было найдено никаких данных, то проверяем, есть ли соединения /// Если не было найдено никаких данных, то проверяем, есть ли соединения
* готовые для чтения. /// готовые для чтения.
*/
if (read_list.empty()) if (read_list.empty())
{ {
Poco::Net::Socket::SocketList write_list; Poco::Net::Socket::SocketList write_list;
@ -287,9 +285,17 @@ ParallelReplicas::ReplicaMap::iterator ParallelReplicas::waitForReadEvent()
if (connection != nullptr) if (connection != nullptr)
read_list.push_back(connection->socket); read_list.push_back(connection->socket);
} }
int n = Poco::Net::Socket::select(read_list, write_list, except_list, settings->poll_interval * 1000000);
int n = Poco::Net::Socket::select(read_list, write_list, except_list, settings->receive_timeout);
if (n == 0) if (n == 0)
return replica_map.end(); {
std::stringstream description;
for (auto it = replica_map.begin(); it != replica_map.end(); ++it)
description << (it != replica_map.begin() ? ", " : "") << it->second->getDescription();
throw Exception("Timeout exceeded while reading from " + description.str(), ErrorCodes::TIMEOUT_EXCEEDED);
}
} }
auto & socket = read_list[rand() % read_list.size()]; auto & socket = read_list[rand() % read_list.size()];

View File

@ -141,9 +141,12 @@ void Block::insertUnique(const ColumnWithTypeAndName & elem)
void Block::erase(size_t position) void Block::erase(size_t position)
{ {
if (index_by_position.empty())
throw Exception("Block is empty", ErrorCodes::POSITION_OUT_OF_BOUND);
if (position >= index_by_position.size()) if (position >= index_by_position.size())
throw Exception("Position out of bound in Block::erase(), max position = " throw Exception("Position out of bound in Block::erase(), max position = "
+ toString(index_by_position.size()), ErrorCodes::POSITION_OUT_OF_BOUND); + toString(index_by_position.size() - 1), ErrorCodes::POSITION_OUT_OF_BOUND);
Container_t::iterator it = index_by_position[position]; Container_t::iterator it = index_by_position[position];
index_by_name.erase(index_by_name.find(it->name)); index_by_name.erase(index_by_name.find(it->name));
@ -177,6 +180,9 @@ void Block::erase(const String & name)
ColumnWithTypeAndName & Block::getByPosition(size_t position) ColumnWithTypeAndName & Block::getByPosition(size_t position)
{ {
if (index_by_position.empty())
throw Exception("Block is empty", ErrorCodes::POSITION_OUT_OF_BOUND);
if (position >= index_by_position.size()) if (position >= index_by_position.size())
throw Exception("Position " + toString(position) throw Exception("Position " + toString(position)
+ " is out of bound in Block::getByPosition(), max position = " + " is out of bound in Block::getByPosition(), max position = "
@ -189,6 +195,9 @@ ColumnWithTypeAndName & Block::getByPosition(size_t position)
const ColumnWithTypeAndName & Block::getByPosition(size_t position) const const ColumnWithTypeAndName & Block::getByPosition(size_t position) const
{ {
if (index_by_position.empty())
throw Exception("Block is empty", ErrorCodes::POSITION_OUT_OF_BOUND);
if (position >= index_by_position.size()) if (position >= index_by_position.size())
throw Exception("Position " + toString(position) throw Exception("Position " + toString(position)
+ " is out of bound in Block::getByPosition(), max position = " + " is out of bound in Block::getByPosition(), max position = "
@ -302,7 +311,13 @@ std::string Block::dumpStructure() const
{ {
if (it != data.begin()) if (it != data.begin())
res << ", "; res << ", ";
res << it->name << ' ' << it->type->getName() << ' ' << it->column->getName() << ' ' << it->column->size();
res << it->name << ' ' << it->type->getName();
if (it->column)
res << ' ' << it->column->getName() << ' ' << it->column->size();
else
res << " nullptr";
} }
return res.str(); return res.str();
} }

View File

@ -52,16 +52,16 @@ inline std::string demangle(const char * const mangled, int & status)
return demangled; return demangled;
} }
void tryLogCurrentException(const char * log_name) void tryLogCurrentException(const char * log_name, const std::string & start_of_message)
{ {
tryLogCurrentException(&Logger::get(log_name)); tryLogCurrentException(&Logger::get(log_name), start_of_message);
} }
void tryLogCurrentException(Poco::Logger * logger) void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_message)
{ {
try try
{ {
LOG_ERROR(logger, getCurrentExceptionMessage(true)); LOG_ERROR(logger, start_of_message << (start_of_message.empty() ? "" : ": ") << getCurrentExceptionMessage(true));
} }
catch (...) catch (...)
{ {

View File

@ -26,7 +26,7 @@ namespace DB
{ {
BlockInputStreamPtr FormatFactory::getInput(const String & name, ReadBuffer & buf, BlockInputStreamPtr FormatFactory::getInput(const String & name, ReadBuffer & buf,
Block & sample, size_t max_block_size) const const Block & sample, size_t max_block_size) const
{ {
if (name == "Native") if (name == "Native")
return new NativeBlockInputStream(buf); return new NativeBlockInputStream(buf);
@ -48,7 +48,7 @@ BlockInputStreamPtr FormatFactory::getInput(const String & name, ReadBuffer & bu
BlockOutputStreamPtr FormatFactory::getOutput(const String & name, WriteBuffer & buf, BlockOutputStreamPtr FormatFactory::getOutput(const String & name, WriteBuffer & buf,
Block & sample) const const Block & sample) const
{ {
if (name == "Native") if (name == "Native")
return new NativeBlockOutputStream(buf); return new NativeBlockOutputStream(buf);

View File

@ -2,6 +2,7 @@
#include <DB/IO/ReadHelpers.h> #include <DB/IO/ReadHelpers.h>
#include <DB/IO/VarInt.h> #include <DB/IO/VarInt.h>
#include <DB/IO/CompressedReadBufferFromFile.h>
#include <DB/Columns/ColumnArray.h> #include <DB/Columns/ColumnArray.h>
#include <DB/DataTypes/DataTypeArray.h> #include <DB/DataTypes/DataTypeArray.h>
@ -14,6 +15,25 @@ namespace DB
{ {
NativeBlockInputStream::NativeBlockInputStream(
ReadBuffer & istr_, UInt64 server_revision_,
bool use_index_,
IndexForNativeFormat::Blocks::const_iterator index_block_it_,
IndexForNativeFormat::Blocks::const_iterator index_block_end_)
: istr(istr_), server_revision(server_revision_),
use_index(use_index_), index_block_it(index_block_it_), index_block_end(index_block_end_)
{
if (use_index)
{
istr_concrete = typeid_cast<CompressedReadBufferFromFile *>(&istr);
if (!istr_concrete)
throw Exception("When need to use index for NativeBlockInputStream, istr must be CompressedReadBufferFromFile.", ErrorCodes::LOGICAL_ERROR);
index_column_it = index_block_it->columns.begin();
}
}
void NativeBlockInputStream::readData(const IDataType & type, IColumn & column, ReadBuffer & istr, size_t rows) void NativeBlockInputStream::readData(const IDataType & type, IColumn & column, ReadBuffer & istr, size_t rows)
{ {
/** Для массивов требуется сначала десериализовать смещения, а потом значения. /** Для массивов требуется сначала десериализовать смещения, а потом значения.
@ -47,9 +67,17 @@ Block NativeBlockInputStream::readImpl()
const DataTypeFactory & data_type_factory = DataTypeFactory::instance(); const DataTypeFactory & data_type_factory = DataTypeFactory::instance();
if (istr.eof()) if (use_index && index_block_it == index_block_end)
return res; return res;
if (istr.eof())
{
if (use_index)
throw Exception("Input doesn't contain all data for index.", ErrorCodes::CANNOT_READ_ALL_DATA);
return res;
}
/// Дополнительная информация о блоке. /// Дополнительная информация о блоке.
if (server_revision >= DBMS_MIN_REVISION_WITH_BLOCK_INFO) if (server_revision >= DBMS_MIN_REVISION_WITH_BLOCK_INFO)
res.info.read(istr); res.info.read(istr);
@ -57,29 +85,103 @@ Block NativeBlockInputStream::readImpl()
/// Размеры /// Размеры
size_t columns = 0; size_t columns = 0;
size_t rows = 0; size_t rows = 0;
if (!use_index)
{
readVarUInt(columns, istr); readVarUInt(columns, istr);
readVarUInt(rows, istr); readVarUInt(rows, istr);
}
else
{
columns = index_block_it->num_columns;
rows = index_block_it->num_rows;
}
for (size_t i = 0; i < columns; ++i) for (size_t i = 0; i < columns; ++i)
{ {
if (use_index)
{
/// Если текущая позиция и так какая требуется, то реального seek-а не происходит.
istr_concrete->seek(index_column_it->location.offset_in_compressed_file, index_column_it->location.offset_in_decompressed_block);
}
ColumnWithTypeAndName column; ColumnWithTypeAndName column;
/// Имя /// Имя
readStringBinary(column.name, istr); readBinary(column.name, istr);
/// Тип /// Тип
String type_name; String type_name;
readStringBinary(type_name, istr); readBinary(type_name, istr);
column.type = data_type_factory.get(type_name); column.type = data_type_factory.get(type_name);
if (use_index)
{
/// Индекс позволяет сделать проверки.
if (index_column_it->name != column.name)
throw Exception("Index points to column with wrong name: corrupted index or data", ErrorCodes::INCORRECT_INDEX);
if (index_column_it->type != type_name)
throw Exception("Index points to column with wrong type: corrupted index or data", ErrorCodes::INCORRECT_INDEX);
}
/// Данные /// Данные
column.column = column.type->createColumn(); column.column = column.type->createColumn();
readData(*column.type, *column.column, istr, rows); readData(*column.type, *column.column, istr, rows);
res.insert(column); res.insert(column);
if (use_index)
++index_column_it;
}
if (use_index)
{
if (index_column_it != index_block_it->columns.end())
throw Exception("Inconsistent index: not all columns were read", ErrorCodes::INCORRECT_INDEX);
++index_block_it;
if (index_block_it != index_block_end)
index_column_it = index_block_it->columns.begin();
} }
return res; return res;
} }
void IndexForNativeFormat::read(ReadBuffer & istr, const NameSet & required_columns)
{
while (!istr.eof())
{
blocks.emplace_back();
IndexOfBlockForNativeFormat & block = blocks.back();
readVarUInt(block.num_columns, istr);
readVarUInt(block.num_rows, istr);
if (block.num_columns < required_columns.size())
throw Exception("Index contain less than required columns", ErrorCodes::INCORRECT_INDEX);
for (size_t i = 0; i < block.num_columns; ++i)
{
IndexOfOneColumnForNativeFormat column_index;
readBinary(column_index.name, istr);
readBinary(column_index.type, istr);
readBinary(column_index.location.offset_in_compressed_file, istr);
readBinary(column_index.location.offset_in_decompressed_block, istr);
if (required_columns.count(column_index.name))
block.columns.push_back(std::move(column_index));
}
if (block.columns.size() < required_columns.size())
throw Exception("Index contain less than required columns", ErrorCodes::INCORRECT_INDEX);
if (block.columns.size() > required_columns.size())
throw Exception("Index contain duplicate columns", ErrorCodes::INCORRECT_INDEX);
block.num_columns = block.columns.size();
}
}
} }

View File

@ -2,12 +2,14 @@
#include <DB/IO/WriteHelpers.h> #include <DB/IO/WriteHelpers.h>
#include <DB/IO/VarInt.h> #include <DB/IO/VarInt.h>
#include <DB/IO/CompressedWriteBuffer.h>
#include <DB/Columns/ColumnConst.h> #include <DB/Columns/ColumnConst.h>
#include <DB/Columns/ColumnArray.h> #include <DB/Columns/ColumnArray.h>
#include <DB/DataTypes/DataTypeArray.h> #include <DB/DataTypes/DataTypeArray.h>
#include <DB/DataStreams/MarkInCompressedFile.h>
#include <DB/DataStreams/NativeBlockOutputStream.h> #include <DB/DataStreams/NativeBlockOutputStream.h>
@ -15,6 +17,21 @@ namespace DB
{ {
NativeBlockOutputStream::NativeBlockOutputStream(
WriteBuffer & ostr_, UInt64 client_revision_,
WriteBuffer * index_ostr_)
: ostr(ostr_), client_revision(client_revision_),
index_ostr(index_ostr_)
{
if (index_ostr)
{
ostr_concrete = typeid_cast<CompressedWriteBuffer *>(&ostr);
if (!ostr_concrete)
throw Exception("When need to write index for NativeBlockOutputStream, ostr must be CompressedWriteBuffer.", ErrorCodes::LOGICAL_ERROR);
}
}
void NativeBlockOutputStream::writeData(const IDataType & type, const ColumnPtr & column, WriteBuffer & ostr, size_t offset, size_t limit) void NativeBlockOutputStream::writeData(const IDataType & type, const ColumnPtr & column, WriteBuffer & ostr, size_t offset, size_t limit)
{ {
/** Если есть столбцы-константы - то материализуем их. /** Если есть столбцы-константы - то материализуем их.
@ -71,11 +88,31 @@ void NativeBlockOutputStream::write(const Block & block)
/// Размеры /// Размеры
size_t columns = block.columns(); size_t columns = block.columns();
size_t rows = block.rows(); size_t rows = block.rows();
writeVarUInt(columns, ostr); writeVarUInt(columns, ostr);
writeVarUInt(rows, ostr); writeVarUInt(rows, ostr);
/** Индекс имеет ту же структуру, что и поток с данными.
* Но вместо значений столбца он содержит засечку, ссылающуюся на место в файле с данными, где находится этот кусочек столбца.
*/
if (index_ostr)
{
writeVarUInt(columns, *index_ostr);
writeVarUInt(rows, *index_ostr);
}
for (size_t i = 0; i < columns; ++i) for (size_t i = 0; i < columns; ++i)
{ {
/// Для индекса.
MarkInCompressedFile mark;
if (index_ostr)
{
ostr_concrete->next(); /// Заканчиваем сжатый блок.
mark.offset_in_compressed_file = ostr_concrete->getCompressedBytes();
mark.offset_in_decompressed_block = ostr_concrete->getRemainingBytes();
}
const ColumnWithTypeAndName & column = block.getByPosition(i); const ColumnWithTypeAndName & column = block.getByPosition(i);
/// Имя /// Имя
@ -86,6 +123,15 @@ void NativeBlockOutputStream::write(const Block & block)
/// Данные /// Данные
writeData(*column.type, column.column, ostr, 0, 0); writeData(*column.type, column.column, ostr, 0, 0);
if (index_ostr)
{
writeStringBinary(column.name, *index_ostr);
writeStringBinary(column.type->getName(), *index_ostr);
writeBinary(mark.offset_in_compressed_file, *index_ostr);
writeBinary(mark.offset_in_decompressed_block, *index_ostr);
}
} }
} }

View File

@ -137,12 +137,28 @@ void DataTypeString::deserializeBinary(IColumn & column, ReadBuffer & istr, size
ColumnString::Chars_t & data = column_string.getChars(); ColumnString::Chars_t & data = column_string.getChars();
ColumnString::Offsets_t & offsets = column_string.getOffsets(); ColumnString::Offsets_t & offsets = column_string.getOffsets();
double avg_chars_size;
if (avg_value_size_hint && avg_value_size_hint > sizeof(offsets[0]))
{
/// Выбрано наугад. /// Выбрано наугад.
constexpr auto avg_value_size_hint_reserve_multiplier = 1.2; constexpr auto avg_value_size_hint_reserve_multiplier = 1.2;
double avg_chars_size = (avg_value_size_hint && avg_value_size_hint > sizeof(offsets[0]) avg_chars_size = (avg_value_size_hint - sizeof(offsets[0])) * avg_value_size_hint_reserve_multiplier;
? (avg_value_size_hint - sizeof(offsets[0])) * avg_value_size_hint_reserve_multiplier }
: DBMS_APPROX_STRING_SIZE); else
{
/** Небольшая эвристика для оценки того, что в столбце много пустых строк.
* В этом случае, для экономии оперативки, будем говорить, что средний размер значения маленький.
*/
if (istr.position() + sizeof(UInt32) <= istr.buffer().end()
&& *reinterpret_cast<const UInt32 *>(istr.position()) == 0) /// Первые 4 строки находятся в буфере и являются пустыми.
{
avg_chars_size = 1;
}
else
avg_chars_size = DBMS_APPROX_STRING_SIZE;
}
data.reserve(data.size() + std::ceil(limit * avg_chars_size)); data.reserve(data.size() + std::ceil(limit * avg_chars_size));

View File

@ -27,6 +27,7 @@ void registerFunctionsArray(FunctionFactory & factory)
factory.registerFunction<FunctionEmptyArrayDate>(); factory.registerFunction<FunctionEmptyArrayDate>();
factory.registerFunction<FunctionEmptyArrayDateTime>(); factory.registerFunction<FunctionEmptyArrayDateTime>();
factory.registerFunction<FunctionEmptyArrayString>(); factory.registerFunction<FunctionEmptyArrayString>();
factory.registerFunction<FunctionEmptyArrayToSingle>();
factory.registerFunction<FunctionRange>(); factory.registerFunction<FunctionRange>();
} }

View File

@ -9,6 +9,7 @@
#include <DB/DataTypes/DataTypeAggregateFunction.h> #include <DB/DataTypes/DataTypeAggregateFunction.h>
#include <DB/Columns/ColumnsNumber.h> #include <DB/Columns/ColumnsNumber.h>
#include <DB/AggregateFunctions/AggregateFunctionCount.h> #include <DB/AggregateFunctions/AggregateFunctionCount.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
#include <DB/Interpreters/Aggregator.h> #include <DB/Interpreters/Aggregator.h>
@ -1688,6 +1689,66 @@ void Aggregator::mergeStream(BlockInputStreamPtr stream, AggregatedDataVariants
} }
Block Aggregator::mergeBlocks(BlocksList & blocks, bool final)
{
if (blocks.empty())
return {};
StringRefs key(keys_size);
ConstColumnPlainPtrs key_columns(keys_size);
AggregateColumnsData aggregate_columns(aggregates_size);
initialize(blocks.front());
/// Каким способом выполнять агрегацию?
for (size_t i = 0; i < keys_size; ++i)
key_columns[i] = sample.getByPosition(i).column;
Sizes key_sizes;
AggregatedDataVariants::Type method = chooseAggregationMethod(key_columns, key_sizes);
/// Временные данные для агрегации.
AggregatedDataVariants result;
/// result будет уничтожать состояния агрегатных функций в деструкторе
result.aggregator = this;
result.init(method);
result.keys_size = keys_size;
result.key_sizes = key_sizes;
LOG_TRACE(log, "Merging partially aggregated blocks.");
for (Block & block : blocks)
{
if (result.type == AggregatedDataVariants::Type::without_key || block.info.is_overflows)
mergeWithoutKeyStreamsImpl(block, result);
#define M(NAME, IS_TWO_LEVEL) \
else if (result.type == AggregatedDataVariants::Type::NAME) \
mergeStreamsImpl(block, key_sizes, result.aggregates_pool, *result.NAME, result.NAME->data);
APPLY_FOR_AGGREGATED_VARIANTS(M)
#undef M
else if (result.type != AggregatedDataVariants::Type::without_key)
throw Exception("Unknown aggregated data variant.", ErrorCodes::UNKNOWN_AGGREGATED_DATA_VARIANT);
}
BlocksList merged_block = convertToBlocks(result, final, 1);
if (merged_block.size() > 1) /// TODO overflows
throw Exception("Logical error: temporary result is not single-level", ErrorCodes::LOGICAL_ERROR);
LOG_TRACE(log, "Merged partially aggregated blocks.");
if (merged_block.empty())
return {};
return merged_block.front();
}
template <typename Method> template <typename Method>
void NO_INLINE Aggregator::destroyImpl( void NO_INLINE Aggregator::destroyImpl(
Method & method) const Method & method) const
@ -1769,4 +1830,5 @@ void Aggregator::setCancellationHook(const CancellationHook cancellation_hook)
isCancelled = cancellation_hook; isCancelled = cancellation_hook;
} }
} }

View File

@ -5,6 +5,7 @@
#include <Poco/SharedPtr.h> #include <Poco/SharedPtr.h>
#include <Poco/Mutex.h> #include <Poco/Mutex.h>
#include <Poco/File.h> #include <Poco/File.h>
#include <Poco/UUIDGenerator.h>
#include <Yandex/logger_useful.h> #include <Yandex/logger_useful.h>
@ -96,6 +97,8 @@ struct ContextShared
/// Создаются при создании Distributed таблиц, так как нужно дождаться пока будут выставлены Settings /// Создаются при создании Distributed таблиц, так как нужно дождаться пока будут выставлены Settings
Poco::SharedPtr<Clusters> clusters; Poco::SharedPtr<Clusters> clusters;
Poco::UUIDGenerator uuid_generator;
bool shutdown_called = false; bool shutdown_called = false;
@ -587,8 +590,12 @@ void Context::setCurrentDatabase(const String & name)
void Context::setCurrentQueryId(const String & query_id) void Context::setCurrentQueryId(const String & query_id)
{ {
String query_id_to_set = query_id;
if (query_id_to_set.empty()) /// Если пользователь не передал свой query_id, то генерируем его самостоятельно.
query_id_to_set = shared->uuid_generator.createRandom().toString();
Poco::ScopedLock<Poco::Mutex> lock(shared->mutex); Poco::ScopedLock<Poco::Mutex> lock(shared->mutex);
current_query_id = query_id; current_query_id = query_id_to_set;
} }

View File

@ -31,6 +31,8 @@ DictionaryPtr DictionaryFactory::create(const std::string & name, Poco::Util::Ab
const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"};
const bool require_nonempty = config.getBool(config_prefix + ".require_nonempty", false);
const auto & layout_type = keys.front(); const auto & layout_type = keys.front();
if ("range_hashed" == layout_type) if ("range_hashed" == layout_type)
@ -41,7 +43,7 @@ DictionaryPtr DictionaryFactory::create(const std::string & name, Poco::Util::Ab
ErrorCodes::BAD_ARGUMENTS ErrorCodes::BAD_ARGUMENTS
}; };
return std::make_unique<RangeHashedDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime); return std::make_unique<RangeHashedDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime, require_nonempty);
} }
else else
{ {
@ -49,16 +51,15 @@ DictionaryPtr DictionaryFactory::create(const std::string & name, Poco::Util::Ab
throw Exception{ throw Exception{
"Elements .structure.range_min and .structure.range_max should be defined only " "Elements .structure.range_min and .structure.range_max should be defined only "
"for a dictionary of layout 'range_hashed'", "for a dictionary of layout 'range_hashed'",
ErrorCodes::BAD_ARGUMENTS ErrorCodes::BAD_ARGUMENTS};
};
if ("flat" == layout_type) if ("flat" == layout_type)
{ {
return std::make_unique<FlatDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime); return std::make_unique<FlatDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime, require_nonempty);
} }
else if ("hashed" == layout_type) else if ("hashed" == layout_type)
{ {
return std::make_unique<HashedDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime); return std::make_unique<HashedDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime, require_nonempty);
} }
else if ("cache" == layout_type) else if ("cache" == layout_type)
{ {
@ -66,8 +67,12 @@ DictionaryPtr DictionaryFactory::create(const std::string & name, Poco::Util::Ab
if (size == 0) if (size == 0)
throw Exception{ throw Exception{
"Dictionary of layout 'cache' cannot have 0 cells", "Dictionary of layout 'cache' cannot have 0 cells",
ErrorCodes::TOO_SMALL_BUFFER_SIZE ErrorCodes::TOO_SMALL_BUFFER_SIZE};
};
if (require_nonempty)
throw Exception{
"Dictionary of layout 'cache' cannot have 'require_nonempty' attribute set",
ErrorCodes::BAD_ARGUMENTS};
return std::make_unique<CacheDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime, size); return std::make_unique<CacheDictionary>(name, dict_struct, std::move(source_ptr), dict_lifetime, size);
} }

View File

@ -6,6 +6,7 @@
#include <DB/DataTypes/DataTypeNested.h> #include <DB/DataTypes/DataTypeNested.h>
#include <DB/DataTypes/DataTypeArray.h> #include <DB/DataTypes/DataTypeArray.h>
#include <DB/Functions/IFunction.h> #include <DB/Functions/IFunction.h>
#include <DB/Functions/FunctionsArray.h>
#include <set> #include <set>
@ -268,6 +269,24 @@ void ExpressionAction::execute(Block & block) const
if (!any_array) if (!any_array)
throw Exception("ARRAY JOIN of not array: " + *array_joined_columns.begin(), ErrorCodes::TYPE_MISMATCH); throw Exception("ARRAY JOIN of not array: " + *array_joined_columns.begin(), ErrorCodes::TYPE_MISMATCH);
/// Если LEFT ARRAY JOIN, то создаём столбцы, в которых пустые массивы заменены на массивы с одним элементом - значением по-умолчанию.
std::map<String, ColumnPtr> non_empty_array_columns;
if (array_join_is_left)
{
for (const auto & name : array_joined_columns)
{
auto src_col = block.getByName(name);
Block tmp_block{src_col, {{}, src_col.type, {}}};
FunctionEmptyArrayToSingle().execute(tmp_block, {0}, 1);
non_empty_array_columns[name] = tmp_block.getByPosition(1).column;
}
any_array_ptr = non_empty_array_columns.begin()->second;
any_array = typeid_cast<const ColumnArray *>(&*any_array_ptr);
}
size_t columns = block.columns(); size_t columns = block.columns();
for (size_t i = 0; i < columns; ++i) for (size_t i = 0; i < columns; ++i)
{ {
@ -278,7 +297,8 @@ void ExpressionAction::execute(Block & block) const
if (!typeid_cast<const DataTypeArray *>(&*current.type)) if (!typeid_cast<const DataTypeArray *>(&*current.type))
throw Exception("ARRAY JOIN of not array: " + current.name, ErrorCodes::TYPE_MISMATCH); throw Exception("ARRAY JOIN of not array: " + current.name, ErrorCodes::TYPE_MISMATCH);
ColumnPtr array_ptr = current.column; ColumnPtr array_ptr = array_join_is_left ? non_empty_array_columns[current.name] : current.column;
if (array_ptr->isConst()) if (array_ptr->isConst())
array_ptr = dynamic_cast<const IColumnConst &>(*array_ptr).convertToFullColumn(); array_ptr = dynamic_cast<const IColumnConst &>(*array_ptr).convertToFullColumn();
@ -379,7 +399,7 @@ std::string ExpressionAction::toString() const
break; break;
case ARRAY_JOIN: case ARRAY_JOIN:
ss << "ARRAY JOIN "; ss << (array_join_is_left ? "LEFT " : "") << "ARRAY JOIN ";
for (NameSet::const_iterator it = array_joined_columns.begin(); it != array_joined_columns.end(); ++it) for (NameSet::const_iterator it = array_joined_columns.begin(); it != array_joined_columns.end(); ++it)
{ {
if (it != array_joined_columns.begin()) if (it != array_joined_columns.begin())
@ -761,7 +781,7 @@ std::string ExpressionActions::getID() const
ss << actions[i].result_name; ss << actions[i].result_name;
if (actions[i].type == ExpressionAction::ARRAY_JOIN) if (actions[i].type == ExpressionAction::ARRAY_JOIN)
{ {
ss << "{"; ss << (actions[i].array_join_is_left ? "LEFT ARRAY JOIN" : "ARRAY JOIN") << "{";
for (NameSet::const_iterator it = actions[i].array_joined_columns.begin(); for (NameSet::const_iterator it = actions[i].array_joined_columns.begin();
it != actions[i].array_joined_columns.end(); ++it) it != actions[i].array_joined_columns.end(); ++it)
{ {

View File

@ -87,6 +87,18 @@ const std::unordered_set<String> possibly_injective_function_names
"dictGetDateTime" "dictGetDateTime"
}; };
static bool functionIsInOperator(const String & name)
{
return name == "in" || name == "notIn";
}
static bool functionIsInOrGlobalInOperator(const String & name)
{
return name == "in" || name == "notIn" || name == "globalIn" || name == "globalNotIn";
}
void ExpressionAnalyzer::init() void ExpressionAnalyzer::init()
{ {
select_query = typeid_cast<ASTSelectQuery *>(&*ast); select_query = typeid_cast<ASTSelectQuery *>(&*ast);
@ -95,6 +107,7 @@ void ExpressionAnalyzer::init()
LogicalExpressionsOptimizer logical_expressions_optimizer(select_query, settings); LogicalExpressionsOptimizer logical_expressions_optimizer(select_query, settings);
logical_expressions_optimizer.optimizeDisjunctiveEqualityChains(); logical_expressions_optimizer.optimizeDisjunctiveEqualityChains();
/// Добавляет в множество известных алиасов те, которые объявлены в структуре таблицы (ALIAS-столбцы).
addStorageAliases(); addStorageAliases();
/// Создаёт словарь aliases: alias -> ASTPtr /// Создаёт словарь aliases: alias -> ASTPtr
@ -103,6 +116,9 @@ void ExpressionAnalyzer::init()
/// Common subexpression elimination. Rewrite rules. /// Common subexpression elimination. Rewrite rules.
normalizeTree(); normalizeTree();
/// Выполнение скалярных подзапросов - замена их на значения-константы.
executeScalarSubqueries();
/// GROUP BY injective function elimination. /// GROUP BY injective function elimination.
optimizeGroupBy(); optimizeGroupBy();
@ -223,9 +239,10 @@ void ExpressionAnalyzer::analyzeAggregation()
void ExpressionAnalyzer::initGlobalSubqueriesAndExternalTables() void ExpressionAnalyzer::initGlobalSubqueriesAndExternalTables()
{ {
/// Преобразует GLOBAL-подзапросы во внешние таблицы; кладёт их в словарь external_tables: name -> StoragePtr.
initGlobalSubqueries(ast); initGlobalSubqueries(ast);
/// Создаёт словарь external_tables: name -> StoragePtr. /// Добавляет уже существующие внешние таблицы (не подзапросы) в словарь external_tables.
findExternalTables(ast); findExternalTables(ast);
} }
@ -388,7 +405,7 @@ void ExpressionAnalyzer::normalizeTreeImpl(
} }
/// Может быть указано IN t, где t - таблица, что равносильно IN (SELECT * FROM t). /// Может быть указано IN t, где t - таблица, что равносильно IN (SELECT * FROM t).
if (func_node->name == "in" || func_node->name == "notIn" || func_node->name == "globalIn" || func_node->name == "globalNotIn") if (functionIsInOrGlobalInOperator(func_node->name))
if (ASTIdentifier * right = typeid_cast<ASTIdentifier *>(&*func_node->arguments->children.at(1))) if (ASTIdentifier * right = typeid_cast<ASTIdentifier *>(&*func_node->arguments->children.at(1)))
right->kind = ASTIdentifier::Table; right->kind = ASTIdentifier::Table;
@ -528,6 +545,145 @@ void ExpressionAnalyzer::normalizeTreeImpl(
finished_asts[initial_ast] = ast; finished_asts[initial_ast] = ast;
} }
void ExpressionAnalyzer::executeScalarSubqueries()
{
if (!select_query)
executeScalarSubqueriesImpl(ast);
else
{
for (auto & child : ast->children)
{
/// Не опускаемся в FROM и JOIN.
if (child.get() != select_query->table.get() && child.get() != select_query->join.get())
executeScalarSubqueriesImpl(child);
}
}
}
static ASTPtr addTypeConversion(ASTLiteral * ast_, const String & type_name)
{
if (0 == type_name.compare(0, strlen("Array"), "Array"))
return ast_; /// Преобразование типов для массивов пока не поддерживаем.
auto ast = std::unique_ptr<ASTLiteral>(ast_);
ASTFunction * func = new ASTFunction(ast->range);
ASTPtr res = func;
func->alias = ast->alias;
ast->alias.clear();
func->kind = ASTFunction::FUNCTION;
func->name = "to" + type_name;
ASTExpressionList * exp_list = new ASTExpressionList(ast->range);
func->arguments = exp_list;
func->children.push_back(func->arguments);
exp_list->children.push_back(ast.release());
return res;
}
void ExpressionAnalyzer::executeScalarSubqueriesImpl(ASTPtr & ast)
{
/** Заменяем подзапросы, возвращающие ровно одну строку
* ("скалярные" подзапросы) на соответствующие константы.
*
* Если подзапрос возвращает более одного столбца, то он заменяется на кортеж констант.
*
* Особенности:
*
* Замена происходит во время анализа запроса, а не во время основной стадии выполнения.
* Это значит, что не будет работать индикатор прогресса во время выполнения этих запросов,
* а также такие запросы нельзя будет прервать.
*
* Зато результат запросов может быть использован для индекса в таблице.
*
* Скалярные подзапросы выполняются на сервере-инициаторе запроса.
* На удалённые серверы запрос отправляется с уже подставленными константами.
*/
if (ASTSubquery * subquery = typeid_cast<ASTSubquery *>(ast.get()))
{
Context subquery_context = context;
Settings subquery_settings = context.getSettings();
subquery_settings.limits.max_result_rows = 1;
subquery_settings.extremes = 0;
subquery_context.setSettings(subquery_settings);
ASTPtr query = subquery->children.at(0);
BlockIO res = InterpreterSelectQuery(query, subquery_context, QueryProcessingStage::Complete, subquery_depth + 1).execute();
Block block;
try
{
block = res.in->read();
if (!block)
throw Exception("Scalar subquery returned empty result", ErrorCodes::INCORRECT_RESULT_OF_SCALAR_SUBQUERY);
if (block.rows() != 1 || res.in->read())
throw Exception("Scalar subquery returned more than one row", ErrorCodes::INCORRECT_RESULT_OF_SCALAR_SUBQUERY);
}
catch (const Exception & e)
{
if (e.code() == ErrorCodes::TOO_MUCH_ROWS)
throw Exception("Scalar subquery returned more than one row", ErrorCodes::INCORRECT_RESULT_OF_SCALAR_SUBQUERY);
else
throw;
}
size_t columns = block.columns();
if (columns == 1)
{
ASTLiteral * lit = new ASTLiteral(ast->range, (*block.getByPosition(0).column)[0]);
lit->alias = subquery->alias;
ast = addTypeConversion(lit, block.getByPosition(0).type->getName());
}
else
{
ASTFunction * tuple = new ASTFunction(ast->range);
tuple->alias = subquery->alias;
ast = tuple;
tuple->kind = ASTFunction::FUNCTION;
tuple->name = "tuple";
ASTExpressionList * exp_list = new ASTExpressionList(ast->range);
tuple->arguments = exp_list;
tuple->children.push_back(tuple->arguments);
exp_list->children.resize(columns);
for (size_t i = 0; i < columns; ++i)
{
exp_list->children[i] = addTypeConversion(
new ASTLiteral(ast->range, (*block.getByPosition(i).column)[0]),
block.getByPosition(i).type->getName());
}
}
}
else
{
/** Не опускаемся в подзапросы в аргументах IN.
* Но если аргумент - не подзапрос, то глубже внутри него могут быть подзапросы, и в них надо опускаться.
*/
ASTFunction * func = typeid_cast<ASTFunction *>(ast.get());
if (func && func->kind == ASTFunction::FUNCTION
&& functionIsInOrGlobalInOperator(func->name))
{
for (auto & child : ast->children)
{
if (child.get() != func->arguments)
executeScalarSubqueriesImpl(child);
else
for (size_t i = 0, size = func->arguments->children.size(); i < size; ++i)
if (i != 1 || !typeid_cast<ASTSubquery *>(func->arguments->children[i].get()))
executeScalarSubqueriesImpl(func->arguments->children[i]);
}
}
else
for (auto & child : ast->children)
executeScalarSubqueriesImpl(child);
}
}
void ExpressionAnalyzer::optimizeGroupBy() void ExpressionAnalyzer::optimizeGroupBy()
{ {
if (!(select_query && select_query->group_expression_list)) if (!(select_query && select_query->group_expression_list))
@ -657,7 +813,7 @@ void ExpressionAnalyzer::makeSetsForIndexImpl(ASTPtr & node, const Block & sampl
makeSetsForIndexImpl(child, sample_block); makeSetsForIndexImpl(child, sample_block);
ASTFunction * func = typeid_cast<ASTFunction *>(node.get()); ASTFunction * func = typeid_cast<ASTFunction *>(node.get());
if (func && func->kind == ASTFunction::FUNCTION && (func->name == "in" || func->name == "notIn")) if (func && func->kind == ASTFunction::FUNCTION && functionIsInOperator(func->name))
{ {
IAST & args = *func->arguments; IAST & args = *func->arguments;
ASTPtr & arg = args.children.at(1); ASTPtr & arg = args.children.at(1);
@ -693,7 +849,8 @@ static SharedPtr<InterpreterSelectQuery> interpretSubquery(
* Так как результат этого поздапроса - ещё не результат всего запроса. * Так как результат этого поздапроса - ещё не результат всего запроса.
* Вместо этого работают ограничения * Вместо этого работают ограничения
* max_rows_in_set, max_bytes_in_set, set_overflow_mode, * max_rows_in_set, max_bytes_in_set, set_overflow_mode,
* max_rows_in_join, max_bytes_in_join, join_overflow_mode. * max_rows_in_join, max_bytes_in_join, join_overflow_mode,
* которые проверяются отдельно (в объектах Set, Join).
*/ */
Context subquery_context = context; Context subquery_context = context;
Settings subquery_settings = context.getSettings(); Settings subquery_settings = context.getSettings();
@ -740,10 +897,6 @@ static SharedPtr<InterpreterSelectQuery> interpretSubquery(
void ExpressionAnalyzer::addExternalStorage(ASTPtr & subquery_or_table_name) void ExpressionAnalyzer::addExternalStorage(ASTPtr & subquery_or_table_name)
{ {
/// Сгенерируем имя для внешней таблицы.
while (context.tryGetExternalTable("_data" + toString(external_table_id)))
++external_table_id;
if (const ASTIdentifier * table = typeid_cast<const ASTIdentifier *>(&*subquery_or_table_name)) if (const ASTIdentifier * table = typeid_cast<const ASTIdentifier *>(&*subquery_or_table_name))
{ {
/// Если это уже внешняя таблица, ничего заполять не нужно. Просто запоминаем ее наличие. /// Если это уже внешняя таблица, ничего заполять не нужно. Просто запоминаем ее наличие.
@ -754,13 +907,20 @@ void ExpressionAnalyzer::addExternalStorage(ASTPtr & subquery_or_table_name)
} }
} }
/// Сгенерируем имя для внешней таблицы.
String external_table_name = "_data" + toString(external_table_id);
while (context.tryGetExternalTable(external_table_name)
|| external_tables.count(external_table_name))
{
++external_table_id;
external_table_name = "_data" + toString(external_table_id);
}
SharedPtr<InterpreterSelectQuery> interpreter = interpretSubquery(subquery_or_table_name, context, subquery_depth + 1); SharedPtr<InterpreterSelectQuery> interpreter = interpretSubquery(subquery_or_table_name, context, subquery_depth + 1);
Block sample = interpreter->getSampleBlock(); Block sample = interpreter->getSampleBlock();
NamesAndTypesListPtr columns = new NamesAndTypesList(sample.getColumnsList()); NamesAndTypesListPtr columns = new NamesAndTypesList(sample.getColumnsList());
String external_table_name = "_data" + toString(external_table_id);
/** Заменяем подзапрос на имя временной таблицы. /** Заменяем подзапрос на имя временной таблицы.
* Именно в таком виде, запрос отправится на удалённый сервер. * Именно в таком виде, запрос отправится на удалённый сервер.
* На удалённый сервер отправится эта временная таблица, и на его стороне, * На удалённый сервер отправится эта временная таблица, и на его стороне,
@ -1213,7 +1373,7 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl
actions_stack.addAction(ExpressionAction::copyColumn(arg->getColumnName(), result_name)); actions_stack.addAction(ExpressionAction::copyColumn(arg->getColumnName(), result_name));
NameSet joined_columns; NameSet joined_columns;
joined_columns.insert(result_name); joined_columns.insert(result_name);
actions_stack.addAction(ExpressionAction::arrayJoin(joined_columns)); actions_stack.addAction(ExpressionAction::arrayJoin(joined_columns, false));
} }
return; return;
@ -1221,7 +1381,7 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl
if (node->kind == ASTFunction::FUNCTION) if (node->kind == ASTFunction::FUNCTION)
{ {
if (node->name == "in" || node->name == "notIn" || node->name == "globalIn" || node->name == "globalNotIn") if (functionIsInOrGlobalInOperator(node->name))
{ {
if (!no_subqueries) if (!no_subqueries)
{ {
@ -1510,7 +1670,7 @@ void ExpressionAnalyzer::addMultipleArrayJoinAction(ExpressionActionsPtr & actio
result_columns.insert(result_source.first); result_columns.insert(result_source.first);
} }
actions->add(ExpressionAction::arrayJoin(result_columns)); actions->add(ExpressionAction::arrayJoin(result_columns, select_query->array_join_is_left));
} }
bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain, bool only_types) bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain, bool only_types)

View File

@ -64,7 +64,7 @@ void ExternalDictionaries::reloadImpl(const bool throw_on_error)
try try
{ {
auto dict_ptr = failed_dictionary.second.dict->clone(); auto dict_ptr = failed_dictionary.second.dict->clone();
if (dict_ptr->getCreationException()) if (const auto exception_ptr = dict_ptr->getCreationException())
{ {
/// recalculate next attempt time /// recalculate next attempt time
std::uniform_int_distribution<std::uint64_t> distribution( std::uniform_int_distribution<std::uint64_t> distribution(
@ -72,10 +72,11 @@ void ExternalDictionaries::reloadImpl(const bool throw_on_error)
failed_dictionary.second.next_attempt_time = std::chrono::system_clock::now() + failed_dictionary.second.next_attempt_time = std::chrono::system_clock::now() +
std::chrono::seconds{ std::chrono::seconds{
std::min<std::uint64_t>(backoff_max_sec, backoff_initial_sec + distribution(rnd_engine)) std::min<std::uint64_t>(backoff_max_sec, backoff_initial_sec + distribution(rnd_engine))};
};
++failed_dictionary.second.error_count; ++failed_dictionary.second.error_count;
std::rethrow_exception(exception_ptr);
} }
else else
{ {
@ -99,7 +100,7 @@ void ExternalDictionaries::reloadImpl(const bool throw_on_error)
} }
catch (...) catch (...)
{ {
LOG_ERROR(log, "Failed reloading " << name << " dictionary due to unexpected error"); tryLogCurrentException(log, "Failed reloading '" + name + "' dictionary");
} }
} }
@ -114,6 +115,7 @@ void ExternalDictionaries::reloadImpl(const bool throw_on_error)
try try
{ {
/// Если словарь не удалось ни разу загрузить или даже не удалось инициализировать из конфига.
if (!dictionary.second.dict) if (!dictionary.second.dict)
continue; continue;
@ -144,6 +146,10 @@ void ExternalDictionaries::reloadImpl(const bool throw_on_error)
{ {
/// create new version of dictionary /// create new version of dictionary
auto new_version = current->clone(); auto new_version = current->clone();
if (const auto exception_ptr = new_version->getCreationException())
std::rethrow_exception(exception_ptr);
dictionary.second.dict->set(new_version.release()); dictionary.second.dict->set(new_version.release());
} }
} }
@ -155,25 +161,7 @@ void ExternalDictionaries::reloadImpl(const bool throw_on_error)
{ {
dictionary.second.exception = std::current_exception(); dictionary.second.exception = std::current_exception();
try tryLogCurrentException(log, "Cannot update external dictionary '" + name + "', leaving old version");
{
throw;
}
catch (const Poco::Exception & e)
{
LOG_ERROR(log, "Cannot update external dictionary '" << name
<< "'! You must resolve this manually. " << e.displayText());
}
catch (const std::exception & e)
{
LOG_ERROR(log, "Cannot update external dictionary '" << name
<< "'! You must resolve this manually. " << e.what());
}
catch (...)
{
LOG_ERROR(log, "Cannot update external dictionary '" << name
<< "'! You must resolve this manually.");
}
} }
} }
} }
@ -235,6 +223,8 @@ void ExternalDictionaries::reloadFromFile(const std::string & config_path, const
throw std::runtime_error{"Overriding dictionary from file " + dict_it->second.origin}; throw std::runtime_error{"Overriding dictionary from file " + dict_it->second.origin};
auto dict_ptr = DictionaryFactory::instance().create(name, *config, key, context); auto dict_ptr = DictionaryFactory::instance().create(name, *config, key, context);
/// Если словарь не удалось загрузить.
if (const auto exception_ptr = dict_ptr->getCreationException()) if (const auto exception_ptr = dict_ptr->getCreationException())
{ {
const auto failed_dict_it = failed_dictionaries.find(name); const auto failed_dict_it = failed_dictionaries.find(name);
@ -292,6 +282,9 @@ void ExternalDictionaries::reloadFromFile(const std::string & config_path, const
{ {
if (!name.empty()) if (!name.empty())
{ {
/// Если для словаря не удалось загрузить данные или даже не удалось инициализировать из конфига.
/// - всё-равно вставляем информацию в dictionaries, с нулевым указателем dict.
const std::lock_guard<std::mutex> lock{dictionaries_mutex}; const std::lock_guard<std::mutex> lock{dictionaries_mutex};
const auto exception_ptr = std::current_exception(); const auto exception_ptr = std::current_exception();
@ -302,25 +295,7 @@ void ExternalDictionaries::reloadFromFile(const std::string & config_path, const
dict_it->second.exception = exception_ptr; dict_it->second.exception = exception_ptr;
} }
try tryLogCurrentException(log, "Cannot create external dictionary '" + name + "' from config path " + config_path);
{
throw;
}
catch (const Poco::Exception & e)
{
LOG_ERROR(log, config_path << ": cannot create external dictionary '" << name
<< "'! You must resolve this manually. " << e.displayText());
}
catch (const std::exception & e)
{
LOG_ERROR(log, config_path << ": cannot create external dictionary '" << name
<< "'! You must resolve this manually. " << e.what());
}
catch (...)
{
LOG_ERROR(log, config_path << ": cannot create external dictionary '" << name
<< "'! You must resolve this manually.");
}
/// propagate exception /// propagate exception
if (throw_on_error) if (throw_on_error)

View File

@ -7,7 +7,7 @@
#include <DB/IO/WriteHelpers.h> #include <DB/IO/WriteHelpers.h>
#include <DB/DataStreams/MaterializingBlockInputStream.h> #include <DB/DataStreams/MaterializingBlockInputStream.h>
#include <DB/DataStreams/copyData.h> #include <DB/DataStreams/NullAndDoCopyBlockInputStream.h>
#include <DB/Parsers/ASTCreateQuery.h> #include <DB/Parsers/ASTCreateQuery.h>
#include <DB/Parsers/ASTNameTypePair.h> #include <DB/Parsers/ASTNameTypePair.h>
@ -42,7 +42,7 @@ InterpreterCreateQuery::InterpreterCreateQuery(ASTPtr query_ptr_, Context & cont
} }
void InterpreterCreateQuery::executeImpl(bool assume_metadata_exists) BlockIO InterpreterCreateQuery::executeImpl(bool assume_metadata_exists)
{ {
String path = context.getPath(); String path = context.getPath();
String current_database = context.getCurrentDatabase(); String current_database = context.getCurrentDatabase();
@ -81,7 +81,7 @@ void InterpreterCreateQuery::executeImpl(bool assume_metadata_exists)
if (!create.if_not_exists || !context.isDatabaseExist(database_name)) if (!create.if_not_exists || !context.isDatabaseExist(database_name))
context.addDatabase(database_name); context.addDatabase(database_name);
return; return {};
} }
SharedPtr<InterpreterSelectQuery> interpreter_select; SharedPtr<InterpreterSelectQuery> interpreter_select;
@ -118,7 +118,7 @@ void InterpreterCreateQuery::executeImpl(bool assume_metadata_exists)
if (context.isTableExist(database_name, table_name)) if (context.isTableExist(database_name, table_name))
{ {
if (create.if_not_exists) if (create.if_not_exists)
return; return {};
else else
throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
} }
@ -251,9 +251,16 @@ void InterpreterCreateQuery::executeImpl(bool assume_metadata_exists)
/// Если запрос CREATE SELECT, то вставим в таблицу данные /// Если запрос CREATE SELECT, то вставим в таблицу данные
if (create.select && storage_name != "View" && (storage_name != "MaterializedView" || create.is_populate)) if (create.select && storage_name != "View" && (storage_name != "MaterializedView" || create.is_populate))
{ {
BlockInputStreamPtr from = new MaterializingBlockInputStream(interpreter_select->execute().in); BlockIO io;
copyData(*from, *res->write(query_ptr)); io.in_sample = select_sample;
io.in = new NullAndDoCopyBlockInputStream(
new MaterializingBlockInputStream(interpreter_select->execute().in),
res->write(query_ptr));
return io;
} }
return {};
} }
InterpreterCreateQuery::ColumnsAndDefaults InterpreterCreateQuery::parseColumns(ASTPtr expression_list) InterpreterCreateQuery::ColumnsAndDefaults InterpreterCreateQuery::parseColumns(ASTPtr expression_list)

View File

@ -100,6 +100,7 @@ BlockIO InterpreterInsertQuery::execute()
InterpreterSelectQuery interpreter_select{query.select, context}; InterpreterSelectQuery interpreter_select{query.select, context};
BlockInputStreamPtr in{interpreter_select.execute().in}; BlockInputStreamPtr in{interpreter_select.execute().in};
res.in = new NullAndDoCopyBlockInputStream{in, out}; res.in = new NullAndDoCopyBlockInputStream{in, out};
res.in_sample = interpreter_select.getSampleBlock();
} }
return res; return res;

View File

@ -6,6 +6,7 @@
#include <DB/DataStreams/MergingSortedBlockInputStream.h> #include <DB/DataStreams/MergingSortedBlockInputStream.h>
#include <DB/DataStreams/AggregatingBlockInputStream.h> #include <DB/DataStreams/AggregatingBlockInputStream.h>
#include <DB/DataStreams/MergingAggregatedBlockInputStream.h> #include <DB/DataStreams/MergingAggregatedBlockInputStream.h>
#include <DB/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.h>
#include <DB/DataStreams/AsynchronousBlockInputStream.h> #include <DB/DataStreams/AsynchronousBlockInputStream.h>
#include <DB/DataStreams/UnionBlockInputStream.h> #include <DB/DataStreams/UnionBlockInputStream.h>
#include <DB/DataStreams/ParallelAggregatingBlockInputStream.h> #include <DB/DataStreams/ParallelAggregatingBlockInputStream.h>
@ -330,9 +331,6 @@ BlockIO InterpreterSelectQuery::execute()
/// Ограничения на результат, квота на результат, а также колбек для прогресса. /// Ограничения на результат, квота на результат, а также колбек для прогресса.
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(&*streams[0])) if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(&*streams[0]))
{ {
stream->setProgressCallback(context.getProgressCallback());
stream->setProcessListElement(context.getProcessListElement());
/// Ограничения действуют только на конечный результат. /// Ограничения действуют только на конечный результат.
if (to_stage == QueryProcessingStage::Complete) if (to_stage == QueryProcessingStage::Complete)
{ {
@ -406,7 +404,7 @@ void InterpreterSelectQuery::executeSingleQuery()
bool has_having = false; bool has_having = false;
bool has_order_by = false; bool has_order_by = false;
ExpressionActionsPtr before_join; ExpressionActionsPtr before_join; /// включая JOIN
ExpressionActionsPtr before_where; ExpressionActionsPtr before_where;
ExpressionActionsPtr before_aggregation; ExpressionActionsPtr before_aggregation;
ExpressionActionsPtr before_having; ExpressionActionsPtr before_having;
@ -859,15 +857,39 @@ void InterpreterSelectQuery::executeAggregation(ExpressionActionsPtr expression,
void InterpreterSelectQuery::executeMergeAggregated(bool overflow_row, bool final) void InterpreterSelectQuery::executeMergeAggregated(bool overflow_row, bool final)
{ {
/// Склеим несколько источников в один
executeUnion();
/// Теперь объединим агрегированные блоки
Names key_names; Names key_names;
AggregateDescriptions aggregates; AggregateDescriptions aggregates;
query_analyzer->getAggregateInfo(key_names, aggregates); query_analyzer->getAggregateInfo(key_names, aggregates);
/** Есть два режима распределённой агрегации.
*
* 1. В разных потоках читать из удалённых серверов блоки.
* Сохранить все блоки в оперативку. Объединить блоки.
* Если агрегация двухуровневая - распараллелить по номерам корзин.
*
* 2. В одном потоке читать по очереди блоки с разных серверов.
* В оперативке хранится только по одному блоку с каждого сервера.
* Если агрегация двухуровневая - последовательно объединяем блоки каждого следующего уровня.
*
* Второй вариант расходует меньше памяти (до 256 раз меньше)
* в случае двухуровневой агрегации, которая используется для больших результатов после GROUP BY,
* но при этом может работать медленнее.
*/
if (!settings.distributed_aggregation_memory_efficient)
{
/// Склеим несколько источников в один, распараллеливая работу.
executeUnion();
/// Теперь объединим агрегированные блоки
streams[0] = new MergingAggregatedBlockInputStream(streams[0], key_names, aggregates, overflow_row, final, original_max_threads); streams[0] = new MergingAggregatedBlockInputStream(streams[0], key_names, aggregates, overflow_row, final, original_max_threads);
} }
else
{
streams[0] = new MergingAggregatedMemoryEfficientBlockInputStream(streams, key_names, aggregates, overflow_row, final);
streams.resize(1);
}
}
void InterpreterSelectQuery::executeHaving(ExpressionActionsPtr expression) void InterpreterSelectQuery::executeHaving(ExpressionActionsPtr expression)

View File

@ -166,6 +166,15 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
/// Держим элемент списка процессов до конца обработки запроса. /// Держим элемент списка процессов до конца обработки запроса.
res.process_list_entry = process_list_entry; res.process_list_entry = process_list_entry;
if (res.in)
{
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(res.in.get()))
{
stream->setProgressCallback(context.getProgressCallback());
stream->setProcessListElement(context.getProcessListElement());
}
}
quota.addQuery(current_time); quota.addQuery(current_time);
/// Всё, что связано с логом запросов. /// Всё, что связано с логом запросов.

View File

@ -41,7 +41,7 @@ static void executeCreateQuery(const String & query, Context & context, const St
{ {
if (const auto id = dynamic_cast<const ASTFunction *>(ast_create_query.storage.get())) if (const auto id = dynamic_cast<const ASTFunction *>(ast_create_query.storage.get()))
{ {
if (id->name == "TinyLog") if (id->name == "TinyLog" || id->name == "StripeLog")
{ {
tryLogCurrentException(__PRETTY_FUNCTION__); tryLogCurrentException(__PRETTY_FUNCTION__);
return; return;

View File

@ -147,28 +147,38 @@ void sortBlock(Block & block, const SortDescription & description, size_t limit)
} }
void stableSortBlock(Block & block, const SortDescription & description) void stableGetPermutation(const Block & block, const SortDescription & description, IColumn::Permutation & out_permutation)
{ {
if (!block) if (!block)
return; return;
size_t size = block.rows(); size_t size = block.rows();
IColumn::Permutation perm(size); out_permutation.resize(size);
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i)
perm[i] = i; out_permutation[i] = i;
ColumnsWithSortDescriptions columns_with_sort_desc; ColumnsWithSortDescriptions columns_with_sort_desc;
for (size_t i = 0, size = description.size(); i < size; ++i) for (size_t i = 0, size = description.size(); i < size; ++i)
{ {
IColumn * column = !description[i].column_name.empty() const IColumn * column = !description[i].column_name.empty()
? block.getByName(description[i].column_name).column ? block.getByName(description[i].column_name).column
: block.getByPosition(description[i].column_number).column; : block.getByPosition(description[i].column_number).column;
columns_with_sort_desc.push_back(std::make_pair(column, description[i])); columns_with_sort_desc.push_back(std::make_pair(column, description[i]));
} }
std::stable_sort(perm.begin(), perm.end(), PartialSortingLess(columns_with_sort_desc)); std::stable_sort(out_permutation.begin(), out_permutation.end(), PartialSortingLess(columns_with_sort_desc));
}
void stableSortBlock(Block & block, const SortDescription & description)
{
if (!block)
return;
IColumn::Permutation perm;
stableGetPermutation(block, description, perm);
size_t columns = block.columns(); size_t columns = block.columns();
for (size_t i = 0; i < columns; ++i) for (size_t i = 0; i < columns; ++i)

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