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