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