Skip to main content

squawk_ide/
hover.rs

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