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