Skip to content

Commit 80cbea7

Browse files
robert3005connortsui20
authored andcommitted
Support borrowed RowFn input decoding
Keep the owned decoder as the required extension point and default borrowed decoding to it. Native primitive inputs share their canonical buffers directly. Retain decoded storage after the input handle is dropped. Extracted from #9979. Signed-off-by: Robert Kruszewski <github@robertk.io> Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent bf27cc1 commit 80cbea7

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

‎vortex-array/src/scalar_fn/unstable/row/types/element/input.rs‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ pub unsafe trait InputElement: 'static {
4949
/// null rows to the row closure.
5050
const DENSE_SAFE: bool;
5151

52-
/// Whether [`decode`](Self::decode) is infallible for _legal_ input data.
52+
/// Whether [`decode`](Self::decode) and [`decode_ref`](Self::decode_ref) are infallible for
53+
/// _legal_ input data.
5354
///
5455
/// This excludes infrastructural failures such as IO or allocation.
5556
/// It is independent of
@@ -67,6 +68,18 @@ pub unsafe trait InputElement: 'static {
6768
/// and other invocation-invariant work into this method.
6869
fn decode(array: ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Self::Column>;
6970

71+
/// Decode a borrowed `array` into its column representation.
72+
///
73+
/// The default clones `array` for [`decode`](Self::decode). Override this when a common
74+
/// representation decodes without taking ownership of the array.
75+
///
76+
/// The result must preserve the valid-row values and row count of [`decode`](Self::decode),
77+
/// and must retain its storage after `array` is dropped. [`DENSE_SAFE`](Self::DENSE_SAFE) and
78+
/// [`DECODE_INFALLIBLE`](Self::DECODE_INFALLIBLE) apply to this method too.
79+
fn decode_ref(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Self::Column> {
80+
Self::decode(array.clone(), ctx)
81+
}
82+
7083
/// Decode one non-null batch-constant input.
7184
///
7285
/// `array` retains its logical batch length. Implementations can extract one value directly

‎vortex-array/src/scalar_fn/unstable/row/types/element/primitive.rs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::ArrayRef;
1414
use crate::ExecutionCtx;
1515
use crate::IntoArray;
1616
use crate::arrays::Constant;
17+
use crate::arrays::Primitive;
1718
use crate::arrays::PrimitiveArray;
1819
use crate::dtype::DType;
1920
use crate::dtype::NativePType;
@@ -45,9 +46,23 @@ unsafe impl<T: NativePType> InputElement for T {
4546
}
4647

4748
fn decode(array: ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Self::Column> {
49+
// A canonical input shares its buffer without executing the array.
50+
if let Some(primitive) = array.as_opt::<Primitive>() {
51+
return Ok(primitive.to_buffer::<T>());
52+
}
53+
4854
Ok(array.execute::<PrimitiveArray>(ctx)?.into_buffer::<T>())
4955
}
5056

57+
#[inline]
58+
fn decode_ref(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Self::Column> {
59+
if let Some(primitive) = array.as_opt::<Primitive>() {
60+
return Ok(primitive.to_buffer::<T>());
61+
}
62+
63+
Self::decode(array.clone(), ctx)
64+
}
65+
5166
fn decode_constant(array: ArrayRef, _ctx: &mut ExecutionCtx) -> VortexResult<Self::Constant> {
5267
let Some(constant) = array.as_opt::<Constant>() else {
5368
vortex_bail!(

‎vortex-array/src/scalar_fn/unstable/row/types/element/tuple/tests.rs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use std::sync::atomic::AtomicUsize;
55
use std::sync::atomic::Ordering;
66

7+
use rstest::rstest;
78
use vortex_buffer::Buffer;
89
use vortex_error::VortexResult;
910
use vortex_error::vortex_bail;
@@ -16,6 +17,7 @@ use crate::ExecutionCtx;
1617
use crate::IntoArray;
1718
use crate::VortexSessionExecute;
1819
use crate::array_session;
20+
use crate::arrays::BoolArray;
1921
use crate::arrays::Constant;
2022
use crate::arrays::ConstantArray;
2123
use crate::arrays::ExtensionArray;
@@ -105,6 +107,32 @@ i64_test_element!(
105107
);
106108
i64_test_element!(DenseFallible, false);
107109

110+
#[rstest]
111+
#[case::canonical(PrimitiveArray::from_iter([1_i64, 2, 3]).into_array(), vec![1, 2, 3])]
112+
#[case::encoded(ConstantArray::new(7_i64, 3).into_array(), vec![7, 7, 7])]
113+
fn test_borrowed_primitive_decode_retains_storage(
114+
#[case] array: ArrayRef,
115+
#[case] expected: Vec<i64>,
116+
) -> VortexResult<()> {
117+
let mut ctx = array_session().create_execution_ctx();
118+
let column = <i64 as InputElement>::decode_ref(&array, &mut ctx)?;
119+
drop(array);
120+
121+
assert_eq!(column.as_slice(), expected);
122+
Ok(())
123+
}
124+
125+
#[test]
126+
fn test_default_borrowed_decode_retains_storage() -> VortexResult<()> {
127+
let array = BoolArray::from_iter([true, false, true]).into_array();
128+
let mut ctx = array_session().create_execution_ctx();
129+
let column = <bool as InputElement>::decode_ref(&array, &mut ctx)?;
130+
drop(array);
131+
132+
assert_eq!(column.iter().collect::<Vec<_>>(), [true, false, true]);
133+
Ok(())
134+
}
135+
108136
#[test]
109137
fn test_view_lens_match_checks_each_view() {
110138
let first: &[i64] = &[1, 2];

0 commit comments

Comments
 (0)