Skip to main content

squawk_ide/
hover.rs

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