#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace DB
{
namespace ErrorCodes
{
extern const int UNSUPPORTED_METHOD;
}
class FunctionGroupingBase : public IFunction
{
protected:
static constexpr UInt64 ONE = 1;
const ColumnNumbers arguments_indexes;
// Initial implementation of GROUPING function returned 1 if the argument is used as an aggregation key.
// This differs from the behavior described in the standard and other DBMS.
const bool force_compatibility;
static constexpr UInt64 COMPATIBLE_MODE[] = {1, 0};
static constexpr UInt64 INCOMPATIBLE_MODE[] = {0, 1};
public:
FunctionGroupingBase(ColumnNumbers arguments_indexes_, bool force_compatibility_)
: arguments_indexes(std::move(arguments_indexes_))
, force_compatibility(force_compatibility_)
{}
bool isVariadic() const override { return true; }
size_t getNumberOfArguments() const override { return 0; }
bool useDefaultImplementationForNulls() const override { return false; }
bool isSuitableForConstantFolding() const override { return false; }
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override
{
return std::make_shared();
}
template
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, size_t input_rows_count, AggregationKeyChecker checker) const
{
const auto * grouping_set_column = checkAndGetColumn(arguments[0].column.get());
auto result = ColumnUInt64::create();
auto & result_data = result->getData();
result_data.reserve(input_rows_count);
const auto * result_table = likely(force_compatibility) ? COMPATIBLE_MODE : INCOMPATIBLE_MODE;
for (size_t i = 0; i < input_rows_count; ++i)
{
UInt64 set_index = grouping_set_column->getElement(i);
UInt64 value = 0;
for (auto index : arguments_indexes)
value = (value << 1) + result_table[checker(set_index, index) ? 1 : 0];
result_data.push_back(value);
}
return result;
}
};
class FunctionGrouping : public FunctionGroupingBase
{
public:
explicit FunctionGrouping(bool force_compatibility_)
: FunctionGroupingBase(ColumnNumbers(), force_compatibility_)
{}
String getName() const override { return "grouping"; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName &, const DataTypePtr &, size_t) const override
{
throw Exception(ErrorCodes::UNSUPPORTED_METHOD,
"Method executeImpl is not supported for 'grouping' function");
}
};
class FunctionGroupingOrdinary : public FunctionGroupingBase
{
public:
FunctionGroupingOrdinary(ColumnNumbers arguments_indexes_, bool force_compatibility_)
: FunctionGroupingBase(std::move(arguments_indexes_), force_compatibility_)
{}
String getName() const override { return "groupingOrdinary"; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName &, const DataTypePtr &, size_t input_rows_count) const override
{
if (likely(force_compatibility))
return ColumnUInt64::create(input_rows_count, 0);
UInt64 value = (ONE << arguments_indexes.size()) - 1;
return ColumnUInt64::create(input_rows_count, value);
}
};
class FunctionGroupingForRollup : public FunctionGroupingBase
{
const UInt64 aggregation_keys_number;
public:
FunctionGroupingForRollup(ColumnNumbers arguments_indexes_, UInt64 aggregation_keys_number_, bool force_compatibility_)
: FunctionGroupingBase(std::move(arguments_indexes_), force_compatibility_)
, aggregation_keys_number(aggregation_keys_number_)
{}
String getName() const override { return "groupingForRollup"; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
{
return FunctionGroupingBase::executeImpl(arguments, input_rows_count,
[this](UInt64 set_index, UInt64 arg_index)
{
// For ROLLUP(a, b, c) there will be following grouping set indexes:
// | GROUPING SET | INDEX |
// | (a, b, c) | 0 |
// | (a, b) | 1 |
// | (a) | 2 |
// | () | 3 |
return arg_index < aggregation_keys_number - set_index;
}
);
}
};
class FunctionGroupingForCube : public FunctionGroupingBase
{
const UInt64 aggregation_keys_number;
public:
FunctionGroupingForCube(ColumnNumbers arguments_indexes_, UInt64 aggregation_keys_number_, bool force_compatibility_)
: FunctionGroupingBase(arguments_indexes_, force_compatibility_)
, aggregation_keys_number(aggregation_keys_number_)
{}
String getName() const override { return "groupingForCube"; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
{
return FunctionGroupingBase::executeImpl(arguments, input_rows_count,
[this](UInt64 set_index, UInt64 arg_index)
{
// For CUBE(a, b) there will be following grouping set indexes:
// | GROUPING SET | INDEX |
// | (a, b) | 0 |
// | (a) | 1 |
// | (b) | 2 |
// | () | 3 |
auto set_mask = (ONE << aggregation_keys_number) - 1 - set_index;
return set_mask & (ONE << (aggregation_keys_number - arg_index - 1));
}
);
}
};
class FunctionGroupingForGroupingSets : public FunctionGroupingBase
{
ColumnNumbersSetList grouping_sets;
public:
FunctionGroupingForGroupingSets(ColumnNumbers arguments_indexes_, ColumnNumbersList const & grouping_sets_, bool force_compatibility_)
: FunctionGroupingBase(std::move(arguments_indexes_), force_compatibility_)
{
for (auto const & set : grouping_sets_)
grouping_sets.emplace_back(set.begin(), set.end());
}
String getName() const override { return "groupingForGroupingSets"; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
{
return FunctionGroupingBase::executeImpl(arguments, input_rows_count,
[this](UInt64 set_index, UInt64 arg_index)
{
return grouping_sets[set_index].contains(arg_index);
}
);
}
};
}