Skip to main content

lance_datafusion/
expr.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4//! Utilities for working with datafusion expressions
5
6use std::sync::Arc;
7
8use arrow::compute::cast;
9use arrow_array::{ArrayRef, cast::AsArray};
10use arrow_schema::{DataType, TimeUnit};
11use datafusion_common::ScalarValue;
12use half::f16;
13
14const MS_PER_DAY: i64 = 86400000;
15
16/// The exact tie between the largest finite `f16` and the value that would
17/// follow it. A tie goes to the even mantissa, which there is the one that does
18/// not exist, so this magnitude and anything above it rounds to an infinity.
19const F16_OVERFLOW_THRESHOLD: f64 = 65520.0;
20
21/// The finite `f16` nearest `value`, with a tie going to the even mantissa.
22///
23/// Neither of `half`'s conversions is correctly rounded, in two different ways:
24/// the software path truncates the low 32 bits of the `f64` mantissa before it
25/// rounds ([half-rs#151]), and the x86 hardware path rounds through `f32` first
26/// ([half-rs#116]). Both land within one step of the right answer, so this starts
27/// from the software path, which at least does not vary by target, and then looks
28/// at the two neighbours. Widening `f16` to `f64` is exact, so comparing the three
29/// distances in `f64` decides exactly.
30///
31/// The caller must have excluded the overflow range first. Truncation only ever
32/// shrinks the magnitude, so below the threshold the starting point is finite.
33///
34/// [half-rs#151]: https://github.com/VoidStarKat/half-rs/issues/151
35/// [half-rs#116]: https://github.com/VoidStarKat/half-rs/issues/116
36fn nearest_finite_f16(value: f64) -> f16 {
37    let start = f16::from_f64_const(value);
38    debug_assert!(
39        start.is_finite(),
40        "caller must reject the overflow range before calling: {value}"
41    );
42    let mut best = start;
43    let mut best_distance = (value - start.to_f64()).abs();
44    let start_bits = start.to_bits();
45    // Stepping the bit pattern by one walks to the adjacent magnitude on either
46    // side of `start`, for negatives as well. Stepping off an end lands on an
47    // infinity or a NaN, which is not a candidate.
48    for candidate in [start_bits.wrapping_sub(1), start_bits.wrapping_add(1)].map(f16::from_bits) {
49        if !candidate.is_finite() {
50            continue;
51        }
52        let distance = (value - candidate.to_f64()).abs();
53        let wins =
54            distance < best_distance || (distance == best_distance && candidate.to_bits() & 1 == 0);
55        if wins {
56            best = candidate;
57            best_distance = distance;
58        }
59    }
60    best
61}
62
63/// Coerce a float to `f16`, rejecting a finite value that leaves the `f16` range
64/// rather than saturating it to an infinity.
65///
66/// A value inside the range lands on the `f16` grid and is inexact in a way that
67/// shows: past 2048 the grid is coarser than the integers, so `= 2049` matches
68/// rows holding 2048. The `Float32` and `Float64` arms below are inexact too, on
69/// a finer grid, and that is not what the rejection is for.
70///
71/// The rejection is for the literal changing kind. Saturated to an infinity,
72/// `= 100000` matches rows that really hold infinity; collapsed to zero,
73/// `= 1e-30` matches real zeros. The cost is the queries where saturating would
74/// have been right: `< 100000` errors instead of returning every finite row.
75/// This function cannot see the operator, so it cannot allow saturation only
76/// where it is harmless, and an error the caller reports beats a wrong row set.
77/// An infinite or NaN input converts faithfully and is kept.
78///
79/// `Float64` to `Float32` still saturates. Reaching that takes a literal above
80/// 1e38, while `f16` overflows at 65520, which an ordinary literal passes.
81fn coerce_to_f16(value: f64) -> Option<f16> {
82    if value.is_nan() {
83        return Some(f16::NAN);
84    }
85    if value.is_infinite() {
86        return Some(if value.is_sign_positive() {
87            f16::INFINITY
88        } else {
89            f16::NEG_INFINITY
90        });
91    }
92    if value.abs() >= F16_OVERFLOW_THRESHOLD {
93        return None;
94    }
95    let nearest = nearest_finite_f16(value);
96    // `f16::ZERO == f16::NEG_ZERO`, and so does `-0.0 == 0.0`, so a signed zero
97    // literal keeps its sign here and only a genuinely nonzero value is rejected.
98    if nearest == f16::ZERO && value != 0.0 {
99        return None;
100    }
101    Some(nearest)
102}
103
104// This is slightly tedious but when we convert expressions from SQL strings to logical
105// datafusion expressions there is no type coercion that happens.  In other words "x = 7"
106// will always yield "x = 7_u64" regardless of the type of the column "x".  As a result, we
107// need to do that literal coercion ourselves.
108pub fn safe_coerce_scalar(value: &ScalarValue, ty: &DataType) -> Option<ScalarValue> {
109    // A dictionary target coerces the value to the dictionary's value type and
110    // re-wraps it as a dictionary literal. Only an untyped `ScalarValue::Null`
111    // keeps its untyped form, matching the behavior for all other targets; a
112    // *typed* null (e.g. `Utf8(None)`) is coerced and wrapped like any other
113    // value so it produces a `Dictionary(..)` literal that matches the column.
114    if let DataType::Dictionary(key_type, value_type) = ty {
115        if matches!(value, ScalarValue::Null) {
116            return Some(value.clone());
117        }
118        let inner = safe_coerce_scalar(value, value_type)?;
119        return Some(ScalarValue::Dictionary(key_type.clone(), Box::new(inner)));
120    }
121    match value {
122        ScalarValue::Int8(val) => match ty {
123            DataType::Int8 => Some(value.clone()),
124            DataType::Int16 => val.map(|v| ScalarValue::Int16(Some(i16::from(v)))),
125            DataType::Int32 => val.map(|v| ScalarValue::Int32(Some(i32::from(v)))),
126            DataType::Int64 => val.map(|v| ScalarValue::Int64(Some(i64::from(v)))),
127            DataType::UInt8 => {
128                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
129            }
130            DataType::UInt16 => {
131                val.and_then(|v| u16::try_from(v).map(|v| ScalarValue::UInt16(Some(v))).ok())
132            }
133            DataType::UInt32 => {
134                val.and_then(|v| u32::try_from(v).map(|v| ScalarValue::UInt32(Some(v))).ok())
135            }
136            DataType::UInt64 => {
137                val.and_then(|v| u64::try_from(v).map(|v| ScalarValue::UInt64(Some(v))).ok())
138            }
139            DataType::Float16 => {
140                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
141            }
142            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(f32::from(v)))),
143            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(f64::from(v)))),
144            _ => None,
145        },
146        ScalarValue::Int16(val) => match ty {
147            DataType::Int8 => {
148                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
149            }
150            DataType::Int16 => Some(value.clone()),
151            DataType::Int32 => val.map(|v| ScalarValue::Int32(Some(i32::from(v)))),
152            DataType::Int64 => val.map(|v| ScalarValue::Int64(Some(i64::from(v)))),
153            DataType::UInt8 => {
154                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
155            }
156            DataType::UInt16 => {
157                val.and_then(|v| u16::try_from(v).map(|v| ScalarValue::UInt16(Some(v))).ok())
158            }
159            DataType::UInt32 => {
160                val.and_then(|v| u32::try_from(v).map(|v| ScalarValue::UInt32(Some(v))).ok())
161            }
162            DataType::UInt64 => {
163                val.and_then(|v| u64::try_from(v).map(|v| ScalarValue::UInt64(Some(v))).ok())
164            }
165            DataType::Float16 => {
166                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
167            }
168            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(f32::from(v)))),
169            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(f64::from(v)))),
170            _ => None,
171        },
172        ScalarValue::Int32(val) => match ty {
173            DataType::Int8 => {
174                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
175            }
176            DataType::Int16 => {
177                val.and_then(|v| i16::try_from(v).map(|v| ScalarValue::Int16(Some(v))).ok())
178            }
179            DataType::Int32 => Some(value.clone()),
180            DataType::Int64 => val.map(|v| ScalarValue::Int64(Some(i64::from(v)))),
181            DataType::UInt8 => {
182                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
183            }
184            DataType::UInt16 => {
185                val.and_then(|v| u16::try_from(v).map(|v| ScalarValue::UInt16(Some(v))).ok())
186            }
187            DataType::UInt32 => {
188                val.and_then(|v| u32::try_from(v).map(|v| ScalarValue::UInt32(Some(v))).ok())
189            }
190            DataType::UInt64 => {
191                val.and_then(|v| u64::try_from(v).map(|v| ScalarValue::UInt64(Some(v))).ok())
192            }
193            // These conversions are inherently lossy as the full range of i32 cannot
194            // be represented in f32.  However, there is no f32::TryFrom(i32) and its not
195            // clear users would want that anyways
196            DataType::Float16 => {
197                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
198            }
199            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(v as f32))),
200            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(v as f64))),
201            _ => None,
202        },
203        ScalarValue::Int64(val) => match ty {
204            DataType::Int8 => {
205                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
206            }
207            DataType::Int16 => {
208                val.and_then(|v| i16::try_from(v).map(|v| ScalarValue::Int16(Some(v))).ok())
209            }
210            DataType::Int32 => {
211                val.and_then(|v| i32::try_from(v).map(|v| ScalarValue::Int32(Some(v))).ok())
212            }
213            DataType::Int64 => Some(value.clone()),
214            DataType::UInt8 => {
215                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
216            }
217            DataType::UInt16 => {
218                val.and_then(|v| u16::try_from(v).map(|v| ScalarValue::UInt16(Some(v))).ok())
219            }
220            DataType::UInt32 => {
221                val.and_then(|v| u32::try_from(v).map(|v| ScalarValue::UInt32(Some(v))).ok())
222            }
223            DataType::UInt64 => {
224                val.and_then(|v| u64::try_from(v).map(|v| ScalarValue::UInt64(Some(v))).ok())
225            }
226            // See above warning about lossy float conversion
227            DataType::Float16 => {
228                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
229            }
230            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(v as f32))),
231            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(v as f64))),
232            DataType::Decimal128(_, _) | DataType::Decimal256(_, _) => value.cast_to(ty).ok(),
233            DataType::Time32(TimeUnit::Second) => val.and_then(|v| {
234                i32::try_from(v)
235                    .ok()
236                    .map(|v| ScalarValue::Time32Second(Some(v)))
237            }),
238            DataType::Time32(TimeUnit::Millisecond) => val.and_then(|v| {
239                i32::try_from(v)
240                    .ok()
241                    .map(|v| ScalarValue::Time32Millisecond(Some(v)))
242            }),
243            _ => None,
244        },
245        ScalarValue::UInt8(val) => match ty {
246            DataType::Int8 => {
247                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
248            }
249            DataType::Int16 => val.map(|v| ScalarValue::Int16(Some(v.into()))),
250            DataType::Int32 => val.map(|v| ScalarValue::Int32(Some(v.into()))),
251            DataType::Int64 => val.map(|v| ScalarValue::Int64(Some(v.into()))),
252            DataType::UInt8 => Some(value.clone()),
253            DataType::UInt16 => val.map(|v| ScalarValue::UInt16(Some(u16::from(v)))),
254            DataType::UInt32 => val.map(|v| ScalarValue::UInt32(Some(u32::from(v)))),
255            DataType::UInt64 => val.map(|v| ScalarValue::UInt64(Some(u64::from(v)))),
256            DataType::Float16 => {
257                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
258            }
259            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(f32::from(v)))),
260            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(f64::from(v)))),
261            _ => None,
262        },
263        ScalarValue::UInt16(val) => match ty {
264            DataType::Int8 => {
265                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
266            }
267            DataType::Int16 => {
268                val.and_then(|v| i16::try_from(v).map(|v| ScalarValue::Int16(Some(v))).ok())
269            }
270            DataType::Int32 => val.map(|v| ScalarValue::Int32(Some(v.into()))),
271            DataType::Int64 => val.map(|v| ScalarValue::Int64(Some(v.into()))),
272            DataType::UInt8 => {
273                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
274            }
275            DataType::UInt16 => Some(value.clone()),
276            DataType::UInt32 => val.map(|v| ScalarValue::UInt32(Some(u32::from(v)))),
277            DataType::UInt64 => val.map(|v| ScalarValue::UInt64(Some(u64::from(v)))),
278            DataType::Float16 => {
279                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
280            }
281            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(f32::from(v)))),
282            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(f64::from(v)))),
283            _ => None,
284        },
285        ScalarValue::UInt32(val) => match ty {
286            DataType::Int8 => {
287                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
288            }
289            DataType::Int16 => {
290                val.and_then(|v| i16::try_from(v).map(|v| ScalarValue::Int16(Some(v))).ok())
291            }
292            DataType::Int32 => {
293                val.and_then(|v| i32::try_from(v).map(|v| ScalarValue::Int32(Some(v))).ok())
294            }
295            DataType::Int64 => val.map(|v| ScalarValue::Int64(Some(v.into()))),
296            DataType::UInt8 => {
297                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
298            }
299            DataType::UInt16 => {
300                val.and_then(|v| u16::try_from(v).map(|v| ScalarValue::UInt16(Some(v))).ok())
301            }
302            DataType::UInt32 => Some(value.clone()),
303            DataType::UInt64 => val.map(|v| ScalarValue::UInt64(Some(u64::from(v)))),
304            // See above warning about lossy float conversion
305            DataType::Float16 => {
306                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
307            }
308            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(v as f32))),
309            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(v as f64))),
310            _ => None,
311        },
312        ScalarValue::UInt64(val) => match ty {
313            DataType::Int8 => {
314                val.and_then(|v| i8::try_from(v).map(|v| ScalarValue::Int8(Some(v))).ok())
315            }
316            DataType::Int16 => {
317                val.and_then(|v| i16::try_from(v).map(|v| ScalarValue::Int16(Some(v))).ok())
318            }
319            DataType::Int32 => {
320                val.and_then(|v| i32::try_from(v).map(|v| ScalarValue::Int32(Some(v))).ok())
321            }
322            DataType::Int64 => {
323                val.and_then(|v| i64::try_from(v).map(|v| ScalarValue::Int64(Some(v))).ok())
324            }
325            DataType::UInt8 => {
326                val.and_then(|v| u8::try_from(v).map(|v| ScalarValue::UInt8(Some(v))).ok())
327            }
328            DataType::UInt16 => {
329                val.and_then(|v| u16::try_from(v).map(|v| ScalarValue::UInt16(Some(v))).ok())
330            }
331            DataType::UInt32 => {
332                val.and_then(|v| u32::try_from(v).map(|v| ScalarValue::UInt32(Some(v))).ok())
333            }
334            DataType::UInt64 => Some(value.clone()),
335            // See above warning about lossy float conversion
336            DataType::Float16 => {
337                val.and_then(|v| coerce_to_f16(v as f64).map(|v| ScalarValue::Float16(Some(v))))
338            }
339            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(v as f32))),
340            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(v as f64))),
341            _ => None,
342        },
343        ScalarValue::Float16(val) => match ty {
344            DataType::Float16 => Some(value.clone()),
345            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(v.to_f32()))),
346            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(v.to_f64()))),
347            _ => None,
348        },
349        ScalarValue::Float32(val) => match ty {
350            DataType::Float16 => {
351                val.and_then(|v| coerce_to_f16(f64::from(v)).map(|v| ScalarValue::Float16(Some(v))))
352            }
353            DataType::Float32 => Some(value.clone()),
354            DataType::Float64 => val.map(|v| ScalarValue::Float64(Some(f64::from(v)))),
355            _ => None,
356        },
357        ScalarValue::Float64(val) => match ty {
358            DataType::Float16 => {
359                val.and_then(|v| coerce_to_f16(v).map(|v| ScalarValue::Float16(Some(v))))
360            }
361            DataType::Float32 => val.map(|v| ScalarValue::Float32(Some(v as f32))),
362            DataType::Float64 => Some(value.clone()),
363            _ => None,
364        },
365        ScalarValue::Utf8(val) => match ty {
366            DataType::Utf8 => Some(value.clone()),
367            DataType::LargeUtf8 => Some(ScalarValue::LargeUtf8(val.clone())),
368            DataType::Utf8View => Some(ScalarValue::Utf8View(val.clone())),
369            _ => None,
370        },
371        ScalarValue::LargeUtf8(val) => match ty {
372            DataType::Utf8 => Some(ScalarValue::Utf8(val.clone())),
373            DataType::LargeUtf8 => Some(value.clone()),
374            DataType::Utf8View => Some(ScalarValue::Utf8View(val.clone())),
375            _ => None,
376        },
377        ScalarValue::Utf8View(val) => match ty {
378            DataType::Utf8 => Some(ScalarValue::Utf8(val.clone())),
379            DataType::LargeUtf8 => Some(ScalarValue::LargeUtf8(val.clone())),
380            DataType::Utf8View => Some(value.clone()),
381            _ => None,
382        },
383        ScalarValue::Boolean(_) => match ty {
384            DataType::Boolean => Some(value.clone()),
385            _ => None,
386        },
387        ScalarValue::Null => Some(value.clone()),
388        ScalarValue::List(values) => {
389            let values = values.clone() as ArrayRef;
390            let new_values = cast(&values, ty).ok()?;
391            match ty {
392                DataType::List(_) => {
393                    Some(ScalarValue::List(Arc::new(new_values.as_list().clone())))
394                }
395                DataType::LargeList(_) => Some(ScalarValue::LargeList(Arc::new(
396                    new_values.as_list().clone(),
397                ))),
398                DataType::FixedSizeList(_, _) => Some(ScalarValue::FixedSizeList(Arc::new(
399                    new_values.as_fixed_size_list().clone(),
400                ))),
401                _ => None,
402            }
403        }
404        ScalarValue::TimestampSecond(seconds, _) => match ty {
405            DataType::Timestamp(TimeUnit::Second, _) => Some(value.clone()),
406            DataType::Timestamp(TimeUnit::Millisecond, tz) => seconds
407                .and_then(|v| v.checked_mul(1000))
408                .map(|val| ScalarValue::TimestampMillisecond(Some(val), tz.clone())),
409            DataType::Timestamp(TimeUnit::Microsecond, tz) => seconds
410                .and_then(|v| v.checked_mul(1000000))
411                .map(|val| ScalarValue::TimestampMicrosecond(Some(val), tz.clone())),
412            DataType::Timestamp(TimeUnit::Nanosecond, tz) => seconds
413                .and_then(|v| v.checked_mul(1000000000))
414                .map(|val| ScalarValue::TimestampNanosecond(Some(val), tz.clone())),
415            _ => None,
416        },
417        ScalarValue::TimestampMillisecond(millis, _) => match ty {
418            DataType::Timestamp(TimeUnit::Second, tz) => {
419                millis.map(|val| ScalarValue::TimestampSecond(Some(val / 1000), tz.clone()))
420            }
421            DataType::Timestamp(TimeUnit::Millisecond, _) => Some(value.clone()),
422            DataType::Timestamp(TimeUnit::Microsecond, tz) => millis
423                .and_then(|v| v.checked_mul(1000))
424                .map(|val| ScalarValue::TimestampMicrosecond(Some(val), tz.clone())),
425            DataType::Timestamp(TimeUnit::Nanosecond, tz) => millis
426                .and_then(|v| v.checked_mul(1000000))
427                .map(|val| ScalarValue::TimestampNanosecond(Some(val), tz.clone())),
428            _ => None,
429        },
430        ScalarValue::TimestampMicrosecond(micros, _) => match ty {
431            DataType::Timestamp(TimeUnit::Second, tz) => {
432                micros.map(|val| ScalarValue::TimestampSecond(Some(val / 1000000), tz.clone()))
433            }
434            DataType::Timestamp(TimeUnit::Millisecond, tz) => {
435                micros.map(|val| ScalarValue::TimestampMillisecond(Some(val / 1000), tz.clone()))
436            }
437            DataType::Timestamp(TimeUnit::Microsecond, _) => Some(value.clone()),
438            DataType::Timestamp(TimeUnit::Nanosecond, tz) => micros
439                .and_then(|v| v.checked_mul(1000))
440                .map(|val| ScalarValue::TimestampNanosecond(Some(val), tz.clone())),
441            _ => None,
442        },
443        ScalarValue::TimestampNanosecond(nanos, _) => {
444            match ty {
445                DataType::Timestamp(TimeUnit::Second, tz) => nanos
446                    .map(|val| ScalarValue::TimestampSecond(Some(val / 1000000000), tz.clone())),
447                DataType::Timestamp(TimeUnit::Millisecond, tz) => nanos
448                    .map(|val| ScalarValue::TimestampMillisecond(Some(val / 1000000), tz.clone())),
449                DataType::Timestamp(TimeUnit::Microsecond, tz) => {
450                    nanos.map(|val| ScalarValue::TimestampMicrosecond(Some(val / 1000), tz.clone()))
451                }
452                DataType::Timestamp(TimeUnit::Nanosecond, _) => Some(value.clone()),
453                _ => None,
454            }
455        }
456        ScalarValue::Date32(ticks) => match ty {
457            DataType::Date32 => Some(value.clone()),
458            DataType::Date64 => Some(ScalarValue::Date64(
459                ticks.map(|v| i64::from(v) * MS_PER_DAY),
460            )),
461            _ => None,
462        },
463        ScalarValue::Date64(ticks) => match ty {
464            DataType::Date32 => Some(ScalarValue::Date32(ticks.map(|v| (v / MS_PER_DAY) as i32))),
465            DataType::Date64 => Some(value.clone()),
466            _ => None,
467        },
468        ScalarValue::Time32Second(seconds) => {
469            match ty {
470                DataType::Time32(TimeUnit::Second) => Some(value.clone()),
471                DataType::Time32(TimeUnit::Millisecond) => {
472                    seconds.map(|val| ScalarValue::Time32Millisecond(Some(val * 1000)))
473                }
474                DataType::Time64(TimeUnit::Microsecond) => seconds
475                    .map(|val| ScalarValue::Time64Microsecond(Some(i64::from(val) * 1000000))),
476                DataType::Time64(TimeUnit::Nanosecond) => seconds
477                    .map(|val| ScalarValue::Time64Nanosecond(Some(i64::from(val) * 1000000000))),
478                _ => None,
479            }
480        }
481        ScalarValue::Time32Millisecond(millis) => match ty {
482            DataType::Time32(TimeUnit::Second) => {
483                millis.map(|val| ScalarValue::Time32Second(Some(val / 1000)))
484            }
485            DataType::Time32(TimeUnit::Millisecond) => Some(value.clone()),
486            DataType::Time64(TimeUnit::Microsecond) => {
487                millis.map(|val| ScalarValue::Time64Microsecond(Some(i64::from(val) * 1000)))
488            }
489            DataType::Time64(TimeUnit::Nanosecond) => {
490                millis.map(|val| ScalarValue::Time64Nanosecond(Some(i64::from(val) * 1000000)))
491            }
492            _ => None,
493        },
494        ScalarValue::Time64Microsecond(micros) => match ty {
495            DataType::Time32(TimeUnit::Second) => {
496                micros.map(|val| ScalarValue::Time32Second(Some((val / 1000000) as i32)))
497            }
498            DataType::Time32(TimeUnit::Millisecond) => {
499                micros.map(|val| ScalarValue::Time32Millisecond(Some((val / 1000) as i32)))
500            }
501            DataType::Time64(TimeUnit::Microsecond) => Some(value.clone()),
502            DataType::Time64(TimeUnit::Nanosecond) => {
503                micros.map(|val| ScalarValue::Time64Nanosecond(Some(val * 1000)))
504            }
505            _ => None,
506        },
507        ScalarValue::Time64Nanosecond(nanos) => match ty {
508            DataType::Time32(TimeUnit::Second) => {
509                nanos.map(|val| ScalarValue::Time32Second(Some((val / 1000000000) as i32)))
510            }
511            DataType::Time32(TimeUnit::Millisecond) => {
512                nanos.map(|val| ScalarValue::Time32Millisecond(Some((val / 1000000) as i32)))
513            }
514            DataType::Time64(TimeUnit::Microsecond) => {
515                nanos.map(|val| ScalarValue::Time64Microsecond(Some(val / 1000)))
516            }
517            DataType::Time64(TimeUnit::Nanosecond) => Some(value.clone()),
518            _ => None,
519        },
520        ScalarValue::LargeList(values) => {
521            let values = values.clone() as ArrayRef;
522            let new_values = cast(&values, ty).ok()?;
523            match ty {
524                DataType::List(_) => {
525                    Some(ScalarValue::List(Arc::new(new_values.as_list().clone())))
526                }
527                DataType::LargeList(_) => Some(ScalarValue::LargeList(Arc::new(
528                    new_values.as_list().clone(),
529                ))),
530                DataType::FixedSizeList(_, _) => Some(ScalarValue::FixedSizeList(Arc::new(
531                    new_values.as_fixed_size_list().clone(),
532                ))),
533                _ => None,
534            }
535        }
536        ScalarValue::FixedSizeList(values) => {
537            let values = values.clone() as ArrayRef;
538            let new_values = cast(&values, ty).ok()?;
539            match ty {
540                DataType::List(_) => {
541                    Some(ScalarValue::List(Arc::new(new_values.as_list().clone())))
542                }
543                DataType::LargeList(_) => Some(ScalarValue::LargeList(Arc::new(
544                    new_values.as_list().clone(),
545                ))),
546                DataType::FixedSizeList(_, _) => Some(ScalarValue::FixedSizeList(Arc::new(
547                    new_values.as_fixed_size_list().clone(),
548                ))),
549                _ => None,
550            }
551        }
552        ScalarValue::FixedSizeBinary(len, value) => match ty {
553            DataType::FixedSizeBinary(len2) => {
554                if len == len2 {
555                    Some(ScalarValue::FixedSizeBinary(*len, value.clone()))
556                } else {
557                    None
558                }
559            }
560            DataType::Binary => Some(ScalarValue::Binary(value.clone())),
561            _ => None,
562        },
563        ScalarValue::Binary(value) => match ty {
564            DataType::Binary => Some(ScalarValue::Binary(value.clone())),
565            DataType::LargeBinary => Some(ScalarValue::LargeBinary(value.clone())),
566            DataType::BinaryView => Some(ScalarValue::BinaryView(value.clone())),
567            DataType::FixedSizeBinary(len) => {
568                if let Some(value) = value {
569                    if value.len() == *len as usize {
570                        Some(ScalarValue::FixedSizeBinary(*len, Some(value.clone())))
571                    } else {
572                        None
573                    }
574                } else {
575                    None
576                }
577            }
578            _ => None,
579        },
580        ScalarValue::BinaryView(val) => match ty {
581            DataType::Binary => Some(ScalarValue::Binary(val.clone())),
582            DataType::LargeBinary => Some(ScalarValue::LargeBinary(val.clone())),
583            DataType::BinaryView => Some(value.clone()),
584            _ => None,
585        },
586        ScalarValue::LargeBinary(_) => match ty {
587            DataType::LargeBinary => Some(value.clone()),
588            _ => None,
589        },
590        ScalarValue::Decimal128(_, _, _) => match ty {
591            DataType::Decimal128(_, _) => value.cast_to(ty).ok(),
592            _ => None,
593        },
594        ScalarValue::Decimal256(_, _, _) => match ty {
595            DataType::Decimal256(_, _) => value.cast_to(ty).ok(),
596            _ => None,
597        },
598        ScalarValue::DurationSecond(_)
599        | ScalarValue::DurationMillisecond(_)
600        | ScalarValue::DurationMicrosecond(_)
601        | ScalarValue::DurationNanosecond(_) => match ty {
602            DataType::Duration(_) => value.cast_to(ty).ok(),
603            _ => None,
604        },
605        // A dictionary-encoded literal (e.g. produced by DataFusion's dictionary
606        // cast in the scalar-index path) coerces by unwrapping its underlying value.
607        ScalarValue::Dictionary(_, inner) => safe_coerce_scalar(inner, ty),
608        _ => None,
609    }
610}
611
612#[cfg(test)]
613mod tests {
614    use arrow::datatypes::i256;
615
616    use super::*;
617
618    #[test]
619    fn test_temporal_coerce() {
620        assert_eq!(
621            safe_coerce_scalar(
622                &ScalarValue::Int64(Some(5)),
623                &DataType::Time32(TimeUnit::Second),
624            ),
625            Some(ScalarValue::Time32Second(Some(5)))
626        );
627        assert_eq!(
628            safe_coerce_scalar(
629                &ScalarValue::Int64(Some(5000)),
630                &DataType::Time32(TimeUnit::Millisecond),
631            ),
632            Some(ScalarValue::Time32Millisecond(Some(5000)))
633        );
634        assert_eq!(
635            safe_coerce_scalar(
636                &ScalarValue::Int64(Some(i64::MAX)),
637                &DataType::Time32(TimeUnit::Second),
638            ),
639            None
640        );
641
642        // Conversion from timestamps in one resolution to timestamps in another resolution is allowed
643        // s->s
644        assert_eq!(
645            safe_coerce_scalar(
646                &ScalarValue::TimestampSecond(Some(5), None),
647                &DataType::Timestamp(TimeUnit::Second, None),
648            ),
649            Some(ScalarValue::TimestampSecond(Some(5), None))
650        );
651        // s->ms
652        assert_eq!(
653            safe_coerce_scalar(
654                &ScalarValue::TimestampSecond(Some(5), None),
655                &DataType::Timestamp(TimeUnit::Millisecond, None),
656            ),
657            Some(ScalarValue::TimestampMillisecond(Some(5000), None))
658        );
659        // s->us
660        assert_eq!(
661            safe_coerce_scalar(
662                &ScalarValue::TimestampSecond(Some(5), None),
663                &DataType::Timestamp(TimeUnit::Microsecond, None),
664            ),
665            Some(ScalarValue::TimestampMicrosecond(Some(5000000), None))
666        );
667        // s->ns
668        assert_eq!(
669            safe_coerce_scalar(
670                &ScalarValue::TimestampSecond(Some(5), None),
671                &DataType::Timestamp(TimeUnit::Nanosecond, None),
672            ),
673            Some(ScalarValue::TimestampNanosecond(Some(5000000000), None))
674        );
675        // ms->s
676        assert_eq!(
677            safe_coerce_scalar(
678                &ScalarValue::TimestampMillisecond(Some(5000), None),
679                &DataType::Timestamp(TimeUnit::Second, None),
680            ),
681            Some(ScalarValue::TimestampSecond(Some(5), None))
682        );
683        // ms->ms
684        assert_eq!(
685            safe_coerce_scalar(
686                &ScalarValue::TimestampMillisecond(Some(5000), None),
687                &DataType::Timestamp(TimeUnit::Millisecond, None),
688            ),
689            Some(ScalarValue::TimestampMillisecond(Some(5000), None))
690        );
691        // ms->us
692        assert_eq!(
693            safe_coerce_scalar(
694                &ScalarValue::TimestampMillisecond(Some(5000), None),
695                &DataType::Timestamp(TimeUnit::Microsecond, None),
696            ),
697            Some(ScalarValue::TimestampMicrosecond(Some(5000000), None))
698        );
699        // ms->ns
700        assert_eq!(
701            safe_coerce_scalar(
702                &ScalarValue::TimestampMillisecond(Some(5000), None),
703                &DataType::Timestamp(TimeUnit::Nanosecond, None),
704            ),
705            Some(ScalarValue::TimestampNanosecond(Some(5000000000), None))
706        );
707        // us->s
708        assert_eq!(
709            safe_coerce_scalar(
710                &ScalarValue::TimestampMicrosecond(Some(5000000), None),
711                &DataType::Timestamp(TimeUnit::Second, None),
712            ),
713            Some(ScalarValue::TimestampSecond(Some(5), None))
714        );
715        // us->ms
716        assert_eq!(
717            safe_coerce_scalar(
718                &ScalarValue::TimestampMicrosecond(Some(5000000), None),
719                &DataType::Timestamp(TimeUnit::Millisecond, None),
720            ),
721            Some(ScalarValue::TimestampMillisecond(Some(5000), None))
722        );
723        // us->us
724        assert_eq!(
725            safe_coerce_scalar(
726                &ScalarValue::TimestampMicrosecond(Some(5000000), None),
727                &DataType::Timestamp(TimeUnit::Microsecond, None),
728            ),
729            Some(ScalarValue::TimestampMicrosecond(Some(5000000), None))
730        );
731        // us->ns
732        assert_eq!(
733            safe_coerce_scalar(
734                &ScalarValue::TimestampMicrosecond(Some(5000000), None),
735                &DataType::Timestamp(TimeUnit::Nanosecond, None),
736            ),
737            Some(ScalarValue::TimestampNanosecond(Some(5000000000), None))
738        );
739        // ns->s
740        assert_eq!(
741            safe_coerce_scalar(
742                &ScalarValue::TimestampNanosecond(Some(5000000000), None),
743                &DataType::Timestamp(TimeUnit::Second, None),
744            ),
745            Some(ScalarValue::TimestampSecond(Some(5), None))
746        );
747        // ns->ms
748        assert_eq!(
749            safe_coerce_scalar(
750                &ScalarValue::TimestampNanosecond(Some(5000000000), None),
751                &DataType::Timestamp(TimeUnit::Millisecond, None),
752            ),
753            Some(ScalarValue::TimestampMillisecond(Some(5000), None))
754        );
755        // ns->us
756        assert_eq!(
757            safe_coerce_scalar(
758                &ScalarValue::TimestampNanosecond(Some(5000000000), None),
759                &DataType::Timestamp(TimeUnit::Microsecond, None),
760            ),
761            Some(ScalarValue::TimestampMicrosecond(Some(5000000), None))
762        );
763        // ns->ns
764        assert_eq!(
765            safe_coerce_scalar(
766                &ScalarValue::TimestampNanosecond(Some(5000000000), None),
767                &DataType::Timestamp(TimeUnit::Nanosecond, None),
768            ),
769            Some(ScalarValue::TimestampNanosecond(Some(5000000000), None))
770        );
771        // Precision loss on coercion is allowed (truncation)
772        // ns->s
773        assert_eq!(
774            safe_coerce_scalar(
775                &ScalarValue::TimestampNanosecond(Some(5987654321), None),
776                &DataType::Timestamp(TimeUnit::Second, None),
777            ),
778            Some(ScalarValue::TimestampSecond(Some(5), None))
779        );
780        // Conversions from date-32 to date-64 is allowed
781        assert_eq!(
782            safe_coerce_scalar(&ScalarValue::Date32(Some(5)), &DataType::Date32,),
783            Some(ScalarValue::Date32(Some(5)))
784        );
785        assert_eq!(
786            safe_coerce_scalar(&ScalarValue::Date32(Some(5)), &DataType::Date64,),
787            Some(ScalarValue::Date64(Some(5 * MS_PER_DAY)))
788        );
789        assert_eq!(
790            safe_coerce_scalar(
791                &ScalarValue::Date64(Some(5 * MS_PER_DAY)),
792                &DataType::Date32,
793            ),
794            Some(ScalarValue::Date32(Some(5)))
795        );
796        assert_eq!(
797            safe_coerce_scalar(&ScalarValue::Date64(Some(5)), &DataType::Date64,),
798            Some(ScalarValue::Date64(Some(5)))
799        );
800        // Time-32 to time-64 (and within time-32 and time-64) is allowed
801        assert_eq!(
802            safe_coerce_scalar(
803                &ScalarValue::Time32Second(Some(5)),
804                &DataType::Time32(TimeUnit::Second),
805            ),
806            Some(ScalarValue::Time32Second(Some(5)))
807        );
808        assert_eq!(
809            safe_coerce_scalar(
810                &ScalarValue::Time32Second(Some(5)),
811                &DataType::Time32(TimeUnit::Millisecond),
812            ),
813            Some(ScalarValue::Time32Millisecond(Some(5000)))
814        );
815        assert_eq!(
816            safe_coerce_scalar(
817                &ScalarValue::Time32Second(Some(5)),
818                &DataType::Time64(TimeUnit::Microsecond),
819            ),
820            Some(ScalarValue::Time64Microsecond(Some(5000000)))
821        );
822        assert_eq!(
823            safe_coerce_scalar(
824                &ScalarValue::Time32Second(Some(5)),
825                &DataType::Time64(TimeUnit::Nanosecond),
826            ),
827            Some(ScalarValue::Time64Nanosecond(Some(5000000000)))
828        );
829        assert_eq!(
830            safe_coerce_scalar(
831                &ScalarValue::Time32Millisecond(Some(5000)),
832                &DataType::Time32(TimeUnit::Second),
833            ),
834            Some(ScalarValue::Time32Second(Some(5)))
835        );
836        assert_eq!(
837            safe_coerce_scalar(
838                &ScalarValue::Time32Millisecond(Some(5000)),
839                &DataType::Time32(TimeUnit::Millisecond),
840            ),
841            Some(ScalarValue::Time32Millisecond(Some(5000)))
842        );
843        assert_eq!(
844            safe_coerce_scalar(
845                &ScalarValue::Time32Millisecond(Some(5000)),
846                &DataType::Time64(TimeUnit::Microsecond),
847            ),
848            Some(ScalarValue::Time64Microsecond(Some(5000000)))
849        );
850        assert_eq!(
851            safe_coerce_scalar(
852                &ScalarValue::Time32Millisecond(Some(5000)),
853                &DataType::Time64(TimeUnit::Nanosecond),
854            ),
855            Some(ScalarValue::Time64Nanosecond(Some(5000000000)))
856        );
857        assert_eq!(
858            safe_coerce_scalar(
859                &ScalarValue::Time64Microsecond(Some(5000000)),
860                &DataType::Time32(TimeUnit::Second),
861            ),
862            Some(ScalarValue::Time32Second(Some(5)))
863        );
864        assert_eq!(
865            safe_coerce_scalar(
866                &ScalarValue::Time64Microsecond(Some(5000000)),
867                &DataType::Time32(TimeUnit::Millisecond),
868            ),
869            Some(ScalarValue::Time32Millisecond(Some(5000)))
870        );
871        assert_eq!(
872            safe_coerce_scalar(
873                &ScalarValue::Time64Microsecond(Some(5000000)),
874                &DataType::Time64(TimeUnit::Microsecond),
875            ),
876            Some(ScalarValue::Time64Microsecond(Some(5000000)))
877        );
878        assert_eq!(
879            safe_coerce_scalar(
880                &ScalarValue::Time64Microsecond(Some(5000000)),
881                &DataType::Time64(TimeUnit::Nanosecond),
882            ),
883            Some(ScalarValue::Time64Nanosecond(Some(5000000000)))
884        );
885        assert_eq!(
886            safe_coerce_scalar(
887                &ScalarValue::Time64Nanosecond(Some(5000000000)),
888                &DataType::Time32(TimeUnit::Second),
889            ),
890            Some(ScalarValue::Time32Second(Some(5)))
891        );
892        assert_eq!(
893            safe_coerce_scalar(
894                &ScalarValue::Time64Nanosecond(Some(5000000000)),
895                &DataType::Time32(TimeUnit::Millisecond),
896            ),
897            Some(ScalarValue::Time32Millisecond(Some(5000)))
898        );
899        assert_eq!(
900            safe_coerce_scalar(
901                &ScalarValue::Time64Nanosecond(Some(5000000000)),
902                &DataType::Time64(TimeUnit::Microsecond),
903            ),
904            Some(ScalarValue::Time64Microsecond(Some(5000000)))
905        );
906        assert_eq!(
907            safe_coerce_scalar(
908                &ScalarValue::Time64Nanosecond(Some(5000000000)),
909                &DataType::Time64(TimeUnit::Nanosecond),
910            ),
911            Some(ScalarValue::Time64Nanosecond(Some(5000000000)))
912        );
913        assert_eq!(
914            safe_coerce_scalar(
915                &ScalarValue::DurationNanosecond(Some(2_000_000)),
916                &DataType::Duration(TimeUnit::Millisecond),
917            ),
918            Some(ScalarValue::DurationMillisecond(Some(2)))
919        );
920    }
921
922    #[test]
923    fn test_string_view_coerce() {
924        // Utf8 <-> Utf8View
925        assert_eq!(
926            safe_coerce_scalar(&ScalarValue::Utf8(Some("hi".into())), &DataType::Utf8View),
927            Some(ScalarValue::Utf8View(Some("hi".into())))
928        );
929        assert_eq!(
930            safe_coerce_scalar(&ScalarValue::Utf8View(Some("hi".into())), &DataType::Utf8),
931            Some(ScalarValue::Utf8(Some("hi".into())))
932        );
933        assert_eq!(
934            safe_coerce_scalar(
935                &ScalarValue::Utf8View(Some("hi".into())),
936                &DataType::LargeUtf8
937            ),
938            Some(ScalarValue::LargeUtf8(Some("hi".into())))
939        );
940        assert_eq!(
941            safe_coerce_scalar(
942                &ScalarValue::LargeUtf8(Some("hi".into())),
943                &DataType::Utf8View
944            ),
945            Some(ScalarValue::Utf8View(Some("hi".into())))
946        );
947        // identity
948        assert_eq!(
949            safe_coerce_scalar(
950                &ScalarValue::Utf8View(Some("hi".into())),
951                &DataType::Utf8View
952            ),
953            Some(ScalarValue::Utf8View(Some("hi".into())))
954        );
955        // Binary <-> BinaryView
956        assert_eq!(
957            safe_coerce_scalar(
958                &ScalarValue::Binary(Some(vec![1, 2, 3])),
959                &DataType::BinaryView
960            ),
961            Some(ScalarValue::BinaryView(Some(vec![1, 2, 3])))
962        );
963        assert_eq!(
964            safe_coerce_scalar(
965                &ScalarValue::BinaryView(Some(vec![1, 2, 3])),
966                &DataType::Binary
967            ),
968            Some(ScalarValue::Binary(Some(vec![1, 2, 3])))
969        );
970        assert_eq!(
971            safe_coerce_scalar(
972                &ScalarValue::BinaryView(Some(vec![1, 2, 3])),
973                &DataType::BinaryView
974            ),
975            Some(ScalarValue::BinaryView(Some(vec![1, 2, 3])))
976        );
977        assert_eq!(
978            safe_coerce_scalar(
979                &ScalarValue::LargeBinary(Some(vec![1, 2, 3])),
980                &DataType::LargeBinary
981            ),
982            Some(ScalarValue::LargeBinary(Some(vec![1, 2, 3])))
983        );
984    }
985
986    /// Every numeric literal type reaches `Float16`. SQL only produces `Int64`
987    /// and `Float64`, but `safe_coerce_scalar` is public and the index layer
988    /// feeds it whatever the planner already coerced, including `Float16`
989    /// itself.
990    #[rstest::rstest]
991    #[case::int8(ScalarValue::Int8(Some(-2)))]
992    #[case::int16(ScalarValue::Int16(Some(-2)))]
993    #[case::int32(ScalarValue::Int32(Some(-2)))]
994    #[case::int64(ScalarValue::Int64(Some(-2)))]
995    #[case::float32(ScalarValue::Float32(Some(-2.0)))]
996    #[case::float64(ScalarValue::Float64(Some(-2.0)))]
997    #[case::float16(ScalarValue::Float16(Some(f16::from_f32(-2.0))))]
998    fn numeric_literals_coerce_to_f16(#[case] value: ScalarValue) {
999        assert_eq!(
1000            safe_coerce_scalar(&value, &DataType::Float16),
1001            Some(ScalarValue::Float16(Some(f16::from_f32(-2.0)))),
1002        );
1003    }
1004
1005    #[rstest::rstest]
1006    #[case::uint8(ScalarValue::UInt8(Some(2)))]
1007    #[case::uint16(ScalarValue::UInt16(Some(2)))]
1008    #[case::uint32(ScalarValue::UInt32(Some(2)))]
1009    #[case::uint64(ScalarValue::UInt64(Some(2)))]
1010    fn unsigned_literals_coerce_to_f16(#[case] value: ScalarValue) {
1011        assert_eq!(
1012            safe_coerce_scalar(&value, &DataType::Float16),
1013            Some(ScalarValue::Float16(Some(f16::from_f32(2.0)))),
1014        );
1015    }
1016
1017    /// A `Float16` literal also has to reach the wider float columns, so a
1018    /// predicate written against one column type still filters another.
1019    #[test]
1020    fn test_f16_literal_widens() {
1021        let half = ScalarValue::Float16(Some(f16::from_f32(0.5)));
1022        assert_eq!(
1023            safe_coerce_scalar(&half, &DataType::Float32),
1024            Some(ScalarValue::Float32(Some(0.5))),
1025        );
1026        assert_eq!(
1027            safe_coerce_scalar(&half, &DataType::Float64),
1028            Some(ScalarValue::Float64(Some(0.5))),
1029        );
1030        assert_eq!(safe_coerce_scalar(&half, &DataType::Int32), None);
1031    }
1032
1033    /// Rounding inside the `f16` range is accepted; leaving the range is not.
1034    /// Expected values are spelled as bits so a change in how the input is
1035    /// rounded fails here rather than being recomputed by the same library call
1036    /// the code under test uses.
1037    #[rstest::rstest]
1038    // 0.1 has no exact binary form, so it lands on the nearest f16, 0x2E66.
1039    #[case::rounds(0.1, Some(0x2E66))]
1040    // Past 2048 the f16 grid is coarser than the integers: 2049 is the exact
1041    // midpoint of 2048 and 2050, and the tie goes to the even mantissa. This is
1042    // the case the doc comment cites for `= 2049` matching rows holding 2048.
1043    #[case::odd_integer_ties_down(2049.0, Some(0x6800))]
1044    // Above that midpoint 2050 is the nearer neighbour, and the coercion picks it.
1045    // `half`'s own conversion returns 2048 here, which is the defect
1046    // `nearest_finite_f16` exists to correct.
1047    #[case::just_above_a_tie(2049.001, Some(0x6801))]
1048    #[case::largest_finite(65504.0, Some(0x7BFF))]
1049    // Rounds down to the largest finite f16 rather than overflowing.
1050    #[case::just_under_overflow(65519.0, Some(0x7BFF))]
1051    #[case::smallest_subnormal(6e-8, Some(0x0001))]
1052    // 65520 is the first value that rounds to infinity, not 65504.
1053    #[case::overflow_threshold(65520.0, None)]
1054    #[case::overflow(70000.0, None)]
1055    #[case::negative_overflow(-70000.0, None)]
1056    #[case::f32_max(f32::MAX as f64, None)]
1057    // Underflows to zero rather than silently matching real zeros.
1058    #[case::underflow(1e-30, None)]
1059    #[case::negative_underflow(-1e-30, None)]
1060    fn test_f16_range_edges(#[case] input: f64, #[case] expected: Option<u16>) {
1061        let coerced = safe_coerce_scalar(&ScalarValue::Float64(Some(input)), &DataType::Float16);
1062        match expected {
1063            Some(bits) => assert_eq!(
1064                coerced,
1065                Some(ScalarValue::Float16(Some(f16::from_bits(bits)))),
1066            ),
1067            None => assert_eq!(coerced, None),
1068        }
1069    }
1070
1071    /// A literal that is already infinite or NaN converts faithfully. Only a
1072    /// finite value that overflowed is rejected.
1073    #[test]
1074    fn test_f16_keeps_non_finite_literals() {
1075        for (input, expected) in [
1076            (f64::INFINITY, f16::INFINITY),
1077            (f64::NEG_INFINITY, f16::NEG_INFINITY),
1078        ] {
1079            assert_eq!(
1080                safe_coerce_scalar(&ScalarValue::Float64(Some(input)), &DataType::Float16),
1081                Some(ScalarValue::Float16(Some(expected))),
1082            );
1083        }
1084        let nan = safe_coerce_scalar(&ScalarValue::Float64(Some(f64::NAN)), &DataType::Float16);
1085        assert!(matches!(nan, Some(ScalarValue::Float16(Some(v))) if v.is_nan()));
1086    }
1087
1088    /// Both zeros survive as themselves. Signed-zero comparison semantics are a
1089    /// separate problem (#5868); coercion must at least not erase the sign.
1090    #[test]
1091    fn test_f16_keeps_zero_sign() {
1092        assert_eq!(
1093            safe_coerce_scalar(&ScalarValue::Float64(Some(-0.0)), &DataType::Float16),
1094            Some(ScalarValue::Float16(Some(f16::NEG_ZERO))),
1095        );
1096        assert_eq!(
1097            safe_coerce_scalar(&ScalarValue::Float64(Some(0.0)), &DataType::Float16),
1098            Some(ScalarValue::Float16(Some(f16::ZERO))),
1099        );
1100        assert_eq!(
1101            safe_coerce_scalar(&ScalarValue::Int64(Some(0)), &DataType::Float16),
1102            Some(ScalarValue::Float16(Some(f16::ZERO))),
1103        );
1104    }
1105
1106    /// Sweep every rounding decision the conversion can make instead of trusting
1107    /// the handful of points named above: for each adjacent pair of finite `f16`
1108    /// values, the exact midpoint and the two `f64` values either side of it. A
1109    /// misrounding anywhere in the range shows up here, which is how the
1110    /// `2049.001` case was found in the first place.
1111    ///
1112    /// The expectation is stated, not recomputed: below the midpoint the lower
1113    /// neighbour, above it the upper one, at it the even mantissa.
1114    #[test]
1115    fn test_f16_rounds_to_nearest_even_across_the_whole_range() {
1116        fn coerce(value: f64) -> Option<f16> {
1117            match safe_coerce_scalar(&ScalarValue::Float64(Some(value)), &DataType::Float16) {
1118                Some(ScalarValue::Float16(Some(v))) => Some(v),
1119                // Rejected, which the underflow side of the range expects.
1120                None => None,
1121                other => panic!("expected a Float16 literal for {value}, got {other:?}"),
1122            }
1123        }
1124        // A nonzero literal that lands on zero is rejected rather than coerced,
1125        // so the smallest pair expects `None` on its lower side.
1126        fn want(expected: f16, input: f64) -> Option<f16> {
1127            if expected == f16::ZERO && input != 0.0 {
1128                None
1129            } else {
1130                Some(expected)
1131            }
1132        }
1133
1134        // 0x7BFF is the largest finite f16, so pairing each bit pattern with the
1135        // next covers every adjacent finite pair on the positive side. Negatives
1136        // are covered by the symmetry check below.
1137        for lower_bits in 0..0x7BFFu16 {
1138            let lower = f16::from_bits(lower_bits);
1139            let upper = f16::from_bits(lower_bits + 1);
1140            // Both operands are f16 widened to f64, so the average is exact.
1141            let midpoint = (lower.to_f64() + upper.to_f64()) / 2.0;
1142            let below = f64::from_bits(midpoint.to_bits() - 1);
1143            let above = f64::from_bits(midpoint.to_bits() + 1);
1144
1145            assert_eq!(coerce(below), want(lower, below), "just below {midpoint}");
1146            assert_eq!(coerce(above), want(upper, above), "just above {midpoint}");
1147            let even = if lower.to_bits() & 1 == 0 {
1148                lower
1149            } else {
1150                upper
1151            };
1152            assert_eq!(coerce(midpoint), want(even, midpoint), "at {midpoint}");
1153        }
1154    }
1155
1156    /// Sign is not part of the rounding decision, so negating the input negates
1157    /// the result. This is what lets the sweep above cover only positives.
1158    #[rstest::rstest]
1159    #[case(0.1)]
1160    #[case(2049.001)]
1161    #[case(65504.0)]
1162    #[case(6e-8)]
1163    #[case(70000.0)]
1164    #[case(1e-30)]
1165    fn test_f16_coercion_is_sign_symmetric(#[case] magnitude: f64) {
1166        let positive =
1167            safe_coerce_scalar(&ScalarValue::Float64(Some(magnitude)), &DataType::Float16);
1168        let negative =
1169            safe_coerce_scalar(&ScalarValue::Float64(Some(-magnitude)), &DataType::Float16);
1170        let flipped = match positive {
1171            Some(ScalarValue::Float16(Some(v))) => Some(ScalarValue::Float16(Some(-v))),
1172            other => other,
1173        };
1174        assert_eq!(negative, flipped);
1175    }
1176
1177    #[test]
1178    fn test_decimal_coerce() {
1179        assert_eq!(
1180            safe_coerce_scalar(
1181                &ScalarValue::Decimal128(Some(2), 10, 0),
1182                &DataType::Decimal128(12, 2),
1183            ),
1184            Some(ScalarValue::Decimal128(Some(200), 12, 2))
1185        );
1186        assert_eq!(
1187            safe_coerce_scalar(
1188                &ScalarValue::Decimal256(Some(i256::from_i128(2)), 76, 0),
1189                &DataType::Decimal256(76, 2),
1190            ),
1191            Some(ScalarValue::Decimal256(Some(i256::from_i128(200)), 76, 2))
1192        );
1193    }
1194
1195    #[test]
1196    fn test_dictionary_coerce() {
1197        let dict_ty = DataType::Dictionary(Box::new(DataType::Int16), Box::new(DataType::Utf8));
1198
1199        // A string literal coerces to a dictionary target by wrapping the
1200        // coerced value in a dictionary scalar.
1201        assert_eq!(
1202            safe_coerce_scalar(&ScalarValue::Utf8(Some("com".to_string())), &dict_ty),
1203            Some(ScalarValue::Dictionary(
1204                Box::new(DataType::Int16),
1205                Box::new(ScalarValue::Utf8(Some("com".to_string()))),
1206            ))
1207        );
1208
1209        // The inner value is coerced through to the dictionary value type, so a
1210        // LargeUtf8 literal lands as a Utf8 value inside the dictionary.
1211        assert_eq!(
1212            safe_coerce_scalar(&ScalarValue::LargeUtf8(Some("com".to_string())), &dict_ty),
1213            Some(ScalarValue::Dictionary(
1214                Box::new(DataType::Int16),
1215                Box::new(ScalarValue::Utf8(Some("com".to_string()))),
1216            ))
1217        );
1218
1219        // A dictionary literal round-trips back to its value type.
1220        assert_eq!(
1221            safe_coerce_scalar(
1222                &ScalarValue::Dictionary(
1223                    Box::new(DataType::Int16),
1224                    Box::new(ScalarValue::Utf8(Some("com".to_string()))),
1225                ),
1226                &DataType::Utf8,
1227            ),
1228            Some(ScalarValue::Utf8(Some("com".to_string())))
1229        );
1230
1231        // A dictionary literal coerces to a dictionary target, adopting the
1232        // target's key type.
1233        assert_eq!(
1234            safe_coerce_scalar(
1235                &ScalarValue::Dictionary(
1236                    Box::new(DataType::Int32),
1237                    Box::new(ScalarValue::Utf8(Some("com".to_string()))),
1238                ),
1239                &dict_ty,
1240            ),
1241            Some(ScalarValue::Dictionary(
1242                Box::new(DataType::Int16),
1243                Box::new(ScalarValue::Utf8(Some("com".to_string()))),
1244            ))
1245        );
1246
1247        // An untyped null keeps its untyped form for a dictionary target, just
1248        // like for every other target type.
1249        assert_eq!(
1250            safe_coerce_scalar(&ScalarValue::Null, &dict_ty),
1251            Some(ScalarValue::Null)
1252        );
1253
1254        // A *typed* null (e.g. an API-built `Utf8(None)` literal, or an IN value
1255        // already typed as Utf8) is still wrapped in the dictionary type so it
1256        // matches the dictionary column. Returning a bare `Utf8(None)` here would
1257        // leave `resolve_value` with a literal whose type does not line up with
1258        // the column, breaking planning/evaluation the same way non-null strings
1259        // used to break.
1260        assert_eq!(
1261            safe_coerce_scalar(&ScalarValue::Utf8(None), &dict_ty),
1262            Some(ScalarValue::Dictionary(
1263                Box::new(DataType::Int16),
1264                Box::new(ScalarValue::Utf8(None)),
1265            ))
1266        );
1267
1268        // The inner null is coerced through to the dictionary value type as well,
1269        // so a LargeUtf8 typed null lands as a Utf8 null inside the dictionary.
1270        assert_eq!(
1271            safe_coerce_scalar(&ScalarValue::LargeUtf8(None), &dict_ty),
1272            Some(ScalarValue::Dictionary(
1273                Box::new(DataType::Int16),
1274                Box::new(ScalarValue::Utf8(None)),
1275            ))
1276        );
1277
1278        // A value that cannot be coerced to the dictionary value type fails.
1279        assert_eq!(
1280            safe_coerce_scalar(
1281                &ScalarValue::Utf8(Some("com".to_string())),
1282                &DataType::Dictionary(Box::new(DataType::Int16), Box::new(DataType::Int32)),
1283            ),
1284            None
1285        );
1286    }
1287}