@@ -477,6 +477,77 @@ macro_rules! require_child {
477477 } } ;
478478}
479479
480+ /// Like [`require_child!`], but for optional children. If the child is `None`, this is a no-op.
481+ /// If the child is `Some` but does not match `$M`, early-returns an [`ExecutionResult`] requesting
482+ /// execution of child `$idx`.
483+ ///
484+ /// Unlike `require_child!`, this is a statement macro (no value produced) and does not clone
485+ /// `$parent` — it is moved into the early-return path.
486+ ///
487+ /// ```ignore
488+ /// require_opt_child!(array, array.patches().map(|p| p.indices()), 1 => Primitive);
489+ /// ```
490+ #[ macro_export]
491+ macro_rules! require_opt_child {
492+ ( $parent: expr, $child_opt: expr, $idx: expr => $M: ty) => {
493+ if $child_opt. is_some_and( |child| !child. is:: <$M>( ) ) {
494+ return Ok ( $crate:: ExecutionResult :: execute_slot:: <$M>( $parent, $idx) ) ;
495+ }
496+ } ;
497+ }
498+
499+ /// Require that all children of a [`Patches`](crate::patches::Patches) (indices, values, and
500+ /// optionally chunk_offsets) are `Primitive`. If no patches are present, this is a no-op.
501+ ///
502+ /// Like [`require_opt_child!`], `$parent` is moved (not cloned) into the early-return path.
503+ ///
504+ /// ```ignore
505+ /// require_patches!(array, array.patches(), PATCH_INDICES_SLOT, PATCH_VALUES_SLOT, PATCH_CHUNK_OFFSETS_SLOT);
506+ /// ```
507+ #[ macro_export]
508+ macro_rules! require_patches {
509+ ( $parent: expr, $patches: expr, $indices_slot: expr, $values_slot: expr, $chunk_offsets_slot: expr) => {
510+ let __patches = $patches;
511+ $crate:: require_opt_child!(
512+ $parent,
513+ __patches. as_ref( ) . map( |p| p. indices( ) ) ,
514+ $indices_slot => $crate:: arrays:: Primitive
515+ ) ;
516+ let __patches = $patches;
517+ $crate:: require_opt_child!(
518+ $parent,
519+ __patches. as_ref( ) . map( |p| p. values( ) ) ,
520+ $values_slot => $crate:: arrays:: Primitive
521+ ) ;
522+ let __patches = $patches;
523+ $crate:: require_opt_child!(
524+ $parent,
525+ __patches. as_ref( ) . and_then( |p| p. chunk_offsets( ) . as_ref( ) ) ,
526+ $chunk_offsets_slot => $crate:: arrays:: Primitive
527+ ) ;
528+ } ;
529+ }
530+
531+ /// Require that a [`Validity::Array`](crate::validity::Validity::Array) child matches `$M`. If validity is not array-backed
532+ /// (e.g. `NonNullable` or `AllValid`), this is a no-op. If it is array-backed but does not
533+ /// match `$M`, early-returns an [`ExecutionResult`] requesting execution of the validity slot.
534+ ///
535+ /// Like [`require_opt_child!`], `$parent` is moved (not cloned) into the early-return path.
536+ ///
537+ /// ```ignore
538+ /// require_validity!(array, &array.validity, VALIDITY_SLOT => AnyCanonical);
539+ /// ```
540+ #[ macro_export]
541+ macro_rules! require_validity {
542+ ( $parent: expr, $validity: expr, $idx: expr => $M: ty) => {
543+ if let $crate:: validity:: Validity :: Array ( v) = $validity {
544+ if !v. is:: <$M>( ) {
545+ return Ok ( $crate:: ExecutionResult :: execute_slot:: <$M>( $parent, $idx) ) ;
546+ }
547+ }
548+ } ;
549+ }
550+
480551/// Extension trait for creating an execution context from a session.
481552pub trait VortexSessionExecute {
482553 /// Create a new execution context from this session.
0 commit comments