(
name: &str,
func: &ast::Function,
table: &crate::resolver::columns::ResolvedTable,
functions: &FunctionRegistry,
extra_filter: Option<&ast::Expr>,
)
| 97 | } |
| 98 | |
| 99 | fn dispatch_trigger( |
| 100 | name: &str, |
| 101 | func: &ast::Function, |
| 102 | table: &crate::resolver::columns::ResolvedTable, |
| 103 | functions: &FunctionRegistry, |
| 104 | extra_filter: Option<&ast::Expr>, |
| 105 | ) -> Result<Option<SqlPlan>> { |
| 106 | // Exhaustive match on `SearchTrigger`: when a new trigger is added, |
| 107 | // this fails to compile until a WHERE-clause routing decision is made |
| 108 | // for it. This is the structural fix for the original bug class — |
| 109 | // silent fall-through on unhandled triggers. |
| 110 | match functions.search_trigger(name) { |
| 111 | SearchTrigger::TextMatch => plan_text_from_where(func, table, extra_filter), |
| 112 | SearchTrigger::SpatialDWithin |
| 113 | | SearchTrigger::SpatialContains |
| 114 | | SearchTrigger::SpatialIntersects |
| 115 | | SearchTrigger::SpatialWithin => plan_spatial_from_where(name, func, table, extra_filter), |
| 116 | SearchTrigger::VectorSearch => plan_vector_from_where(name, func, table, extra_filter), |
| 117 | SearchTrigger::MultiVectorSearch => plan_multi_vector_from_where(func, table, extra_filter), |
| 118 | // The remaining triggers either have no WHERE-clause shape advertised |
| 119 | // anywhere in the docs (`HybridSearch`, `TextSearch`, the array TVFs, |
| 120 | // `TimeBucket`) or are not search triggers at all (`None`). We fall |
| 121 | // back to scalar evaluation, which matches the existing contract for |
| 122 | // these surfaces. The match is exhaustive so a new trigger added to |
| 123 | // the enum will fail this file at compile time, forcing a routing |
| 124 | // decision rather than another silent fall-through. |
| 125 | SearchTrigger::HybridSearch |
| 126 | | SearchTrigger::TextSearch |
| 127 | | SearchTrigger::TimeBucket |
| 128 | | SearchTrigger::ArraySlice |
| 129 | | SearchTrigger::ArrayProject |
| 130 | | SearchTrigger::ArrayAgg |
| 131 | | SearchTrigger::ArrayElementwise |
| 132 | | SearchTrigger::ArrayFlush |
| 133 | | SearchTrigger::ArrayCompact |
| 134 | // graph_score() is a planner-intercepted marker inside rrf_score(...); |
| 135 | // it has no standalone WHERE-clause shape — fall through to scalar eval. |
| 136 | | SearchTrigger::GraphSearch |
| 137 | | SearchTrigger::None => Ok(None), |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | fn extra_filter_to_filters(extra: Option<&ast::Expr>) -> Result<Vec<Filter>> { |
| 142 | match extra { |
no test coverage detected