pub fn compile_match_expr(
ctx: &SessionContext,
match_expr: &str,
) -> Result<Arc<dyn PhysicalExpr>, DataFusionError>Expand description
Parses, validates, and plans match_expr into an Arc<dyn PhysicalExpr> – the whole
evaluator, correct by construction because DataFusion does the parsing and the evaluation.
- Parse with
ctx.parse_sql_expr, againstmatch_schema.ctxis expected to be a bareSessionContext::new()(no lakehouse tables or catalog registered) – seeQueryDenyList’s doc comment for why the same bare context is used both at refresh and fromdeny_queries. - Validate with exactly two checks: the result type is
Boolean, and at least one column is referenced. An unknown column or function already fails at step 1; a subquery, aggregate, or window function cannot be lowered to a scalarPhysicalExprand fails at step 3 – each with DataFusion’s own diagnostic. - Plan the validated
Exprinto aPhysicalExprwith the free-functiondatafusion::physical_expr::create_physical_expr– deliberately notSessionContext::create_physical_expr/SessionState::create_physical_expr, both of which run the expression throughExprSimplifier::coercefirst and would silently insert aCASTfor a type-mismatched comparison (client = 42becomesCAST(client AS Int64) = 42, which compiles, then either errors on every real value or – worse – happens to parse and quietly matches something the admin never intended). The free function’s own doc comment is explicit that it performs no coercion (“There should be no coercion during physical planning”) – but it also performs no operand-type checking at this stage:binary()happily builds aBinaryExprnode comparing aUtf8column against anInt64/Timestampliteral, and Arrow’s comparison kernels are the ones that actually refuse it, only once the node is evaluated. Left there, that would be exactly the “silent per-query failure” this design wants to avoid – so step 4 forces that evaluation now. - Canary-evaluate the compiled expression once against
QueryAttribution::probe, a fixed one-row batch that exists for no other reason. Every match-context column isUtf8, so the only way this can fail is the type mismatch step 3 doesn’t catch; catching it here turns “never fires” into a loud compile-time error, at the one-time cost of a single evaluate() call per rule (refresh/deny_queriestime, never per query).