1 | #include <Functions/IFunctionImpl.h> |
2 | #include <Functions/FunctionHelpers.h> |
3 | #include <Columns/ColumnString.h> |
4 | #include <Columns/ColumnVector.h> |
5 | #include <Columns/ColumnConst.h> |
6 | #include <DataTypes/DataTypeString.h> |
7 | #include <DataTypes/DataTypesNumber.h> |
8 | #include <IO/WriteBufferFromVector.h> |
9 | #include <IO/WriteHelpers.h> |
10 | #include <Common/formatReadable.h> |
11 | #include <Common/typeid_cast.h> |
12 | #include <type_traits> |
13 | |
14 | |
15 | namespace DB |
16 | { |
17 | |
18 | namespace ErrorCodes |
19 | { |
20 | extern const int ILLEGAL_COLUMN; |
21 | } |
22 | |
23 | |
24 | /** Function for an unusual conversion to a string: |
25 | * |
26 | * bitmaskToList - takes an integer - a bitmask, returns a string of degrees of 2 separated by a comma. |
27 | * for example, bitmaskToList(50) = '2,16,32' |
28 | * |
29 | * formatReadableSize - prints the transferred size in bytes in form `123.45 GiB`. |
30 | */ |
31 | |
32 | class FunctionBitmaskToList : public IFunction |
33 | { |
34 | public: |
35 | static constexpr auto name = "bitmaskToList" ; |
36 | static FunctionPtr create(const Context &) { return std::make_shared<FunctionBitmaskToList>(); } |
37 | |
38 | String getName() const override |
39 | { |
40 | return name; |
41 | } |
42 | |
43 | size_t getNumberOfArguments() const override { return 1; } |
44 | bool isInjective(const Block &) override { return true; } |
45 | |
46 | DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override |
47 | { |
48 | const DataTypePtr & type = arguments[0]; |
49 | |
50 | if (!isInteger(type)) |
51 | throw Exception("Cannot format " + type->getName() + " as bitmask string" , ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
52 | |
53 | return std::make_shared<DataTypeString>(); |
54 | } |
55 | |
56 | bool useDefaultImplementationForConstants() const override { return true; } |
57 | |
58 | void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override |
59 | { |
60 | if (!(executeType<UInt8>(block, arguments, result) |
61 | || executeType<UInt16>(block, arguments, result) |
62 | || executeType<UInt32>(block, arguments, result) |
63 | || executeType<UInt64>(block, arguments, result) |
64 | || executeType<Int8>(block, arguments, result) |
65 | || executeType<Int16>(block, arguments, result) |
66 | || executeType<Int32>(block, arguments, result) |
67 | || executeType<Int64>(block, arguments, result))) |
68 | throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() |
69 | + " of argument of function " + getName(), |
70 | ErrorCodes::ILLEGAL_COLUMN); |
71 | } |
72 | |
73 | private: |
74 | template <typename T> |
75 | inline static void writeBitmask(T x, WriteBuffer & out) |
76 | { |
77 | using UnsignedT = std::make_unsigned_t<T>; |
78 | UnsignedT u_x = x; |
79 | |
80 | bool first = true; |
81 | while (u_x) |
82 | { |
83 | UnsignedT y = u_x & (u_x - 1); |
84 | UnsignedT bit = u_x ^ y; |
85 | u_x = y; |
86 | if (!first) |
87 | writeChar(',', out); |
88 | first = false; |
89 | writeIntText(T(bit), out); |
90 | } |
91 | } |
92 | |
93 | template <typename T> |
94 | bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) |
95 | { |
96 | if (const ColumnVector<T> * col_from = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get())) |
97 | { |
98 | auto col_to = ColumnString::create(); |
99 | |
100 | const typename ColumnVector<T>::Container & vec_from = col_from->getData(); |
101 | ColumnString::Chars & data_to = col_to->getChars(); |
102 | ColumnString::Offsets & offsets_to = col_to->getOffsets(); |
103 | size_t size = vec_from.size(); |
104 | data_to.resize(size * 2); |
105 | offsets_to.resize(size); |
106 | |
107 | WriteBufferFromVector<ColumnString::Chars> buf_to(data_to); |
108 | |
109 | for (size_t i = 0; i < size; ++i) |
110 | { |
111 | writeBitmask<T>(vec_from[i], buf_to); |
112 | writeChar(0, buf_to); |
113 | offsets_to[i] = buf_to.count(); |
114 | } |
115 | |
116 | buf_to.finish(); |
117 | block.getByPosition(result).column = std::move(col_to); |
118 | } |
119 | else |
120 | { |
121 | return false; |
122 | } |
123 | |
124 | return true; |
125 | } |
126 | }; |
127 | |
128 | |
129 | class FunctionFormatReadableSize : public IFunction |
130 | { |
131 | public: |
132 | static constexpr auto name = "formatReadableSize" ; |
133 | static FunctionPtr create(const Context &) { return std::make_shared<FunctionFormatReadableSize>(); } |
134 | |
135 | String getName() const override |
136 | { |
137 | return name; |
138 | } |
139 | |
140 | size_t getNumberOfArguments() const override { return 1; } |
141 | |
142 | DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override |
143 | { |
144 | const IDataType & type = *arguments[0]; |
145 | |
146 | if (!isNativeNumber(type)) |
147 | throw Exception("Cannot format " + type.getName() + " as size in bytes" , ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
148 | |
149 | return std::make_shared<DataTypeString>(); |
150 | } |
151 | |
152 | bool useDefaultImplementationForConstants() const override { return true; } |
153 | |
154 | void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override |
155 | { |
156 | if (!(executeType<UInt8>(block, arguments, result) |
157 | || executeType<UInt16>(block, arguments, result) |
158 | || executeType<UInt32>(block, arguments, result) |
159 | || executeType<UInt64>(block, arguments, result) |
160 | || executeType<Int8>(block, arguments, result) |
161 | || executeType<Int16>(block, arguments, result) |
162 | || executeType<Int32>(block, arguments, result) |
163 | || executeType<Int64>(block, arguments, result) |
164 | || executeType<Float32>(block, arguments, result) |
165 | || executeType<Float64>(block, arguments, result))) |
166 | throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() |
167 | + " of argument of function " + getName(), |
168 | ErrorCodes::ILLEGAL_COLUMN); |
169 | } |
170 | |
171 | private: |
172 | template <typename T> |
173 | bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) |
174 | { |
175 | if (const ColumnVector<T> * col_from = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get())) |
176 | { |
177 | auto col_to = ColumnString::create(); |
178 | |
179 | const typename ColumnVector<T>::Container & vec_from = col_from->getData(); |
180 | ColumnString::Chars & data_to = col_to->getChars(); |
181 | ColumnString::Offsets & offsets_to = col_to->getOffsets(); |
182 | size_t size = vec_from.size(); |
183 | data_to.resize(size * 2); |
184 | offsets_to.resize(size); |
185 | |
186 | WriteBufferFromVector<ColumnString::Chars> buf_to(data_to); |
187 | |
188 | for (size_t i = 0; i < size; ++i) |
189 | { |
190 | formatReadableSizeWithBinarySuffix(static_cast<double>(vec_from[i]), buf_to); |
191 | writeChar(0, buf_to); |
192 | offsets_to[i] = buf_to.count(); |
193 | } |
194 | |
195 | buf_to.finish(); |
196 | block.getByPosition(result).column = std::move(col_to); |
197 | return true; |
198 | } |
199 | |
200 | return false; |
201 | } |
202 | }; |
203 | |
204 | } |
205 | |