Skip to content

Commit c0cf95a

Browse files
committed
ALPRD: assert patch dtype instead of casting; drop execution context
ALPRD patch values are always stored as the non-nullable left-parts dtype, so the runtime cast in `canonicalize_patches` (which required an `ExecutionCtx` to materialise a lazy cast and an `all_valid` check) was unnecessary. - Replace `ALPRDData::canonicalize_patches(left_parts, patches, ctx)` with a ctx-free `validate_patches(left_parts, patches)` that asserts the patch values already have the non-nullable left-parts dtype (a non-nullable dtype also guarantees the values contain no nulls, so no `all_valid` execution is needed). Resolves the `TODO(ngates)/TODO(joe): assert the DType, don't cast`. - `validate_parts` likewise asserts the exact non-nullable patch dtype instead of `eq_ignore_nullability` + an `all_valid(LEGACY_SESSION)` execution. - `deserialize` no longer mints a `LEGACY_SESSION` ctx for patch handling. - `take`: `Patches::take` ignores null indices but reports a nullable dtype; drop the spurious nullability with a lazy `cast_values` (no execution at construction) so the patch values satisfy the non-nullable invariant. Net: removes 3 `LEGACY_SESSION` uses from ALPRD construction/validation. ALP already asserts its patch dtype (its patch values are the original floats), so no change was needed there. Signed-off-by: Robert <robert@spiraldb.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mkrgj5SuJpaKBXK9jydtSc
1 parent 7635ae6 commit c0cf95a

2 files changed

Lines changed: 29 additions & 43 deletions

File tree

