#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { /** Вспомогательные функции: * * visibleWidth(x) - вычисляет приблизительную ширину при выводе значения в текстовом (tab-separated) виде на консоль. * * toTypeName(x) - получить имя типа * blockSize() - получить размер блока * materialize(x) - материализовать константу * * in(x, set) - функция для вычисления оператора IN * notIn(x, set) - и NOT IN. * * tuple(x, y, ...) - функция, позволяющая сгруппировать несколько столбцов */ template static void numWidthVector(const std::vector & a, std::vector & c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) if (a[i] >= 0) c[i] = a[i] ? 1 + log10(a[i]) : 1; else c[i] = 2 + log10(-a[i]); } template static void numWidthConstant(T a, UInt64 & c) { if (a >= 0) c = a ? 1 + log10(a) : 1; else c = 2 + log10(-a); } inline UInt64 floatWidth(double x) { /// Не быстро. unsigned size = WRITE_HELPERS_DEFAULT_FLOAT_PRECISION + 10; char tmp[size]; /// знаки, +0.0e+123\0 int res = std::snprintf(tmp, size, "%.*g", WRITE_HELPERS_DEFAULT_FLOAT_PRECISION, x); if (res >= static_cast(size) || res <= 0) throw Exception("Cannot print float or double number", ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER); return res; } template static void floatWidthVector(const std::vector & a, std::vector & c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) c[i] = floatWidth(a[i]); } template static void floatWidthConstant(T a, UInt64 & c) { c = floatWidth(a); } template <> inline void numWidthVector(const std::vector & a, std::vector & c) { floatWidthVector(a, c); } template <> inline void numWidthVector(const std::vector & a, std::vector & c) { floatWidthVector(a, c); } template <> inline void numWidthConstant(Float64 a, UInt64 & c) { floatWidthConstant(a, c); } template <> inline void numWidthConstant(Float32 a, UInt64 & c) { floatWidthConstant(a, c); } static inline UInt64 stringWidth(const UInt8 * pos, const UInt8 * end) { UInt64 res = 0; for (; pos < end; ++pos) { if (*pos == '\b' || *pos == '\f' || *pos == '\n' || *pos == '\r' || *pos == '\t' || *pos == '\0' || *pos == '\'' || *pos == '\\') ++res; if (*pos <= 0x7F || *pos >= 0xC0) ++res; } return res; } static inline void stringWidthVector(const std::vector & data, const std::vector & offsets, std::vector & res) { size_t size = offsets.size(); size_t prev_offset = 0; for (size_t i = 0; i < size; ++i) { res[i] = stringWidth(&data[prev_offset], &data[offsets[i] - 1]); prev_offset = offsets[i]; } } static inline void stringWidthFixedVector(const std::vector & data, size_t n, std::vector & res) { size_t size = data.size() / n; for (size_t i = 0; i < size; ++i) res[i] = stringWidth(&data[i * n], &data[(i + 1) * n]); } inline void stringWidthConstant(const String & data, UInt64 & res) { res = stringWidth(reinterpret_cast(data.data()), reinterpret_cast(data.data()) + data.size()); } class FunctionVisibleWidth : public IFunction { private: template bool executeConstNumber(Block & block, const ColumnPtr & column, size_t result) { if (const ColumnConst * col = dynamic_cast *>(&*column)) { UInt64 res = 0; numWidthConstant(col->getData(), res); block.getByPosition(result).column = new ColumnConstUInt64(column->size(), res); return true; } else return false; } template bool executeNumber(Block & block, const ColumnPtr & column, size_t result) { if (const ColumnVector * col = dynamic_cast *>(&*column)) { ColumnUInt64 * res = new ColumnUInt64(column->size()); numWidthVector(col->getData(), res->getData()); block.getByPosition(result).column = res; return true; } else return false; } public: /// Получить имя функции. String getName() const { return "visibleWidth"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); return new DataTypeUInt64; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { const ColumnPtr column = block.getByPosition(arguments[0]).column; const DataTypePtr type = block.getByPosition(arguments[0]).type; size_t rows = column->size(); if (dynamic_cast(&*type)) { block.getByPosition(result).column = new ColumnConstUInt64(rows, strlen("0000-00-00")); } else if (dynamic_cast(&*type)) { block.getByPosition(result).column = new ColumnConstUInt64(rows, strlen("0000-00-00 00:00:00")); } else if (executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) || executeConstNumber(block, column, result) /// TODO: правильная работа с float || executeConstNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result) || executeNumber(block, column, result)) { } else if (const ColumnString * col = dynamic_cast(&*column)) { ColumnUInt64 * res = new ColumnUInt64(rows); stringWidthVector(dynamic_cast(col->getData()).getData(), col->getOffsets(), res->getData()); block.getByPosition(result).column = res; } else if (const ColumnFixedString * col = dynamic_cast(&*column)) { ColumnUInt64 * res = new ColumnUInt64(rows); stringWidthFixedVector(dynamic_cast(col->getData()).getData(), col->getN(), res->getData()); block.getByPosition(result).column = res; } else if (const ColumnConstString * col = dynamic_cast(&*column)) { UInt64 res = 0; stringWidthConstant(col->getData(), res); block.getByPosition(result).column = new ColumnConstUInt64(rows, res); } else throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; class FunctionToTypeName : public IFunction { public: /// Получить имя функции. String getName() const { return "toTypeName"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); return new DataTypeString; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { block.getByPosition(result).column = new ColumnConstString(block.getByPosition(0).column->size(), block.getByPosition(arguments[0]).type->getName()); } }; class FunctionBlockSize : public IFunction { public: /// Получить имя функции. String getName() const { return "blockSize"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (!arguments.empty()) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 0.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); return new DataTypeUInt64; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { size_t size = block.getByPosition(0).column->size(); block.getByPosition(result).column = ColumnConstUInt64(size, size).convertToFullColumn(); } }; class FunctionMaterialize : public IFunction { public: /// Получить имя функции. String getName() const { return "materialize"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); return arguments[0]; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { const IColumn & argument = *block.getByPosition(arguments[0]).column; if (!argument.isConst()) throw Exception("Argument for function 'materialize' must be constant.", ErrorCodes::ILLEGAL_COLUMN); block.getByPosition(result).column = dynamic_cast(argument).convertToFullColumn(); } }; class FunctionIn : public IFunction { private: bool negative; public: FunctionIn(bool negative_ = false) : negative(negative_) {} /// Получить имя функции. String getName() const { return negative ? "notIn" : "in"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 2) throw Exception("Number of arguments for function '" + getName() + "' doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 2.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); return new DataTypeUInt8; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { /// Второй аргумент - обязательно ColumnSet. const ColumnSet * column_set = dynamic_cast(&*block.getByPosition(arguments[1]).column); if (!column_set) throw Exception("Second argument for function '" + getName() + "' must be Set.", ErrorCodes::ILLEGAL_COLUMN); /// Столбцы, которые проверяются на принадлежность множеству. ColumnNumbers left_arguments; /// Первый аргумент может быть tuple или одиночным столбцом. const ColumnTuple * tuple = dynamic_cast(&*block.getByPosition(arguments[0]).column); if (tuple) { /// Находим в блоке столбцы из tuple. const Block & tuple_elems = tuple->getData(); size_t tuple_size = tuple_elems.columns(); for (size_t i = 0; i < tuple_size; ++i) left_arguments.push_back(block.getPositionByName(tuple_elems.getByPosition(i).name)); } else left_arguments.push_back(arguments[0]); column_set->getData()->execute(block, left_arguments, result, negative); } }; class FunctionTuple : public IFunction { public: /// Получить имя функции. String getName() const { return "tuple"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() < 2) throw Exception("Function tuple requires at least two arguments.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); return new DataTypeTuple(arguments); } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { Block tuple_block; for (ColumnNumbers::const_iterator it = arguments.begin(); it != arguments.end(); ++it) tuple_block.insert(block.getByPosition(*it)); block.getByPosition(result).column = new ColumnTuple(tuple_block); } }; }