From ebb60dedb69b613772f3888e43b8a5bbc7154056 Mon Sep 17 00:00:00 2001 From: Andrew Duffy Date: Tue, 17 Feb 2026 10:14:50 -0500 Subject: [PATCH 1/3] fix is_sorted check failing for large multiplier values Signed-off-by: Andrew Duffy --- encodings/sequence/src/array.rs | 34 ++++++++-- .../src/scalar/typed_view/primitive/pvalue.rs | 62 +++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/encodings/sequence/src/array.rs b/encodings/sequence/src/array.rs index e1a2d6d9039..9c53212d324 100644 --- a/encodings/sequence/src/array.rs +++ b/encodings/sequence/src/array.rs @@ -17,6 +17,7 @@ use vortex_array::arrays::PrimitiveArray; use vortex_array::buffer::BufferHandle; use vortex_array::expr::stats::Precision as StatPrecision; use vortex_array::expr::stats::Stat; +use vortex_array::match_each_pvalue; use vortex_array::scalar::PValue; use vortex_array::scalar::Scalar; use vortex_array::scalar::ScalarValue; @@ -133,11 +134,13 @@ impl SequenceArray { // A sequence A[i] = base + i * multiplier is sorted iff multiplier >= 0, // and strictly sorted iff multiplier > 0. - let m_int = multiplier - .cast::() - .vortex_expect("must be able to cast"); - let is_sorted = m_int >= 0; - let is_strict_sorted = m_int > 0; + + let (is_sorted, is_strict_sorted) = match_each_pvalue!( + multiplier, + uint: |v| { (true, v> 0) }, + int: |v| { (v >= 0, v > 0) }, + float: |v| { (!v.is_sign_negative(), v.is_sign_positive()) } + ); // SAFETY: we don't have duplicate stats let stats_set = unsafe { @@ -521,4 +524,25 @@ mod tests { assert_eq!(is_strict_sorted, Some(StatPrecision::Exact(false))); Ok(()) } + + // This is regression test for an issue caught by the fuzzer, where SequenceArrays with + // multiplier > i64::MAX were unable to be constructed. + #[test] + fn test_large_multiplier_sorted() -> VortexResult<()> { + let large_multiplier = (i64::MAX as u64) + 1; + let arr = SequenceArray::typed_new(0, large_multiplier, Nullability::NonNullable, 2)?; + + let is_sorted = arr + .statistics() + .with_typed_stats_set(|s| s.get_as::(Stat::IsSorted)); + + let is_strict_sorted = arr + .statistics() + .with_typed_stats_set(|s| s.get_as::(Stat::IsStrictSorted)); + + assert_eq!(is_sorted, Some(StatPrecision::Exact(true))); + assert_eq!(is_strict_sorted, Some(StatPrecision::Exact(true))); + + Ok(()) + } } diff --git a/vortex-array/src/scalar/typed_view/primitive/pvalue.rs b/vortex-array/src/scalar/typed_view/primitive/pvalue.rs index 6adfd5e0be7..5997f767a66 100644 --- a/vortex-array/src/scalar/typed_view/primitive/pvalue.rs +++ b/vortex-array/src/scalar/typed_view/primitive/pvalue.rs @@ -23,6 +23,68 @@ use vortex_error::vortex_bail; use vortex_error::vortex_ensure; use vortex_error::vortex_err; +/// Utility macro that makes it easy to write expressions generic over the different `PValue` +/// variants. +#[macro_export] +macro_rules! match_each_pvalue { + ( + $this:expr,uint: | + $vuint:ident | + $buint:block,int: | + $vint:ident | + $bint:block,float: | + $vfloat:ident | + $bfloat:block + ) => {{ + match $this { + PValue::U8(x) => { + let $vuint = x; + $buint + } + PValue::U16(x) => { + let $vuint = x; + $buint + } + PValue::U32(x) => { + let $vuint = x; + $buint + } + PValue::U64(x) => { + let $vuint = x; + $buint + } + PValue::I8(x) => { + let $vint = x; + $bint + } + PValue::I16(x) => { + let $vint = x; + $bint + } + PValue::I32(x) => { + let $vint = x; + $bint + } + PValue::I64(x) => { + let $vint = x; + $bint + } + PValue::F16(x) => { + let $vfloat = x; + $bfloat + } + PValue::F32(x) => { + let $vfloat = x; + $bfloat + } + PValue::F64(x) => { + let $vfloat = x; + $bfloat + } + } + }}; +} + /// A primitive value that can represent any primitive type supported by Vortex. /// /// `PValue` is used to store primitive scalar values in a type-erased manner, From d2d639b34f8a1a35a5607d1fe930dec6167be1e1 Mon Sep 17 00:00:00 2001 From: Andrew Duffy Date: Tue, 17 Feb 2026 10:54:29 -0500 Subject: [PATCH 2/3] public-api Signed-off-by: Andrew Duffy --- vortex-array/public-api.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vortex-array/public-api.lock b/vortex-array/public-api.lock index 647aad00cbe..a3d4fb7759a 100644 --- a/vortex-array/public-api.lock +++ b/vortex-array/public-api.lock @@ -14578,6 +14578,8 @@ pub fn vortex_array::vtable::validity_to_child(validity: &vortex_array::validity pub type vortex_array::vtable::ArrayId = arcref::ArcRef +pub macro vortex_array::match_each_pvalue! + pub macro vortex_array::register_kernel! pub macro vortex_array::vtable! From ac5d5d6199921c53039a00e47823ba62ec651597 Mon Sep 17 00:00:00 2001 From: Andrew Duffy Date: Tue, 17 Feb 2026 11:28:56 -0500 Subject: [PATCH 3/3] explicit "unreachable" for floats Signed-off-by: Andrew Duffy --- encodings/sequence/src/array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encodings/sequence/src/array.rs b/encodings/sequence/src/array.rs index 9c53212d324..437fddab829 100644 --- a/encodings/sequence/src/array.rs +++ b/encodings/sequence/src/array.rs @@ -139,7 +139,7 @@ impl SequenceArray { multiplier, uint: |v| { (true, v> 0) }, int: |v| { (v >= 0, v > 0) }, - float: |v| { (!v.is_sign_negative(), v.is_sign_positive()) } + float: |_v| { unreachable!("float multiplier not supported") } ); // SAFETY: we don't have duplicate stats