encodings/alp/src/alp_rd/array.rs

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@ use vortex_array::ArrayParts;
1717
use vortex_array::ArrayRef;
1818
use vortex_array::ArraySlots;
1919
use vortex_array::ArrayView;
20-
use vortex_array::Canonical;
2120
use vortex_array::EqMode;
2221
use vortex_array::ExecutionCtx;
2322
use vortex_array::ExecutionResult;
2423
use vortex_array::IntoArray;
25-
use vortex_array::LEGACY_SESSION;
2624
use vortex_array::TypedArrayRef;
27-
use vortex_array::VortexSessionExecute;
2825
use vortex_array::arrays::Primitive;
2926
use vortex_array::arrays::PrimitiveArray;
3027
use vortex_array::buffer::BufferHandle;
@@ -210,13 +207,7 @@ impl VTable for ALPRD {
210207
)
211208
})
212209
.transpose()?;
213-
// NOTE: `VTable::deserialize` has a fixed trait signature without `ExecutionCtx`, so we
214-
// cannot plumb a ctx in here. We construct a legacy ctx locally at this trait boundary.
215-
let left_parts_patches = ALPRDData::canonicalize_patches(
216-
&left_parts,
217-
left_parts_patches,
218-
&mut LEGACY_SESSION.create_execution_ctx(),
219-
)?;
210+
let left_parts_patches = ALPRDData::validate_patches(&left_parts, left_parts_patches)?;
220211
let slots = ALPRDData::make_slots(&left_parts, &right_parts, left_parts_patches.as_ref());
221212
let data = ALPRDData::new(
222213
left_parts_dictionary,
@@ -372,8 +363,7 @@ impl ALPRD {
372363
ctx: &mut ExecutionCtx,
373364
) -> VortexResult<ALPRDArray> {
374365
let len = left_parts.len();
375-
let left_parts_patches =
376-
ALPRDData::canonicalize_patches(&left_parts, left_parts_patches, ctx)?;
366+
let left_parts_patches = ALPRDData::validate_patches(&left_parts, left_parts_patches)?;
377367
let slots = ALPRDData::make_slots(&left_parts, &right_parts, left_parts_patches.as_ref());
378368
let data = ALPRDData::new(left_parts_dictionary, right_bit_width, left_parts_patches);
379369
Array::try_from_parts(
@@ -404,26 +394,27 @@ impl ALPRD {
404394
}
405395

406396
impl ALPRDData {
407-
fn canonicalize_patches(
397+
/// Validate that `left_parts_patches` are well-formed for an `ALPRDArray` without performing
398+
/// any execution.
399+
///
400+
/// Patch values must already be stored as the non-nullable `left_parts` dtype. Requiring the
401+
/// non-nullable dtype also guarantees the patch values contain no nulls, so no execution is
402+
/// needed to verify validity. Callers are responsible for constructing patches with the correct
403+
/// value dtype (the encoder, `deserialize`, and the compute kernels all already do so).
404+
fn validate_patches(
408405
left_parts: &ArrayRef,
409406
left_parts_patches: Option<Patches>,
410-
ctx: &mut ExecutionCtx,
411407
) -> VortexResult<Option<Patches>> {
412-
left_parts_patches
413-
.map(|patches| {
414-
if !patches.values().all_valid(ctx)? {
415-
vortex_bail!("patches must be all valid: {}", patches.values());
416-
}
417-
// TODO(ngates): assert the DType, don't cast it.
418-
// TODO(joe): assert the DType, don't cast it in the next PR.
419-
let mut patches = patches.cast_values(&left_parts.dtype().as_nonnullable())?;
420-
// Force execution of the lazy cast so patch values are materialized
421-
// before serialization.
422-
let canonical = patches.values().clone().execute::<Canonical>(ctx)?;
423-
*patches.values_mut() = canonical.into_array();
424-
Ok(patches)
425-
})
426-
.transpose()
408+
if let Some(patches) = &left_parts_patches {
409+
let expected = left_parts.dtype().as_nonnullable();
410+
vortex_ensure!(
411+
patches.values().dtype() == &expected,
412+
"ALPRD patch values must have dtype {expected} (the non-nullable left-parts dtype), \
413+
got {}",
414+
patches.values().dtype(),
415+
);
416+
}
417+
Ok(left_parts_patches)
427418
}
428419

429420
/// Build a new `ALPRDArray` from components.
@@ -552,18 +543,12 @@ fn validate_parts(
552543
"patches array_len {} != outer len {len}",
553544
patches.array_len(),
554545
);
546+
let expected_patches_dtype = left_parts.dtype().as_nonnullable();
555547
vortex_ensure!(
556-
patches.dtype().eq_ignore_nullability(left_parts.dtype()),
557-
"patches dtype {} does not match left_parts dtype {}",
548+
patches.dtype() == &expected_patches_dtype,
549+
"patches dtype {} does not match expected non-nullable left_parts dtype {}",
558550
patches.dtype(),
559-
left_parts.dtype(),
560-
);
561-
vortex_ensure!(
562-
patches
563-
.values()
564-
.all_valid(&mut LEGACY_SESSION.create_execution_ctx())?,
565-
"patches must be all valid: {}",
566-
patches.values()
551+
expected_patches_dtype,
567552
);
568553
}
569554

encodings/alp/src/alp_rd/compute/take.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ impl TakeExecute for ALPRD {
2020
ctx: &mut ExecutionCtx,
2121
) -> VortexResult<Option<ArrayRef>> {
2222
let taken_left_parts = array.left_parts().take(indices.clone())?;
23+
// `Patches::take` ignores null take-indices, so the resulting patch values are all valid,
24+
// but it reports a nullable dtype. Patch values must be stored as the non-nullable
25+
// left-parts dtype (the invariant `ALPRD::try_new` asserts), so drop the spurious
26+
// nullability with a lazy cast — no execution happens here, it is materialized later.
2327
let left_parts_exceptions = array
2428
.left_parts_patches()
2529
.map(|patches| patches.take(indices, ctx))
2630
.transpose()?
2731
.flatten()
2832
.map(|p| {
29-
let values_dtype = p
30-
.values()
31-
.dtype()
32-
.with_nullability(taken_left_parts.dtype().nullability());
33+
let values_dtype = p.values().dtype().as_nonnullable();
3334
p.cast_values(&values_dtype)
3435
})
3536
.transpose()?;

0 commit comments

Comments
 (0)