@@ -234,3 +234,144 @@ fn schema_contains_column(
234234 . iter ( )
235235 . any ( |candidate| arena. same_column ( * candidate, * column) )
236236}
237+
238+ // GRCOV_EXCL_START
239+ #[ cfg( all( test, not( target_arch = "wasm32" ) ) ) ]
240+ mod tests {
241+ use super :: * ;
242+ use crate :: catalog:: { ColumnCatalog , ColumnDesc } ;
243+ use crate :: optimizer:: core:: rule:: NormalizationRule ;
244+ use crate :: planner:: operator:: filter:: FilterOperator ;
245+ use crate :: planner:: { PlanArena , TableArenaCell } ;
246+ use crate :: types:: LogicalType ;
247+
248+ fn column ( arena : & mut PlanArena , name : & str ) -> ColumnRef {
249+ arena. alloc_column ( ColumnCatalog :: new (
250+ name. to_string ( ) ,
251+ true ,
252+ ColumnDesc :: new ( LogicalType :: Integer , None , false , None ) . unwrap ( ) ,
253+ ) )
254+ }
255+
256+ fn eq ( left : ScalarExpression , right : ScalarExpression ) -> ScalarExpression {
257+ ScalarExpression :: Binary {
258+ op : BinaryOperator :: Eq ,
259+ left_expr : Box :: new ( left) ,
260+ right_expr : Box :: new ( right) ,
261+ evaluator : None ,
262+ ty : LogicalType :: Boolean ,
263+ }
264+ }
265+
266+ #[ test]
267+ fn probe_detection_covers_quantifiers_and_rejected_sides ( ) -> Result < ( ) , DatabaseError > {
268+ let table_arena = TableArenaCell :: default ( ) ;
269+ let mut arena = PlanArena :: new ( & table_arena) ;
270+ let left = column ( & mut arena, "left" ) ;
271+ let right = column ( & mut arena, "right" ) ;
272+ let outside = column ( & mut arena, "outside" ) ;
273+ let left_schema = vec ! [ left] ;
274+ let right_schema = vec ! [ right] ;
275+ let overlapping_schema = vec ! [ left, right] ;
276+
277+ assert ! ( find_parameterized_probe(
278+ MarkApplyKind :: Quantified ( MarkApplyQuantifier :: Any ) ,
279+ & [ ] ,
280+ & left_schema,
281+ & right_schema,
282+ & arena,
283+ ) ?
284+ . is_none( ) ) ;
285+ assert ! ( find_parameterized_probe(
286+ MarkApplyKind :: Quantified ( MarkApplyQuantifier :: All ) ,
287+ & [ eq(
288+ ScalarExpression :: column_expr( right, 0 ) ,
289+ ScalarExpression :: column_expr( left, 0 ) ,
290+ ) ] ,
291+ & left_schema,
292+ & right_schema,
293+ & arena,
294+ ) ?
295+ . is_none( ) ) ;
296+
297+ let predicates = vec ! [
298+ ScalarExpression :: from( true ) ,
299+ eq(
300+ ScalarExpression :: column_expr( right, 0 ) ,
301+ ScalarExpression :: column_expr( left, 0 ) ,
302+ ) ,
303+ ] ;
304+ let probe = find_parameterized_probe (
305+ MarkApplyKind :: Exists ,
306+ & predicates,
307+ & left_schema,
308+ & right_schema,
309+ & arena,
310+ ) ?
311+ . expect ( "right = left should be parameterizable" ) ;
312+ assert_eq ! ( probe. 0 , right) ;
313+
314+ assert ! ( extract_parameterized_probe(
315+ & eq(
316+ ScalarExpression :: column_expr( right, 0 ) ,
317+ ScalarExpression :: column_expr( outside, 0 ) ,
318+ ) ,
319+ & left_schema,
320+ & right_schema,
321+ & arena,
322+ ) ?
323+ . is_none( ) ) ;
324+ assert ! ( extract_parameterized_probe(
325+ & eq(
326+ ScalarExpression :: column_expr( right, 0 ) ,
327+ ScalarExpression :: column_expr( right, 0 ) ,
328+ ) ,
329+ & overlapping_schema,
330+ & right_schema,
331+ & arena,
332+ ) ?
333+ . is_none( ) ) ;
334+ assert ! ( extract_parameterized_probe(
335+ & ScalarExpression :: from( false ) ,
336+ & left_schema,
337+ & right_schema,
338+ & arena,
339+ ) ?
340+ . is_none( ) ) ;
341+
342+ Ok ( ( ) )
343+ }
344+
345+ #[ test]
346+ fn parameterization_rejects_unsupported_operator_and_child_shapes ( ) -> Result < ( ) , DatabaseError >
347+ {
348+ let table_arena = TableArenaCell :: default ( ) ;
349+ let mut arena = PlanArena :: new ( & table_arena) ;
350+ let column = column ( & mut arena, "value" ) ;
351+ let mut plan = LogicalPlan :: new ( Operator :: Dummy , Childrens :: None ) ;
352+
353+ assert ! ( !ParameterizeMarkApply . apply( & mut plan, & mut arena) ?) ;
354+ assert ! ( !parameterize_right_subtree( & mut plan, & column, & arena) ) ;
355+
356+ let mut filter = LogicalPlan :: new (
357+ Operator :: Filter ( FilterOperator {
358+ predicate : ScalarExpression :: from ( true ) ,
359+ is_optimized : false ,
360+ having : false ,
361+ } ) ,
362+ Childrens :: None ,
363+ ) ;
364+ assert ! ( !parameterize_right_subtree( & mut filter, & column, & arena) ) ;
365+
366+ assert_eq ! (
367+ index_priority( IndexType :: PrimaryKey { is_multiple: false } ) ,
368+ 0
369+ ) ;
370+ assert_eq ! ( index_priority( IndexType :: Unique ) , 1 ) ;
371+ assert_eq ! ( index_priority( IndexType :: Composite ) , 2 ) ;
372+ assert_eq ! ( index_priority( IndexType :: Normal ) , 3 ) ;
373+
374+ Ok ( ( ) )
375+ }
376+ }
377+ // GRCOV_EXCL_STOP
0 commit comments