#include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int LOGICAL_ERROR; extern const int ILLEGAL_COLUMN; } class FunctionToStringCutToZero : public IFunction { public: static constexpr auto name = "toStringCutToZero"; static FunctionPtr create(ContextPtr) { return std::make_shared(); } String getName() const override { return name; } size_t getNumberOfArguments() const override { return 1; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { if (!isStringOrFixedString(arguments[0])) throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return std::make_shared(); } bool useDefaultImplementationForConstants() const override { return true; } static bool tryExecuteString(const IColumn * col, ColumnPtr & col_res) { const ColumnString * col_str_in = checkAndGetColumn(col); if (col_str_in) { auto col_str = ColumnString::create(); ColumnString::Chars & out_vec = col_str->getChars(); ColumnString::Offsets & out_offsets = col_str->getOffsets(); const ColumnString::Chars & in_vec = col_str_in->getChars(); const ColumnString::Offsets & in_offsets = col_str_in->getOffsets(); size_t size = in_offsets.size(); out_offsets.resize(size); out_vec.resize(in_vec.size()); char * begin = reinterpret_cast(out_vec.data()); char * pos = begin; ColumnString::Offset current_in_offset = 0; for (size_t i = 0; i < size; ++i) { const char * pos_in = reinterpret_cast(&in_vec[current_in_offset]); size_t current_size = strlen(pos_in); memcpySmallAllowReadWriteOverflow15(pos, pos_in, current_size); pos += current_size; *pos = '\0'; ++pos; out_offsets[i] = pos - begin; current_in_offset = in_offsets[i]; } out_vec.resize(pos - begin); if (!out_offsets.empty() && out_offsets.back() != out_vec.size()) throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR); col_res = std::move(col_str); return true; } else { return false; } } static bool tryExecuteFixedString(const IColumn * col, ColumnPtr & col_res) { const ColumnFixedString * col_fstr_in = checkAndGetColumn(col); if (col_fstr_in) { auto col_str = ColumnString::create(); ColumnString::Chars & out_vec = col_str->getChars(); ColumnString::Offsets & out_offsets = col_str->getOffsets(); const ColumnString::Chars & in_vec = col_fstr_in->getChars(); size_t size = col_fstr_in->size(); out_offsets.resize(size); out_vec.resize(in_vec.size() + size); char * begin = reinterpret_cast(out_vec.data()); char * pos = begin; const char * pos_in = reinterpret_cast(in_vec.data()); size_t n = col_fstr_in->getN(); for (size_t i = 0; i < size; ++i) { size_t current_size = strnlen(pos_in, n); memcpySmallAllowReadWriteOverflow15(pos, pos_in, current_size); pos += current_size; *pos = '\0'; out_offsets[i] = ++pos - begin; pos_in += n; } out_vec.resize(pos - begin); if (!out_offsets.empty() && out_offsets.back() != out_vec.size()) throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR); col_res = std::move(col_str); return true; } else { return false; } } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override { const IColumn * column = arguments[0].column.get(); ColumnPtr res_column; if (tryExecuteFixedString(column, res_column) || tryExecuteString(column, res_column)) return res_column; throw Exception("Illegal column " + arguments[0].column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; REGISTER_FUNCTION(ToStringCutToZero) { factory.registerFunction(); } }