fix(completion): treat a bare in-scope table alias as an alias, not an unknown column (#31)
A bare table alias typed where a column is expected — `… GROUP BY o`,
with `o` aliasing `FROM Orders o` — was a blind spot: completion offered
nothing for `o`, and the hint panel called the in-scope alias an unknown
column (`no such column o on table Orders, ...`).
Completion now offers each FROM source's qualifier (alias-if-present-else
table-name) at a bare sql_expr_ident slot, folded into the column
candidate list; on an exact-qualifier partial the alias source steps
aside so the diagnostic can surface. The bare-reference diagnostic arm
emits a targeted `alias_used_as_column` / `table_used_as_column` hint
("`o` is a table alias — write `o.<column>` ...") after the
projection-alias check, so ORDER-BY alias refs still win and a genuine
unknown column still reports `unknown_column`.
Two guards keep the qualified-form advice correct: SQL only (role
`sql_expr_ident`, so the DSL `expr_column` path keeps `unknown_column`
since the DSL has no `table.column` syntax) and effective-qualifier
match (alias-if-present-else-table, so an aliased source referenced by
its shadowed real name falls through rather than being advised as
`name.<column>`). The diagnostic is a drop-in replacement for
`unknown_column` at the same span/Error severity, so verdict/overlay/hint
paths are unchanged.
ADR-0032 Amendment 3; +10 tests.
This commit is contained in:
@@ -1765,6 +1765,65 @@ mod tests {
|
||||
cache
|
||||
}
|
||||
|
||||
fn issue31_join_cache() -> crate::completion::SchemaCache {
|
||||
use crate::completion::{SchemaCache, TableColumn};
|
||||
use crate::dsl::types::Type;
|
||||
let mut cache = SchemaCache::default();
|
||||
let tables: &[(&str, &[(&str, Type)])] = &[
|
||||
("Customers", &[("id", Type::Serial), ("name", Type::Text)]),
|
||||
(
|
||||
"Products",
|
||||
&[("id", Type::Serial), ("name", Type::Text), ("price", Type::Decimal)],
|
||||
),
|
||||
(
|
||||
"OrderLines",
|
||||
&[
|
||||
("id", Type::Serial),
|
||||
("order_id", Type::Int),
|
||||
("product_id", Type::Int),
|
||||
("count", Type::Int),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Orders",
|
||||
&[("id", Type::Serial), ("customer_id", Type::Int), ("date", Type::Date)],
|
||||
),
|
||||
];
|
||||
for (t, cols) in tables {
|
||||
cache.tables.push((*t).to_string());
|
||||
let tc: Vec<TableColumn> =
|
||||
cols.iter().map(|(n, ty)| TableColumn::new(*n, *ty)).collect();
|
||||
for c in &tc {
|
||||
cache.columns.push(c.name.clone());
|
||||
}
|
||||
cache.table_columns.insert((*t).to_string(), tc);
|
||||
}
|
||||
cache
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue31_group_by_partial_alias_shows_alias_hint() {
|
||||
// Issue #31 end-to-end: the manual-testing query ended in
|
||||
// `… group by o`, where `o` aliases `Orders`. The ambient
|
||||
// hint must guide the learner to `o.<column>`, not claim
|
||||
// `o` is an unknown column.
|
||||
let cache = issue31_join_cache();
|
||||
let input = "select c.name as customer_name, o.id as order_id, o.date, sum(ol.count*p.price) as total from Orders o join OrderLines ol on o.id=ol.order_id join Products p on p.id=ol.product_id join Customers c on c.id=o.customer_id group by o";
|
||||
match ambient_hint_in_mode(input, input.len(), None, &cache, Mode::Advanced) {
|
||||
Some(AmbientHint::Prose(p)) => {
|
||||
assert!(
|
||||
p.contains("`o` is a table alias") && p.contains("o.<column>"),
|
||||
"expected the alias hint; got: {p:?}",
|
||||
);
|
||||
assert!(
|
||||
!p.contains("no such column"),
|
||||
"must not show the misleading unknown-column message; got: {p:?}",
|
||||
);
|
||||
}
|
||||
other => panic!("expected a Prose alias hint; got: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ambient_hint_at_insert_first_value_shows_int_prose() {
|
||||
use crate::dsl::types::Type;
|
||||
|
||||
Reference in New Issue
Block a user