1#include "duckdb/function/cast/default_casts.hpp"
2#include "duckdb/function/cast/vector_cast_helpers.hpp"
3
4#include "duckdb/common/vector_operations/general_cast.hpp"
5#include "duckdb/common/types/decimal.hpp"
6#include "duckdb/common/vector_operations/unary_executor.hpp"
7#include "duckdb/common/types/cast_helpers.hpp"
8
9namespace duckdb {
10
11template <class T>
12static bool FromDecimalCast(Vector &source, Vector &result, idx_t count, CastParameters &parameters) {
13 auto &source_type = source.GetType();
14 auto width = DecimalType::GetWidth(type: source_type);
15 auto scale = DecimalType::GetScale(type: source_type);
16 switch (source_type.InternalType()) {
17 case PhysicalType::INT16:
18 return VectorCastHelpers::TemplatedDecimalCast<int16_t, T, TryCastFromDecimal>(
19 source, result, count, parameters.error_message, width, scale);
20 case PhysicalType::INT32:
21 return VectorCastHelpers::TemplatedDecimalCast<int32_t, T, TryCastFromDecimal>(
22 source, result, count, parameters.error_message, width, scale);
23 case PhysicalType::INT64:
24 return VectorCastHelpers::TemplatedDecimalCast<int64_t, T, TryCastFromDecimal>(
25 source, result, count, parameters.error_message, width, scale);
26 case PhysicalType::INT128:
27 return VectorCastHelpers::TemplatedDecimalCast<hugeint_t, T, TryCastFromDecimal>(
28 source, result, count, parameters.error_message, width, scale);
29 default:
30 throw InternalException("Unimplemented internal type for decimal");
31 }
32}
33
34template <class LIMIT_TYPE, class FACTOR_TYPE = LIMIT_TYPE>
35struct DecimalScaleInput {
36 DecimalScaleInput(Vector &result_p, FACTOR_TYPE factor_p) : result(result_p), factor(factor_p) {
37 }
38 DecimalScaleInput(Vector &result_p, LIMIT_TYPE limit_p, FACTOR_TYPE factor_p, string *error_message_p,
39 uint8_t source_width_p, uint8_t source_scale_p)
40 : result(result_p), limit(limit_p), factor(factor_p), error_message(error_message_p),
41 source_width(source_width_p), source_scale(source_scale_p) {
42 }
43
44 Vector &result;
45 LIMIT_TYPE limit;
46 FACTOR_TYPE factor;
47 bool all_converted = true;
48 string *error_message;
49 uint8_t source_width;
50 uint8_t source_scale;
51};
52
53struct DecimalScaleUpOperator {
54 template <class INPUT_TYPE, class RESULT_TYPE>
55 static RESULT_TYPE Operation(INPUT_TYPE input, ValidityMask &mask, idx_t idx, void *dataptr) {
56 auto data = (DecimalScaleInput<INPUT_TYPE, RESULT_TYPE> *)dataptr;
57 return Cast::Operation<INPUT_TYPE, RESULT_TYPE>(input) * data->factor;
58 }
59};
60
61struct DecimalScaleUpCheckOperator {
62 template <class INPUT_TYPE, class RESULT_TYPE>
63 static RESULT_TYPE Operation(INPUT_TYPE input, ValidityMask &mask, idx_t idx, void *dataptr) {
64 auto data = (DecimalScaleInput<INPUT_TYPE, RESULT_TYPE> *)dataptr;
65 if (input >= data->limit || input <= -data->limit) {
66 auto error = StringUtil::Format("Casting value \"%s\" to type %s failed: value is out of range!",
67 Decimal::ToString(input, data->source_width, data->source_scale),
68 data->result.GetType().ToString());
69 return HandleVectorCastError::Operation<RESULT_TYPE>(std::move(error), mask, idx, data->error_message,
70 data->all_converted);
71 }
72 return Cast::Operation<INPUT_TYPE, RESULT_TYPE>(input) * data->factor;
73 }
74};
75
76template <class SOURCE, class DEST, class POWERS_SOURCE, class POWERS_DEST>
77bool TemplatedDecimalScaleUp(Vector &source, Vector &result, idx_t count, string *error_message) {
78 auto source_scale = DecimalType::GetScale(type: source.GetType());
79 auto source_width = DecimalType::GetWidth(type: source.GetType());
80 auto result_scale = DecimalType::GetScale(type: result.GetType());
81 auto result_width = DecimalType::GetWidth(type: result.GetType());
82 D_ASSERT(result_scale >= source_scale);
83 idx_t scale_difference = result_scale - source_scale;
84 DEST multiply_factor = POWERS_DEST::POWERS_OF_TEN[scale_difference];
85 idx_t target_width = result_width - scale_difference;
86 if (source_width < target_width) {
87 DecimalScaleInput<SOURCE, DEST> input(result, multiply_factor);
88 // type will always fit: no need to check limit
89 UnaryExecutor::GenericExecute<SOURCE, DEST, DecimalScaleUpOperator>(source, result, count, &input);
90 return true;
91 } else {
92 // type might not fit: check limit
93 auto limit = POWERS_SOURCE::POWERS_OF_TEN[target_width];
94 DecimalScaleInput<SOURCE, DEST> input(result, limit, multiply_factor, error_message, source_width,
95 source_scale);
96 UnaryExecutor::GenericExecute<SOURCE, DEST, DecimalScaleUpCheckOperator>(source, result, count, &input,
97 error_message);
98 return input.all_converted;
99 }
100}
101
102struct DecimalScaleDownOperator {
103 template <class INPUT_TYPE, class RESULT_TYPE>
104 static RESULT_TYPE Operation(INPUT_TYPE input, ValidityMask &mask, idx_t idx, void *dataptr) {
105 auto data = (DecimalScaleInput<INPUT_TYPE> *)dataptr;
106 return Cast::Operation<INPUT_TYPE, RESULT_TYPE>(input / data->factor);
107 }
108};
109
110struct DecimalScaleDownCheckOperator {
111 template <class INPUT_TYPE, class RESULT_TYPE>
112 static RESULT_TYPE Operation(INPUT_TYPE input, ValidityMask &mask, idx_t idx, void *dataptr) {
113 auto data = (DecimalScaleInput<INPUT_TYPE> *)dataptr;
114 if (input >= data->limit || input <= -data->limit) {
115 auto error = StringUtil::Format("Casting value \"%s\" to type %s failed: value is out of range!",
116 Decimal::ToString(input, data->source_width, data->source_scale),
117 data->result.GetType().ToString());
118 return HandleVectorCastError::Operation<RESULT_TYPE>(std::move(error), mask, idx, data->error_message,
119 data->all_converted);
120 }
121 return Cast::Operation<INPUT_TYPE, RESULT_TYPE>(input / data->factor);
122 }
123};
124
125template <class SOURCE, class DEST, class POWERS_SOURCE>
126bool TemplatedDecimalScaleDown(Vector &source, Vector &result, idx_t count, string *error_message) {
127 auto source_scale = DecimalType::GetScale(type: source.GetType());
128 auto source_width = DecimalType::GetWidth(type: source.GetType());
129 auto result_scale = DecimalType::GetScale(type: result.GetType());
130 auto result_width = DecimalType::GetWidth(type: result.GetType());
131 D_ASSERT(result_scale < source_scale);
132 idx_t scale_difference = source_scale - result_scale;
133 idx_t target_width = result_width + scale_difference;
134 SOURCE divide_factor = POWERS_SOURCE::POWERS_OF_TEN[scale_difference];
135 if (source_width < target_width) {
136 DecimalScaleInput<SOURCE> input(result, divide_factor);
137 // type will always fit: no need to check limit
138 UnaryExecutor::GenericExecute<SOURCE, DEST, DecimalScaleDownOperator>(source, result, count, &input);
139 return true;
140 } else {
141 // type might not fit: check limit
142 auto limit = POWERS_SOURCE::POWERS_OF_TEN[target_width];
143 DecimalScaleInput<SOURCE> input(result, limit, divide_factor, error_message, source_width, source_scale);
144 UnaryExecutor::GenericExecute<SOURCE, DEST, DecimalScaleDownCheckOperator>(source, result, count, &input,
145 error_message);
146 return input.all_converted;
147 }
148}
149
150template <class SOURCE, class POWERS_SOURCE>
151static bool DecimalDecimalCastSwitch(Vector &source, Vector &result, idx_t count, CastParameters &parameters) {
152 auto source_scale = DecimalType::GetScale(type: source.GetType());
153 auto result_scale = DecimalType::GetScale(type: result.GetType());
154 source.GetType().Verify();
155 result.GetType().Verify();
156
157 // we need to either multiply or divide by the difference in scales
158 if (result_scale >= source_scale) {
159 // multiply
160 switch (result.GetType().InternalType()) {
161 case PhysicalType::INT16:
162 return TemplatedDecimalScaleUp<SOURCE, int16_t, POWERS_SOURCE, NumericHelper>(source, result, count,
163 parameters.error_message);
164 case PhysicalType::INT32:
165 return TemplatedDecimalScaleUp<SOURCE, int32_t, POWERS_SOURCE, NumericHelper>(source, result, count,
166 parameters.error_message);
167 case PhysicalType::INT64:
168 return TemplatedDecimalScaleUp<SOURCE, int64_t, POWERS_SOURCE, NumericHelper>(source, result, count,
169 parameters.error_message);
170 case PhysicalType::INT128:
171 return TemplatedDecimalScaleUp<SOURCE, hugeint_t, POWERS_SOURCE, Hugeint>(source, result, count,
172 parameters.error_message);
173 default:
174 throw NotImplementedException("Unimplemented internal type for decimal");
175 }
176 } else {
177 // divide
178 switch (result.GetType().InternalType()) {
179 case PhysicalType::INT16:
180 return TemplatedDecimalScaleDown<SOURCE, int16_t, POWERS_SOURCE>(source, result, count,
181 parameters.error_message);
182 case PhysicalType::INT32:
183 return TemplatedDecimalScaleDown<SOURCE, int32_t, POWERS_SOURCE>(source, result, count,
184 parameters.error_message);
185 case PhysicalType::INT64:
186 return TemplatedDecimalScaleDown<SOURCE, int64_t, POWERS_SOURCE>(source, result, count,
187 parameters.error_message);
188 case PhysicalType::INT128:
189 return TemplatedDecimalScaleDown<SOURCE, hugeint_t, POWERS_SOURCE>(source, result, count,
190 parameters.error_message);
191 default:
192 throw NotImplementedException("Unimplemented internal type for decimal");
193 }
194 }
195}
196
197struct DecimalCastInput {
198 DecimalCastInput(Vector &result_p, uint8_t width_p, uint8_t scale_p)
199 : result(result_p), width(width_p), scale(scale_p) {
200 }
201
202 Vector &result;
203 uint8_t width;
204 uint8_t scale;
205};
206
207struct StringCastFromDecimalOperator {
208 template <class INPUT_TYPE, class RESULT_TYPE>
209 static RESULT_TYPE Operation(INPUT_TYPE input, ValidityMask &mask, idx_t idx, void *dataptr) {
210 auto data = reinterpret_cast<DecimalCastInput *>(dataptr);
211 return StringCastFromDecimal::Operation<INPUT_TYPE>(input, data->width, data->scale, data->result);
212 }
213};
214
215template <class SRC>
216static bool DecimalToStringCast(Vector &source, Vector &result, idx_t count, CastParameters &parameters) {
217 auto &source_type = source.GetType();
218 auto width = DecimalType::GetWidth(type: source_type);
219 auto scale = DecimalType::GetScale(type: source_type);
220 DecimalCastInput input(result, width, scale);
221
222 UnaryExecutor::GenericExecute<SRC, string_t, StringCastFromDecimalOperator>(source, result, count, (void *)&input);
223 return true;
224}
225
226BoundCastInfo DefaultCasts::DecimalCastSwitch(BindCastInput &input, const LogicalType &source,
227 const LogicalType &target) {
228 // now switch on the result type
229 switch (target.id()) {
230 case LogicalTypeId::BOOLEAN:
231 return FromDecimalCast<bool>;
232 case LogicalTypeId::TINYINT:
233 return FromDecimalCast<int8_t>;
234 case LogicalTypeId::SMALLINT:
235 return FromDecimalCast<int16_t>;
236 case LogicalTypeId::INTEGER:
237 return FromDecimalCast<int32_t>;
238 case LogicalTypeId::BIGINT:
239 return FromDecimalCast<int64_t>;
240 case LogicalTypeId::UTINYINT:
241 return FromDecimalCast<uint8_t>;
242 case LogicalTypeId::USMALLINT:
243 return FromDecimalCast<uint16_t>;
244 case LogicalTypeId::UINTEGER:
245 return FromDecimalCast<uint32_t>;
246 case LogicalTypeId::UBIGINT:
247 return FromDecimalCast<uint64_t>;
248 case LogicalTypeId::HUGEINT:
249 return FromDecimalCast<hugeint_t>;
250 case LogicalTypeId::DECIMAL: {
251 // decimal to decimal cast
252 // first we need to figure out the source and target internal types
253 switch (source.InternalType()) {
254 case PhysicalType::INT16:
255 return DecimalDecimalCastSwitch<int16_t, NumericHelper>;
256 case PhysicalType::INT32:
257 return DecimalDecimalCastSwitch<int32_t, NumericHelper>;
258 case PhysicalType::INT64:
259 return DecimalDecimalCastSwitch<int64_t, NumericHelper>;
260 case PhysicalType::INT128:
261 return DecimalDecimalCastSwitch<hugeint_t, Hugeint>;
262 default:
263 throw NotImplementedException("Unimplemented internal type for decimal in decimal_decimal cast");
264 }
265 }
266 case LogicalTypeId::FLOAT:
267 return FromDecimalCast<float>;
268 case LogicalTypeId::DOUBLE:
269 return FromDecimalCast<double>;
270 case LogicalTypeId::VARCHAR: {
271 switch (source.InternalType()) {
272 case PhysicalType::INT16:
273 return DecimalToStringCast<int16_t>;
274 case PhysicalType::INT32:
275 return DecimalToStringCast<int32_t>;
276 case PhysicalType::INT64:
277 return DecimalToStringCast<int64_t>;
278 case PhysicalType::INT128:
279 return DecimalToStringCast<hugeint_t>;
280 default:
281 throw InternalException("Unimplemented internal decimal type");
282 }
283 }
284 default:
285 return DefaultCasts::TryVectorNullCast;
286 }
287}
288
289} // namespace duckdb
290