Skip to main content

squawk_ide/
hover.rs

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