@@ -254,6 +254,78 @@ pub const CLICKHOUSE_BUILTINS: &[ClickHouseBuiltin] = &[
254254 } ,
255255] ;
256256
257+ /// Aggregate function names DataFusion's own planner resolves out of the box
258+ /// that this catalog deliberately does *not* map to a canonical
259+ /// [`AggSemantic`] -- either because `AggIntent` has no shape for them
260+ /// (a multi-column correlation/regression aggregate, a bitwise/boolean
261+ /// aggregate, a string concatenation aggregate, ...) or because this front
262+ /// end already rejects them explicitly elsewhere (`array_agg`, `grouping`).
263+ ///
264+ /// This list exists for one reason: `asap-frontend-sql`'s DataFusion-registry
265+ /// drift test (issue #225, item 3 -- see
266+ /// `asap_frontend_sql::sql::catalog_drift`) walks a real `SessionContext`'s
267+ /// resolved aggregate names and requires every one to be either in
268+ /// [`NATIVE_FUNCTIONS`], a [`CLICKHOUSE_BUILTINS`] name, or listed here. A
269+ /// name landing here is a *recorded decision*, not a silenced test failure --
270+ /// each group below says why. Do not add an entry just to make the test
271+ /// pass; add a `NativeFunction` instead if the name should actually lower.
272+ ///
273+ /// Two known, narrow gaps rather than a deliberate non-goal: `var_sample` and
274+ /// `var_population` are DataFusion's own alias spellings of `var_samp` /
275+ /// `var_pop` (see `variance.rs`'s `aliases()` in `datafusion-functions-
276+ /// aggregate`) that [`NATIVE_FUNCTIONS`] doesn't also list under those
277+ /// spellings. Surfaced here rather than silently added to
278+ /// [`NATIVE_FUNCTIONS`], since accepting a new spelling is a maintainer's
279+ /// call, not something this catalog should do on its own.
280+ pub const KNOWN_UNMAPPED_NATIVE_FUNCTIONS : & [ & str ] = & [
281+ // Selector aggregates: this front end only supports these as window
282+ // functions (`WindowFuncKind::FirstValue`/`LastValue`/`NthValue`, via
283+ // `OVER (...)`), not as plain `GROUP BY` aggregates -- no `AggIntent`
284+ // variant models "the value from a particular row" as a reduction.
285+ "first_value" ,
286+ "last_value" ,
287+ "nth_value" ,
288+ // Bitwise / boolean aggregates -- no corresponding `AggIntent` variant.
289+ "bit_and" ,
290+ "bit_or" ,
291+ "bit_xor" ,
292+ "bool_and" ,
293+ "bool_or" ,
294+ // Two-column correlation / linear-regression aggregates -- every
295+ // `AggIntent` value reducer takes one input column (`reducer_col` in
296+ // `asap-frontend-sql`), so these have no home yet.
297+ "corr" ,
298+ "covar" ,
299+ "covar_pop" ,
300+ "covar_samp" ,
301+ "regr_avgx" ,
302+ "regr_avgy" ,
303+ "regr_count" ,
304+ "regr_intercept" ,
305+ "regr_r2" ,
306+ "regr_slope" ,
307+ "regr_sxx" ,
308+ "regr_sxy" ,
309+ "regr_syy" ,
310+ // String concatenation -- no `AggIntent` equivalent.
311+ "string_agg" ,
312+ // The weighted-percentile variant of `approx_percentile_cont` (an extra
313+ // weight-column argument); only the unweighted form is in
314+ // `NATIVE_FUNCTIONS`.
315+ "approx_percentile_cont_with_weight" ,
316+ // Explicitly rejected elsewhere, not merely unmapped:
317+ // `array_agg_is_deliberately_rejected` (asap-frontend-sql's
318+ // sql_lowering tests) covers `array_agg`; `lower_grouping_sets`'s own
319+ // doc comment covers `grouping` (`GROUPING(col)` -- observable only via
320+ // the `__grouping_id` discriminator this front end drops).
321+ "array_agg" ,
322+ "grouping" ,
323+ // Known narrow gaps (see doc comment above) -- alias spellings of
324+ // `var_samp` / `var_pop` this catalog doesn't accept yet.
325+ "var_sample" ,
326+ "var_population" ,
327+ ] ;
328+
257329/// Look up a native function name's canonical semantic (case-sensitive --
258330/// callers normalize case first, as `asap-frontend-sql` already does via
259331/// `.to_lowercase()`).
@@ -316,4 +388,38 @@ mod tests {
316388 ) ;
317389 }
318390 }
391+
392+ /// `KNOWN_UNMAPPED_NATIVE_FUNCTIONS` documents names this catalog
393+ /// deliberately does *not* map -- it should never overlap with a table
394+ /// that *does* map the same name (that would be a contradiction: mapped
395+ /// and "known unmapped" at once), and shouldn't repeat itself either
396+ /// (each name is a one-time recorded decision).
397+ #[ test]
398+ fn known_unmapped_list_is_disjoint_from_the_mapped_tables_and_has_no_duplicates ( ) {
399+ for name in KNOWN_UNMAPPED_NATIVE_FUNCTIONS {
400+ assert ! (
401+ lookup_native( name) . is_none( ) ,
402+ "{name} is both in NATIVE_FUNCTIONS and KNOWN_UNMAPPED_NATIVE_FUNCTIONS"
403+ ) ;
404+ assert ! (
405+ lookup_clickhouse_builtin( name) . is_none( ) ,
406+ "{name} is both in CLICKHOUSE_BUILTINS and KNOWN_UNMAPPED_NATIVE_FUNCTIONS"
407+ ) ;
408+ }
409+ let mut sorted = KNOWN_UNMAPPED_NATIVE_FUNCTIONS . to_vec ( ) ;
410+ sorted. sort_unstable ( ) ;
411+ sorted. dedup ( ) ;
412+ assert_eq ! (
413+ sorted. len( ) ,
414+ KNOWN_UNMAPPED_NATIVE_FUNCTIONS . len( ) ,
415+ "KNOWN_UNMAPPED_NATIVE_FUNCTIONS has a duplicate entry"
416+ ) ;
417+ }
418+
419+ #[ test]
420+ fn known_unmapped_names_are_lowercase ( ) {
421+ for name in KNOWN_UNMAPPED_NATIVE_FUNCTIONS {
422+ assert_eq ! ( * name, name. to_lowercase( ) , "not lowercase: {name}" ) ;
423+ }
424+ }
319425}
0 commit comments