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