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