1 | #include <Functions/IFunctionImpl.h> |
2 | #include <Functions/FunctionHelpers.h> |
3 | #include <Functions/FunctionFactory.h> |
4 | #include <DataTypes/DataTypeString.h> |
5 | #include <Columns/ColumnString.h> |
6 | #include <Columns/ColumnVector.h> |
7 | #include <Common/UnicodeBar.h> |
8 | #include <Common/FieldVisitors.h> |
9 | #include <IO/WriteHelpers.h> |
10 | |
11 | |
12 | namespace DB |
13 | { |
14 | |
15 | namespace ErrorCodes |
16 | { |
17 | extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; |
18 | extern const int ARGUMENT_OUT_OF_BOUND; |
19 | extern const int ILLEGAL_COLUMN; |
20 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
21 | } |
22 | |
23 | /** bar(x, min, max, width) - draws a strip from the number of characters proportional to (x - min) and equal to width for x == max. |
24 | * Returns a string with nice Unicode-art bar with resolution of 1/8 part of symbol. |
25 | */ |
26 | class FunctionBar : public IFunction |
27 | { |
28 | public: |
29 | static constexpr auto name = "bar" ; |
30 | static FunctionPtr create(const Context &) |
31 | { |
32 | return std::make_shared<FunctionBar>(); |
33 | } |
34 | |
35 | String getName() const override |
36 | { |
37 | return name; |
38 | } |
39 | |
40 | bool isVariadic() const override |
41 | { |
42 | return true; |
43 | } |
44 | size_t getNumberOfArguments() const override |
45 | { |
46 | return 0; |
47 | } |
48 | |
49 | DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override |
50 | { |
51 | if (arguments.size() != 3 && arguments.size() != 4) |
52 | throw Exception("Function " + getName() |
53 | + " requires from 3 or 4 parameters: value, min_value, max_value, [max_width_of_bar = 80]. Passed " |
54 | + toString(arguments.size()) |
55 | + "." , |
56 | ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); |
57 | |
58 | if (!isNativeNumber(arguments[0]) || !isNativeNumber(arguments[1]) || !isNativeNumber(arguments[2]) |
59 | || (arguments.size() == 4 && !isNativeNumber(arguments[3]))) |
60 | throw Exception("All arguments for function " + getName() + " must be numeric." , ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
61 | |
62 | return std::make_shared<DataTypeString>(); |
63 | } |
64 | |
65 | bool useDefaultImplementationForConstants() const override { return true; } |
66 | ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2, 3}; } |
67 | |
68 | void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override |
69 | { |
70 | Int64 min = extractConstant<Int64>(block, arguments, 1, "Second" ); /// The level at which the line has zero length. |
71 | Int64 max = extractConstant<Int64>(block, arguments, 2, "Third" ); /// The level at which the line has the maximum length. |
72 | |
73 | /// The maximum width of the bar in characters, by default. |
74 | Float64 max_width = arguments.size() == 4 ? extractConstant<Float64>(block, arguments, 3, "Fourth" ) : 80; |
75 | |
76 | if (max_width < 1) |
77 | throw Exception("Max_width argument must be >= 1." , ErrorCodes::ARGUMENT_OUT_OF_BOUND); |
78 | |
79 | if (max_width > 1000) |
80 | throw Exception("Too large max_width." , ErrorCodes::ARGUMENT_OUT_OF_BOUND); |
81 | |
82 | const auto & src = *block.getByPosition(arguments[0]).column; |
83 | |
84 | auto res_column = ColumnString::create(); |
85 | |
86 | if (executeNumber<UInt8>(src, *res_column, min, max, max_width) |
87 | || executeNumber<UInt16>(src, *res_column, min, max, max_width) |
88 | || executeNumber<UInt32>(src, *res_column, min, max, max_width) |
89 | || executeNumber<UInt64>(src, *res_column, min, max, max_width) |
90 | || executeNumber<Int8>(src, *res_column, min, max, max_width) |
91 | || executeNumber<Int16>(src, *res_column, min, max, max_width) |
92 | || executeNumber<Int32>(src, *res_column, min, max, max_width) |
93 | || executeNumber<Int64>(src, *res_column, min, max, max_width) |
94 | || executeNumber<Float32>(src, *res_column, min, max, max_width) |
95 | || executeNumber<Float64>(src, *res_column, min, max, max_width)) |
96 | { |
97 | block.getByPosition(result).column = std::move(res_column); |
98 | } |
99 | else |
100 | throw Exception( |
101 | "Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), |
102 | ErrorCodes::ILLEGAL_COLUMN); |
103 | } |
104 | |
105 | private: |
106 | template <typename T> |
107 | T (Block & block, const ColumnNumbers & arguments, size_t argument_pos, const char * which_argument) const |
108 | { |
109 | const auto & column = *block.getByPosition(arguments[argument_pos]).column; |
110 | |
111 | if (!isColumnConst(column)) |
112 | throw Exception( |
113 | which_argument + String(" argument for function " ) + getName() + " must be constant." , ErrorCodes::ILLEGAL_COLUMN); |
114 | |
115 | return applyVisitor(FieldVisitorConvertToNumber<T>(), column[0]); |
116 | } |
117 | |
118 | template <typename T> |
119 | static void fill(const PaddedPODArray<T> & src, |
120 | ColumnString::Chars & dst_chars, |
121 | ColumnString::Offsets & dst_offsets, |
122 | Int64 min, |
123 | Int64 max, |
124 | Float64 max_width) |
125 | { |
126 | size_t size = src.size(); |
127 | size_t current_offset = 0; |
128 | |
129 | dst_offsets.resize(size); |
130 | dst_chars.reserve(size * (UnicodeBar::getWidthInBytes(max_width) + 1)); /// lines 0-terminated. |
131 | |
132 | for (size_t i = 0; i < size; ++i) |
133 | { |
134 | Float64 width = UnicodeBar::getWidth(src[i], min, max, max_width); |
135 | size_t next_size = current_offset + UnicodeBar::getWidthInBytes(width) + 1; |
136 | dst_chars.resize(next_size); |
137 | UnicodeBar::render(width, reinterpret_cast<char *>(&dst_chars[current_offset])); |
138 | current_offset = next_size; |
139 | dst_offsets[i] = current_offset; |
140 | } |
141 | } |
142 | |
143 | template <typename T> |
144 | static void fill(T src, String & dst_chars, Int64 min, Int64 max, Float64 max_width) |
145 | { |
146 | Float64 width = UnicodeBar::getWidth(src, min, max, max_width); |
147 | dst_chars.resize(UnicodeBar::getWidthInBytes(width)); |
148 | UnicodeBar::render(width, dst_chars.data()); |
149 | } |
150 | |
151 | template <typename T> |
152 | static bool executeNumber(const IColumn & src, ColumnString & dst, Int64 min, Int64 max, Float64 max_width) |
153 | { |
154 | if (const ColumnVector<T> * col = checkAndGetColumn<ColumnVector<T>>(&src)) |
155 | { |
156 | fill(col->getData(), dst.getChars(), dst.getOffsets(), min, max, max_width); |
157 | return true; |
158 | } |
159 | else |
160 | return false; |
161 | } |
162 | }; |
163 | |
164 | |
165 | void registerFunctionBar(FunctionFactory & factory) |
166 | { |
167 | factory.registerFunction<FunctionBar>(); |
168 | } |
169 | |
170 | } |
171 | |