Skip to main content

squawk_ide/
hover.rs

1use crate::ast_nav;
2use crate::builtins::builtins_file;
3use crate::collect;
4use crate::column_name::ColumnName;
5use crate::comments::preceding_comment;
6use crate::db::{File, bind, parse};
7use crate::location::{Location, LocationKind};
8use crate::name;
9use crate::offsets::token_from_offset;
10use crate::symbols::{Name, Schema};
11use crate::{goto_definition, resolve};
12use rowan::TextSize;
13use salsa::Database as Db;
14use squawk_syntax::SyntaxNode;
15use squawk_syntax::SyntaxNodePtr;
16use squawk_syntax::{
17    SyntaxKind,
18    ast::{self, AstNode},
19};
20
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct Hover {
23    pub snippet: String,
24    pub comment: Option<String>,
25}
26
27impl Hover {
28    fn snippet(snippet: impl Into<String>) -> Hover {
29        Hover {
30            snippet: snippet.into(),
31            comment: None,
32        }
33    }
34
35    fn new(snippet: impl Into<String>, comment: impl Into<String>) -> Hover {
36        Hover {
37            snippet: snippet.into(),
38            comment: Some(comment.into()),
39        }
40    }
41
42    pub fn markdown(&self) -> String {
43        let snippet = &self.snippet;
44        let mut out = format!(
45            "
46```sql
47{snippet}
48```
49"
50        );
51
52        if let Some(comment) = &self.comment {
53            out.push_str(&format!(
54                "---
55{comment}
56"
57            ))
58        }
59
60        out
61    }
62}
63
64fn merge_hovers(hovers: Vec<Hover>) -> Option<Hover> {
65    if hovers.is_empty() {
66        return None;
67    }
68
69    Some(Hover::snippet(
70        hovers
71            .into_iter()
72            .map(|hover| hover.snippet)
73            .collect::<Vec<_>>()
74            .join("\n"),
75    ))
76}
77
78#[salsa::tracked]
79pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<Hover> {
80    let token = token_from_offset(db, file, offset)?;
81    let parent = token.parent()?;
82
83    if token.kind() == SyntaxKind::STAR {
84        if let Some(field_expr) = ast::FieldExpr::cast(parent.clone())
85            && field_expr.star_token().is_some()
86            && let Some(result) = hover_qualified_star(db, file, field_expr)
87        {
88            return Some(result);
89        }
90
91        if let Some(arg_list) = ast::ArgList::cast(parent.clone())
92            && let Some(result) = hover_unqualified_star_in_arg_list(db, file, arg_list)
93        {
94            return Some(result);
95        }
96
97        if let Some(target) = ast::Target::cast(parent.clone())
98            && target.star_token().is_some()
99            && let Some(result) = hover_unqualified_star(db, file, target)
100        {
101            return Some(result);
102        }
103        return None;
104    }
105
106    if ast::NameRef::can_cast(parent.kind()) {
107        return hover_name_ref(db, file, offset);
108    }
109
110    if let Some(name) = ast::Name::cast(parent) {
111        return hover_name(db, file, name);
112    }
113
114    None
115}
116
117fn hover_name(db: &dyn Db, file: File, name: ast::Name) -> Option<Hover> {
118    let def = Location::from_node(file, name.syntax())?;
119    match def.kind {
120        LocationKind::Aggregate => hover_aggregate(db, def),
121        LocationKind::CaseExpr | LocationKind::CommitBegin | LocationKind::CommitEnd => None,
122        LocationKind::Channel => hover_channel(db, def),
123        LocationKind::Column => hover_name_column(db, def),
124        LocationKind::Cursor => hover_cursor(db, def),
125        LocationKind::Database => hover_database(db, def),
126        LocationKind::EventTrigger => hover_event_trigger(db, def),
127        LocationKind::Extension => hover_extension(db, def),
128        LocationKind::Function => hover_function(db, def),
129        LocationKind::Index => hover_index(db, def),
130        LocationKind::NamedArgParameter => hover_named_arg_parameter(db, def),
131        LocationKind::Policy => hover_policy(db, def),
132        LocationKind::PreparedStatement => hover_prepared_statement(db, def),
133        LocationKind::Procedure => hover_procedure(db, def),
134        LocationKind::PropertyGraph => hover_property_graph(db, def),
135        LocationKind::Role => hover_role(db, def),
136        LocationKind::Schema => hover_schema(db, def),
137        LocationKind::Sequence => hover_sequence(db, def),
138        LocationKind::Server => hover_server(db, def),
139        LocationKind::Table => hover_table(db, def),
140        LocationKind::Tablespace => hover_tablespace(db, def),
141        LocationKind::View => {
142            if let Some(hover) = format_create_view(db, def) {
143                return Some(hover);
144            }
145            hover_table(db, def)
146        }
147        LocationKind::Trigger => hover_trigger(db, def),
148        LocationKind::Type => hover_type(db, def),
149        LocationKind::Window => hover_window(db, def),
150    }
151}
152
153fn hover_name_column(db: &dyn Db, def: Location) -> Option<Hover> {
154    if let Some(result) = hover_composite_type_field(db, def) {
155        return Some(result);
156    }
157
158    let def_node = def.to_node(db)?;
159    if let Some(column) = def_node.parent().and_then(ast::Column::cast)
160        && let Some(create_table) = def_node.ancestors().find_map(ast::CreateTableLike::cast)
161    {
162        return hover_column_definition(db, def.file, create_table, column);
163    }
164
165    if def_node
166        .ancestors()
167        .any(|ancestor| ast::ColumnList::can_cast(ancestor.kind()))
168        && let Some(create_view) = def_node.ancestors().find_map(ast::CreateView::cast)
169    {
170        return format_view_column(db, def.file, create_view, &def_node);
171    }
172
173    None
174}
175
176fn hover_name_ref(db: &dyn Db, file: File, offset: TextSize) -> Option<Hover> {
177    // We can get multiple in the case of using
178    //
179    // select * from t join u using (id);
180    //
181    let definitions = goto_definition::goto_definition(db, file, offset);
182    let def = *definitions.first()?;
183    match def.kind {
184        LocationKind::Aggregate => hover_aggregate(db, def),
185        LocationKind::CaseExpr | LocationKind::CommitBegin | LocationKind::CommitEnd => None,
186        LocationKind::Channel => hover_channel(db, def),
187        LocationKind::Column => {
188            if let Some(result) = hover_composite_type_field(db, def) {
189                return Some(result);
190            }
191            if let Some(result) = hover_column(db, &definitions) {
192                return Some(result);
193            }
194            // If no column, try as function (handles field-style function calls like `t.b`)
195            if let Some(result) = hover_function(db, def) {
196                return Some(result);
197            }
198            // Finally try as table (handles case like `select t from t;` where t is the table)
199            hover_table(db, def)
200        }
201        LocationKind::Cursor => hover_cursor(db, def),
202        LocationKind::Database => hover_database(db, def),
203        LocationKind::EventTrigger => hover_event_trigger(db, def),
204        LocationKind::Extension => hover_extension(db, def),
205        LocationKind::Function => {
206            if let Some(result) = hover_function(db, def) {
207                return Some(result);
208            }
209            if let Some(result) = hover_routine(db, def) {
210                return Some(result);
211            }
212            hover_column(db, &definitions)
213        }
214        LocationKind::Index => hover_index(db, def),
215        LocationKind::NamedArgParameter => hover_named_arg_parameter(db, def),
216        LocationKind::Policy => hover_policy(db, def),
217        LocationKind::PreparedStatement => hover_prepared_statement(db, def),
218        LocationKind::Procedure => hover_procedure(db, def),
219        LocationKind::PropertyGraph => hover_property_graph(db, def),
220        LocationKind::Role => hover_role(db, def),
221        LocationKind::Schema => hover_schema(db, def),
222        LocationKind::Sequence => hover_sequence(db, def),
223        LocationKind::Server => hover_server(db, def),
224        LocationKind::Table | LocationKind::View => hover_table(db, def),
225        LocationKind::Tablespace => hover_tablespace(db, def),
226        LocationKind::Trigger => hover_trigger(db, def),
227        LocationKind::Type => hover_type(db, def),
228        LocationKind::Window => hover_window(db, def),
229    }
230}
231
232struct ColumnHover;
233impl ColumnHover {
234    fn table_column(table_name: &str, column_name: &str) -> String {
235        format!("column {table_name}.{column_name}")
236    }
237
238    fn table_column_type(table_name: &str, column_name: &str, ty: &str) -> String {
239        format!("column {table_name}.{column_name} {ty}")
240    }
241
242    fn schema_table_column_type(
243        schema: &str,
244        table_name: &str,
245        column_name: &str,
246        ty: &str,
247    ) -> String {
248        format!("column {schema}.{table_name}.{column_name} {ty}")
249    }
250    fn schema_table_column(schema: &str, table_name: &str, column_name: &str) -> String {
251        format!("column {schema}.{table_name}.{column_name}")
252    }
253
254    fn anon_column(col_name: &str) -> String {
255        format!("column {}", col_name)
256    }
257    fn anon_column_type(col_name: &str, ty: &str) -> String {
258        format!("column {} {}", col_name, ty)
259    }
260}
261
262fn hover_column(db: &dyn Db, definitions: &[Location]) -> Option<Hover> {
263    let results: Vec<Hover> = definitions
264        .iter()
265        .filter_map(|def| format_hover_for_column_ptr(db, *def))
266        .collect();
267
268    merge_hovers(results)
269}
270
271fn column_name_from_node(column_name_node: &SyntaxNode) -> Option<Name> {
272    if column_name_node
273        .ancestors()
274        .any(|ancestor| ast::Values::can_cast(ancestor.kind()))
275    {
276        let row = column_name_node.ancestors().find_map(ast::Row::cast)?;
277        let idx = row
278            .exprs()
279            .position(|expr| expr.syntax() == column_name_node)?;
280        Some(Name::from_string(format!("column{}", idx + 1)))
281    } else if let Some(name) = ast::Name::cast(column_name_node.clone()) {
282        Some(Name::from_node(&name))
283    } else {
284        let target = column_name_node.ancestors().find_map(ast::Target::cast)?;
285        let (col_name, _) = ColumnName::from_target(target)?;
286        Some(Name::from_string(col_name.to_string()?))
287    }
288}
289
290fn format_hover_for_column_ptr(db: &dyn Db, def: Location) -> Option<Hover> {
291    let def_node = &def.to_node(db)?;
292    match ast_nav::parent_source(def_node)? {
293        ast_nav::ParentSouce::WithTable(with_table) => {
294            let cte_name = with_table.name()?;
295            let column_name = column_name_from_node(def_node)?;
296            let table_name = Name::from_node(&cte_name);
297            return Some(Hover::snippet(ColumnHover::table_column(
298                &table_name.to_string(),
299                &column_name.to_string(),
300            )));
301        }
302        ast_nav::ParentSouce::ParenSelect(paren_select) => {
303            // Qualified access like `t.a`
304            if let Some(table_name) = subquery_alias_name(&paren_select) {
305                let column_name = column_name_from_node(def_node)?;
306                return Some(Hover::snippet(ColumnHover::table_column(
307                    &table_name.to_string(),
308                    &column_name.to_string(),
309                )));
310            }
311
312            // Unqualified access like `a` from `select a from (select 1 a)`
313            let column_name = column_name_from_node(def_node)?;
314
315            let ty = collect::paren_select_columns_with_types(db, def.file, &paren_select)
316                .into_iter()
317                .find(|(name, _)| *name == column_name)
318                .and_then(|(_, ty)| ty)?;
319            return Some(Hover::snippet(ColumnHover::anon_column_type(
320                &column_name.to_string(),
321                &ty.to_string(),
322            )));
323        }
324        // create view v(a) as select 1;
325        // select a from v;
326        //        ^
327        ast_nav::ParentSouce::CreateView(create_view) => {
328            let column_name = column_name_from_node(def_node)?;
329            let path = create_view.path()?;
330            let (schema, view_name) = resolve::resolve_view_info(db, def.file, &path)?;
331            return Some(Hover::snippet(ColumnHover::schema_table_column(
332                &schema.to_string(),
333                &view_name,
334                &column_name.to_string(),
335            )));
336        }
337        ast_nav::ParentSouce::Alias(alias) => {
338            let alias_name = alias.name()?;
339            alias.column_list()?;
340            let table_name = Name::from_node(&alias_name);
341            let column_name = Name::from_string(def_node.text().to_string());
342            return Some(Hover::snippet(ColumnHover::table_column(
343                &table_name.to_string(),
344                &column_name.to_string(),
345            )));
346        }
347        ast_nav::ParentSouce::CreateTableAs(create_table_as) => {
348            let column_name = column_name_from_node(def_node)?;
349            let path = create_table_as.path()?;
350            let (schema, table_name) = resolve::resolve_table_info(db, def.file, &path)?;
351            return Some(Hover::snippet(ColumnHover::schema_table_column(
352                &schema.to_string(),
353                &table_name,
354                &column_name.to_string(),
355            )));
356        }
357        ast_nav::ParentSouce::CreateTable(create_table) => {
358            let column = def_node.ancestors().find_map(ast::Column::cast)?;
359            let column_name = column.name()?;
360            let ty = column.ty()?;
361            let path = create_table.path()?;
362            let (schema, table_name) = resolve::resolve_table_info(db, def.file, &path)?;
363
364            return Some(Hover::snippet(ColumnHover::schema_table_column_type(
365                &schema.to_string(),
366                &table_name,
367                &Name::from_node(&column_name).to_string(),
368                &ty.syntax().text().to_string(),
369            )));
370        }
371    }
372}
373
374fn hover_composite_type_field(db: &dyn Db, def: Location) -> Option<Hover> {
375    let column = def.to_node(db)?.ancestors().find_map(ast::Column::cast)?;
376    let field_name = column.name()?.syntax().text().to_string();
377    let ty = column.ty()?;
378
379    let create_type = column
380        .syntax()
381        .ancestors()
382        .find_map(ast::CreateType::cast)?;
383    let type_path = create_type.path()?;
384    let (schema, type_name) = resolve::resolve_type_info(db, def.file, &type_path)?;
385
386    Some(Hover::snippet(format!(
387        "field {}.{}.{} {}",
388        schema,
389        type_name,
390        field_name,
391        ty.syntax().text()
392    )))
393}
394
395fn hover_column_definition(
396    db: &dyn Db,
397    file: File,
398    create_table: impl ast::HasCreateTable,
399    column: ast::Column,
400) -> Option<Hover> {
401    let column_name = column.name()?.syntax().text().to_string();
402    let ty = column.ty()?;
403    let path = create_table.path()?;
404    let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?;
405    let ty = ty.syntax().text().to_string();
406    Some(Hover::snippet(ColumnHover::schema_table_column_type(
407        &schema.to_string(),
408        &table_name,
409        &column_name,
410        &ty,
411    )))
412}
413
414fn format_table_source(db: &dyn Db, file: File, source: ast_nav::ParentSouce) -> Option<Hover> {
415    match source {
416        ast_nav::ParentSouce::Alias(alias) => format_alias_with_column_list(db, file, alias),
417        ast_nav::ParentSouce::WithTable(with_table) => format_with_table(with_table),
418        ast_nav::ParentSouce::CreateView(create_view) => {
419            format_create_view_like(db, file, create_view)
420        }
421        ast_nav::ParentSouce::CreateTable(create_table) => {
422            format_create_table(db, file, create_table)
423        }
424        ast_nav::ParentSouce::CreateTableAs(create_table_as) => {
425            format_create_table_as(db, file, create_table_as)
426        }
427        ast_nav::ParentSouce::ParenSelect(paren_select) => format_paren_select(paren_select),
428    }
429}
430
431fn hover_table(db: &dyn Db, def: Location) -> Option<Hover> {
432    let source = ast_nav::parent_source(&def.to_node(db)?)?;
433    format_table_source(db, def.file, source)
434}
435
436fn format_alias_with_column_list(db: &dyn Db, file: File, alias: ast::Alias) -> Option<Hover> {
437    let alias_name = alias.name()?;
438    let name = Name::from_node(&alias_name);
439
440    let Some(column_list) = alias.column_list() else {
441        let name = Name::from_node(&alias.name()?);
442        let from_item = alias.syntax().ancestors().find_map(ast::FromItem::cast)?;
443        let paren_select = from_item.paren_select()?;
444        return format_subquery_table(name, paren_select);
445    };
446
447    let mut columns: Vec<Name> = column_list
448        .columns()
449        .filter_map(|column| {
450            column
451                .name()
452                .map(|column_name| Name::from_node(&column_name))
453        })
454        .collect();
455
456    if let Some(from_item) = alias.syntax().ancestors().find_map(ast::FromItem::cast)
457        && let Some(table_ptr) = resolve::table_ptr_from_from_item(db, file, &from_item)
458    {
459        let base_columns = collect::star_column_names(db, file, &table_ptr);
460        for column in base_columns.iter().skip(columns.len()) {
461            columns.push(column.clone());
462        }
463    }
464
465    let columns = columns
466        .iter()
467        .map(|column| column.to_string())
468        .collect::<Vec<_>>()
469        .join(", ");
470    Some(Hover::snippet(format!("table {}({})", name, columns)))
471}
472
473fn hover_qualified_star(db: &dyn Db, file: File, field_expr: ast::FieldExpr) -> Option<Hover> {
474    let table_ptr = qualified_star_table_ptr(db, file, field_expr)?;
475    hover_qualified_star_columns(db, file, &table_ptr)
476}
477
478fn hover_unqualified_star(db: &dyn Db, file: File, target: ast::Target) -> Option<Hover> {
479    let mut results = hover_unqualified_star_with_binder(db, file, &target);
480
481    if results.is_empty() && target_has_schema_qualified_from_item(&target) {
482        results = hover_unqualified_star_with_binder(db, builtins_file(db), &target);
483    }
484
485    merge_hovers(results)
486}
487
488fn hover_unqualified_star_with_binder(db: &dyn Db, file: File, target: &ast::Target) -> Vec<Hover> {
489    let mut results = vec![];
490
491    if let Some(table_ptrs) = unqualified_star_table_ptrs(db, file, target) {
492        for table_ptr in table_ptrs {
493            if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) {
494                results.push(columns);
495            }
496        }
497    }
498
499    results
500}
501
502fn target_has_schema_qualified_from_item(target: &ast::Target) -> bool {
503    let Some(select) = target.syntax().ancestors().find_map(ast::Select::cast) else {
504        return false;
505    };
506    let Some(from_clause) = select.from_clause() else {
507        return false;
508    };
509
510    for from_item in from_clause.from_items() {
511        if from_item.field_expr().is_some() {
512            return true;
513        }
514    }
515
516    false
517}
518
519fn hover_unqualified_star_in_arg_list(
520    db: &dyn Db,
521    file: File,
522    arg_list: ast::ArgList,
523) -> Option<Hover> {
524    let table_ptrs = unqualified_star_in_arg_list_ptrs(db, file, &arg_list)?;
525    let mut results = vec![];
526    for table_ptr in table_ptrs {
527        if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) {
528            results.push(columns);
529        }
530    }
531
532    merge_hovers(results)
533}
534
535fn format_subquery_table(name: Name, paren_select: ast::ParenSelect) -> Option<Hover> {
536    let name = name.to_string();
537    let query = paren_select.syntax().text().to_string();
538    Some(Hover::snippet(format!("subquery {} as {}", name, query)))
539}
540
541fn hover_qualified_star_columns(
542    db: &dyn Db,
543    file: File,
544    table_ptr: &squawk_syntax::SyntaxNodePtr,
545) -> Option<Hover> {
546    let source_file = parse(db, file).tree();
547    let root = source_file.syntax();
548    let table_name_node = table_ptr.to_node(root);
549
550    match ast_nav::parent_source(&table_name_node)? {
551        ast_nav::ParentSouce::Alias(alias) => {
552            hover_qualified_star_columns_from_alias(db, file, &alias)
553        }
554        ast_nav::ParentSouce::WithTable(with_table) => {
555            hover_qualified_star_columns_from_cte(db, file, with_table)
556        }
557        ast_nav::ParentSouce::CreateTable(create_table) => {
558            hover_qualified_star_columns_from_table(db, file, create_table)
559        }
560        ast_nav::ParentSouce::CreateTableAs(create_table_as) => {
561            hover_qualified_star_columns_from_table_as(db, file, &create_table_as)
562        }
563        ast_nav::ParentSouce::CreateView(create_view) => {
564            hover_qualified_star_columns_from_view_like(db, file, &create_view)
565        }
566        ast_nav::ParentSouce::ParenSelect(paren_select) => {
567            hover_qualified_star_columns_from_subquery(db, file, &paren_select)
568        }
569    }
570}
571
572fn hover_qualified_star_columns_from_alias(
573    db: &dyn Db,
574    file: File,
575    alias: &ast::Alias,
576) -> Option<Hover> {
577    let alias_name = Name::from_node(&alias.name()?);
578    let alias_columns: Vec<Name> = alias
579        .column_list()?
580        .columns()
581        .filter_map(|column| column.name().map(|name| Name::from_node(&name)))
582        .collect();
583
584    if alias_columns.is_empty() {
585        return None;
586    }
587
588    let mut results: Vec<Hover> = alias_columns
589        .iter()
590        .map(|column_name| {
591            Hover::snippet(ColumnHover::table_column(
592                &alias_name.to_string(),
593                &column_name.to_string(),
594            ))
595        })
596        .collect();
597
598    let from_item = alias.syntax().ancestors().find_map(ast::FromItem::cast)?;
599    let table_ptr = resolve::table_ptr_from_from_item(db, file, &from_item)?;
600    let base_column_names = collect::star_column_names(db, file, &table_ptr);
601
602    for column_name in base_column_names.iter().skip(alias_columns.len()) {
603        results.push(Hover::snippet(ColumnHover::table_column(
604            &alias_name.to_string(),
605            &column_name.to_string(),
606        )));
607    }
608
609    merge_hovers(results)
610}
611
612fn hover_qualified_star_columns_from_table(
613    db: &dyn Db,
614    file: File,
615    create_table: impl ast::HasCreateTable,
616) -> Option<Hover> {
617    let path = create_table.path()?;
618    let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?;
619    let schema = schema.to_string();
620    let results: Vec<Hover> = collect::table_columns(db, file, &create_table)
621        .into_iter()
622        .filter_map(|(column_name, ty)| {
623            let ty = ty?;
624            Some(Hover::snippet(ColumnHover::schema_table_column_type(
625                &schema,
626                &table_name,
627                &column_name.to_string(),
628                &ty.to_string(),
629            )))
630        })
631        .collect();
632
633    merge_hovers(results)
634}
635
636fn hover_qualified_star_columns_from_table_as(
637    db: &dyn Db,
638    file: File,
639    create_table_as: &ast::CreateTableAs,
640) -> Option<Hover> {
641    let path = create_table_as.path()?;
642    let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?;
643    let schema_str = schema.to_string();
644
645    let columns = collect::create_table_as_columns_with_types(create_table_as);
646    let results: Vec<Hover> = columns
647        .into_iter()
648        .map(|(column_name, ty)| {
649            if let Some(ty) = ty {
650                return Hover::snippet(ColumnHover::schema_table_column_type(
651                    &schema_str,
652                    &table_name,
653                    &column_name.to_string(),
654                    &ty.to_string(),
655                ));
656            }
657            Hover::snippet(ColumnHover::schema_table_column(
658                &schema_str,
659                &table_name,
660                &column_name.to_string(),
661            ))
662        })
663        .collect();
664
665    merge_hovers(results)
666}
667
668fn hover_qualified_star_columns_from_cte(
669    db: &dyn Db,
670    file: File,
671    with_table: ast::WithTable,
672) -> Option<Hover> {
673    let cte_name = Name::from_node(&with_table.name()?);
674    let cte_name = cte_name.to_string();
675    let columns = collect::with_table_columns_with_types(db, file, with_table);
676    let results: Vec<Hover> = columns
677        .into_iter()
678        .map(|(column_name, ty)| {
679            let column_name = column_name.to_string();
680            if let Some(ty) = ty {
681                return Hover::snippet(ColumnHover::table_column_type(
682                    &cte_name,
683                    &column_name,
684                    &ty.to_string(),
685                ));
686            }
687
688            Hover::snippet(ColumnHover::table_column(&cte_name, &column_name))
689        })
690        .collect();
691
692    merge_hovers(results)
693}
694
695fn hover_qualified_star_columns_from_view_like(
696    db: &dyn Db,
697    file: File,
698    create_view: &ast::CreateViewLike,
699) -> Option<Hover> {
700    let path = create_view.path()?;
701    let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?;
702
703    let schema_str = schema.to_string();
704    let columns = collect::view_like_columns_with_types(create_view);
705    let results: Vec<Hover> = columns
706        .into_iter()
707        .map(|(column_name, ty)| {
708            if let Some(ty) = ty {
709                return Hover::snippet(ColumnHover::schema_table_column_type(
710                    &schema_str,
711                    &view_name,
712                    &column_name.to_string(),
713                    &ty.to_string(),
714                ));
715            }
716
717            Hover::snippet(ColumnHover::schema_table_column(
718                &schema_str,
719                &view_name,
720                &column_name.to_string(),
721            ))
722        })
723        .collect();
724
725    merge_hovers(results)
726}
727
728fn hover_qualified_star_columns_from_subquery(
729    db: &dyn Db,
730    file: File,
731    paren_select: &ast::ParenSelect,
732) -> Option<Hover> {
733    let select_variant = paren_select.select()?;
734
735    if let Some(select) = ast_nav::select_from_variant(select_variant) {
736        let target_list = select.select_clause()?.target_list()?;
737
738        let mut results = vec![];
739        let subquery_alias = subquery_alias_name(paren_select);
740
741        for target in target_list.targets() {
742            if target.star_token().is_some() {
743                let table_ptrs = unqualified_star_table_ptrs(db, file, &target)?;
744                for table_ptr in table_ptrs {
745                    if let Some(columns) = hover_qualified_star_columns(db, file, &table_ptr) {
746                        results.push(columns)
747                    }
748                }
749                continue;
750            }
751
752            if let Some(result) =
753                hover_subquery_target_column(db, file, &target, subquery_alias.as_ref())
754            {
755                results.push(result);
756            }
757        }
758
759        return merge_hovers(results);
760    }
761
762    let subquery_alias = subquery_alias_name(paren_select);
763    let results: Vec<Hover> = collect::paren_select_columns_with_types(db, file, paren_select)
764        .into_iter()
765        .map(|(column_name, ty)| {
766            if let Some(alias) = &subquery_alias {
767                return Hover::snippet(ColumnHover::table_column(
768                    &alias.to_string(),
769                    &column_name.to_string(),
770                ));
771            }
772            if let Some(ty) = ty {
773                return Hover::snippet(ColumnHover::anon_column_type(
774                    &column_name.to_string(),
775                    &ty.to_string(),
776                ));
777            }
778            Hover::snippet(ColumnHover::anon_column(&column_name.to_string()))
779        })
780        .collect();
781
782    merge_hovers(results)
783}
784
785fn subquery_alias_name(paren_select: &ast::ParenSelect) -> Option<Name> {
786    let from_item = paren_select
787        .syntax()
788        .ancestors()
789        .find_map(ast::FromItem::cast)?;
790    let alias_name = from_item.alias()?.name()?;
791    Some(Name::from_node(&alias_name))
792}
793
794fn hover_subquery_target_column(
795    db: &dyn Db,
796    file: File,
797    target: &ast::Target,
798    subquery_alias: Option<&Name>,
799) -> Option<Hover> {
800    if let Some(alias) = subquery_alias
801        && let Some((col_name, _node)) = ColumnName::from_target(target.clone())
802        && let Some(col_name) = col_name.to_string()
803    {
804        return Some(Hover::snippet(ColumnHover::table_column(
805            &alias.to_string(),
806            &col_name,
807        )));
808    }
809
810    let result = match target.expr()? {
811        ast::Expr::NameRef(name_ref) => hover(db, file, name_ref.syntax().text_range().start()),
812        ast::Expr::FieldExpr(field_expr) => {
813            let field = field_expr.field()?;
814            hover(db, file, field.syntax().text_range().start())
815        }
816        _ => None,
817    };
818
819    if result.is_some() {
820        return result;
821    }
822
823    if let Some((col_name, _node)) = ColumnName::from_target(target.clone())
824        && let Some(col_name) = col_name.to_string()
825    {
826        return Some(Hover::snippet(ColumnHover::anon_column(&col_name)));
827    }
828
829    None
830}
831
832fn hover_index(db: &dyn Db, def: Location) -> Option<Hover> {
833    let create_index = def
834        .to_node(db)?
835        .ancestors()
836        .find_map(ast::CreateIndex::cast)?;
837    format_create_index(db, def.file, create_index)
838}
839
840fn hover_sequence(db: &dyn Db, def: Location) -> Option<Hover> {
841    let create_sequence = def
842        .to_node(db)?
843        .ancestors()
844        .find_map(ast::CreateSequence::cast)?;
845    format_create_sequence(db, def.file, create_sequence)
846}
847
848fn hover_trigger(db: &dyn Db, def: Location) -> Option<Hover> {
849    let create_trigger = def
850        .to_node(db)?
851        .ancestors()
852        .find_map(ast::CreateTrigger::cast)?;
853    format_create_trigger(db, def.file, create_trigger)
854}
855
856fn hover_policy(db: &dyn Db, def: Location) -> Option<Hover> {
857    let create_policy = def
858        .to_node(db)?
859        .ancestors()
860        .find_map(ast::CreatePolicy::cast)?;
861    format_create_policy(db, def.file, create_policy)
862}
863
864fn hover_property_graph(db: &dyn Db, def: Location) -> Option<Hover> {
865    let create_property_graph = def
866        .to_node(db)?
867        .ancestors()
868        .find_map(ast::CreatePropertyGraph::cast)?;
869    format_create_property_graph(db, def.file, create_property_graph)
870}
871
872fn hover_event_trigger(db: &dyn Db, def: Location) -> Option<Hover> {
873    let create_event_trigger = def
874        .to_node(db)?
875        .ancestors()
876        .find_map(ast::CreateEventTrigger::cast)?;
877
878    format_create_event_trigger(create_event_trigger)
879}
880
881fn hover_tablespace(db: &dyn Db, def: Location) -> Option<Hover> {
882    let def_node = def.to_node(db)?;
883    if let Some(create_tablespace) = def_node.ancestors().find_map(ast::CreateTablespace::cast) {
884        return format_create_tablespace(create_tablespace);
885    }
886    Some(Hover::snippet(format!("tablespace {}", def_node.text())))
887}
888
889fn hover_database(db: &dyn Db, def: Location) -> Option<Hover> {
890    let def_node = def.to_node(db)?;
891    if let Some(create_database) = def_node.ancestors().find_map(ast::CreateDatabase::cast) {
892        return format_create_database(create_database);
893    }
894    Some(Hover::snippet(format!("database {}", def_node.text())))
895}
896
897fn hover_server(db: &dyn Db, def: Location) -> Option<Hover> {
898    let def_node = def.to_node(db)?;
899    if let Some(create_server) = def_node.ancestors().find_map(ast::CreateServer::cast) {
900        return format_create_server(create_server);
901    }
902    Some(Hover::snippet(format!("server {}", def_node.text())))
903}
904
905fn hover_extension(db: &dyn Db, def: Location) -> Option<Hover> {
906    let def_node = def.to_node(db)?;
907    if let Some(create_extension) = def_node.ancestors().find_map(ast::CreateExtension::cast) {
908        return format_create_extension(create_extension);
909    }
910    Some(Hover::snippet(format!("extension {}", def_node.text())))
911}
912
913fn hover_role(db: &dyn Db, def: Location) -> Option<Hover> {
914    let def_node = def.to_node(db)?;
915    if let Some(create_role) = def_node.ancestors().find_map(ast::CreateRole::cast) {
916        return format_create_role(create_role);
917    }
918    Some(Hover::snippet(format!("role {}", def_node.text())))
919}
920
921fn hover_cursor(db: &dyn Db, def: Location) -> Option<Hover> {
922    let declare = def.to_node(db)?.ancestors().find_map(ast::Declare::cast)?;
923    format_declare_cursor(declare)
924}
925
926fn hover_prepared_statement(db: &dyn Db, def: Location) -> Option<Hover> {
927    let prepare = def.to_node(db)?.ancestors().find_map(ast::Prepare::cast)?;
928    format_prepare(prepare)
929}
930
931fn hover_channel(db: &dyn Db, def: Location) -> Option<Hover> {
932    let listen = def.to_node(db)?.ancestors().find_map(ast::Listen::cast)?;
933    format_listen(listen)
934}
935
936fn hover_window(db: &dyn Db, def: Location) -> Option<Hover> {
937    let window_def = def
938        .to_node(db)?
939        .ancestors()
940        .find_map(ast::WindowDef::cast)?;
941
942    Some(Hover::snippet(format!(
943        "window {}",
944        window_def.syntax().text()
945    )))
946}
947
948fn hover_type(db: &dyn Db, def: Location) -> Option<Hover> {
949    let create_type = def
950        .to_node(db)?
951        .ancestors()
952        .find_map(ast::CreateType::cast)?;
953    format_create_type(db, def.file, create_type)
954}
955
956fn format_declare_cursor(declare: ast::Declare) -> Option<Hover> {
957    let name = declare.name()?;
958    let query = declare.query()?;
959    Some(Hover::snippet(format!(
960        "cursor {} for {}",
961        name.syntax().text(),
962        query.syntax().text()
963    )))
964}
965
966fn format_prepare(prepare: ast::Prepare) -> Option<Hover> {
967    let name = prepare.name()?;
968    let stmt = prepare.preparable_stmt()?;
969    Some(Hover::snippet(format!(
970        "prepare {} as {}",
971        name.syntax().text(),
972        stmt.syntax().text()
973    )))
974}
975
976fn format_listen(listen: ast::Listen) -> Option<Hover> {
977    let name = listen.name()?;
978    Some(Hover::snippet(format!("listen {}", name.syntax().text())))
979}
980
981fn format_create_table(
982    db: &dyn Db,
983    file: File,
984    create_table: impl ast::HasCreateTable,
985) -> Option<Hover> {
986    let path = create_table.path()?;
987    let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?;
988    let schema = schema.to_string();
989    let args = create_table.table_arg_list()?.syntax().text().to_string();
990
991    let foreign = if create_table.syntax().kind() == SyntaxKind::CREATE_FOREIGN_TABLE {
992        "foreign "
993    } else {
994        ""
995    };
996
997    Some(Hover::snippet(format!(
998        "{foreign}table {schema}.{table_name}{args}"
999    )))
1000}
1001
1002fn format_create_table_as(
1003    db: &dyn Db,
1004    file: File,
1005    create_table_as: ast::CreateTableAs,
1006) -> Option<Hover> {
1007    let path = create_table_as.path()?;
1008    let (schema, table_name) = resolve::resolve_table_info(db, file, &path)?;
1009    let query = create_table_as.query()?.syntax().text().to_string();
1010    Some(Hover::snippet(format!(
1011        "table {}.{} as {}",
1012        schema, table_name, query
1013    )))
1014}
1015
1016fn format_create_view(db: &dyn Db, def: Location) -> Option<Hover> {
1017    let create_view = ast::CreateViewLike::cast(def.to_node(db)?)?;
1018    format_create_view_like(db, def.file, create_view)
1019}
1020
1021fn format_create_view_like(
1022    db: &dyn Db,
1023    file: File,
1024    create_view: ast::CreateViewLike,
1025) -> Option<Hover> {
1026    let path = create_view.path()?;
1027    // TODO: we use this to infer the schema, we should either rename this or
1028    // create a different function
1029    let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?;
1030    let schema = schema.to_string();
1031
1032    let column_list = create_view
1033        .column_list()
1034        .map(|cl| cl.syntax().text().to_string())
1035        .unwrap_or_default();
1036
1037    let query = create_view.query()?.syntax().text().to_string();
1038
1039    let view_kind = if create_view.syntax().kind() == SyntaxKind::CREATE_MATERIALIZED_VIEW {
1040        "materialized view"
1041    } else {
1042        "view"
1043    };
1044
1045    Some(Hover::snippet(format!(
1046        "{view_kind} {}.{}{} as {}",
1047        schema, view_name, column_list, query
1048    )))
1049}
1050
1051fn format_view_column(
1052    db: &dyn Db,
1053    file: File,
1054    create_view: ast::CreateView,
1055    def_node: &SyntaxNode,
1056) -> Option<Hover> {
1057    let path = create_view.path()?;
1058    let (schema, view_name) = resolve::resolve_view_info(db, file, &path)?;
1059    Some(Hover::snippet(ColumnHover::schema_table_column(
1060        &schema.to_string(),
1061        &view_name,
1062        &def_node.to_string(),
1063    )))
1064}
1065
1066fn format_with_table(with_table: ast::WithTable) -> Option<Hover> {
1067    let name = with_table.name()?.syntax().text().to_string();
1068    let query = with_table.query()?.syntax().text().to_string();
1069    Some(Hover::snippet(format!("with {} as ({})", name, query)))
1070}
1071
1072fn format_paren_select(paren_select: ast::ParenSelect) -> Option<Hover> {
1073    let query = paren_select.select()?.syntax().text().to_string();
1074    Some(Hover::snippet(format!("({})", query)))
1075}
1076
1077fn format_create_index(db: &dyn Db, file: File, create_index: ast::CreateIndex) -> Option<Hover> {
1078    let index_name = create_index.name()?.syntax().text().to_string();
1079
1080    let index_schema = index_schema(db, file, create_index.clone())?;
1081
1082    let path = create_index.relation_name()?.path()?;
1083    let (table_schema, table_name) = resolve::resolve_table_info(db, file, &path)?;
1084
1085    let partition_item_list = create_index.partition_item_list()?;
1086    let columns = partition_item_list.syntax().text().to_string();
1087
1088    Some(Hover::snippet(format!(
1089        "index {}.{} on {}.{}{}",
1090        index_schema, index_name, table_schema, table_name, columns
1091    )))
1092}
1093
1094fn format_create_sequence(
1095    db: &dyn Db,
1096    file: File,
1097    create_sequence: ast::CreateSequence,
1098) -> Option<Hover> {
1099    let path = create_sequence.path()?;
1100    let (schema, sequence_name) = resolve::resolve_sequence_info(db, file, &path)?;
1101
1102    Some(Hover::snippet(format!(
1103        "sequence {}.{}",
1104        schema, sequence_name
1105    )))
1106}
1107
1108fn format_create_trigger(
1109    db: &dyn Db,
1110    file: File,
1111    create_trigger: ast::CreateTrigger,
1112) -> Option<Hover> {
1113    let trigger_name = create_trigger.name()?.syntax().text().to_string();
1114    let on_table_path = create_trigger.on_table()?.path()?;
1115
1116    let (schema, table_name) = resolve::resolve_table_info(db, file, &on_table_path)?;
1117    Some(Hover::snippet(format!(
1118        "trigger {}.{} on {}.{}",
1119        schema, trigger_name, schema, table_name
1120    )))
1121}
1122
1123fn format_create_policy(
1124    db: &dyn Db,
1125    file: File,
1126    create_policy: ast::CreatePolicy,
1127) -> Option<Hover> {
1128    let policy_name = create_policy.name()?.syntax().text().to_string();
1129    let on_table_path = create_policy.on_table()?.path()?;
1130
1131    let (schema, table_name) = resolve::resolve_table_info(db, file, &on_table_path)?;
1132    Some(Hover::snippet(format!(
1133        "policy {}.{} on {}.{}",
1134        schema, policy_name, schema, table_name
1135    )))
1136}
1137
1138fn format_create_property_graph(
1139    db: &dyn Db,
1140    file: File,
1141    create_property_graph: ast::CreatePropertyGraph,
1142) -> Option<Hover> {
1143    let path = create_property_graph.path()?;
1144    let (schema, name) = resolve::resolve_property_graph_info(db, file, &path)?;
1145    Some(Hover::snippet(format!(
1146        "property graph {}.{}",
1147        schema, name
1148    )))
1149}
1150
1151fn format_create_event_trigger(create_event_trigger: ast::CreateEventTrigger) -> Option<Hover> {
1152    let name = create_event_trigger.name()?.syntax().text().to_string();
1153    Some(Hover::snippet(format!("event trigger {}", name)))
1154}
1155
1156fn format_create_tablespace(create_tablespace: ast::CreateTablespace) -> Option<Hover> {
1157    let name = create_tablespace.name()?.syntax().text().to_string();
1158    Some(Hover::snippet(format!("tablespace {}", name)))
1159}
1160
1161fn format_create_database(create_database: ast::CreateDatabase) -> Option<Hover> {
1162    let name = create_database.name()?.syntax().text().to_string();
1163    Some(Hover::snippet(format!("database {}", name)))
1164}
1165
1166fn format_create_server(create_server: ast::CreateServer) -> Option<Hover> {
1167    let name = create_server.name()?.syntax().text().to_string();
1168    Some(Hover::snippet(format!("server {}", name)))
1169}
1170
1171fn format_create_extension(create_extension: ast::CreateExtension) -> Option<Hover> {
1172    let name = create_extension.name()?.syntax().text().to_string();
1173    Some(Hover::snippet(format!("extension {}", name)))
1174}
1175
1176fn format_create_role(create_role: ast::CreateRole) -> Option<Hover> {
1177    let name = create_role.name()?.syntax().text().to_string();
1178    Some(Hover::snippet(format!("role {}", name)))
1179}
1180
1181fn index_schema(db: &dyn Db, file: File, create_index: ast::CreateIndex) -> Option<String> {
1182    let position = create_index.syntax().text_range().start();
1183    bind(db, file)
1184        .search_path_at(position)
1185        .first()
1186        .map(|s| s.to_string())
1187}
1188
1189fn format_create_type(db: &dyn Db, file: File, create_type: ast::CreateType) -> Option<Hover> {
1190    let path = create_type.path()?;
1191    let (schema, type_name) = resolve::resolve_type_info(db, file, &path)?;
1192
1193    if let Some(variant_list) = create_type.variant_list() {
1194        let variants = variant_list.syntax().text().to_string();
1195        return Some(Hover::snippet(format!(
1196            "type {}.{} as enum {}",
1197            schema, type_name, variants
1198        )));
1199    }
1200
1201    if let Some(column_list) = create_type.column_list() {
1202        let columns = column_list.syntax().text().to_string();
1203        return Some(Hover::snippet(format!(
1204            "type {}.{} as {}",
1205            schema, type_name, columns
1206        )));
1207    }
1208
1209    if let Some(attribute_list) = create_type.attribute_list() {
1210        let attributes = attribute_list.syntax().text().to_string();
1211        return Some(Hover::snippet(format!(
1212            "type {}.{} {}",
1213            schema, type_name, attributes
1214        )));
1215    }
1216
1217    Some(Hover::snippet(format!("type {}.{}", schema, type_name)))
1218}
1219
1220fn hover_schema(db: &dyn Db, def: Location) -> Option<Hover> {
1221    let create_schema = def
1222        .to_node(db)?
1223        .ancestors()
1224        .find_map(ast::CreateSchema::cast)?;
1225    format_create_schema(create_schema)
1226}
1227
1228fn create_schema_name(create_schema: ast::CreateSchema) -> Option<String> {
1229    if let Some(schema_name) = create_schema.name() {
1230        return Some(schema_name.syntax().text().to_string());
1231    }
1232
1233    create_schema
1234        .role()
1235        .and_then(|r| r.name())
1236        .map(|n| n.syntax().text().to_string())
1237}
1238
1239fn format_create_schema(create_schema: ast::CreateSchema) -> Option<Hover> {
1240    let schema_name = create_schema_name(create_schema)?;
1241    Some(Hover::snippet(format!("schema {}", schema_name)))
1242}
1243
1244fn hover_function(db: &dyn Db, def: Location) -> Option<Hover> {
1245    let create_function = def
1246        .to_node(db)?
1247        .ancestors()
1248        .find_map(ast::CreateFunction::cast)?;
1249    format_create_function(db, def.file, create_function)
1250}
1251
1252fn hover_named_arg_parameter(db: &dyn Db, def: Location) -> Option<Hover> {
1253    let def_node = def.to_node(db)?;
1254    let param = def_node.ancestors().find_map(ast::Param::cast)?;
1255    let param_name = param.name().map(|name| Name::from_node(&name))?;
1256    let param_type = param.ty().map(|ty| ty.syntax().text().to_string());
1257
1258    for ancestor in def_node.ancestors() {
1259        if let Some(create_function) = ast::CreateFunction::cast(ancestor.clone()) {
1260            let path = create_function.path()?;
1261            let (schema, function_name) = resolve::resolve_function_info(db, def.file, &path)?;
1262            return Some(format_param_hover(
1263                schema,
1264                function_name,
1265                param_name,
1266                param_type,
1267            ));
1268        }
1269        if let Some(create_procedure) = ast::CreateProcedure::cast(ancestor.clone()) {
1270            let path = create_procedure.path()?;
1271            let (schema, procedure_name) = resolve::resolve_procedure_info(db, def.file, &path)?;
1272            return Some(format_param_hover(
1273                schema,
1274                procedure_name,
1275                param_name,
1276                param_type,
1277            ));
1278        }
1279        if let Some(create_aggregate) = ast::CreateAggregate::cast(ancestor) {
1280            let path = create_aggregate.path()?;
1281            let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, def.file, &path)?;
1282            return Some(format_param_hover(
1283                schema,
1284                aggregate_name,
1285                param_name,
1286                param_type,
1287            ));
1288        }
1289    }
1290
1291    None
1292}
1293
1294fn format_param_hover(
1295    schema: Schema,
1296    routine_name: String,
1297    param_name: Name,
1298    param_type: Option<String>,
1299) -> Hover {
1300    if let Some(param_type) = param_type {
1301        return Hover::snippet(format!(
1302            "parameter {}.{}.{} {}",
1303            schema, routine_name, param_name, param_type
1304        ));
1305    }
1306
1307    Hover::snippet(format!(
1308        "parameter {}.{}.{}",
1309        schema, routine_name, param_name
1310    ))
1311}
1312
1313fn format_create_function(
1314    db: &dyn Db,
1315    file: File,
1316    create_function: ast::CreateFunction,
1317) -> Option<Hover> {
1318    let path = create_function.path()?;
1319    let (schema, function_name) = resolve::resolve_function_info(db, file, &path)?;
1320
1321    let params = create_function.param_list()?.syntax().text().to_string();
1322    let return_type = create_function.ret_type()?.syntax().text().to_string();
1323    let snippet = format!(
1324        "function {}.{}{} {}",
1325        schema, function_name, params, return_type
1326    );
1327
1328    if let Some(comment) = preceding_comment(create_function.syntax()) {
1329        return Some(Hover::new(snippet, comment));
1330    }
1331
1332    Some(Hover::snippet(snippet))
1333}
1334
1335fn hover_aggregate(db: &dyn Db, def: Location) -> Option<Hover> {
1336    let create_aggregate = def
1337        .to_node(db)?
1338        .ancestors()
1339        .find_map(ast::CreateAggregate::cast)?;
1340    format_create_aggregate(db, def.file, create_aggregate)
1341}
1342
1343fn format_create_aggregate(
1344    db: &dyn Db,
1345    file: File,
1346    create_aggregate: ast::CreateAggregate,
1347) -> Option<Hover> {
1348    let path = create_aggregate.path()?;
1349    let (schema, aggregate_name) = resolve::resolve_aggregate_info(db, file, &path)?;
1350
1351    let param_list = create_aggregate.param_list()?;
1352    let params = param_list.syntax().text().to_string();
1353
1354    Some(Hover::snippet(format!(
1355        "aggregate {}.{}{}",
1356        schema, aggregate_name, params
1357    )))
1358}
1359
1360fn hover_procedure(db: &dyn Db, def: Location) -> Option<Hover> {
1361    let create_procedure = def
1362        .to_node(db)?
1363        .ancestors()
1364        .find_map(ast::CreateProcedure::cast)?;
1365    format_create_procedure(db, def.file, create_procedure)
1366}
1367
1368fn format_create_procedure(
1369    db: &dyn Db,
1370    file: File,
1371    create_procedure: ast::CreateProcedure,
1372) -> Option<Hover> {
1373    let path = create_procedure.path()?;
1374    let (schema, procedure_name) = resolve::resolve_procedure_info(db, file, &path)?;
1375
1376    let param_list = create_procedure.param_list()?;
1377    let params = param_list.syntax().text().to_string();
1378
1379    Some(Hover::snippet(format!(
1380        "procedure {}.{}{}",
1381        schema, procedure_name, params
1382    )))
1383}
1384
1385fn hover_routine(db: &dyn Db, def: Location) -> Option<Hover> {
1386    for ancestor in def.to_node(db)?.ancestors() {
1387        if let Some(create_function) = ast::CreateFunction::cast(ancestor.clone()) {
1388            return format_create_function(db, def.file, create_function);
1389        }
1390        if let Some(create_aggregate) = ast::CreateAggregate::cast(ancestor.clone()) {
1391            return format_create_aggregate(db, def.file, create_aggregate);
1392        }
1393        if let Some(create_procedure) = ast::CreateProcedure::cast(ancestor) {
1394            return format_create_procedure(db, def.file, create_procedure);
1395        }
1396    }
1397
1398    None
1399}
1400
1401fn qualified_star_table_ptr(
1402    db: &dyn Db,
1403    file: File,
1404    field_expr: ast::FieldExpr,
1405) -> Option<SyntaxNodePtr> {
1406    let table_name = resolve::qualified_star_table_name(&field_expr)?;
1407    let position = field_expr.syntax().text_range().start();
1408    let target = field_expr
1409        .syntax()
1410        .ancestors()
1411        .find_map(ast::Target::cast)?;
1412
1413    let path = match ast_nav::target_parent_query(target)? {
1414        ast_nav::ParentQuery::Select(select) => {
1415            let from_clause = select.from_clause()?;
1416            let from_item = resolve::find_from_item_in_from_clause(&from_clause, &table_name)?;
1417
1418            if let Some(alias) = from_item.alias()
1419                && alias.column_list().is_some()
1420            {
1421                return Some(SyntaxNodePtr::new(alias.syntax()));
1422            }
1423
1424            let (schema, table_name) = name::schema_and_table_from_from_item(&from_item)?;
1425
1426            let name_ref = from_item.name_ref();
1427            if let Some((table_like_ptr, _kind)) = resolve::resolve_table_like(
1428                db,
1429                file,
1430                name_ref.as_ref(),
1431                &table_name,
1432                &schema,
1433                position,
1434            ) {
1435                return Some(table_like_ptr);
1436            }
1437
1438            return None;
1439        }
1440        ast_nav::ParentQuery::Update(update) => update.relation_name()?.path()?,
1441        ast_nav::ParentQuery::Delete(delete) => delete.relation_name()?.path()?,
1442        ast_nav::ParentQuery::Insert(insert) => insert.path()?,
1443        ast_nav::ParentQuery::Merge(merge) => merge.relation_name()?.path()?,
1444    };
1445
1446    table_or_view_or_cte_ptrs(db, file, position, &path)?
1447        .into_iter()
1448        .next()
1449}
1450
1451fn table_or_view_or_cte_ptrs(
1452    db: &dyn Db,
1453    file: File,
1454    position: TextSize,
1455    path: &ast::Path,
1456) -> Option<Vec<SyntaxNodePtr>> {
1457    let (schema, table_name) = name::schema_and_name_path(path)?;
1458    let mut results = vec![];
1459    let name_ref = path.segment().and_then(|x| x.name_ref());
1460
1461    if let Some((table_like_ptr, _kind)) =
1462        resolve::resolve_table_like(db, file, name_ref.as_ref(), &table_name, &schema, position)
1463    {
1464        results.push(table_like_ptr);
1465    }
1466
1467    if results.is_empty() {
1468        return None;
1469    }
1470    Some(results)
1471}
1472
1473fn unqualified_star_table_ptrs(
1474    db: &dyn Db,
1475    file: File,
1476    target: &ast::Target,
1477) -> Option<Vec<SyntaxNodePtr>> {
1478    target.star_token()?;
1479
1480    let path = match ast_nav::target_parent_query(target.clone())? {
1481        ast_nav::ParentQuery::Select(select) => {
1482            let from_clause = select.from_clause()?;
1483            let results = resolve::table_ptrs_from_clause(db, file, &from_clause);
1484            if results.is_empty() {
1485                return None;
1486            }
1487            return Some(results);
1488        }
1489        ast_nav::ParentQuery::Update(update) => update.relation_name()?.path(),
1490        ast_nav::ParentQuery::Insert(insert) => insert.path(),
1491        ast_nav::ParentQuery::Delete(delete) => delete.relation_name()?.path(),
1492        ast_nav::ParentQuery::Merge(merge) => merge.relation_name()?.path(),
1493    }?;
1494
1495    let position = target.syntax().text_range().start();
1496    table_or_view_or_cte_ptrs(db, file, position, &path)
1497}
1498
1499fn unqualified_star_in_arg_list_ptrs(
1500    db: &dyn Db,
1501    file: File,
1502    arg_list: &ast::ArgList,
1503) -> Option<Vec<SyntaxNodePtr>> {
1504    let from_clause = arg_list
1505        .syntax()
1506        .ancestors()
1507        .find_map(ast::Select::cast)?
1508        .from_clause()?;
1509    let results = resolve::table_ptrs_from_clause(db, file, &from_clause);
1510
1511    if results.is_empty() {
1512        return None;
1513    }
1514
1515    Some(results)
1516}
1517
1518#[cfg(test)]
1519mod test {
1520    use crate::db::{Database, File};
1521    use crate::hover::hover;
1522    use crate::test_utils::Fixture;
1523    use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
1524    use insta::assert_snapshot;
1525
1526    #[track_caller]
1527    fn check_hover(sql: &str) -> String {
1528        check_hover_(sql).expect("should find hover information")
1529    }
1530
1531    #[track_caller]
1532    fn check_hover_(sql: &str) -> Option<String> {
1533        let db = Database::default();
1534        let fixture = Fixture::new(sql);
1535        let marker = fixture.marker();
1536        let offset = marker.offset_before();
1537        let hover_span = marker.range();
1538        let sql = fixture.sql();
1539        let file = File::new(&db, sql.into());
1540        assert_eq!(crate::db::parse(&db, file).errors(), vec![]);
1541
1542        if let Some(type_info) = hover(&db, file, offset) {
1543            let title = format!("hover: {}", type_info.snippet);
1544            let group = Level::INFO.primary_title(&title).element(
1545                Snippet::source(sql)
1546                    .fold(true)
1547                    .annotation(AnnotationKind::Context.span(hover_span).label("hover")),
1548            );
1549            let renderer = Renderer::plain().decor_style(DecorStyle::Unicode);
1550            return Some(
1551                renderer
1552                    .render(&[group])
1553                    .to_string()
1554                    // neater
1555                    .replace("info: hover:", "hover:"),
1556            );
1557        }
1558        None
1559    }
1560
1561    #[track_caller]
1562    fn check_hover_info(sql: &str) -> super::Hover {
1563        let db = Database::default();
1564        let fixture = Fixture::new(sql);
1565        let offset = fixture.marker().offset_before();
1566        let sql = fixture.sql();
1567        let file = File::new(&db, sql.into());
1568        assert_eq!(crate::db::parse(&db, file).errors(), vec![]);
1569
1570        hover(&db, file, offset).expect("should find hover information")
1571    }
1572
1573    #[test]
1574    fn hover_column_in_create_index() {
1575        assert_snapshot!(check_hover("
1576create table users(id int, email text);
1577create index idx_email on users(email$0);
1578"), @r"
1579        hover: column public.users.email text
1580          ╭▸ 
1581        3 │ create index idx_email on users(email);
1582          ╰╴                                    ─ hover
1583        ");
1584    }
1585
1586    #[test]
1587    fn hover_column_int_type() {
1588        assert_snapshot!(check_hover("
1589create table users(id int, email text);
1590create index idx_id on users(id$0);
1591"), @r"
1592        hover: column public.users.id int
1593          ╭▸ 
1594        3 │ create index idx_id on users(id);
1595          ╰╴                              ─ hover
1596        ");
1597    }
1598
1599    #[test]
1600    fn hover_column_with_schema() {
1601        assert_snapshot!(check_hover("
1602create table public.users(id int, email text);
1603create index idx_email on public.users(email$0);
1604"), @r"
1605        hover: column public.users.email text
1606          ╭▸ 
1607        3 │ create index idx_email on public.users(email);
1608          ╰╴                                           ─ hover
1609        ");
1610    }
1611
1612    #[test]
1613    fn hover_column_temp_table() {
1614        assert_snapshot!(check_hover("
1615create temp table users(id int, email text);
1616create index idx_email on users(email$0);
1617"), @r"
1618        hover: column pg_temp.users.email text
1619          ╭▸ 
1620        3 │ create index idx_email on users(email);
1621          ╰╴                                    ─ hover
1622        ");
1623    }
1624
1625    #[test]
1626    fn hover_column_multiple_columns() {
1627        assert_snapshot!(check_hover("
1628create table users(id int, email text, name varchar(100));
1629create index idx_users on users(id, email$0, name);
1630"), @r"
1631        hover: column public.users.email text
1632          ╭▸ 
1633        3 │ create index idx_users on users(id, email, name);
1634          ╰╴                                        ─ hover
1635        ");
1636    }
1637
1638    #[test]
1639    fn hover_column_varchar() {
1640        assert_snapshot!(check_hover("
1641create table users(id int, name varchar(100));
1642create index idx_name on users(name$0);
1643"), @r"
1644        hover: column public.users.name varchar(100)
1645          ╭▸ 
1646        3 │ create index idx_name on users(name);
1647          ╰╴                                  ─ hover
1648        ");
1649    }
1650
1651    #[test]
1652    fn hover_column_bigint() {
1653        assert_snapshot!(check_hover("
1654create table metrics(value bigint);
1655create index idx_value on metrics(value$0);
1656"), @r"
1657        hover: column public.metrics.value bigint
1658          ╭▸ 
1659        3 │ create index idx_value on metrics(value);
1660          ╰╴                                      ─ hover
1661        ");
1662    }
1663
1664    #[test]
1665    fn hover_column_timestamp() {
1666        assert_snapshot!(check_hover("
1667create table events(created_at timestamp with time zone);
1668create index idx_created on events(created_at$0);
1669"), @r"
1670        hover: column public.events.created_at timestamp with time zone
1671          ╭▸ 
1672        3 │ create index idx_created on events(created_at);
1673          ╰╴                                            ─ hover
1674        ");
1675    }
1676
1677    #[test]
1678    fn hover_column_with_search_path() {
1679        assert_snapshot!(check_hover(r#"
1680set search_path to myschema;
1681create table myschema.users(id int, email text);
1682create index idx_email on users(email$0);
1683"#), @r"
1684        hover: column myschema.users.email text
1685          ╭▸ 
1686        4 │ create index idx_email on users(email);
1687          ╰╴                                    ─ hover
1688        ");
1689    }
1690
1691    #[test]
1692    fn hover_column_explicit_schema_overrides_search_path() {
1693        assert_snapshot!(check_hover(r#"
1694set search_path to myschema;
1695create table public.users(id int, email text);
1696create table myschema.users(value bigint);
1697create index idx_email on public.users(email$0);
1698"#), @r"
1699        hover: column public.users.email text
1700          ╭▸ 
1701        5 │ create index idx_email on public.users(email);
1702          ╰╴                                           ─ hover
1703        ");
1704    }
1705
1706    #[test]
1707    fn hover_on_table_name() {
1708        assert_snapshot!(check_hover("
1709create table t(id int);
1710create index idx on t$0(id);
1711"), @r"
1712        hover: table public.t(id int)
1713          ╭▸ 
1714        3 │ create index idx on t(id);
1715          ╰╴                    ─ hover
1716        ");
1717    }
1718
1719    #[test]
1720    fn hover_on_index_name_in_create() {
1721        assert_snapshot!(check_hover("
1722create table users(id int);
1723create index idx$0 on users(id);
1724"), @r"
1725        hover: index public.idx on public.users(id)
1726          ╭▸ 
1727        3 │ create index idx on users(id);
1728          ╰╴               ─ hover
1729        ");
1730    }
1731
1732    #[test]
1733    fn hover_table_in_create_index() {
1734        assert_snapshot!(check_hover("
1735create table users(id int, email text);
1736create index idx_email on users$0(email);
1737"), @r"
1738        hover: table public.users(id int, email text)
1739          ╭▸ 
1740        3 │ create index idx_email on users(email);
1741          ╰╴                              ─ hover
1742        ");
1743    }
1744
1745    #[test]
1746    fn hover_table_with_schema() {
1747        assert_snapshot!(check_hover("
1748create table public.users(id int, email text);
1749create index idx on public.users$0(id);
1750"), @r"
1751        hover: table public.users(id int, email text)
1752          ╭▸ 
1753        3 │ create index idx on public.users(id);
1754          ╰╴                               ─ hover
1755        ");
1756    }
1757
1758    #[test]
1759    fn hover_table_temp() {
1760        assert_snapshot!(check_hover("
1761create temp table users(id int, email text);
1762create index idx on users$0(id);
1763"), @r"
1764        hover: table pg_temp.users(id int, email text)
1765          ╭▸ 
1766        3 │ create index idx on users(id);
1767          ╰╴                        ─ hover
1768        ");
1769    }
1770
1771    #[test]
1772    fn hover_table_multiline() {
1773        assert_snapshot!(check_hover("
1774create table users(
1775    id int,
1776    email text,
1777    name varchar(100)
1778);
1779create index idx on users$0(id);
1780"), @r"
1781        hover: table public.users(
1782                  id int,
1783                  email text,
1784                  name varchar(100)
1785              )
1786          ╭▸ 
1787        7 │ create index idx on users(id);
1788          ╰╴                        ─ hover
1789        ");
1790    }
1791
1792    #[test]
1793    fn hover_table_with_search_path() {
1794        assert_snapshot!(check_hover(r#"
1795set search_path to myschema;
1796create table users(id int, email text);
1797create index idx on users$0(id);
1798"#), @r"
1799        hover: table myschema.users(id int, email text)
1800          ╭▸ 
1801        4 │ create index idx on users(id);
1802          ╰╴                        ─ hover
1803        ");
1804    }
1805
1806    #[test]
1807    fn hover_table_search_path_at_definition() {
1808        assert_snapshot!(check_hover(r#"
1809set search_path to myschema;
1810create table users(id int, email text);
1811set search_path to myschema, otherschema;
1812create index idx on users$0(id);
1813"#), @r"
1814        hover: table myschema.users(id int, email text)
1815          ╭▸ 
1816        5 │ create index idx on users(id);
1817          ╰╴                        ─ hover
1818        ");
1819    }
1820
1821    #[test]
1822    fn hover_on_create_table_definition() {
1823        assert_snapshot!(check_hover("
1824create table t$0(x bigint);
1825"), @r"
1826        hover: table public.t(x bigint)
1827          ╭▸ 
1828        2 │ create table t(x bigint);
1829          ╰╴             ─ hover
1830        ");
1831    }
1832
1833    #[test]
1834    fn hover_on_create_table_definition_with_schema() {
1835        assert_snapshot!(check_hover("
1836create table myschema.users$0(id int);
1837"), @r"
1838        hover: table myschema.users(id int)
1839          ╭▸ 
1840        2 │ create table myschema.users(id int);
1841          ╰╴                          ─ hover
1842        ");
1843    }
1844
1845    #[test]
1846    fn hover_on_create_temp_table_definition() {
1847        assert_snapshot!(check_hover("
1848create temp table t$0(x bigint);
1849"), @r"
1850        hover: table pg_temp.t(x bigint)
1851          ╭▸ 
1852        2 │ create temp table t(x bigint);
1853          ╰╴                  ─ hover
1854        ");
1855    }
1856
1857    #[test]
1858    fn hover_on_column_in_create_table() {
1859        assert_snapshot!(check_hover("
1860create table t(id$0 int);
1861"), @r"
1862        hover: column public.t.id int
1863          ╭▸ 
1864        2 │ create table t(id int);
1865          ╰╴                ─ hover
1866        ");
1867    }
1868
1869    #[test]
1870    fn hover_on_column_in_create_table_with_schema() {
1871        assert_snapshot!(check_hover("
1872create table myschema.users(id$0 int, name text);
1873"), @r"
1874        hover: column myschema.users.id int
1875          ╭▸ 
1876        2 │ create table myschema.users(id int, name text);
1877          ╰╴                             ─ hover
1878        ");
1879    }
1880
1881    #[test]
1882    fn hover_on_column_in_temp_table() {
1883        assert_snapshot!(check_hover("
1884create temp table t(x$0 bigint);
1885"), @r"
1886        hover: column pg_temp.t.x bigint
1887          ╭▸ 
1888        2 │ create temp table t(x bigint);
1889          ╰╴                    ─ hover
1890        ");
1891    }
1892
1893    #[test]
1894    fn hover_on_multiple_columns() {
1895        assert_snapshot!(check_hover("
1896create table t(id int, email$0 text, name varchar(100));
1897"), @r"
1898        hover: column public.t.email text
1899          ╭▸ 
1900        2 │ create table t(id int, email text, name varchar(100));
1901          ╰╴                           ─ hover
1902        ");
1903    }
1904
1905    #[test]
1906    fn hover_on_drop_table() {
1907        assert_snapshot!(check_hover("
1908create table users(id int, email text);
1909drop table users$0;
1910"), @r"
1911        hover: table public.users(id int, email text)
1912          ╭▸ 
1913        3 │ drop table users;
1914          ╰╴               ─ hover
1915        ");
1916    }
1917
1918    #[test]
1919    fn hover_on_drop_table_with_schema() {
1920        assert_snapshot!(check_hover("
1921create table myschema.users(id int);
1922drop table myschema.users$0;
1923"), @r"
1924        hover: table myschema.users(id int)
1925          ╭▸ 
1926        3 │ drop table myschema.users;
1927          ╰╴                        ─ hover
1928        ");
1929    }
1930
1931    #[test]
1932    fn hover_on_drop_temp_table() {
1933        assert_snapshot!(check_hover("
1934create temp table t(x bigint);
1935drop table t$0;
1936"), @r"
1937        hover: table pg_temp.t(x bigint)
1938          ╭▸ 
1939        3 │ drop table t;
1940          ╰╴           ─ hover
1941        ");
1942    }
1943
1944    #[test]
1945    fn hover_on_create_index_definition() {
1946        assert_snapshot!(check_hover("
1947create table t(x bigint);
1948create index idx$0 on t(x);
1949"), @r"
1950        hover: index public.idx on public.t(x)
1951          ╭▸ 
1952        3 │ create index idx on t(x);
1953          ╰╴               ─ hover
1954        ");
1955    }
1956
1957    #[test]
1958    fn hover_on_drop_index() {
1959        assert_snapshot!(check_hover("
1960create table t(x bigint);
1961create index idx_x on t(x);
1962drop index idx_x$0;
1963"), @r"
1964        hover: index public.idx_x on public.t(x)
1965          ╭▸ 
1966        4 │ drop index idx_x;
1967          ╰╴               ─ hover
1968        ");
1969    }
1970
1971    #[test]
1972    fn hover_on_create_type_definition() {
1973        assert_snapshot!(check_hover("
1974create type status$0 as enum ('active', 'inactive');
1975"), @r"
1976        hover: type public.status as enum ('active', 'inactive')
1977          ╭▸ 
1978        2 │ create type status as enum ('active', 'inactive');
1979          ╰╴                 ─ hover
1980        ");
1981    }
1982
1983    #[test]
1984    fn hover_on_create_type_definition_with_schema() {
1985        assert_snapshot!(check_hover("
1986create type myschema.status$0 as enum ('active', 'inactive');
1987"), @r"
1988        hover: type myschema.status as enum ('active', 'inactive')
1989          ╭▸ 
1990        2 │ create type myschema.status as enum ('active', 'inactive');
1991          ╰╴                          ─ hover
1992        ");
1993    }
1994
1995    #[test]
1996    fn hover_on_drop_type() {
1997        assert_snapshot!(check_hover("
1998create type status as enum ('active', 'inactive');
1999drop type status$0;
2000"), @r"
2001        hover: type public.status as enum ('active', 'inactive')
2002          ╭▸ 
2003        3 │ drop type status;
2004          ╰╴               ─ hover
2005        ");
2006    }
2007
2008    #[test]
2009    fn hover_on_drop_type_with_schema() {
2010        assert_snapshot!(check_hover("
2011create type myschema.status as enum ('active', 'inactive');
2012drop type myschema.status$0;
2013"), @r"
2014        hover: type myschema.status as enum ('active', 'inactive')
2015          ╭▸ 
2016        3 │ drop type myschema.status;
2017          ╰╴                        ─ hover
2018        ");
2019    }
2020
2021    #[test]
2022    fn hover_on_create_type_composite() {
2023        assert_snapshot!(check_hover("
2024create type person$0 as (name text, age int);
2025"), @r"
2026        hover: type public.person as (name text, age int)
2027          ╭▸ 
2028        2 │ create type person as (name text, age int);
2029          ╰╴                 ─ hover
2030        ");
2031    }
2032
2033    #[test]
2034    fn hover_on_drop_type_composite() {
2035        assert_snapshot!(check_hover("
2036create type person as (name text, age int);
2037drop type person$0;
2038"), @r"
2039        hover: type public.person as (name text, age int)
2040          ╭▸ 
2041        3 │ drop type person;
2042          ╰╴               ─ hover
2043        ");
2044    }
2045
2046    #[test]
2047    fn hover_on_create_type_range() {
2048        assert_snapshot!(check_hover("
2049create type int4_range$0 as range (subtype = int4);
2050"), @r"
2051        hover: type public.int4_range (subtype = int4)
2052          ╭▸ 
2053        2 │ create type int4_range as range (subtype = int4);
2054          ╰╴                     ─ hover
2055        ");
2056    }
2057
2058    #[test]
2059    fn hover_on_drop_type_range() {
2060        assert_snapshot!(check_hover("
2061create type int4_range as range (subtype = int4);
2062drop type int4_range$0;
2063"), @r"
2064        hover: type public.int4_range (subtype = int4)
2065          ╭▸ 
2066        3 │ drop type int4_range;
2067          ╰╴                   ─ hover
2068        ");
2069    }
2070
2071    #[test]
2072    fn hover_on_cast_operator() {
2073        assert_snapshot!(check_hover("
2074create type foo as enum ('a', 'b');
2075select x::foo$0;
2076"), @r"
2077        hover: type public.foo as enum ('a', 'b')
2078          ╭▸ 
2079        3 │ select x::foo;
2080          ╰╴            ─ hover
2081        ");
2082    }
2083
2084    #[test]
2085    fn hover_on_cast_function() {
2086        assert_snapshot!(check_hover("
2087create type bar as enum ('x', 'y');
2088select cast(x as bar$0);
2089"), @r"
2090        hover: type public.bar as enum ('x', 'y')
2091          ╭▸ 
2092        3 │ select cast(x as bar);
2093          ╰╴                   ─ hover
2094        ");
2095    }
2096
2097    #[test]
2098    fn hover_on_cast_with_schema() {
2099        assert_snapshot!(check_hover("
2100create type myschema.baz as enum ('m', 'n');
2101select x::myschema.baz$0;
2102"), @r"
2103        hover: type myschema.baz as enum ('m', 'n')
2104          ╭▸ 
2105        3 │ select x::myschema.baz;
2106          ╰╴                     ─ hover
2107        ");
2108    }
2109
2110    #[test]
2111    fn hover_on_drop_function() {
2112        assert_snapshot!(check_hover("
2113create function foo() returns int as $$ select 1 $$ language sql;
2114drop function foo$0();
2115"), @r"
2116        hover: function public.foo() returns int
2117          ╭▸ 
2118        3 │ drop function foo();
2119          ╰╴                ─ hover
2120        ");
2121    }
2122
2123    #[test]
2124    fn hover_on_drop_function_with_schema() {
2125        assert_snapshot!(check_hover("
2126create function myschema.foo() returns int as $$ select 1 $$ language sql;
2127drop function myschema.foo$0();
2128"), @r"
2129        hover: function myschema.foo() returns int
2130          ╭▸ 
2131        3 │ drop function myschema.foo();
2132          ╰╴                         ─ hover
2133        ");
2134    }
2135
2136    #[test]
2137    fn hover_on_create_function_definition() {
2138        assert_snapshot!(check_hover("
2139create function foo$0() returns int as $$ select 1 $$ language sql;
2140"), @r"
2141        hover: function public.foo() returns int
2142          ╭▸ 
2143        2 │ create function foo() returns int as $$ select 1 $$ language sql;
2144          ╰╴                  ─ hover
2145        ");
2146    }
2147
2148    #[test]
2149    fn hover_on_create_function_with_explicit_schema() {
2150        assert_snapshot!(check_hover("
2151create function myschema.foo$0() returns int as $$ select 1 $$ language sql;
2152"), @r"
2153        hover: function myschema.foo() returns int
2154          ╭▸ 
2155        2 │ create function myschema.foo() returns int as $$ select 1 $$ language sql;
2156          ╰╴                           ─ hover
2157        ");
2158    }
2159
2160    #[test]
2161    fn hover_function_extracts_preceding_comment() {
2162        let hover = check_hover_info(
2163            "
2164-- this is a doc comment
2165-- for foo
2166create function foo() returns int as $$ select 1 $$ language sql;
2167select foo$0();
2168",
2169        );
2170        assert_snapshot!(hover.markdown(), @"
2171        ```sql
2172        function public.foo() returns int
2173        ```
2174        ---
2175        this is a doc comment
2176        for foo
2177        ");
2178    }
2179
2180    #[test]
2181    fn hover_on_drop_function_with_search_path() {
2182        assert_snapshot!(check_hover(r#"
2183set search_path to myschema;
2184create function foo() returns int as $$ select 1 $$ language sql;
2185drop function foo$0();
2186"#), @r"
2187        hover: function myschema.foo() returns int
2188          ╭▸ 
2189        4 │ drop function foo();
2190          ╰╴                ─ hover
2191        ");
2192    }
2193
2194    #[test]
2195    fn hover_on_drop_function_overloaded() {
2196        assert_snapshot!(check_hover("
2197create function add(complex) returns complex as $$ select null $$ language sql;
2198create function add(bigint) returns bigint as $$ select 1 $$ language sql;
2199drop function add$0(complex);
2200"), @r"
2201        hover: function public.add(complex) returns complex
2202          ╭▸ 
2203        4 │ drop function add(complex);
2204          ╰╴                ─ hover
2205        ");
2206    }
2207
2208    #[test]
2209    fn hover_on_drop_function_second_overload() {
2210        assert_snapshot!(check_hover("
2211create function add(complex) returns complex as $$ select null $$ language sql;
2212create function add(bigint) returns bigint as $$ select 1 $$ language sql;
2213drop function add$0(bigint);
2214"), @r"
2215        hover: function public.add(bigint) returns bigint
2216          ╭▸ 
2217        4 │ drop function add(bigint);
2218          ╰╴                ─ hover
2219        ");
2220    }
2221
2222    #[test]
2223    fn hover_on_drop_aggregate() {
2224        assert_snapshot!(check_hover("
2225create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
2226drop aggregate myavg$0(int);
2227"), @r"
2228        hover: aggregate public.myavg(int)
2229          ╭▸ 
2230        3 │ drop aggregate myavg(int);
2231          ╰╴                   ─ hover
2232        ");
2233    }
2234
2235    #[test]
2236    fn hover_on_drop_aggregate_with_schema() {
2237        assert_snapshot!(check_hover("
2238create aggregate myschema.myavg(int) (sfunc = int4_avg_accum, stype = _int8);
2239drop aggregate myschema.myavg$0(int);
2240"), @r"
2241        hover: aggregate myschema.myavg(int)
2242          ╭▸ 
2243        3 │ drop aggregate myschema.myavg(int);
2244          ╰╴                            ─ hover
2245        ");
2246    }
2247
2248    #[test]
2249    fn hover_on_create_aggregate_definition() {
2250        assert_snapshot!(check_hover("
2251create aggregate myavg$0(int) (sfunc = int4_avg_accum, stype = _int8);
2252"), @r"
2253        hover: aggregate public.myavg(int)
2254          ╭▸ 
2255        2 │ create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
2256          ╰╴                     ─ hover
2257        ");
2258    }
2259
2260    #[test]
2261    fn hover_on_drop_aggregate_with_search_path() {
2262        assert_snapshot!(check_hover(r#"
2263set search_path to myschema;
2264create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
2265drop aggregate myavg$0(int);
2266"#), @r"
2267        hover: aggregate myschema.myavg(int)
2268          ╭▸ 
2269        4 │ drop aggregate myavg(int);
2270          ╰╴                   ─ hover
2271        ");
2272    }
2273
2274    #[test]
2275    fn hover_on_drop_aggregate_overloaded() {
2276        assert_snapshot!(check_hover("
2277create aggregate sum(complex) (sfunc = complex_add, stype = complex, initcond = '(0,0)');
2278create aggregate sum(bigint) (sfunc = bigint_add, stype = bigint, initcond = '0');
2279drop aggregate sum$0(complex);
2280"), @r"
2281        hover: aggregate public.sum(complex)
2282          ╭▸ 
2283        4 │ drop aggregate sum(complex);
2284          ╰╴                 ─ hover
2285        ");
2286    }
2287
2288    #[test]
2289    fn hover_on_drop_aggregate_second_overload() {
2290        assert_snapshot!(check_hover("
2291create aggregate sum(complex) (sfunc = complex_add, stype = complex, initcond = '(0,0)');
2292create aggregate sum(bigint) (sfunc = bigint_add, stype = bigint, initcond = '0');
2293drop aggregate sum$0(bigint);
2294"), @r"
2295        hover: aggregate public.sum(bigint)
2296          ╭▸ 
2297        4 │ drop aggregate sum(bigint);
2298          ╰╴                 ─ hover
2299        ");
2300    }
2301
2302    #[test]
2303    fn hover_on_select_function_call() {
2304        assert_snapshot!(check_hover("
2305create function foo() returns int as $$ select 1 $$ language sql;
2306select foo$0();
2307"), @r"
2308        hover: function public.foo() returns int
2309          ╭▸ 
2310        3 │ select foo();
2311          ╰╴         ─ hover
2312        ");
2313    }
2314
2315    #[test]
2316    fn hover_on_select_function_call_with_schema() {
2317        assert_snapshot!(check_hover("
2318create function public.foo() returns int as $$ select 1 $$ language sql;
2319select public.foo$0();
2320"), @r"
2321        hover: function public.foo() returns int
2322          ╭▸ 
2323        3 │ select public.foo();
2324          ╰╴                ─ hover
2325        ");
2326    }
2327
2328    #[test]
2329    fn hover_on_select_function_call_with_search_path() {
2330        assert_snapshot!(check_hover(r#"
2331set search_path to myschema;
2332create function foo() returns int as $$ select 1 $$ language sql;
2333select foo$0();
2334"#), @r"
2335        hover: function myschema.foo() returns int
2336          ╭▸ 
2337        4 │ select foo();
2338          ╰╴         ─ hover
2339        ");
2340    }
2341
2342    #[test]
2343    fn hover_on_select_function_call_with_params() {
2344        assert_snapshot!(check_hover("
2345create function add(a int, b int) returns int as $$ select a + b $$ language sql;
2346select add$0(1, 2);
2347"), @r"
2348        hover: function public.add(a int, b int) returns int
2349          ╭▸ 
2350        3 │ select add(1, 2);
2351          ╰╴         ─ hover
2352        ");
2353    }
2354
2355    #[test]
2356    fn hover_on_builtin_function_call() {
2357        assert_snapshot!(check_hover("
2358select now$0();
2359"), @r"
2360        hover: function pg_catalog.now() returns timestamp with time zone
2361          ╭▸ 
2362        2 │ select now();
2363          ╰╴         ─ hover
2364        ");
2365    }
2366
2367    #[test]
2368    fn hover_on_named_arg_param() {
2369        assert_snapshot!(check_hover("
2370create function foo(bar_param int) returns int as $$ select 1 $$ language sql;
2371select foo(bar_param$0 := 5);
2372"), @r"
2373        hover: parameter public.foo.bar_param int
2374          ╭▸ 
2375        3 │ select foo(bar_param := 5);
2376          ╰╴                   ─ hover
2377        ");
2378    }
2379
2380    #[test]
2381    fn hover_on_named_arg_param_schema_qualified() {
2382        assert_snapshot!(check_hover("
2383create schema s;
2384create function s.foo(my_param int) returns int as $$ select 1 $$ language sql;
2385select s.foo(my_param$0 := 10);
2386"), @r"
2387        hover: parameter s.foo.my_param int
2388          ╭▸ 
2389        4 │ select s.foo(my_param := 10);
2390          ╰╴                    ─ hover
2391        ");
2392    }
2393
2394    #[test]
2395    fn hover_on_named_arg_param_procedure() {
2396        assert_snapshot!(check_hover("
2397create procedure proc(param_x int) as 'select 1' language sql;
2398call proc(param_x$0 := 42);
2399"), @r"
2400        hover: parameter public.proc.param_x int
2401          ╭▸ 
2402        3 │ call proc(param_x := 42);
2403          ╰╴                ─ hover
2404        ");
2405    }
2406
2407    #[test]
2408    fn hover_on_function_call_style_column_access() {
2409        assert_snapshot!(check_hover("
2410create table t(a int, b int);
2411select a$0(t) from t;
2412"), @r"
2413        hover: column public.t.a int
2414          ╭▸ 
2415        3 │ select a(t) from t;
2416          ╰╴       ─ hover
2417        ");
2418    }
2419
2420    #[test]
2421    fn hover_on_function_call_style_column_access_with_function_precedence() {
2422        assert_snapshot!(check_hover("
2423create table t(a int, b int);
2424create function b(t) returns int as 'select 1' LANGUAGE sql;
2425select b$0(t) from t;
2426"), @r"
2427        hover: function public.b(t) returns int
2428          ╭▸ 
2429        4 │ select b(t) from t;
2430          ╰╴       ─ hover
2431        ");
2432    }
2433
2434    #[test]
2435    fn hover_on_function_call_style_table_arg() {
2436        assert_snapshot!(check_hover("
2437create table t(a int, b int);
2438select a(t$0) from t;
2439"), @r"
2440        hover: table public.t(a int, b int)
2441          ╭▸ 
2442        3 │ select a(t) from t;
2443          ╰╴         ─ hover
2444        ");
2445    }
2446
2447    #[test]
2448    fn hover_on_function_call_style_table_arg_with_function() {
2449        assert_snapshot!(check_hover("
2450create table t(a int, b int);
2451create function b(t) returns int as 'select 1' LANGUAGE sql;
2452select b(t$0) from t;
2453"), @r"
2454        hover: table public.t(a int, b int)
2455          ╭▸ 
2456        4 │ select b(t) from t;
2457          ╰╴         ─ hover
2458        ");
2459    }
2460
2461    #[test]
2462    fn hover_on_function_call_style_table_arg_in_where() {
2463        assert_snapshot!(check_hover("
2464create table t(a int);
2465select * from t where a(t$0) > 2;
2466"), @r"
2467        hover: table public.t(a int)
2468          ╭▸ 
2469        3 │ select * from t where a(t) > 2;
2470          ╰╴                        ─ hover
2471        ");
2472    }
2473
2474    #[test]
2475    fn hover_on_qualified_table_ref_in_where() {
2476        assert_snapshot!(check_hover("
2477create table t(a int);
2478create function b(t) returns int as 'select 1' language sql;
2479select * from t where t$0.b > 2;
2480"), @r"
2481        hover: table public.t(a int)
2482          ╭▸ 
2483        4 │ select * from t where t.b > 2;
2484          ╰╴                      ─ hover
2485        ");
2486    }
2487
2488    #[test]
2489    fn hover_on_field_style_function_call() {
2490        assert_snapshot!(check_hover("
2491create table t(a int);
2492create function b(t) returns int as 'select 1' language sql;
2493select t.b$0 from t;
2494"), @r"
2495        hover: function public.b(t) returns int
2496          ╭▸ 
2497        4 │ select t.b from t;
2498          ╰╴         ─ hover
2499        ");
2500    }
2501
2502    #[test]
2503    fn hover_on_field_style_function_call_column_precedence() {
2504        assert_snapshot!(check_hover("
2505create table t(a int, b int);
2506create function b(t) returns int as 'select 1' language sql;
2507select t.b$0 from t;
2508"), @r"
2509        hover: column public.t.b int
2510          ╭▸ 
2511        4 │ select t.b from t;
2512          ╰╴         ─ hover
2513        ");
2514    }
2515
2516    #[test]
2517    fn hover_on_field_style_function_call_table_ref() {
2518        assert_snapshot!(check_hover("
2519create table t(a int);
2520create function b(t) returns int as 'select 1' language sql;
2521select t$0.b from t;
2522"), @r"
2523        hover: table public.t(a int)
2524          ╭▸ 
2525        4 │ select t.b from t;
2526          ╰╴       ─ hover
2527        ");
2528    }
2529
2530    #[test]
2531    fn hover_on_select_from_table() {
2532        assert_snapshot!(check_hover("
2533create table users(id int, email text);
2534select * from users$0;
2535"), @r"
2536        hover: table public.users(id int, email text)
2537          ╭▸ 
2538        3 │ select * from users;
2539          ╰╴                  ─ hover
2540        ");
2541    }
2542
2543    #[test]
2544    fn hover_on_subquery_qualified_table_ref() {
2545        assert_snapshot!(check_hover("
2546select t$0.a from (select 1 a) t;
2547"), @r"
2548        hover: subquery t as (select 1 a)
2549          ╭▸ 
2550        2 │ select t.a from (select 1 a) t;
2551          ╰╴       ─ hover
2552        ");
2553    }
2554
2555    #[test]
2556    fn hover_on_subquery_qualified_column_ref() {
2557        assert_snapshot!(check_hover("
2558select t.a$0 from (select 1 a) t;
2559"), @r"
2560        hover: column t.a
2561          ╭▸ 
2562        2 │ select t.a from (select 1 a) t;
2563          ╰╴         ─ hover
2564        ");
2565    }
2566
2567    #[test]
2568    fn hover_on_subquery_unqualified_column_ref_with_alias() {
2569        assert_snapshot!(check_hover("
2570select a$0 from (select 1 a) t;
2571"), @"
2572        hover: column t.a
2573          ╭▸ 
2574        2 │ select a from (select 1 a) t;
2575          ╰╴       ─ hover
2576        ");
2577    }
2578
2579    #[test]
2580    fn hover_on_select_from_table_with_schema() {
2581        assert_snapshot!(check_hover("
2582create table public.users(id int, email text);
2583select * from public.users$0;
2584"), @r"
2585        hover: table public.users(id int, email text)
2586          ╭▸ 
2587        3 │ select * from public.users;
2588          ╰╴                         ─ hover
2589        ");
2590    }
2591
2592    #[test]
2593    fn hover_on_select_from_table_with_search_path() {
2594        assert_snapshot!(check_hover("
2595set search_path to foo;
2596create table foo.users(id int, email text);
2597select * from users$0;
2598"), @r"
2599        hover: table foo.users(id int, email text)
2600          ╭▸ 
2601        4 │ select * from users;
2602          ╰╴                  ─ hover
2603        ");
2604    }
2605
2606    #[test]
2607    fn hover_on_select_from_temp_table() {
2608        assert_snapshot!(check_hover("
2609create temp table users(id int, email text);
2610select * from users$0;
2611"), @r"
2612        hover: table pg_temp.users(id int, email text)
2613          ╭▸ 
2614        3 │ select * from users;
2615          ╰╴                  ─ hover
2616        ");
2617    }
2618
2619    #[test]
2620    fn hover_on_select_from_multiline_table() {
2621        assert_snapshot!(check_hover("
2622create table users(
2623    id int,
2624    email text,
2625    name varchar(100)
2626);
2627select * from users$0;
2628"), @r"
2629        hover: table public.users(
2630                  id int,
2631                  email text,
2632                  name varchar(100)
2633              )
2634          ╭▸ 
2635        7 │ select * from users;
2636          ╰╴                  ─ hover
2637        ");
2638    }
2639
2640    #[test]
2641    fn hover_on_select_column() {
2642        assert_snapshot!(check_hover("
2643create table users(id int, email text);
2644select id$0 from users;
2645"), @r"
2646        hover: column public.users.id int
2647          ╭▸ 
2648        3 │ select id from users;
2649          ╰╴        ─ hover
2650        ");
2651    }
2652
2653    #[test]
2654    fn hover_on_select_column_second() {
2655        assert_snapshot!(check_hover("
2656create table users(id int, email text);
2657select id, email$0 from users;
2658"), @r"
2659        hover: column public.users.email text
2660          ╭▸ 
2661        3 │ select id, email from users;
2662          ╰╴               ─ hover
2663        ");
2664    }
2665
2666    #[test]
2667    fn hover_on_select_column_with_schema() {
2668        assert_snapshot!(check_hover("
2669create table public.users(id int, email text);
2670select email$0 from public.users;
2671"), @r"
2672        hover: column public.users.email text
2673          ╭▸ 
2674        3 │ select email from public.users;
2675          ╰╴           ─ hover
2676        ");
2677    }
2678
2679    #[test]
2680    fn hover_on_select_column_with_search_path() {
2681        assert_snapshot!(check_hover("
2682set search_path to foo;
2683create table foo.users(id int, email text);
2684select id$0 from users;
2685"), @r"
2686        hover: column foo.users.id int
2687          ╭▸ 
2688        4 │ select id from users;
2689          ╰╴        ─ hover
2690        ");
2691    }
2692
2693    #[test]
2694    fn hover_on_select_qualified_star() {
2695        assert_snapshot!(check_hover("
2696create table u(id int, b int);
2697select u.*$0 from u;
2698"), @r"
2699        hover: column public.u.id int
2700              column public.u.b int
2701          ╭▸ 
2702        3 │ select u.* from u;
2703          ╰╴         ─ hover
2704        ");
2705    }
2706
2707    #[test]
2708    fn hover_on_select_unqualified_star() {
2709        assert_snapshot!(check_hover("
2710create table u(id int, b int);
2711select *$0 from u;
2712"), @r"
2713        hover: column public.u.id int
2714              column public.u.b int
2715          ╭▸ 
2716        3 │ select * from u;
2717          ╰╴       ─ hover
2718        ");
2719    }
2720
2721    #[test]
2722    fn hover_on_select_count_star() {
2723        assert_snapshot!(check_hover("
2724create table u(id int, b int);
2725select count(*$0) from u;
2726"), @r"
2727        hover: column public.u.id int
2728              column public.u.b int
2729          ╭▸ 
2730        3 │ select count(*) from u;
2731          ╰╴             ─ hover
2732        ");
2733    }
2734
2735    #[test]
2736    fn hover_on_insert_table() {
2737        assert_snapshot!(check_hover("
2738create table users(id int, email text);
2739insert into users$0(id, email) values (1, 'test');
2740"), @r"
2741        hover: table public.users(id int, email text)
2742          ╭▸ 
2743        3 │ insert into users(id, email) values (1, 'test');
2744          ╰╴                ─ hover
2745        ");
2746    }
2747
2748    #[test]
2749    fn hover_on_insert_table_with_schema() {
2750        assert_snapshot!(check_hover("
2751create table public.users(id int, email text);
2752insert into public.users$0(id, email) values (1, 'test');
2753"), @r"
2754        hover: table public.users(id int, email text)
2755          ╭▸ 
2756        3 │ insert into public.users(id, email) values (1, 'test');
2757          ╰╴                       ─ hover
2758        ");
2759    }
2760
2761    #[test]
2762    fn hover_on_insert_column() {
2763        assert_snapshot!(check_hover("
2764create table users(id int, email text);
2765insert into users(id$0, email) values (1, 'test');
2766"), @r"
2767        hover: column public.users.id int
2768          ╭▸ 
2769        3 │ insert into users(id, email) values (1, 'test');
2770          ╰╴                   ─ hover
2771        ");
2772    }
2773
2774    #[test]
2775    fn hover_on_insert_column_second() {
2776        assert_snapshot!(check_hover("
2777create table users(id int, email text);
2778insert into users(id, email$0) values (1, 'test');
2779"), @r"
2780        hover: column public.users.email text
2781          ╭▸ 
2782        3 │ insert into users(id, email) values (1, 'test');
2783          ╰╴                          ─ hover
2784        ");
2785    }
2786
2787    #[test]
2788    fn hover_on_insert_column_with_schema() {
2789        assert_snapshot!(check_hover("
2790create table public.users(id int, email text);
2791insert into public.users(email$0) values ('test');
2792"), @r"
2793        hover: column public.users.email text
2794          ╭▸ 
2795        3 │ insert into public.users(email) values ('test');
2796          ╰╴                             ─ hover
2797        ");
2798    }
2799
2800    #[test]
2801    fn hover_on_delete_table() {
2802        assert_snapshot!(check_hover("
2803create table users(id int, email text);
2804delete from users$0 where id = 1;
2805"), @r"
2806        hover: table public.users(id int, email text)
2807          ╭▸ 
2808        3 │ delete from users where id = 1;
2809          ╰╴                ─ hover
2810        ");
2811    }
2812
2813    #[test]
2814    fn hover_on_delete_table_with_schema() {
2815        assert_snapshot!(check_hover("
2816create table public.users(id int, email text);
2817delete from public.users$0 where id = 1;
2818"), @r"
2819        hover: table public.users(id int, email text)
2820          ╭▸ 
2821        3 │ delete from public.users where id = 1;
2822          ╰╴                       ─ hover
2823        ");
2824    }
2825
2826    #[test]
2827    fn hover_on_delete_where_column() {
2828        assert_snapshot!(check_hover("
2829create table users(id int, email text);
2830delete from users where id$0 = 1;
2831"), @r"
2832        hover: column public.users.id int
2833          ╭▸ 
2834        3 │ delete from users where id = 1;
2835          ╰╴                         ─ hover
2836        ");
2837    }
2838
2839    #[test]
2840    fn hover_on_delete_where_column_second() {
2841        assert_snapshot!(check_hover("
2842create table users(id int, email text, active boolean);
2843delete from users where id = 1 and email$0 = 'test';
2844"), @r"
2845        hover: column public.users.email text
2846          ╭▸ 
2847        3 │ delete from users where id = 1 and email = 'test';
2848          ╰╴                                       ─ hover
2849        ");
2850    }
2851
2852    #[test]
2853    fn hover_on_delete_where_column_with_schema() {
2854        assert_snapshot!(check_hover("
2855create table public.users(id int, email text);
2856delete from public.users where email$0 = 'test';
2857"), @r"
2858        hover: column public.users.email text
2859          ╭▸ 
2860        3 │ delete from public.users where email = 'test';
2861          ╰╴                                   ─ hover
2862        ");
2863    }
2864
2865    #[test]
2866    fn hover_on_select_table_as_column() {
2867        assert_snapshot!(check_hover("
2868create table t(x bigint, y bigint);
2869select t$0 from t;
2870"), @r"
2871        hover: table public.t(x bigint, y bigint)
2872          ╭▸ 
2873        3 │ select t from t;
2874          ╰╴       ─ hover
2875        ");
2876    }
2877
2878    #[test]
2879    fn hover_on_select_table_as_column_with_schema() {
2880        assert_snapshot!(check_hover("
2881create table public.t(x bigint, y bigint);
2882select t$0 from public.t;
2883"), @r"
2884        hover: table public.t(x bigint, y bigint)
2885          ╭▸ 
2886        3 │ select t from public.t;
2887          ╰╴       ─ hover
2888        ");
2889    }
2890
2891    #[test]
2892    fn hover_on_select_table_as_column_with_search_path() {
2893        assert_snapshot!(check_hover("
2894set search_path to foo;
2895create table foo.users(id int, email text);
2896select users$0 from users;
2897"), @r"
2898        hover: table foo.users(id int, email text)
2899          ╭▸ 
2900        4 │ select users from users;
2901          ╰╴           ─ hover
2902        ");
2903    }
2904
2905    #[test]
2906    fn hover_on_select_column_with_same_name_as_table() {
2907        assert_snapshot!(check_hover("
2908create table t(t int);
2909select t$0 from t;
2910"), @r"
2911        hover: column public.t.t int
2912          ╭▸ 
2913        3 │ select t from t;
2914          ╰╴       ─ hover
2915        ");
2916    }
2917
2918    #[test]
2919    fn hover_on_create_schema() {
2920        assert_snapshot!(check_hover("
2921create schema foo$0;
2922"), @r"
2923        hover: schema foo
2924          ╭▸ 
2925        2 │ create schema foo;
2926          ╰╴                ─ hover
2927        ");
2928    }
2929
2930    #[test]
2931    fn hover_on_create_schema_authorization() {
2932        assert_snapshot!(check_hover("
2933create schema authorization foo$0;
2934"), @r"
2935        hover: schema foo
2936          ╭▸ 
2937        2 │ create schema authorization foo;
2938          ╰╴                              ─ hover
2939        ");
2940    }
2941
2942    #[test]
2943    fn hover_on_drop_schema_authorization() {
2944        assert_snapshot!(check_hover("
2945create schema authorization foo;
2946drop schema foo$0;
2947"), @r"
2948        hover: schema foo
2949          ╭▸ 
2950        3 │ drop schema foo;
2951          ╰╴              ─ hover
2952        ");
2953    }
2954
2955    #[test]
2956    fn hover_on_drop_schema() {
2957        assert_snapshot!(check_hover("
2958create schema foo;
2959drop schema foo$0;
2960"), @r"
2961        hover: schema foo
2962          ╭▸ 
2963        3 │ drop schema foo;
2964          ╰╴              ─ hover
2965        ");
2966    }
2967
2968    #[test]
2969    fn hover_on_schema_after_definition() {
2970        assert_snapshot!(check_hover("
2971drop schema foo$0;
2972create schema foo;
2973"), @r"
2974        hover: schema foo
2975          ╭▸ 
2976        2 │ drop schema foo;
2977          ╰╴              ─ hover
2978        ");
2979    }
2980
2981    #[test]
2982    fn hover_on_cte_table() {
2983        assert_snapshot!(check_hover("
2984with t as (select 1 a)
2985select a from t$0;
2986"), @r"
2987        hover: with t as (select 1 a)
2988          ╭▸ 
2989        3 │ select a from t;
2990          ╰╴              ─ hover
2991        ");
2992    }
2993
2994    #[test]
2995    fn hover_on_select_cte_table_as_column() {
2996        assert_snapshot!(check_hover("
2997with t as (select 1 a, 2 b, 3 c)
2998select t$0 from t;
2999"), @r"
3000        hover: with t as (select 1 a, 2 b, 3 c)
3001          ╭▸ 
3002        3 │ select t from t;
3003          ╰╴       ─ hover
3004        ");
3005    }
3006
3007    #[test]
3008    fn hover_on_cte_column() {
3009        assert_snapshot!(check_hover("
3010with t as (select 1 a)
3011select a$0 from t;
3012"), @r"
3013        hover: column t.a
3014          ╭▸ 
3015        3 │ select a from t;
3016          ╰╴       ─ hover
3017        ");
3018    }
3019
3020    #[test]
3021    fn hover_on_cte_with_multiple_columns() {
3022        assert_snapshot!(check_hover("
3023with t as (select 1 a, 2 b)
3024select b$0 from t;
3025"), @r"
3026        hover: column t.b
3027          ╭▸ 
3028        3 │ select b from t;
3029          ╰╴       ─ hover
3030        ");
3031    }
3032
3033    #[test]
3034    fn hover_on_cte_with_column_list() {
3035        assert_snapshot!(check_hover("
3036with t(a) as (select 1)
3037select a$0 from t;
3038"), @r"
3039        hover: column t.a
3040          ╭▸ 
3041        3 │ select a from t;
3042          ╰╴       ─ hover
3043        ");
3044    }
3045
3046    #[test]
3047    fn hover_on_nested_cte() {
3048        assert_snapshot!(check_hover("
3049with x as (select 1 a),
3050     y as (select a from x)
3051select a$0 from y;
3052"), @r"
3053        hover: column y.a
3054          ╭▸ 
3055        4 │ select a from y;
3056          ╰╴       ─ hover
3057        ");
3058    }
3059
3060    #[test]
3061    fn hover_on_cte_shadowing_table_with_star() {
3062        assert_snapshot!(check_hover("
3063create table t(a bigint);
3064with t as (select * from t)
3065select a$0 from t;
3066"), @r"
3067        hover: column public.t.a bigint
3068          ╭▸ 
3069        4 │ select a from t;
3070          ╰╴       ─ hover
3071        ");
3072    }
3073
3074    #[test]
3075    fn hover_on_cte_definition() {
3076        assert_snapshot!(check_hover("
3077with t$0 as (select 1 a)
3078select a from t;
3079"), @r"
3080        hover: with t as (select 1 a)
3081          ╭▸ 
3082        2 │ with t as (select 1 a)
3083          ╰╴     ─ hover
3084        ");
3085    }
3086
3087    #[test]
3088    fn hover_on_cte_values_column1() {
3089        assert_snapshot!(check_hover("
3090with t as (
3091    values (1, 2), (3, 4)
3092)
3093select column1$0, column2 from t;
3094"), @r"
3095        hover: column t.column1
3096          ╭▸ 
3097        5 │ select column1, column2 from t;
3098          ╰╴             ─ hover
3099        ");
3100    }
3101
3102    #[test]
3103    fn hover_on_cte_values_column2() {
3104        assert_snapshot!(check_hover("
3105with t as (
3106    values (1, 2), (3, 4)
3107)
3108select column1, column2$0 from t;
3109"), @r"
3110        hover: column t.column2
3111          ╭▸ 
3112        5 │ select column1, column2 from t;
3113          ╰╴                      ─ hover
3114        ");
3115    }
3116
3117    #[test]
3118    fn hover_on_cte_values_single_column() {
3119        assert_snapshot!(check_hover("
3120with t as (
3121    values (1), (2), (3)
3122)
3123select column1$0 from t;
3124"), @r"
3125        hover: column t.column1
3126          ╭▸ 
3127        5 │ select column1 from t;
3128          ╰╴             ─ hover
3129        ");
3130    }
3131
3132    #[test]
3133    fn hover_on_cte_values_uppercase_column_names() {
3134        assert_snapshot!(check_hover("
3135with t as (
3136    values (1, 2), (3, 4)
3137)
3138select COLUMN1$0, COLUMN2 from t;
3139"), @r"
3140        hover: column t.column1
3141          ╭▸ 
3142        5 │ select COLUMN1, COLUMN2 from t;
3143          ╰╴             ─ hover
3144        ");
3145    }
3146
3147    #[test]
3148    fn hover_on_subquery_column() {
3149        assert_snapshot!(check_hover("
3150select a$0 from (select 1 a);
3151"), @r"
3152        hover: column a integer
3153          ╭▸ 
3154        2 │ select a from (select 1 a);
3155          ╰╴       ─ hover
3156        ");
3157    }
3158
3159    #[test]
3160    fn hover_on_subquery_values_column() {
3161        assert_snapshot!(check_hover("
3162select column1$0 from (values (1, 'foo'));
3163"), @r"
3164        hover: column column1 integer
3165          ╭▸ 
3166        2 │ select column1 from (values (1, 'foo'));
3167          ╰╴             ─ hover
3168        ");
3169    }
3170
3171    #[test]
3172    fn hover_on_cte_qualified_star() {
3173        assert_snapshot!(check_hover("
3174with u as (select 1 id, 2 b)
3175select u.*$0 from u;
3176"), @"
3177        hover: column u.id integer
3178              column u.b integer
3179          ╭▸ 
3180        3 │ select u.* from u;
3181          ╰╴         ─ hover
3182        ");
3183    }
3184
3185    #[test]
3186    fn hover_on_cte_values_qualified_star() {
3187        assert_snapshot!(check_hover("
3188with t as (values (1, 2), (3, 4))
3189select t.*$0 from t;
3190"), @"
3191        hover: column t.column1 integer
3192              column t.column2 integer
3193          ╭▸ 
3194        3 │ select t.* from t;
3195          ╰╴         ─ hover
3196        ");
3197    }
3198
3199    #[test]
3200    fn hover_on_cte_table_alias_with_column_list() {
3201        assert_snapshot!(check_hover("
3202with t as (select 1 a, 2 b, 3 c)
3203select u$0.x, u.y from t as u(x, y);
3204"), @"
3205        hover: table u(x, y, c)
3206          ╭▸ 
3207        3 │ select u.x, u.y from t as u(x, y);
3208          ╰╴       ─ hover
3209        ");
3210    }
3211
3212    #[test]
3213    fn hover_on_cte_table_alias_with_column_list_column_ref() {
3214        assert_snapshot!(check_hover("
3215with t as (select 1 a, 2 b, 3 c)
3216select u.x$0 from t as u(x, y);
3217"), @"
3218        hover: column u.x
3219          ╭▸ 
3220        3 │ select u.x from t as u(x, y);
3221          ╰╴         ─ hover
3222        ");
3223    }
3224
3225    #[test]
3226    fn hover_on_cte_table_alias_with_column_list_table_ref() {
3227        assert_snapshot!(check_hover("
3228with t as (select 1 a, 2 b, 3 c)
3229select u$0 from t as u(x, y);
3230"), @"
3231        hover: table u(x, y, c)
3232          ╭▸ 
3233        3 │ select u from t as u(x, y);
3234          ╰╴       ─ hover
3235        ");
3236    }
3237
3238    #[test]
3239    fn hover_on_subquery_alias_with_column_list_table_ref() {
3240        assert_snapshot!(check_hover("
3241with t as (select 1 a, 2 b, 3 c)
3242select z$0 from (select * from t) as z(x, y);
3243"), @"
3244        hover: table z(x, y, c)
3245          ╭▸ 
3246        3 │ select z from (select * from t) as z(x, y);
3247          ╰╴       ─ hover
3248        ");
3249    }
3250
3251    #[test]
3252    fn hover_on_subquery_nested_paren_alias_with_column_list_table_ref() {
3253        assert_snapshot!(check_hover("
3254with t as (select 1 a, 2 b, 3 c)
3255select z$0 from ((select * from t)) as z(x, y);
3256"), @"
3257        hover: table z(x, y, c)
3258          ╭▸ 
3259        3 │ select z from ((select * from t)) as z(x, y);
3260          ╰╴       ─ hover
3261        ");
3262    }
3263
3264    #[test]
3265    fn hover_on_cte_table_alias_with_partial_column_list_star() {
3266        assert_snapshot!(check_hover("
3267with t as (select 1 a, 2 b, 3 c)
3268select *$0 from t u(x, y);
3269"), @"
3270        hover: column u.x
3271              column u.y
3272              column u.c
3273          ╭▸ 
3274        3 │ select * from t u(x, y);
3275          ╰╴       ─ hover
3276        ");
3277    }
3278
3279    #[test]
3280    fn hover_on_cte_table_alias_with_partial_column_list_star_from_information_schema() {
3281        assert_snapshot!(check_hover("
3282with t as (select * from information_schema.sql_features)
3283select *$0 from t u(x);
3284"), @"
3285        hover: column u.x
3286              column u.feature_name
3287              column u.sub_feature_id
3288              column u.sub_feature_name
3289              column u.is_supported
3290              column u.is_verified_by
3291              column u.comments
3292          ╭▸ 
3293        3 │ select * from t u(x);
3294          ╰╴       ─ hover
3295        ");
3296    }
3297
3298    #[test]
3299    fn hover_on_cte_table_alias_with_partial_column_list_qualified_star() {
3300        assert_snapshot!(check_hover("
3301with t as (select 1 a, 2 b, 3 c)
3302select u.*$0 from t u(x, y);
3303"), @"
3304        hover: column u.x
3305              column u.y
3306              column u.c
3307          ╭▸ 
3308        3 │ select u.* from t u(x, y);
3309          ╰╴         ─ hover
3310        ");
3311    }
3312
3313    #[test]
3314    fn hover_on_star_from_cte_empty_select() {
3315        assert!(
3316            check_hover_(
3317                "
3318with t as (select)
3319select *$0 from t;
3320",
3321            )
3322            .is_none()
3323        );
3324    }
3325
3326    #[test]
3327    fn hover_on_star_with_subquery_from_cte() {
3328        assert_snapshot!(check_hover("
3329with u as (select 1 id, 2 b)
3330select *$0 from (select *, *, * from u);
3331"), @"
3332        hover: column u.id integer
3333              column u.b integer
3334              column u.id integer
3335              column u.b integer
3336              column u.id integer
3337              column u.b integer
3338          ╭▸ 
3339        3 │ select * from (select *, *, * from u);
3340          ╰╴       ─ hover
3341        ");
3342    }
3343
3344    #[test]
3345    fn hover_on_star_with_subquery_from_table() {
3346        assert_snapshot!(check_hover("
3347create table t(a int, b int);
3348select *$0 from (select a from t);
3349"), @r"
3350        hover: column public.t.a int
3351          ╭▸ 
3352        3 │ select * from (select a from t);
3353          ╰╴       ─ hover
3354        ");
3355    }
3356
3357    #[test]
3358    fn hover_on_star_with_subquery_from_table_statement() {
3359        assert_snapshot!(check_hover("
3360with t as (select 1 a, 2 b)
3361select *$0 from (table t);
3362"), @r"
3363        hover: column a
3364              column b
3365          ╭▸ 
3366        3 │ select * from (table t);
3367          ╰╴       ─ hover
3368        ");
3369    }
3370
3371    #[test]
3372    fn hover_on_star_from_information_schema_table() {
3373        assert_snapshot!(check_hover("
3374select *$0 from information_schema.sql_features;
3375"), @"
3376        hover: column information_schema.sql_features.feature_id character_data
3377              column information_schema.sql_features.feature_name character_data
3378              column information_schema.sql_features.sub_feature_id character_data
3379              column information_schema.sql_features.sub_feature_name character_data
3380              column information_schema.sql_features.is_supported yes_or_no
3381              column information_schema.sql_features.is_verified_by character_data
3382              column information_schema.sql_features.comments character_data
3383          ╭▸ 
3384        2 │ select * from information_schema.sql_features;
3385          ╰╴       ─ hover
3386        ");
3387    }
3388
3389    #[test]
3390    fn hover_on_star_with_subquery_literal() {
3391        assert_snapshot!(check_hover("
3392select *$0 from (select 1);
3393"), @r"
3394        hover: column ?column?
3395          ╭▸ 
3396        2 │ select * from (select 1);
3397          ╰╴       ─ hover
3398        ");
3399    }
3400
3401    #[test]
3402    fn hover_on_star_with_subquery_literal_with_alias() {
3403        assert_snapshot!(check_hover("
3404select *$0 from (select 1) as sub;
3405"), @r"
3406        hover: column sub.?column?
3407          ╭▸ 
3408        2 │ select * from (select 1) as sub;
3409          ╰╴       ─ hover
3410        ");
3411    }
3412
3413    #[test]
3414    fn hover_on_view_inferred_column_anme() {
3415        assert_snapshot!(check_hover(r#"
3416create view v as select 1;
3417select "?column?"$0 from v;
3418"#), @r#"
3419        hover: column public.v.?column?
3420          ╭▸ 
3421        3 │ select "?column?" from v;
3422          ╰╴                ─ hover
3423        "#);
3424    }
3425
3426    #[test]
3427    fn hover_on_cte_inferred_column_name() {
3428        assert_snapshot!(check_hover(r#"
3429with x as (select 1)
3430select "?column?"$0 from x;
3431"#), @r#"
3432        hover: column x.?column?
3433          ╭▸ 
3434        3 │ select "?column?" from x;
3435          ╰╴                ─ hover
3436        "#);
3437    }
3438
3439    #[test]
3440    fn hover_on_create_table_as_inferred_column_name() {
3441        assert_snapshot!(check_hover(r#"
3442create table t as select 1;
3443select "?column?"$0 from t;
3444"#), @r#"
3445        hover: column public.t.?column?
3446          ╭▸ 
3447        3 │ select "?column?" from t;
3448          ╰╴                ─ hover
3449        "#);
3450    }
3451
3452    #[test]
3453    fn hover_on_paren_select_inferred_column_name() {
3454        assert_snapshot!(check_hover(r#"
3455select "?column?"$0 from (select 1);
3456"#), @r#"
3457        hover: column ?column? integer
3458          ╭▸ 
3459        2 │ select "?column?" from (select 1);
3460          ╰╴                ─ hover
3461        "#);
3462    }
3463
3464    #[test]
3465    fn hover_on_paren_select_aliased_inferred_column_name() {
3466        assert_snapshot!(check_hover(r#"
3467select sub."?column?"$0 from (select 1) sub;
3468"#), @r#"
3469        hover: column sub.?column?
3470          ╭▸ 
3471        2 │ select sub."?column?" from (select 1) sub;
3472          ╰╴                    ─ hover
3473        "#);
3474    }
3475
3476    #[test]
3477    fn hover_on_view_qualified_star() {
3478        assert_snapshot!(check_hover("
3479create view v as select 1 id, 2 b;
3480select v.*$0 from v;
3481"), @"
3482        hover: column public.v.id integer
3483              column public.v.b integer
3484          ╭▸ 
3485        3 │ select v.* from v;
3486          ╰╴         ─ hover
3487        ");
3488    }
3489
3490    #[test]
3491    fn hover_on_materialized_view_qualified_star() {
3492        assert_snapshot!(check_hover("
3493  create materialized view v as select 1 id, 2 b;
3494  select v.*$0 from v;
3495  "), @"
3496        hover: column public.v.id integer
3497              column public.v.b integer
3498          ╭▸ 
3499        3 │   select v.* from v;
3500          ╰╴           ─ hover
3501        ");
3502    }
3503
3504    #[test]
3505    fn hover_on_view_qualified_star_with_column_list() {
3506        assert_snapshot!(check_hover("
3507create view v (x, y) as select 1, 2, 3;
3508select v.*$0 from v;
3509"), @"
3510        hover: column public.v.x integer
3511              column public.v.y integer
3512              column public.v.?column? integer
3513          ╭▸ 
3514        3 │ select v.* from v;
3515          ╰╴         ─ hover
3516        ");
3517    }
3518
3519    #[test]
3520    fn hover_on_materialized_view_qualified_star_with_column_list() {
3521        assert_snapshot!(check_hover("
3522create materialized view mv (x, y) as select 1, 2, 3;
3523select mv.*$0 from mv;
3524"), @"
3525        hover: column public.mv.x integer
3526              column public.mv.y integer
3527              column public.mv.?column? integer
3528          ╭▸ 
3529        3 │ select mv.* from mv;
3530          ╰╴          ─ hover
3531        ");
3532    }
3533
3534    #[test]
3535    fn hover_on_drop_procedure() {
3536        assert_snapshot!(check_hover("
3537create procedure foo() language sql as $$ select 1 $$;
3538drop procedure foo$0();
3539"), @r"
3540        hover: procedure public.foo()
3541          ╭▸ 
3542        3 │ drop procedure foo();
3543          ╰╴                 ─ hover
3544        ");
3545    }
3546
3547    #[test]
3548    fn hover_on_drop_procedure_with_schema() {
3549        assert_snapshot!(check_hover("
3550create procedure myschema.foo() language sql as $$ select 1 $$;
3551drop procedure myschema.foo$0();
3552"), @r"
3553        hover: procedure myschema.foo()
3554          ╭▸ 
3555        3 │ drop procedure myschema.foo();
3556          ╰╴                          ─ hover
3557        ");
3558    }
3559
3560    #[test]
3561    fn hover_on_create_procedure_definition() {
3562        assert_snapshot!(check_hover("
3563create procedure foo$0() language sql as $$ select 1 $$;
3564"), @r"
3565        hover: procedure public.foo()
3566          ╭▸ 
3567        2 │ create procedure foo() language sql as $$ select 1 $$;
3568          ╰╴                   ─ hover
3569        ");
3570    }
3571
3572    #[test]
3573    fn hover_on_create_procedure_with_explicit_schema() {
3574        assert_snapshot!(check_hover("
3575create procedure myschema.foo$0() language sql as $$ select 1 $$;
3576"), @r"
3577        hover: procedure myschema.foo()
3578          ╭▸ 
3579        2 │ create procedure myschema.foo() language sql as $$ select 1 $$;
3580          ╰╴                            ─ hover
3581        ");
3582    }
3583
3584    #[test]
3585    fn hover_on_drop_procedure_with_search_path() {
3586        assert_snapshot!(check_hover(r#"
3587set search_path to myschema;
3588create procedure foo() language sql as $$ select 1 $$;
3589drop procedure foo$0();
3590"#), @r"
3591        hover: procedure myschema.foo()
3592          ╭▸ 
3593        4 │ drop procedure foo();
3594          ╰╴                 ─ hover
3595        ");
3596    }
3597
3598    #[test]
3599    fn hover_on_drop_procedure_overloaded() {
3600        assert_snapshot!(check_hover("
3601create procedure add(complex) language sql as $$ select null $$;
3602create procedure add(bigint) language sql as $$ select 1 $$;
3603drop procedure add$0(complex);
3604"), @r"
3605        hover: procedure public.add(complex)
3606          ╭▸ 
3607        4 │ drop procedure add(complex);
3608          ╰╴                 ─ hover
3609        ");
3610    }
3611
3612    #[test]
3613    fn hover_on_drop_procedure_second_overload() {
3614        assert_snapshot!(check_hover("
3615create procedure add(complex) language sql as $$ select null $$;
3616create procedure add(bigint) language sql as $$ select 1 $$;
3617drop procedure add$0(bigint);
3618"), @r"
3619        hover: procedure public.add(bigint)
3620          ╭▸ 
3621        4 │ drop procedure add(bigint);
3622          ╰╴                 ─ hover
3623        ");
3624    }
3625
3626    #[test]
3627    fn hover_on_call_procedure() {
3628        assert_snapshot!(check_hover("
3629create procedure foo() language sql as $$ select 1 $$;
3630call foo$0();
3631"), @r"
3632        hover: procedure public.foo()
3633          ╭▸ 
3634        3 │ call foo();
3635          ╰╴       ─ hover
3636        ");
3637    }
3638
3639    #[test]
3640    fn hover_on_call_procedure_with_schema() {
3641        assert_snapshot!(check_hover("
3642create procedure public.foo() language sql as $$ select 1 $$;
3643call public.foo$0();
3644"), @r"
3645        hover: procedure public.foo()
3646          ╭▸ 
3647        3 │ call public.foo();
3648          ╰╴              ─ hover
3649        ");
3650    }
3651
3652    #[test]
3653    fn hover_on_call_procedure_with_search_path() {
3654        assert_snapshot!(check_hover(r#"
3655set search_path to myschema;
3656create procedure foo() language sql as $$ select 1 $$;
3657call foo$0();
3658"#), @r"
3659        hover: procedure myschema.foo()
3660          ╭▸ 
3661        4 │ call foo();
3662          ╰╴       ─ hover
3663        ");
3664    }
3665
3666    #[test]
3667    fn hover_on_call_procedure_with_params() {
3668        assert_snapshot!(check_hover("
3669create procedure add(a int, b int) language sql as $$ select a + b $$;
3670call add$0(1, 2);
3671"), @r"
3672        hover: procedure public.add(a int, b int)
3673          ╭▸ 
3674        3 │ call add(1, 2);
3675          ╰╴       ─ hover
3676        ");
3677    }
3678
3679    #[test]
3680    fn hover_on_drop_routine_function() {
3681        assert_snapshot!(check_hover("
3682create function foo() returns int as $$ select 1 $$ language sql;
3683drop routine foo$0();
3684"), @r"
3685        hover: function public.foo() returns int
3686          ╭▸ 
3687        3 │ drop routine foo();
3688          ╰╴               ─ hover
3689        ");
3690    }
3691
3692    #[test]
3693    fn hover_on_drop_routine_aggregate() {
3694        assert_snapshot!(check_hover("
3695create aggregate myavg(int) (sfunc = int4_avg_accum, stype = _int8);
3696drop routine myavg$0(int);
3697"), @r"
3698        hover: aggregate public.myavg(int)
3699          ╭▸ 
3700        3 │ drop routine myavg(int);
3701          ╰╴                 ─ hover
3702        ");
3703    }
3704
3705    #[test]
3706    fn hover_on_drop_routine_procedure() {
3707        assert_snapshot!(check_hover("
3708create procedure foo() language sql as $$ select 1 $$;
3709drop routine foo$0();
3710"), @r"
3711        hover: procedure public.foo()
3712          ╭▸ 
3713        3 │ drop routine foo();
3714          ╰╴               ─ hover
3715        ");
3716    }
3717
3718    #[test]
3719    fn hover_on_drop_routine_with_schema() {
3720        assert_snapshot!(check_hover("
3721set search_path to public;
3722create function foo() returns int as $$ select 1 $$ language sql;
3723drop routine public.foo$0();
3724"), @r"
3725        hover: function public.foo() returns int
3726          ╭▸ 
3727        4 │ drop routine public.foo();
3728          ╰╴                      ─ hover
3729        ");
3730    }
3731
3732    #[test]
3733    fn hover_on_drop_routine_with_search_path() {
3734        assert_snapshot!(check_hover(r#"
3735set search_path to myschema;
3736create function foo() returns int as $$ select 1 $$ language sql;
3737drop routine foo$0();
3738"#), @r"
3739        hover: function myschema.foo() returns int
3740          ╭▸ 
3741        4 │ drop routine foo();
3742          ╰╴               ─ hover
3743        ");
3744    }
3745
3746    #[test]
3747    fn hover_on_drop_routine_overloaded() {
3748        assert_snapshot!(check_hover("
3749create function add(complex) returns complex as $$ select null $$ language sql;
3750create function add(bigint) returns bigint as $$ select 1 $$ language sql;
3751drop routine add$0(complex);
3752"), @r"
3753        hover: function public.add(complex) returns complex
3754          ╭▸ 
3755        4 │ drop routine add(complex);
3756          ╰╴               ─ hover
3757        ");
3758    }
3759
3760    #[test]
3761    fn hover_on_drop_routine_prefers_function_over_procedure() {
3762        assert_snapshot!(check_hover("
3763create function foo() returns int as $$ select 1 $$ language sql;
3764create procedure foo() language sql as $$ select 1 $$;
3765drop routine foo$0();
3766"), @r"
3767        hover: function public.foo() returns int
3768          ╭▸ 
3769        4 │ drop routine foo();
3770          ╰╴               ─ hover
3771        ");
3772    }
3773
3774    #[test]
3775    fn hover_on_drop_routine_prefers_aggregate_over_procedure() {
3776        assert_snapshot!(check_hover("
3777create aggregate foo(int) (sfunc = int4_avg_accum, stype = _int8);
3778create procedure foo(int) language sql as $$ select 1 $$;
3779drop routine foo$0(int);
3780"), @r"
3781        hover: aggregate public.foo(int)
3782          ╭▸ 
3783        4 │ drop routine foo(int);
3784          ╰╴               ─ hover
3785        ");
3786    }
3787
3788    #[test]
3789    fn hover_on_update_table() {
3790        assert_snapshot!(check_hover("
3791create table users(id int, email text);
3792update users$0 set email = 'new@example.com';
3793"), @r"
3794        hover: table public.users(id int, email text)
3795          ╭▸ 
3796        3 │ update users set email = 'new@example.com';
3797          ╰╴           ─ hover
3798        ");
3799    }
3800
3801    #[test]
3802    fn hover_on_update_table_with_schema() {
3803        assert_snapshot!(check_hover("
3804create table public.users(id int, email text);
3805update public.users$0 set email = 'new@example.com';
3806"), @r"
3807        hover: table public.users(id int, email text)
3808          ╭▸ 
3809        3 │ update public.users set email = 'new@example.com';
3810          ╰╴                  ─ hover
3811        ");
3812    }
3813
3814    #[test]
3815    fn hover_on_update_set_column() {
3816        assert_snapshot!(check_hover("
3817create table users(id int, email text);
3818update users set email$0 = 'new@example.com' where id = 1;
3819"), @r"
3820        hover: column public.users.email text
3821          ╭▸ 
3822        3 │ update users set email = 'new@example.com' where id = 1;
3823          ╰╴                     ─ hover
3824        ");
3825    }
3826
3827    #[test]
3828    fn hover_on_update_set_column_with_schema() {
3829        assert_snapshot!(check_hover("
3830create table public.users(id int, email text);
3831update public.users set email$0 = 'new@example.com' where id = 1;
3832"), @r"
3833        hover: column public.users.email text
3834          ╭▸ 
3835        3 │ update public.users set email = 'new@example.com' where id = 1;
3836          ╰╴                            ─ hover
3837        ");
3838    }
3839
3840    #[test]
3841    fn hover_on_update_where_column() {
3842        assert_snapshot!(check_hover("
3843create table users(id int, email text);
3844update users set email = 'new@example.com' where id$0 = 1;
3845"), @r"
3846        hover: column public.users.id int
3847          ╭▸ 
3848        3 │ update users set email = 'new@example.com' where id = 1;
3849          ╰╴                                                  ─ hover
3850        ");
3851    }
3852
3853    #[test]
3854    fn hover_on_update_where_column_with_schema() {
3855        assert_snapshot!(check_hover("
3856create table public.users(id int, email text);
3857update public.users set email = 'new@example.com' where id$0 = 1;
3858"), @r"
3859        hover: column public.users.id int
3860          ╭▸ 
3861        3 │ update public.users set email = 'new@example.com' where id = 1;
3862          ╰╴                                                         ─ hover
3863        ");
3864    }
3865
3866    #[test]
3867    fn hover_on_update_from_table() {
3868        assert_snapshot!(check_hover("
3869create table users(id int, email text);
3870create table messages(id int, user_id int, email text);
3871update users set email = messages.email from messages$0 where users.id = messages.user_id;
3872"), @r"
3873        hover: table public.messages(id int, user_id int, email text)
3874          ╭▸ 
3875        4 │ update users set email = messages.email from messages where users.id = messages.user_id;
3876          ╰╴                                                    ─ hover
3877        ");
3878    }
3879
3880    #[test]
3881    fn hover_on_update_from_table_with_schema() {
3882        assert_snapshot!(check_hover("
3883create table users(id int, email text);
3884create table public.messages(id int, user_id int, email text);
3885update users set email = messages.email from public.messages$0 where users.id = messages.user_id;
3886"), @r"
3887        hover: table public.messages(id int, user_id int, email text)
3888          ╭▸ 
3889        4 │ update users set email = messages.email from public.messages where users.id = messages.user_id;
3890          ╰╴                                                           ─ hover
3891        ");
3892    }
3893
3894    #[test]
3895    fn hover_on_update_with_cte_table() {
3896        assert_snapshot!(check_hover("
3897create table users(id int, email text);
3898with new_data as (
3899    select 1 as id, 'new@example.com' as email
3900)
3901update users set email = new_data.email from new_data$0 where users.id = new_data.id;
3902"), @r"
3903        hover: with new_data as (select 1 as id, 'new@example.com' as email)
3904          ╭▸ 
3905        6 │ update users set email = new_data.email from new_data where users.id = new_data.id;
3906          ╰╴                                                    ─ hover
3907        ");
3908    }
3909
3910    #[test]
3911    fn hover_on_update_with_cte_column_in_set() {
3912        assert_snapshot!(check_hover("
3913create table users(id int, email text);
3914with new_data as (
3915    select 1 as id, 'new@example.com' as email
3916)
3917update users set email = new_data.email$0 from new_data where users.id = new_data.id;
3918"), @r"
3919        hover: column new_data.email
3920          ╭▸ 
3921        6 │ update users set email = new_data.email from new_data where users.id = new_data.id;
3922          ╰╴                                      ─ hover
3923        ");
3924    }
3925
3926    #[test]
3927    fn hover_on_update_with_cte_column_in_where() {
3928        assert_snapshot!(check_hover("
3929create table users(id int, email text);
3930with new_data as (
3931    select 1 as id, 'new@example.com' as email
3932)
3933update users set email = new_data.email from new_data where new_data.id$0 = users.id;
3934"), @r"
3935        hover: column new_data.id
3936          ╭▸ 
3937        6 │ update users set email = new_data.email from new_data where new_data.id = users.id;
3938          ╰╴                                                                      ─ hover
3939        ");
3940    }
3941
3942    #[test]
3943    fn hover_on_create_view_definition() {
3944        assert_snapshot!(check_hover("
3945create view v$0 as select 1;
3946"), @r"
3947        hover: view public.v as select 1
3948          ╭▸ 
3949        2 │ create view v as select 1;
3950          ╰╴            ─ hover
3951        ");
3952    }
3953
3954    #[test]
3955    fn hover_on_create_view_definition_with_schema() {
3956        assert_snapshot!(check_hover("
3957create view myschema.v$0 as select 1;
3958"), @r"
3959        hover: view myschema.v as select 1
3960          ╭▸ 
3961        2 │ create view myschema.v as select 1;
3962          ╰╴                     ─ hover
3963        ");
3964    }
3965
3966    #[test]
3967    fn hover_on_create_temp_view_definition() {
3968        assert_snapshot!(check_hover("
3969create temp view v$0 as select 1;
3970"), @r"
3971        hover: view pg_temp.v as select 1
3972          ╭▸ 
3973        2 │ create temp view v as select 1;
3974          ╰╴                 ─ hover
3975        ");
3976    }
3977
3978    #[test]
3979    fn hover_on_create_view_with_column_list() {
3980        assert_snapshot!(check_hover("
3981create view v(col1$0) as select 1;
3982"), @r"
3983        hover: column public.v.col1
3984          ╭▸ 
3985        2 │ create view v(col1) as select 1;
3986          ╰╴                 ─ hover
3987        ");
3988    }
3989
3990    #[test]
3991    fn hover_on_select_from_view() {
3992        assert_snapshot!(check_hover("
3993create view v as select 1;
3994select * from v$0;
3995"), @r"
3996        hover: view public.v as select 1
3997          ╭▸ 
3998        3 │ select * from v;
3999          ╰╴              ─ hover
4000        ");
4001    }
4002
4003    #[test]
4004    fn hover_on_select_column_from_view_column_list() {
4005        assert_snapshot!(check_hover("
4006create view v(a) as select 1;
4007select a$0 from v;
4008"), @r"
4009        hover: column public.v.a
4010          ╭▸ 
4011        3 │ select a from v;
4012          ╰╴       ─ hover
4013        ");
4014    }
4015
4016    #[test]
4017    fn hover_on_select_column_from_view_column_list_overrides_target() {
4018        assert_snapshot!(check_hover("
4019create view v(a) as select 1, 2 b;
4020select a, b$0 from v;
4021"), @r"
4022        hover: column public.v.b
4023          ╭▸ 
4024        3 │ select a, b from v;
4025          ╰╴          ─ hover
4026        ");
4027    }
4028
4029    #[test]
4030    fn hover_on_select_column_from_view_target_list() {
4031        assert_snapshot!(check_hover("
4032create view v as select 1 a, 2 b;
4033select a$0, b from v;
4034"), @r"
4035        hover: column public.v.a
4036          ╭▸ 
4037        3 │ select a, b from v;
4038          ╰╴       ─ hover
4039        ");
4040    }
4041
4042    #[test]
4043    fn hover_on_create_table_as_column() {
4044        assert_snapshot!(check_hover("
4045create table t as select 1 a;
4046select a$0 from t;
4047"), @r"
4048        hover: column public.t.a
4049          ╭▸ 
4050        3 │ select a from t;
4051          ╰╴       ─ hover
4052        ");
4053    }
4054
4055    #[test]
4056    fn hover_on_create_table_as_table() {
4057        assert_snapshot!(check_hover("
4058create table t as select 1 a;
4059select a from t$0;
4060"), @"
4061        hover: table public.t as select 1 a
4062          ╭▸ 
4063        3 │ select a from t;
4064          ╰╴              ─ hover
4065        ");
4066    }
4067
4068    #[test]
4069    fn hover_on_select_from_view_with_schema() {
4070        assert_snapshot!(check_hover("
4071create view myschema.v as select 1;
4072select * from myschema.v$0;
4073"), @r"
4074        hover: view myschema.v as select 1
4075          ╭▸ 
4076        3 │ select * from myschema.v;
4077          ╰╴                       ─ hover
4078        ");
4079    }
4080
4081    #[test]
4082    fn hover_on_drop_view() {
4083        assert_snapshot!(check_hover("
4084create view v as select 1;
4085drop view v$0;
4086"), @r"
4087        hover: view public.v as select 1
4088          ╭▸ 
4089        3 │ drop view v;
4090          ╰╴          ─ hover
4091        ");
4092    }
4093
4094    #[test]
4095    fn hover_composite_type_field() {
4096        assert_snapshot!(check_hover("
4097create type person_info as (name varchar(50), age int);
4098with team as (
4099    select 1 as id, ('Alice', 30)::person_info as member
4100)
4101select (member).name$0, (member).age from team;
4102"), @r"
4103        hover: field public.person_info.name varchar(50)
4104          ╭▸ 
4105        6 │ select (member).name, (member).age from team;
4106          ╰╴                   ─ hover
4107        ");
4108    }
4109
4110    #[test]
4111    fn hover_composite_type_field_age() {
4112        assert_snapshot!(check_hover("
4113create type person_info as (name varchar(50), age int);
4114with team as (
4115    select 1 as id, ('Alice', 30)::person_info as member
4116)
4117select (member).name, (member).age$0 from team;
4118"), @r"
4119        hover: field public.person_info.age int
4120          ╭▸ 
4121        6 │ select (member).name, (member).age from team;
4122          ╰╴                                 ─ hover
4123        ");
4124    }
4125
4126    #[test]
4127    fn hover_composite_type_field_nested_parens() {
4128        assert_snapshot!(check_hover("
4129create type person_info as (name varchar(50), age int);
4130with team as (
4131    select 1 as id, ('Alice', 30)::person_info as member
4132)
4133select ((((member))).name$0) from team;
4134"), @r"
4135        hover: field public.person_info.name varchar(50)
4136          ╭▸ 
4137        6 │ select ((((member))).name) from team;
4138          ╰╴                        ─ hover
4139        ");
4140    }
4141
4142    #[test]
4143    fn hover_on_join_using_column() {
4144        assert_snapshot!(check_hover("
4145create table t(id int);
4146create table u(id int);
4147select * from t join u using (id$0);
4148"), @r"
4149        hover: column public.t.id int
4150              column public.u.id int
4151          ╭▸ 
4152        4 │ select * from t join u using (id);
4153          ╰╴                               ─ hover
4154        ");
4155    }
4156
4157    #[test]
4158    fn hover_on_truncate_table() {
4159        assert_snapshot!(check_hover("
4160create table users(id int, email text);
4161truncate table users$0;
4162"), @r"
4163        hover: table public.users(id int, email text)
4164          ╭▸ 
4165        3 │ truncate table users;
4166          ╰╴                   ─ hover
4167        ");
4168    }
4169
4170    #[test]
4171    fn hover_on_truncate_table_without_table_keyword() {
4172        assert_snapshot!(check_hover("
4173create table users(id int, email text);
4174truncate users$0;
4175"), @r"
4176        hover: table public.users(id int, email text)
4177          ╭▸ 
4178        3 │ truncate users;
4179          ╰╴             ─ hover
4180        ");
4181    }
4182
4183    #[test]
4184    fn hover_on_lock_table() {
4185        assert_snapshot!(check_hover("
4186create table users(id int, email text);
4187lock table users$0;
4188"), @r"
4189        hover: table public.users(id int, email text)
4190          ╭▸ 
4191        3 │ lock table users;
4192          ╰╴               ─ hover
4193        ");
4194    }
4195
4196    #[test]
4197    fn hover_on_lock_table_without_table_keyword() {
4198        assert_snapshot!(check_hover("
4199create table users(id int, email text);
4200lock users$0;
4201"), @r"
4202        hover: table public.users(id int, email text)
4203          ╭▸ 
4204        3 │ lock users;
4205          ╰╴         ─ hover
4206        ");
4207    }
4208
4209    #[test]
4210    fn hover_on_vacuum_table() {
4211        assert_snapshot!(check_hover("
4212create table users(id int, email text);
4213vacuum users$0;
4214"), @r"
4215        hover: table public.users(id int, email text)
4216          ╭▸ 
4217        3 │ vacuum users;
4218          ╰╴           ─ hover
4219        ");
4220    }
4221
4222    #[test]
4223    fn hover_on_vacuum_with_analyze() {
4224        assert_snapshot!(check_hover("
4225create table users(id int, email text);
4226vacuum analyze users$0;
4227"), @r"
4228        hover: table public.users(id int, email text)
4229          ╭▸ 
4230        3 │ vacuum analyze users;
4231          ╰╴                   ─ hover
4232        ");
4233    }
4234
4235    #[test]
4236    fn hover_on_alter_table() {
4237        assert_snapshot!(check_hover("
4238create table users(id int, email text);
4239alter table users$0 alter email set not null;
4240"), @r"
4241        hover: table public.users(id int, email text)
4242          ╭▸ 
4243        3 │ alter table users alter email set not null;
4244          ╰╴                ─ hover
4245        ");
4246    }
4247
4248    #[test]
4249    fn hover_on_alter_table_column() {
4250        assert_snapshot!(check_hover("
4251create table users(id int, email text);
4252alter table users alter email$0 set not null;
4253"), @r"
4254        hover: column public.users.email text
4255          ╭▸ 
4256        3 │ alter table users alter email set not null;
4257          ╰╴                            ─ hover
4258        ");
4259    }
4260
4261    #[test]
4262    fn hover_on_refresh_materialized_view() {
4263        assert_snapshot!(check_hover("
4264create materialized view mv as select 1;
4265refresh materialized view mv$0;
4266"), @r"
4267        hover: materialized view public.mv as select 1
4268          ╭▸ 
4269        3 │ refresh materialized view mv;
4270          ╰╴                           ─ hover
4271        ");
4272    }
4273
4274    #[test]
4275    fn hover_on_reindex_table() {
4276        assert_snapshot!(check_hover("
4277create table users(id int);
4278reindex table users$0;
4279"), @r"
4280        hover: table public.users(id int)
4281          ╭▸ 
4282        3 │ reindex table users;
4283          ╰╴                  ─ hover
4284        ");
4285    }
4286
4287    #[test]
4288    fn hover_on_reindex_index() {
4289        assert_snapshot!(check_hover("
4290create table t(c int);
4291create index idx on t(c);
4292reindex index idx$0;
4293"), @r"
4294        hover: index public.idx on public.t(c)
4295          ╭▸ 
4296        4 │ reindex index idx;
4297          ╰╴                ─ hover
4298        ");
4299    }
4300
4301    #[test]
4302    fn hover_merge_returning_star_from_cte() {
4303        assert_snapshot!(check_hover("
4304create table t(a int, b int);
4305with u(x, y) as (
4306  select 1, 2
4307),
4308merged as (
4309  merge into t
4310    using u
4311      on t.a = u.x
4312  when matched then
4313    do nothing
4314  when not matched then
4315    do nothing
4316  returning a as x, b as y
4317)
4318select *$0 from merged;
4319"), @r"
4320        hover: column merged.x
4321              column merged.y
4322           ╭▸ 
4323        16 │ select * from merged;
4324           ╰╴       ─ hover
4325        ");
4326    }
4327
4328    #[test]
4329    fn hover_update_returning_star() {
4330        assert_snapshot!(check_hover("
4331create table t(a int, b int);
4332update t set a = 1
4333returning *$0;
4334"), @r"
4335        hover: column public.t.a int
4336              column public.t.b int
4337          ╭▸ 
4338        4 │ returning *;
4339          ╰╴          ─ hover
4340        ");
4341    }
4342
4343    #[test]
4344    fn hover_insert_returning_star() {
4345        assert_snapshot!(check_hover("
4346create table t(a int, b int);
4347insert into t values (1, 2)
4348returning *$0;
4349"), @r"
4350        hover: column public.t.a int
4351              column public.t.b int
4352          ╭▸ 
4353        4 │ returning *;
4354          ╰╴          ─ hover
4355        ");
4356    }
4357
4358    #[test]
4359    fn hover_delete_returning_star() {
4360        assert_snapshot!(check_hover("
4361create table t(a int, b int);
4362delete from t
4363returning *$0;
4364"), @r"
4365        hover: column public.t.a int
4366              column public.t.b int
4367          ╭▸ 
4368        4 │ returning *;
4369          ╰╴          ─ hover
4370        ");
4371    }
4372
4373    #[test]
4374    fn hover_merge_returning_star() {
4375        assert_snapshot!(check_hover("
4376create table t(a int, b int);
4377merge into t
4378  using (select 1 as x, 2 as y) u
4379    on t.a = u.x
4380  when matched then
4381    do nothing
4382returning *$0;
4383"), @r"
4384        hover: column public.t.a int
4385              column public.t.b int
4386          ╭▸ 
4387        8 │ returning *;
4388          ╰╴          ─ hover
4389        ");
4390    }
4391
4392    #[test]
4393    fn hover_merge_returning_qualified_star_old() {
4394        assert_snapshot!(check_hover("
4395create table t(a int, b int);
4396merge into t
4397  using (select 1 as x, 2 as y) u
4398    on t.a = u.x
4399  when matched then
4400    update set a = 99
4401returning old$0.*;
4402"), @r"
4403        hover: table public.t(a int, b int)
4404          ╭▸ 
4405        8 │ returning old.*;
4406          ╰╴            ─ hover
4407        ");
4408    }
4409
4410    #[test]
4411    fn hover_merge_returning_qualified_star_new() {
4412        assert_snapshot!(check_hover("
4413create table t(a int, b int);
4414merge into t
4415  using (select 1 as x, 2 as y) u
4416    on t.a = u.x
4417  when matched then
4418    update set a = 99
4419returning new$0.*;
4420"), @r"
4421        hover: table public.t(a int, b int)
4422          ╭▸ 
4423        8 │ returning new.*;
4424          ╰╴            ─ hover
4425        ");
4426    }
4427
4428    #[test]
4429    fn hover_merge_returning_qualified_star_table() {
4430        assert_snapshot!(check_hover("
4431create table t(a int, b int);
4432merge into t
4433  using (select 1 as x, 2 as y) u
4434    on t.a = u.x
4435  when matched then
4436    update set a = 99
4437returning t$0.*;
4438"), @r"
4439        hover: table public.t(a int, b int)
4440          ╭▸ 
4441        8 │ returning t.*;
4442          ╰╴          ─ hover
4443        ");
4444    }
4445
4446    #[test]
4447    fn hover_merge_returning_qualified_star_old_on_star() {
4448        assert_snapshot!(check_hover("
4449create table t(a int, b int);
4450merge into t
4451  using (select 1 as x, 2 as y) u
4452    on t.a = u.x
4453  when matched then
4454    update set a = 99
4455returning old.*$0;
4456"), @r"
4457        hover: column public.t.a int
4458              column public.t.b int
4459          ╭▸ 
4460        8 │ returning old.*;
4461          ╰╴              ─ hover
4462        ");
4463    }
4464
4465    #[test]
4466    fn hover_merge_returning_qualified_star_new_on_star() {
4467        assert_snapshot!(check_hover("
4468create table t(a int, b int);
4469merge into t
4470  using (select 1 as x, 2 as y) u
4471    on t.a = u.x
4472  when matched then
4473    update set a = 99
4474returning new.*$0;
4475"), @r"
4476        hover: column public.t.a int
4477              column public.t.b int
4478          ╭▸ 
4479        8 │ returning new.*;
4480          ╰╴              ─ hover
4481        ");
4482    }
4483
4484    #[test]
4485    fn hover_merge_returning_qualified_star_table_on_star() {
4486        assert_snapshot!(check_hover("
4487create table t(a int, b int);
4488merge into t
4489  using (select 1 as x, 2 as y) u
4490    on t.a = u.x
4491  when matched then
4492    update set a = 99
4493returning t.*$0;
4494"), @r"
4495        hover: column public.t.a int
4496              column public.t.b int
4497          ╭▸ 
4498        8 │ returning t.*;
4499          ╰╴            ─ hover
4500        ");
4501    }
4502
4503    #[test]
4504    fn hover_partition_table_column() {
4505        assert_snapshot!(check_hover("
4506create table part (
4507  a int,
4508  inserted_at timestamptz not null default now()
4509) partition by range (inserted_at);
4510create table part_2026_01_02 partition of part
4511    for values from ('2026-01-02') to ('2026-01-03');
4512select a$0 from part_2026_01_02;
4513"), @r"
4514        hover: column public.part.a int
4515          ╭▸ 
4516        8 │ select a from part_2026_01_02;
4517          ╰╴       ─ hover
4518        ");
4519    }
4520
4521    #[test]
4522    fn hover_select_window_def_reuse() {
4523        assert_snapshot!(check_hover("
4524create table tbl (
4525  id bigint primary key,
4526  group_col text not null,
4527  update_date date not null,
4528  value text
4529);
4530select
4531  id,
4532  group_col,
4533  row_number() over w as rn,
4534  lag(value) over w$0 as prev_value
4535from tbl
4536window w as (
4537  partition by group_col
4538  order by update_date desc
4539);
4540"), @r"
4541hover: window w as (
4542        partition by group_col
4543        order by update_date desc
4544      )
4545   ╭▸ 
454612 │   lag(value) over w as prev_value
4547   ╰╴                  ─ hover
4548        ");
4549    }
4550
4551    #[test]
4552    fn hover_create_table_like_multi_star() {
4553        assert_snapshot!(check_hover("
4554create table t(a int, b int);
4555create table u(x int, y int);
4556create table k(like t, like u, c int);
4557select *$0 from k;
4558"), @r"
4559        hover: column public.k.a int
4560              column public.k.b int
4561              column public.k.x int
4562              column public.k.y int
4563              column public.k.c int
4564          ╭▸ 
4565        5 │ select * from k;
4566          ╰╴       ─ hover
4567        ");
4568    }
4569
4570    #[test]
4571    fn hover_create_table_inherits_star() {
4572        assert_snapshot!(check_hover("
4573create table t (
4574  a int, b text
4575);
4576create table u (
4577  c int
4578) inherits (t);
4579select *$0 from u;
4580"), @r"
4581        hover: column public.u.a int
4582              column public.u.b text
4583              column public.u.c int
4584          ╭▸ 
4585        8 │ select * from u;
4586          ╰╴       ─ hover
4587        ");
4588    }
4589
4590    #[test]
4591    fn hover_create_table_inherits_column() {
4592        assert_snapshot!(check_hover("
4593create table t (
4594  a int, b text
4595);
4596create table u (
4597  c int
4598) inherits (t);
4599select a$0 from u;
4600"), @r"
4601        hover: column public.t.a int
4602          ╭▸ 
4603        8 │ select a from u;
4604          ╰╴       ─ hover
4605        ");
4606    }
4607
4608    #[test]
4609    fn hover_create_table_inherits_local_column() {
4610        assert_snapshot!(check_hover("
4611create table t (
4612  a int, b text
4613);
4614create table u (
4615  c int
4616) inherits (t);
4617select c$0 from u;
4618"), @r"
4619        hover: column public.u.c int
4620          ╭▸ 
4621        8 │ select c from u;
4622          ╰╴       ─ hover
4623        ");
4624    }
4625
4626    #[test]
4627    fn hover_create_table_inherits_multiple_parents() {
4628        assert_snapshot!(check_hover("
4629create table t1 (
4630  a int
4631);
4632create table t2 (
4633  b text
4634);
4635create table u (
4636  c int
4637) inherits (t1, t2);
4638select b$0 from u;
4639"), @r"
4640        hover: column public.t2.b text
4641           ╭▸ 
4642        11 │ select b from u;
4643           ╰╴       ─ hover
4644        ");
4645    }
4646
4647    #[test]
4648    fn hover_create_foreign_table_inherits_column() {
4649        assert_snapshot!(check_hover("
4650create server myserver foreign data wrapper postgres_fdw;
4651create table t (
4652  a int, b text
4653);
4654create foreign table u (
4655  c int
4656) inherits (t) server myserver;
4657select a$0 from u;
4658"), @r"
4659        hover: column public.t.a int
4660          ╭▸ 
4661        9 │ select a from u;
4662          ╰╴       ─ hover
4663        ");
4664    }
4665
4666    #[test]
4667    fn hover_extension_on_create() {
4668        assert_snapshot!(check_hover("
4669create extension my$0ext;
4670"), @r"
4671        hover: extension myext
4672          ╭▸ 
4673        2 │ create extension myext;
4674          ╰╴                  ─ hover
4675        ");
4676    }
4677
4678    #[test]
4679    fn hover_extension_on_drop() {
4680        assert_snapshot!(check_hover("
4681create extension myext;
4682drop extension my$0ext;
4683"), @r"
4684        hover: extension myext
4685          ╭▸ 
4686        3 │ drop extension myext;
4687          ╰╴                ─ hover
4688        ");
4689    }
4690
4691    #[test]
4692    fn hover_extension_on_alter() {
4693        assert_snapshot!(check_hover("
4694create extension myext;
4695alter extension my$0ext update to '2.0';
4696"), @r"
4697        hover: extension myext
4698          ╭▸ 
4699        3 │ alter extension myext update to '2.0';
4700          ╰╴                 ─ hover
4701        ");
4702    }
4703
4704    #[test]
4705    fn hover_role_on_create() {
4706        assert_snapshot!(check_hover("
4707create role read$0er;
4708"), @r"
4709        hover: role reader
4710          ╭▸ 
4711        2 │ create role reader;
4712          ╰╴               ─ hover
4713        ");
4714    }
4715
4716    #[test]
4717    fn hover_role_on_alter() {
4718        assert_snapshot!(check_hover("
4719create role reader;
4720alter role read$0er rename to writer;
4721"), @r"
4722        hover: role reader
4723          ╭▸ 
4724        3 │ alter role reader rename to writer;
4725          ╰╴              ─ hover
4726        ");
4727    }
4728
4729    #[test]
4730    fn hover_role_on_drop() {
4731        assert_snapshot!(check_hover("
4732create role reader;
4733drop role read$0er;
4734"), @r"
4735        hover: role reader
4736          ╭▸ 
4737        3 │ drop role reader;
4738          ╰╴             ─ hover
4739        ");
4740    }
4741
4742    #[test]
4743    fn hover_role_on_set() {
4744        assert_snapshot!(check_hover("
4745create role reader;
4746set role read$0er;
4747"), @r"
4748        hover: role reader
4749          ╭▸ 
4750        3 │ set role reader;
4751          ╰╴            ─ hover
4752        ");
4753    }
4754
4755    #[test]
4756    fn hover_role_on_create_tablespace_owner() {
4757        assert_snapshot!(check_hover("
4758create role reader;
4759create tablespace t owner read$0er location 'foo';
4760"), @r"
4761        hover: role reader
4762          ╭▸ 
4763        3 │ create tablespace t owner reader location 'foo';
4764          ╰╴                             ─ hover
4765        ");
4766    }
4767
4768    #[test]
4769    fn hover_on_fetch_cursor() {
4770        assert_snapshot!(check_hover("
4771declare c scroll cursor for select * from t;
4772fetch forward 5 from c$0;
4773"), @r"
4774        hover: cursor c for select * from t
4775          ╭▸ 
4776        3 │ fetch forward 5 from c;
4777          ╰╴                     ─ hover
4778        ");
4779    }
4780
4781    #[test]
4782    fn hover_on_close_cursor() {
4783        assert_snapshot!(check_hover("
4784declare c scroll cursor for select * from t;
4785close c$0;
4786"), @r"
4787        hover: cursor c for select * from t
4788          ╭▸ 
4789        3 │ close c;
4790          ╰╴      ─ hover
4791        ");
4792    }
4793
4794    #[test]
4795    fn hover_on_move_cursor() {
4796        assert_snapshot!(check_hover("
4797declare c scroll cursor for select * from t;
4798move forward 10 from c$0;
4799"), @r"
4800        hover: cursor c for select * from t
4801          ╭▸ 
4802        3 │ move forward 10 from c;
4803          ╰╴                     ─ hover
4804        ");
4805    }
4806
4807    #[test]
4808    fn hover_on_prepare_statement() {
4809        assert_snapshot!(check_hover("
4810prepare stmt$0 as select 1;
4811"), @r"
4812        hover: prepare stmt as select 1
4813          ╭▸ 
4814        2 │ prepare stmt as select 1;
4815          ╰╴           ─ hover
4816        ");
4817    }
4818
4819    #[test]
4820    fn hover_on_execute_prepared_statement() {
4821        assert_snapshot!(check_hover("
4822prepare stmt as select 1;
4823execute stmt$0;
4824"), @r"
4825        hover: prepare stmt as select 1
4826          ╭▸ 
4827        3 │ execute stmt;
4828          ╰╴           ─ hover
4829        ");
4830    }
4831
4832    #[test]
4833    fn hover_on_deallocate_prepared_statement() {
4834        assert_snapshot!(check_hover("
4835prepare stmt as select 1;
4836deallocate stmt$0;
4837"), @r"
4838        hover: prepare stmt as select 1
4839          ╭▸ 
4840        3 │ deallocate stmt;
4841          ╰╴              ─ hover
4842        ");
4843    }
4844
4845    #[test]
4846    fn hover_on_listen_definition() {
4847        assert_snapshot!(check_hover("
4848listen updates$0;
4849"), @r"
4850        hover: listen updates
4851          ╭▸ 
4852        2 │ listen updates;
4853          ╰╴             ─ hover
4854        ");
4855    }
4856
4857    #[test]
4858    fn hover_on_notify_channel() {
4859        assert_snapshot!(check_hover("
4860listen updates;
4861notify updates$0;
4862"), @r"
4863        hover: listen updates
4864          ╭▸ 
4865        3 │ notify updates;
4866          ╰╴             ─ hover
4867        ");
4868    }
4869
4870    #[test]
4871    fn hover_on_unlisten_channel() {
4872        assert_snapshot!(check_hover("
4873listen updates;
4874unlisten updates$0;
4875"), @"
4876        hover: listen updates
4877          ╭▸ 
4878        3 │ unlisten updates;
4879          ╰╴               ─ hover
4880        ");
4881    }
4882
4883    #[test]
4884    fn hover_property_graph_on_create() {
4885        assert_snapshot!(check_hover("
4886create property graph foo.ba$0r vertex tables (t key (a) no properties);
4887"), @"
4888        hover: property graph foo.bar
4889          ╭▸ 
4890        2 │ create property graph foo.bar vertex tables (t key (a) no properties);
4891          ╰╴                           ─ hover
4892        ");
4893    }
4894
4895    #[test]
4896    fn hover_property_graph_on_drop() {
4897        assert_snapshot!(check_hover("
4898create property graph foo.bar vertex tables (t key (a) no properties);
4899drop property graph foo.ba$0r;
4900"), @"
4901        hover: property graph foo.bar
4902          ╭▸ 
4903        3 │ drop property graph foo.bar;
4904          ╰╴                         ─ hover
4905        ");
4906    }
4907
4908    #[test]
4909    fn hover_property_graph_on_alter() {
4910        assert_snapshot!(check_hover("
4911create property graph foo.bar vertex tables (t key (a) no properties);
4912alter property graph foo.ba$0r rename to baz;
4913"), @"
4914        hover: property graph foo.bar
4915          ╭▸ 
4916        3 │ alter property graph foo.bar rename to baz;
4917          ╰╴                          ─ hover
4918        ");
4919    }
4920}