2020-12-10 22:16:58 +00:00
|
|
|
#pragma once
|
|
|
|
#include <Processors/ISimpleTransform.h>
|
|
|
|
|
2020-12-15 00:36:03 +00:00
|
|
|
#include <Interpreters/AggregateDescription.h>
|
|
|
|
|
|
|
|
#include <Common/AlignedBuffer.h>
|
|
|
|
|
2021-01-22 23:03:07 +00:00
|
|
|
#include <deque>
|
|
|
|
|
2020-12-10 22:16:58 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
class ExpressionActions;
|
|
|
|
using ExpressionActionsPtr = std::shared_ptr<ExpressionActions>;
|
|
|
|
|
2020-12-15 00:36:03 +00:00
|
|
|
class Arena;
|
|
|
|
|
2020-12-18 17:13:28 +00:00
|
|
|
// Runtime data for computing one window function
|
2020-12-15 00:36:03 +00:00
|
|
|
struct WindowFunctionWorkspace
|
|
|
|
{
|
|
|
|
WindowFunctionDescription window_function;
|
|
|
|
AlignedBuffer aggregate_function_state;
|
|
|
|
std::vector<size_t> argument_column_indices;
|
2020-12-21 21:23:45 +00:00
|
|
|
|
2021-01-22 23:03:07 +00:00
|
|
|
/*
|
2020-12-21 21:23:45 +00:00
|
|
|
// Argument and result columns. Be careful, they are per-chunk.
|
2020-12-15 00:36:03 +00:00
|
|
|
std::vector<const IColumn *> argument_columns;
|
2020-12-21 21:23:45 +00:00
|
|
|
MutableColumnPtr result_column;
|
2021-01-22 23:03:07 +00:00
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
|
|
|
struct WindowTransformBlock
|
|
|
|
{
|
|
|
|
Columns input_columns;
|
|
|
|
MutableColumns output_columns;
|
|
|
|
|
|
|
|
// Even in case of `count() over ()` we should have a dummy input column.
|
|
|
|
// Not sure how reliable this is...
|
|
|
|
size_t numRows() const { return input_columns[0]->size(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
// Use half the range of the unsigned int data type, to allow wraparound and
|
|
|
|
// comparison. I.e. even when the counter overflows we can still tell that it is
|
|
|
|
// greater than another counter, unless they are more than half the range apart.
|
|
|
|
template <typename T>
|
|
|
|
struct Wraparound
|
|
|
|
{
|
|
|
|
T value;
|
|
|
|
|
|
|
|
// exclusive?
|
|
|
|
constexpr auto max_value = T(1) << (sizeof(T) * 8 - 1);
|
|
|
|
|
|
|
|
operator T() const { return value; }
|
|
|
|
operator T&() { return value; }
|
|
|
|
bool operator == (const T & other) { return other.value = value; }
|
|
|
|
Wraparound & operator ++ () { value++; return *this; }
|
|
|
|
bool operator < (const T & other) { return value % max_value < other.value % max_value; }
|
|
|
|
Wraparound & operator + (const T & other) { value = value + other.value; return *this; }
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
struct RowNumber
|
|
|
|
{
|
|
|
|
uint64_t block = 0;
|
|
|
|
uint16_t row = 0;
|
|
|
|
|
|
|
|
bool operator < (const RowNumber & other) const
|
|
|
|
{
|
|
|
|
return block < other.block
|
|
|
|
|| (block == other.block && row < other.row);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator == (const RowNumber & other) const
|
|
|
|
{
|
|
|
|
return block == other.block && row == other.row;
|
|
|
|
}
|
2020-12-15 00:36:03 +00:00
|
|
|
};
|
|
|
|
|
2020-12-18 17:13:28 +00:00
|
|
|
/*
|
|
|
|
* Computes several window functions that share the same window. The input must
|
|
|
|
* be sorted correctly for this window (PARTITION BY, then ORDER BY).
|
2021-01-22 23:03:07 +00:00
|
|
|
* We need to track the following pointers:
|
|
|
|
* 1) start of partition -- rows that compare equal w/PARTITION BY.
|
|
|
|
* 2) current frame boundaries.
|
|
|
|
* 3) start of peer group -- rows that compare equal w/ORDER BY (empty ORDER BY
|
|
|
|
* means all rows are equal).
|
|
|
|
* These row ranges are (almost) nested -- peer group is inside frame inside
|
|
|
|
* partition. The only exception is when the exclusion clause is specified that
|
|
|
|
* excludes current peer group, but we don't support it anyway.
|
|
|
|
* All pointers only move forward.
|
|
|
|
* The value of the function is the same for all rows of the peer group.
|
|
|
|
* (partition [frame {group} ] )
|
2020-12-18 17:13:28 +00:00
|
|
|
*/
|
2021-01-20 07:10:54 +00:00
|
|
|
class WindowTransform : public IProcessor /* public ISimpleTransform */
|
2020-12-10 22:16:58 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
WindowTransform(
|
2020-12-22 01:41:02 +00:00
|
|
|
const Block & input_header_,
|
|
|
|
const Block & output_header_,
|
|
|
|
const WindowDescription & window_description_,
|
|
|
|
const std::vector<WindowFunctionDescription> &
|
2021-01-20 17:23:15 +00:00
|
|
|
functions);
|
2020-12-15 00:36:03 +00:00
|
|
|
|
|
|
|
~WindowTransform() override;
|
2020-12-10 22:16:58 +00:00
|
|
|
|
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return "WindowTransform";
|
|
|
|
}
|
|
|
|
|
|
|
|
static Block transformHeader(Block header, const ExpressionActionsPtr & expression);
|
|
|
|
|
2021-01-20 07:10:54 +00:00
|
|
|
/*
|
2021-01-20 17:23:15 +00:00
|
|
|
* (former) Implementation of ISimpleTransform.
|
2021-01-20 07:10:54 +00:00
|
|
|
*/
|
2021-01-22 23:03:07 +00:00
|
|
|
void appendChunk(Chunk & chunk) /*override*/;
|
2021-01-20 07:10:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Implementation of IProcessor;
|
|
|
|
*/
|
|
|
|
Status prepare() override;
|
|
|
|
void work() override;
|
2020-12-10 22:16:58 +00:00
|
|
|
|
2021-01-22 23:03:07 +00:00
|
|
|
private:
|
|
|
|
void advancePartitionEnd();
|
|
|
|
void advanceGroupEnd();
|
|
|
|
void advanceGroupEndGroups();
|
|
|
|
void advanceGroupEndRows();
|
|
|
|
void advanceGroupEndRange();
|
|
|
|
void advanceFrameStart();
|
|
|
|
void advanceFrameEnd();
|
|
|
|
void writeOutGroup();
|
|
|
|
|
|
|
|
Columns & inputAt(const RowNumber & x)
|
|
|
|
{
|
|
|
|
assert(x.block >= first_block_number);
|
|
|
|
assert(x.block - first_block_number < blocks.size());
|
|
|
|
return blocks[x.block - first_block_number].input_columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Columns & inputAt(const RowNumber & x) const
|
|
|
|
{ return const_cast<WindowTransform *>(this)->inputAt(x); }
|
|
|
|
|
|
|
|
MutableColumns & outputAt(const RowNumber & x)
|
|
|
|
{
|
|
|
|
assert(x.block >= first_block_number);
|
|
|
|
assert(x.block - first_block_number < blocks.size());
|
|
|
|
return blocks[x.block - first_block_number].output_columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
void advanceRowNumber(RowNumber & x) const
|
|
|
|
{
|
|
|
|
assert(x.block >= first_block_number);
|
|
|
|
assert(x.block - first_block_number < blocks.size());
|
|
|
|
|
|
|
|
const int block_rows = inputAt(x)[0]->size();
|
|
|
|
assert(x.row < block_rows);
|
|
|
|
|
|
|
|
x.row++;
|
|
|
|
if (x.row < block_rows)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
x.row = 0;
|
|
|
|
++x.block;
|
|
|
|
}
|
|
|
|
|
|
|
|
void retreatRowNumber(RowNumber & x) const
|
|
|
|
{
|
|
|
|
if (x.row > 0)
|
|
|
|
{
|
|
|
|
--x.row;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
--x.block;
|
|
|
|
assert(x.block >= first_block_number);
|
|
|
|
assert(x.block < first_block_number + blocks.size());
|
|
|
|
assert(inputAt(x)[0]->size() > 0);
|
|
|
|
x.row = inputAt(x)[0]->size() - 1;
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
auto xx = x;
|
|
|
|
advanceRowNumber(xx);
|
|
|
|
assert(xx == x);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
RowNumber blocksEnd() const
|
|
|
|
{ return RowNumber{first_block_number + blocks.size(), 0}; }
|
|
|
|
|
2020-12-15 00:36:03 +00:00
|
|
|
public:
|
2021-01-20 07:10:54 +00:00
|
|
|
/*
|
|
|
|
* Data (formerly) inherited from ISimpleTransform.
|
|
|
|
*/
|
|
|
|
InputPort & input;
|
|
|
|
OutputPort & output;
|
|
|
|
|
|
|
|
bool has_input = false;
|
2021-01-22 23:03:07 +00:00
|
|
|
bool input_is_finished = false;
|
2021-01-20 07:10:54 +00:00
|
|
|
Port::Data input_data;
|
|
|
|
bool has_output = false;
|
|
|
|
Port::Data output_data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Data for window transform itself.
|
|
|
|
*/
|
2020-12-15 00:36:03 +00:00
|
|
|
Block input_header;
|
|
|
|
|
|
|
|
WindowDescription window_description;
|
2020-12-18 17:13:28 +00:00
|
|
|
|
2020-12-16 12:57:47 +00:00
|
|
|
// Indices of the PARTITION BY columns in block.
|
2020-12-15 00:36:03 +00:00
|
|
|
std::vector<size_t> partition_by_indices;
|
2021-01-22 23:03:07 +00:00
|
|
|
// Indices of the ORDER BY columns in block;
|
|
|
|
std::vector<size_t> order_by_indices;
|
2020-12-18 17:13:28 +00:00
|
|
|
|
2021-01-22 23:03:07 +00:00
|
|
|
// Per-window-function scratch spaces.
|
2020-12-15 00:36:03 +00:00
|
|
|
std::vector<WindowFunctionWorkspace> workspaces;
|
|
|
|
|
2021-01-22 23:03:07 +00:00
|
|
|
// FIXME Reset it when the partition changes. We only save the temporary
|
|
|
|
// states in it (probably?).
|
2020-12-15 00:36:03 +00:00
|
|
|
std::unique_ptr<Arena> arena;
|
2021-01-22 23:03:07 +00:00
|
|
|
|
|
|
|
// A sliding window of blocks we currently need. We add the input blocks as
|
|
|
|
// they arrive, and discard the blocks we don't need anymore. The blocks
|
|
|
|
// have an always-incrementing index. The index of the first block is in
|
|
|
|
// `first_block_number`.
|
|
|
|
std::deque<WindowTransformBlock> blocks;
|
|
|
|
uint64_t first_block_number = 0;
|
|
|
|
// The next block we are going to pass to the consumer.
|
|
|
|
uint64_t next_output_block_number = 0;
|
|
|
|
// The first row for which we still haven't calculated the window functions.
|
|
|
|
// Used to determine which resulting blocks we can pass to the consumer.
|
|
|
|
RowNumber first_not_ready_row;
|
|
|
|
|
|
|
|
// We don't keep the pointer to start of partition, because we don't really
|
|
|
|
// need it, and we want to be able to drop the starting blocks to save memory.
|
|
|
|
// The `partition_end` is past-the-end, as usual. When partition_ended = false,
|
|
|
|
// it still haven't ended, and partition_end is the next row to check.
|
|
|
|
RowNumber partition_end;
|
|
|
|
bool partition_ended = false;
|
|
|
|
|
|
|
|
// Current peer group is [group_start, group_end) if group_ended,
|
|
|
|
// [group_start, ?) otherwise.
|
|
|
|
RowNumber group_start;
|
|
|
|
RowNumber group_end;
|
|
|
|
bool group_ended = false;
|
|
|
|
|
|
|
|
// After we have found the final boundaries of the frame, we can immediately
|
|
|
|
// output the result for the current group, w/o waiting for more data.
|
|
|
|
RowNumber frame_start;
|
|
|
|
RowNumber frame_end;
|
|
|
|
bool frame_ended = false;
|
2020-12-10 22:16:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
2021-01-22 23:03:07 +00:00
|
|
|
|
|
|
|
/// See https://fmt.dev/latest/api.html#formatting-user-defined-types
|
|
|
|
template <>
|
|
|
|
struct fmt::formatter<DB::RowNumber>
|
|
|
|
{
|
|
|
|
constexpr auto parse(format_parse_context & ctx)
|
|
|
|
{
|
|
|
|
auto it = ctx.begin();
|
|
|
|
auto end = ctx.end();
|
|
|
|
|
|
|
|
/// Only support {}.
|
|
|
|
if (it != end && *it != '}')
|
|
|
|
throw format_error("invalid format");
|
|
|
|
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename FormatContext>
|
|
|
|
auto format(const DB::RowNumber & x, FormatContext & ctx)
|
|
|
|
{
|
|
|
|
return format_to(ctx.out(), "{}:{}", x.block, x.row);
|
|
|
|
}
|
|
|
|
};
|