@@ -142,54 +142,74 @@ impl PreparedSetData {
142142 /// a null element gives `null` for a needle that matches no element.
143143 ///
144144 /// A constant needle is probed once, and gives a constant result.
145+ ///
146+ /// # Errors
147+ ///
148+ /// Fails when the needles do not have the dtype of the list's elements.
145149 pub fn contains (
146150 & self ,
147151 needles : & ArrayRef ,
148152 options : & ListContainsOptions ,
149153 ctx : & mut ExecutionCtx ,
150154 ) -> VortexResult < ArrayRef > {
151- let element_dtype = self . elements . dtype ( ) ;
152- if !element_dtype. eq_ignore_nullability ( needles. dtype ( ) ) {
153- vortex_bail ! (
154- "Element type {} of list does not match search value {}" ,
155- element_dtype,
156- needles. dtype( ) ,
157- ) ;
158- }
159-
160155 if let Some ( needle) = needles. as_constant ( ) {
161156 let result = self . contains_scalar ( & needle, options, ctx) ?;
162157 return Ok ( ConstantArray :: new ( result, needles. len ( ) ) . into_array ( ) ) ;
163158 }
164159
160+ self . check_needle_dtype ( needles. dtype ( ) ) ?;
165161 let ( bits, needle_validity) = self . set . probe . contains ( needles, ctx) ?;
166- self . finish ( bits, needle_validity, needles. dtype ( ) , options)
162+ self . result_from_bits ( bits, needle_validity, needles. dtype ( ) , options)
167163 }
168164
169165 /// Whether the constant `needle` is an element of the list, under `options`.
170166 ///
171- /// The answer is the one [`Self::contains`] gives for each row of a needle with this value. The
172- /// probe gets one row, not a row per needle.
173- fn contains_scalar (
167+ /// The answer is the one [`Self::contains`] gives for each row of a needle with this value, for
168+ /// example for the fill value of a sparse needle. The probe gets one row.
169+ ///
170+ /// # Errors
171+ ///
172+ /// Fails when the needle does not have the dtype of the list's elements.
173+ pub fn contains_scalar (
174174 & self ,
175175 needle : & Scalar ,
176176 options : & ListContainsOptions ,
177177 ctx : & mut ExecutionCtx ,
178178 ) -> VortexResult < Scalar > {
179+ self . check_needle_dtype ( needle. dtype ( ) ) ?;
179180 let needles = ConstantArray :: new ( needle. clone ( ) , 1 ) . into_array ( ) ;
180181 let ( bits, needle_validity) = self . set . probe . contains ( & needles, ctx) ?;
181- self . finish ( bits, needle_validity, needle. dtype ( ) , options) ?
182+ self . result_from_bits ( bits, needle_validity, needle. dtype ( ) , options) ?
182183 . execute_scalar ( 0 , ctx)
183184 }
184185
185- /// Makes the result from one membership bit per needle and the validity of the needles.
186- fn finish (
186+ /// Makes the result of [`Self::contains`] from one membership bit per needle.
187+ ///
188+ /// A kernel that finds the matches of its needles without this probe, for example from bounds
189+ /// of its own, uses this to apply the null semantics of [`Self::contains`]. A set bit in `bits`
190+ /// tells that the needle equals an element. `needle_validity` is the validity of the needles,
191+ /// and `needle_dtype` is their dtype. The bit of a null needle has no effect.
192+ ///
193+ /// # Errors
194+ ///
195+ /// Fails when `needle_dtype` is not the dtype of the list's elements, or when
196+ /// `needle_validity` does not have one row per bit.
197+ pub fn result_from_bits (
187198 & self ,
188199 bits : BitBuffer ,
189200 needle_validity : Validity ,
190201 needle_dtype : & DType ,
191202 options : & ListContainsOptions ,
192203 ) -> VortexResult < ArrayRef > {
204+ self . check_needle_dtype ( needle_dtype) ?;
205+ if let Some ( len) = needle_validity. maybe_len ( ) {
206+ vortex_ensure ! (
207+ len == bits. len( ) ,
208+ "Needle validity has {len} rows, but there are {} membership bits" ,
209+ bits. len( )
210+ ) ;
211+ }
212+
193213 let nullability = options. result_nullability ( & self . list_dtype ( ) , needle_dtype) ;
194214
195215 let validity = if self . set . is_empty && !options. sql_null_semantics {
@@ -203,6 +223,19 @@ impl PreparedSetData {
203223
204224 Ok ( BoolArray :: new ( bits, validity. union_nullability ( nullability) ) . into_array ( ) )
205225 }
226+
227+ /// Fails when needles of `needle_dtype` cannot be elements of the list.
228+ fn check_needle_dtype ( & self , needle_dtype : & DType ) -> VortexResult < ( ) > {
229+ let element_dtype = self . elements . dtype ( ) ;
230+ if !element_dtype. eq_ignore_nullability ( needle_dtype) {
231+ vortex_bail ! (
232+ "Element type {} of list does not match search value {}" ,
233+ element_dtype,
234+ needle_dtype,
235+ ) ;
236+ }
237+ Ok ( ( ) )
238+ }
206239}
207240
208241impl Debug for PreparedSetData {
@@ -236,7 +269,17 @@ impl ArrayEq for PreparedSetData {
236269impl Array < PreparedSet > {
237270 /// Prepares `elements`, the elements of a non-null list with the list nullability
238271 /// `nullability`, as a set repeated `len` times.
239- pub ( crate ) fn try_new (
272+ ///
273+ /// [`ListContains`] prepares a constant list itself. A kernel crate can use this to test its
274+ /// [`ListContainsElementKernel`] against a prepared set.
275+ ///
276+ /// [`ListContains`]: crate::scalar_fn::fns::list_contains::ListContains
277+ /// [`ListContainsElementKernel`]: crate::scalar_fn::fns::list_contains::ListContainsElementKernel
278+ ///
279+ /// # Errors
280+ ///
281+ /// Fails when the elements cannot be executed into the probe.
282+ pub fn try_new (
240283 elements : ArrayRef ,
241284 nullability : Nullability ,
242285 len : usize ,
0 commit comments