From 5707bb88e7af3fcba3d5bdd3002cb3d4802934d7 Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 7 Nov 2025 18:51:38 +0100 Subject: [PATCH 01/38] pickin up fast --- Cargo.lock | 18 +- crates/pgls_completions/Cargo.toml | 3 + .../pgls_completions/src/providers/columns.rs | 106 +++---- .../src/relevance/filtering.rs | 2 +- ...ompletions__test_helper__snapshot.snap.new | 90 ++++++ crates/pgls_completions/src/test_helper.rs | 294 ++++++++++++++++++ 6 files changed, 440 insertions(+), 73 deletions(-) create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new diff --git a/Cargo.lock b/Cargo.lock index f3f5914ea..9df934efd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -749,7 +749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.13", "serde", ] @@ -1667,7 +1667,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.9", + "regex-automata 0.4.13", "regex-syntax 0.8.5", ] @@ -1971,7 +1971,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.13", "same-file", "walkdir", "winapi-util", @@ -2717,11 +2717,13 @@ dependencies = [ "async-std", "criterion", "fuzzy-matcher", + "insta", "pgls_schema_cache", "pgls_test_utils", "pgls_text_size", "pgls_treesitter", "pgls_treesitter_grammar", + "regex", "schemars", "serde", "serde_json", @@ -3634,13 +3636,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.13", "regex-syntax 0.8.5", ] @@ -3655,9 +3657,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", diff --git a/crates/pgls_completions/Cargo.toml b/crates/pgls_completions/Cargo.toml index cfb4fa864..7f89ff737 100644 --- a/crates/pgls_completions/Cargo.toml +++ b/crates/pgls_completions/Cargo.toml @@ -33,6 +33,9 @@ tokio = { version = "1.41.1", features = ["full"] } [dev-dependencies] criterion.workspace = true pgls_test_utils.workspace = true +insta.workspace = true +regex = "1.12.2" + [lib] doctest = false diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index a902579e4..764cd4780 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -58,7 +58,8 @@ mod tests { use crate::{ CompletionItem, CompletionItemKind, complete, test_helper::{ - CompletionAssertion, assert_complete_results, get_test_deps, get_test_params, + CompletionAssertion, TestCompletionsBuilder, assert_complete_results, get_test_deps, + get_test_params, }, }; @@ -381,7 +382,7 @@ mod tests { } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] - async fn filters_out_by_aliases(pool: PgPool) { + async fn filters_out_by_aliases_in_join_on(pool: PgPool) { let setup = r#" create schema auth; @@ -400,52 +401,41 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); + TestCompletionsBuilder::new(&pool, Some(setup)) + .prefix_static("select u.id, p.content from auth.users u join auth.posts p") + .type_sql("on u<1>.id = p.<2>user_id") + .comment("Should prefer primary indices here.") + .comment("We should only get columns from the auth.posts table.") + .snapshot() + .await; + } - // test in SELECT clause - assert_complete_results( - format!( - "select u.id, p.{} from auth.users u join auth.posts p on u.id = p.user_id;", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::LabelNotExists("uid".to_string()), - CompletionAssertion::LabelNotExists("name".to_string()), - CompletionAssertion::LabelNotExists("email".to_string()), - CompletionAssertion::Label("content".to_string()), - CompletionAssertion::Label("created_at".to_string()), - CompletionAssertion::Label("pid".to_string()), - CompletionAssertion::Label("title".to_string()), - CompletionAssertion::Label("user_id".to_string()), - ], - None, - &pool, - ) - .await; + #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] + async fn filters_out_by_aliases_in_select(pool: PgPool) { + let setup = r#" + create schema auth; - // test in JOIN clause - assert_complete_results( - format!( - "select u.id, p.content from auth.users u join auth.posts p on u.id = p.{};", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::LabelNotExists("uid".to_string()), - CompletionAssertion::LabelNotExists("name".to_string()), - CompletionAssertion::LabelNotExists("email".to_string()), - // primary keys are preferred - CompletionAssertion::Label("pid".to_string()), - CompletionAssertion::Label("content".to_string()), - CompletionAssertion::Label("created_at".to_string()), - CompletionAssertion::Label("title".to_string()), - CompletionAssertion::Label("user_id".to_string()), - ], - None, - &pool, - ) - .await; + create table auth.users ( + uid serial primary key, + name text not null, + email text unique not null + ); + + create table auth.posts ( + pid serial primary key, + user_id int not null references auth.users(uid), + title text not null, + content text, + created_at timestamp default now() + ); + "#; + + TestCompletionsBuilder::new(&pool, Some(setup)) + .type_sql("select u.id, p.pid<1>") + .comment("We should only get columns from the auth.posts table.") + .append_static("from auth.users u join auth.posts p on u.id = p.user_id;") + .snapshot() + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -468,24 +458,12 @@ mod tests { ); "#; - /* - * We are not in the "ON" part of the JOIN clause, so we should not complete columns. - */ - assert_complete_results( - format!( - "select u.id, p.content from auth.users u join auth.{}", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::KindNotExists(CompletionItemKind::Column), - CompletionAssertion::LabelAndKind("posts".to_string(), CompletionItemKind::Table), - CompletionAssertion::LabelAndKind("users".to_string(), CompletionItemKind::Table), - ], - Some(setup), - &pool, - ) - .await; + TestCompletionsBuilder::new(&pool, Some(setup)) + .type_sql("select u.uid, p.content from auth<1>.users<2> u join auth.posts p on u.uid = p.user_id") + .comment("Schema suggestions should be prioritized, since we want to push users to specify them.") + .comment("Here, we shouldn't have schema completions.") + .snapshot() + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index 7aa3531a6..c0c3087d6 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -324,7 +324,7 @@ impl CompletionFilter<'_> { WrappingClause::DropRole | WrappingClause::AlterRole => true, WrappingClause::SetStatement => ctx - .before_cursor_matches_kind(&["keyword_role", "keyword_authorization"]), + .before_cursor_matches_kind(&["eyword_role", "keyword_authorization"]), WrappingClause::RevokeStatement | WrappingClause::GrantStatement => { ctx.history_ends_with(&["role_specification", "any_identifier"]) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new new file mode 100644 index 000000000..4cd0170d9 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new @@ -0,0 +1,90 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +assertion_line: 415 +expression: snapshot_result +--- +s| +from auth.users u join auth.posts p on u.id = p.user_id; + +No Results + +-------------- + +select | +from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) +u.name - auth.users.name (Column) +p.pid - auth.posts.pid (Column) + +-------------- + +select u.| +from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +email - auth.users.email (Column) +name - auth.users.name (Column) +uid - auth.users.uid (Column) + +-------------- + +select u.i| +from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +email - auth.users.email (Column) +uid - auth.users.uid (Column) +name - auth.users.name (Column) + +-------------- + +select u.id, | +from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) +u.name - auth.users.name (Column) +p.pid - auth.posts.pid (Column) + +-------------- + +select u.id, p.| +from auth.users u join auth.posts p on u.id = p.user_id; + +**We should only get columns from the auth.posts table.** + +Results: +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +pid - auth.posts.pid (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.id, p.p| +from auth.users u join auth.posts p on u.id = p.user_id; + +**We should only get columns from the auth.posts table.** + +Results: +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.id, p.pid | +from auth.users u join auth.posts p on u.id = p.user_id; + +No Results + +-------------- diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index cb27353a5..293b6d141 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -1,7 +1,9 @@ use pgls_schema_cache::SchemaCache; use pgls_test_utils::QueryWithCursorPosition; use pgls_text_size::TextRange; +use regex::Regex; use sqlx::{Executor, PgPool}; +use std::{collections::HashMap, fmt::Write, sync::OnceLock}; use crate::{CompletionItem, CompletionItemKind, CompletionParams, complete}; @@ -201,3 +203,295 @@ pub(crate) async fn assert_no_complete_results(query: &str, setup: Option<&str>, assert_eq!(items.len(), 0) } + +enum ChunkToType { + WithCompletions(String), + WithoutCompletions(String), +} + +static COMMENT_RE: OnceLock = OnceLock::new(); + +fn comment_regex() -> &'static Regex { + COMMENT_RE.get_or_init(|| Regex::new(r"<\d+>").unwrap()) +} + +pub(crate) struct TestCompletionsBuilder<'a> { + prefixes: Vec, + tokens_to_type: Vec, + appendices: Vec, + comments: std::collections::HashMap, + pool: &'a PgPool, + setup: Option<&'a str>, + comment_position: usize, +} + +impl<'a> TestCompletionsBuilder<'a> { + pub(crate) fn new(pool: &'a PgPool, setup: Option<&'a str>) -> Self { + Self { + prefixes: Vec::new(), + tokens_to_type: Vec::new(), + appendices: Vec::new(), + comments: HashMap::new(), + pool, + setup, + comment_position: 0, + } + } + + pub(crate) fn prefix_static(mut self, it: &str) -> Self { + self.prefixes.push(it.trim().to_string()); + self + } + + pub(crate) fn append_static(mut self, it: &str) -> Self { + self.appendices.push(it.trim().to_string()); + self + } + + pub(crate) fn type_sql(mut self, it: &str) -> Self { + assert_eq!( + self.appendices.len(), + 0, + "Make sure to call appendices LAST." + ); + self.tokens_to_type + .push(ChunkToType::WithCompletions(it.trim().to_string())); + self + } + + pub(crate) fn type_without_completions(mut self, it: &str) -> Self { + assert_eq!( + self.appendices.len(), + 0, + "Make sure to call appendices LAST." + ); + self.tokens_to_type + .push(ChunkToType::WithoutCompletions(it.trim().to_string())); + self + } + + pub(crate) fn comment(mut self, comment: &str) -> Self { + self.comment_position += 1; + self.comments + .insert(self.comment_position, comment.to_string()); + self + } + + pub(crate) async fn snapshot(self) { + if let Some(setup) = self.setup { + self.pool.execute(setup).await.expect("Invalid Setup!"); + } + + let schema_cache = SchemaCache::load(self.pool) + .await + .expect("Failed to load Schema Cache"); + + let mut parser = tree_sitter::Parser::new(); + parser + .set_language(&pgls_treesitter_grammar::LANGUAGE.into()) + .expect("Error loading sql language"); + + let mut joined_prefix = self.prefixes.join("\n"); + if joined_prefix.len() > 0 { + joined_prefix.push_str("\n"); + }; + + let mut joined_appendix = String::new(); + if self.appendices.len() > 0 { + joined_appendix.push_str("\n"); + } + joined_appendix.push_str(self.appendices.join("\n").as_str()); + + let mut snapshot_result = String::new(); + + for chunk in &self.tokens_to_type { + match chunk { + ChunkToType::WithCompletions(sql) => { + let whitespace_count = sql.chars().filter(|c| c.is_ascii_whitespace()).count(); + let whitespace_split = sql.split_ascii_whitespace().enumerate(); + + for (whitespace_idx, token) in whitespace_split { + let dot_count = token.chars().filter(|c| *c == '.').count(); + let dotted_split = token.split(".").enumerate(); + + for (dot_idx, og_part) in dotted_split { + let comment_indicator = comment_regex().find(og_part); + let comment = comment_indicator.and_then(|n| { + let num = n.as_str().replace("<", "").replace(">", ""); + let num: usize = num + .parse() + .expect("Regex should only find matches with numbers"); + self.comments.get(&num).map(|s| s.as_str()) + }); + + let part = comment_regex().replace_all(og_part, ""); + + if joined_prefix.len() > 0 { + let query = format!( + "{}{}{}{}", + joined_prefix, + if dot_idx <= dot_count { "" } else { "." }, + QueryWithCursorPosition::cursor_marker(), + joined_appendix, + ); + + self.completions_snapshot( + query.into(), + &mut snapshot_result, + &schema_cache, + &mut parser, + comment, + ) + .await; + } + + if part.len() > 1 { + let query = format!( + "{}{}{}{}", + joined_prefix, + &part[..1], + QueryWithCursorPosition::cursor_marker(), + joined_appendix, + ); + + self.completions_snapshot( + query.into(), + &mut snapshot_result, + &schema_cache, + &mut parser, + if comment_indicator + .is_some_and(|txt| og_part.starts_with(txt.as_str())) + { + None + } else { + comment + }, + ) + .await; + }; + + if whitespace_idx == whitespace_count && dot_idx == dot_count { + let query = format!( + "{}{} {}{}", + joined_prefix, + part, + QueryWithCursorPosition::cursor_marker(), + joined_appendix, + ); + + self.completions_snapshot( + query.into(), + &mut snapshot_result, + &schema_cache, + &mut parser, + None, + ) + .await; + } + + joined_prefix.push_str(&part); + + if dot_idx < dot_count { + joined_prefix.push_str("."); + } + } + + if whitespace_idx < whitespace_count { + // note: we're sanitizing the white_space of typed SQL to simple spaces. + joined_prefix.push_str(" "); + } + } + + joined_prefix.push_str("\n"); + } + + ChunkToType::WithoutCompletions(sql) => { + joined_prefix.push_str(sql.as_str()); + joined_prefix.push_str("\n"); + } + } + } + + insta::assert_snapshot!(snapshot_result); + } + + async fn completions_snapshot( + &self, + query: QueryWithCursorPosition, + writer: &mut String, + schema: &SchemaCache, + parser: &mut tree_sitter::Parser, + comment: Option<&str>, + ) { + let (pos, mut sql) = query.get_text_and_position(); + if sql.len() == 0 { + return; + } + + println!("'{sql}', {pos}"); + + let tree = parser.parse(&sql, None).expect("Invalid TS Tree!"); + + let params = CompletionParams { + text: sql.clone(), + position: (pos as u32).into(), + schema, + tree: &tree, + }; + + let items = complete(params); + + if pos < sql.len() { + sql.replace_range(pos..pos, "|"); + } else { + let diff = pos - sql.len(); + + sql.push_str(&" ".repeat(diff)); + sql.push_str("|"); + } + writeln!(writer, "{sql}").unwrap(); + writeln!(writer).unwrap(); + + if let Some(c) = comment { + writeln!(writer, "**{}**", c).unwrap(); + writeln!(writer).unwrap(); + } + + if items.len() == 0 { + writeln!(writer, "No Results").unwrap(); + } else { + writeln!(writer, "Results:").unwrap(); + + let max_idx = std::cmp::min(items.len(), 5); + for item in &items[..max_idx] { + write!( + writer, + "{}", + item.completion_text + .as_ref() + .map(|c| c.text.as_str()) + .unwrap_or(item.label.as_str()) + ) + .unwrap(); + + write!(writer, " - ").unwrap(); + + match item.kind { + CompletionItemKind::Schema | CompletionItemKind::Role => {} + _ => { + write!(writer, "{}.", item.description).unwrap(); + } + } + + write!(writer, "{} ({})", item.label, item.kind).unwrap(); + + writeln!(writer).unwrap(); + } + } + + writeln!(writer).unwrap(); + + writeln!(writer, "--------------").unwrap(); + writeln!(writer).unwrap(); + } +} From 8b96f00bcabee82f9a8d3ca23e88dee6b4f9c866 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 8 Nov 2025 18:27:21 +0100 Subject: [PATCH 02/38] so far --- Cargo.lock | 10 + crates/pgls_completions/Cargo.toml | 1 + .../pgls_completions/src/providers/columns.rs | 1084 ++++------------- ...ompletions__test_helper__snapshot.snap.new | 90 -- crates/pgls_completions/src/test_helper.rs | 316 +++-- 5 files changed, 472 insertions(+), 1029 deletions(-) delete mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new diff --git a/Cargo.lock b/Cargo.lock index 9df934efd..46c1a2869 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1999,6 +1999,15 @@ dependencies = [ "serde", ] +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + [[package]] name = "insta" version = "1.42.1" @@ -2717,6 +2726,7 @@ dependencies = [ "async-std", "criterion", "fuzzy-matcher", + "indoc", "insta", "pgls_schema_cache", "pgls_test_utils", diff --git a/crates/pgls_completions/Cargo.toml b/crates/pgls_completions/Cargo.toml index 7f89ff737..32cc4cf20 100644 --- a/crates/pgls_completions/Cargo.toml +++ b/crates/pgls_completions/Cargo.toml @@ -35,6 +35,7 @@ criterion.workspace = true pgls_test_utils.workspace = true insta.workspace = true regex = "1.12.2" +indoc = "2.0.7" [lib] diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 764cd4780..6b2b03692 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -50,16 +50,16 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText #[cfg(test)] mod tests { - use std::vec; + use indoc::indoc; use pgls_text_size::TextRange; use sqlx::{Executor, PgPool}; use crate::{ - CompletionItem, CompletionItemKind, complete, + CompletionItemKind, complete, test_helper::{ - CompletionAssertion, TestCompletionsBuilder, assert_complete_results, get_test_deps, - get_test_params, + CompletionAssertion, TestCompletionsCase, TestCompletionsSuite, + assert_complete_results, get_test_deps, get_test_params, }, }; @@ -67,9 +67,6 @@ mod tests { struct TestCase { query: String, - message: &'static str, - label: &'static str, - description: &'static str, } impl TestCase { @@ -80,7 +77,7 @@ mod tests { } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] - async fn completes_columns(pool: PgPool) { + async fn handles_nested_queries(pool: PgPool) { let setup = r#" create schema private; @@ -100,62 +97,20 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - let queries: Vec = vec![ - TestCase { - message: "correctly prefers the columns of present tables", - query: format!( - r#"select na{} from public.audio_books;"#, - QueryWithCursorPosition::cursor_marker() - ), - label: "narrator", - description: "public.audio_books", - }, - TestCase { - message: "correctly handles nested queries", - query: format!( - r#" - select - * - from ( - select id, na{} - from private.audio_books - ) as subquery - join public.users u - on u.id = subquery.id; - "#, - QueryWithCursorPosition::cursor_marker() - ), - label: "narrator_id", - description: "private.audio_books", - }, - TestCase { - message: "works without a schema", - query: format!( - r#"select na{} from users;"#, - QueryWithCursorPosition::cursor_marker() - ), - label: "name", - description: "public.users", - }, - ]; - - for q in queries { - let (tree, cache) = get_test_deps(None, q.get_input_query(), &pool).await; - let params = get_test_params(&tree, &cache, q.get_input_query()); - let results = complete(params); - - let CompletionItem { - label, description, .. - } = results - .into_iter() - .next() - .expect("Should return at least one completion item"); - - assert_eq!(label, q.label, "{}", q.message); - assert_eq!(description, q.description, "{}", q.message); - } + TestCompletionsSuite::new(&pool, Some(setup)).with_case( + TestCompletionsCase::new() + .inside_static_statement(indoc! {r#" + select * from ( + + ) as subquery + join public.users u + on u.id = subquery.id; + "#}) + .type_sql("select id, narrator_id<1> from private.audio_books") + .comment("Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.") + ) + .snapshot("handles_nested_queries") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -179,49 +134,14 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - let case = TestCase { - query: format!(r#"select n{};"#, QueryWithCursorPosition::cursor_marker()), - description: "", - label: "", - message: "", - }; - - let (tree, cache) = get_test_deps(None, case.get_input_query(), &pool).await; - let params = get_test_params(&tree, &cache, case.get_input_query()); - let mut items = complete(params); - - let _ = items.split_off(4); - - #[derive(Eq, PartialEq, Debug)] - struct LabelAndDesc { - label: String, - desc: String, - } - - let labels: Vec = items - .into_iter() - .map(|c| LabelAndDesc { - label: c.label, - desc: c.description, - }) - .collect(); - - let expected = vec![ - ("name", "public.users"), - ("narrator", "public.audio_books"), - ("narrator_id", "private.audio_books"), - ("id", "public.audio_books"), - ] - .into_iter() - .map(|(label, schema)| LabelAndDesc { - label: label.into(), - desc: schema.into(), - }) - .collect::>(); - - assert_eq!(labels, expected); + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .type_sql("select narrator_id<1>") + .comment("Should suggest all columns with n first"), + ) + .snapshot("shows_multiple_columns_if_no_relation_specified") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -235,44 +155,10 @@ mod tests { ); "#; - let test_case = TestCase { - message: "suggests user created tables first", - query: format!( - r#"select {} from users"#, - QueryWithCursorPosition::cursor_marker() - ), - label: "", - description: "", - }; - - let (tree, cache) = get_test_deps(Some(setup), test_case.get_input_query(), &pool).await; - let params = get_test_params(&tree, &cache, test_case.get_input_query()); - let results = complete(params); - - let (first_four, _rest) = results.split_at(4); - - let has_column_in_first_four = |col: &'static str| { - first_four - .iter() - .any(|compl_item| compl_item.label.as_str() == col) - }; - - assert!( - has_column_in_first_four("id"), - "`id` not present in first four completion items." - ); - assert!( - has_column_in_first_four("name"), - "`name` not present in first four completion items." - ); - assert!( - has_column_in_first_four("address"), - "`address` not present in first four completion items." - ); - assert!( - has_column_in_first_four("email"), - "`email` not present in first four completion items." - ); + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case(TestCompletionsCase::new().type_sql("select name from users")) + .snapshot("suggests_relevant_columns_without_letters") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -284,29 +170,20 @@ mod tests { id serial primary key, name text, address text, - email text + email text, + public boolean ); "#; - let test_case = TestCase { - message: "suggests user created tables first", - query: format!( - r#"select * from private.{}"#, - QueryWithCursorPosition::cursor_marker() - ), - label: "", - description: "", - }; - - let (tree, cache) = get_test_deps(Some(setup), test_case.get_input_query(), &pool).await; - let params = get_test_params(&tree, &cache, test_case.get_input_query()); - let results = complete(params); - - assert!( - !results - .into_iter() - .any(|item| item.kind == CompletionItemKind::Column) - ); + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement("select * from ") + .type_sql("private<1>.users") + .comment("No column suggestions."), + ) + .snapshot("ignores_cols_in_from_clause") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -331,54 +208,27 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - assert_complete_results( - format!( - r#"select {} from users"#, - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("address2".into()), - CompletionAssertion::Label("email2".into()), - CompletionAssertion::Label("id2".into()), - CompletionAssertion::Label("name2".into()), - ], - None, - &pool, - ) - .await; - - assert_complete_results( - format!( - r#"select {} from private.users"#, - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("address1".into()), - CompletionAssertion::Label("email1".into()), - CompletionAssertion::Label("id1".into()), - CompletionAssertion::Label("name1".into()), - ], - None, - &pool, - ) - .await; - - // asserts fuzzy finding for "settings" - assert_complete_results( - format!( - r#"select sett{} from private.users"#, - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![CompletionAssertion::Label("user_settings".into())], - None, - &pool, - ) - .await; + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(" from public.users") + .type_sql("select address2<1>") + .comment("Should suggest address 2 from public table"), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(" from private.users") + .type_sql("select address1<1>") + .comment("Should suggest address 1 from private table"), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(" from private.users") + .type_sql("select settings<1>") + .comment("Should prioritize columns starting with s"), + ) + .snapshot("prefers_columns_of_mentioned_tables") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -401,12 +251,17 @@ mod tests { ); "#; - TestCompletionsBuilder::new(&pool, Some(setup)) - .prefix_static("select u.id, p.content from auth.users u join auth.posts p") - .type_sql("on u<1>.id = p.<2>user_id") - .comment("Should prefer primary indices here.") - .comment("We should only get columns from the auth.posts table.") - .snapshot() + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + "select u.id, p.content from auth.users u join auth.posts p ", + ) + .type_sql("on u<1>.id = p.<2>user_id") + .comment("Should prefer primary indices here.") + .comment("We should only get columns from the auth.posts table."), + ) + .snapshot("filters_out_by_aliases_in_join_on") .await; } @@ -430,11 +285,16 @@ mod tests { ); "#; - TestCompletionsBuilder::new(&pool, Some(setup)) - .type_sql("select u.id, p.pid<1>") - .comment("We should only get columns from the auth.posts table.") - .append_static("from auth.users u join auth.posts p on u.id = p.user_id;") - .snapshot() + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + " from auth.users u join auth.posts p on u.id = p.user_id;", + ) + .type_sql("select u.id, p.pid<1>") + .comment("We should only get columns from the auth.posts table."), + ) + .snapshot("filters_out_by_aliases_in_select") .await; } @@ -458,12 +318,12 @@ mod tests { ); "#; - TestCompletionsBuilder::new(&pool, Some(setup)) + TestCompletionsSuite::new(&pool, Some(setup)).with_case( + TestCompletionsCase::new() .type_sql("select u.uid, p.content from auth<1>.users<2> u join auth.posts p on u.uid = p.user_id") .comment("Schema suggestions should be prioritized, since we want to push users to specify them.") .comment("Here, we shouldn't have schema completions.") - .snapshot() - .await; + ).snapshot("does_not_complete_cols_in_join_clauses").await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -486,41 +346,16 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - assert_complete_results( - format!( - "select u.id, auth.posts.content from auth.users u join auth.posts on u.{}", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::KindNotExists(CompletionItemKind::Table), - CompletionAssertion::LabelAndKind("uid".to_string(), CompletionItemKind::Column), - CompletionAssertion::LabelAndKind("email".to_string(), CompletionItemKind::Column), - CompletionAssertion::LabelAndKind("name".to_string(), CompletionItemKind::Column), - ], - None, - &pool, - ) - .await; - - assert_complete_results( - format!( - "select u.id, p.content from auth.users u join auth.posts p on p.user_id = u.{}", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::KindNotExists(CompletionItemKind::Table), - CompletionAssertion::LabelAndKind("uid".to_string(), CompletionItemKind::Column), - CompletionAssertion::LabelAndKind("email".to_string(), CompletionItemKind::Column), - CompletionAssertion::LabelAndKind("name".to_string(), CompletionItemKind::Column), - ], - None, - &pool, - ) - .await; + TestCompletionsSuite::new(&pool, Some(setup)).with_case( + TestCompletionsCase::new() + .inside_static_statement( + "select u.id, auth.posts.content from auth.users u join auth.posts p on ", + ) + .type_sql("<1>p.user_id<2> = u.uid<3>;") + .comment("Should prioritize primary keys here.") + .comment("Should only consider columns from auth.posts here.") + .comment("Should only consider columns from auth.users here.") + ).snapshot("completes_in_join_on_clause").await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -543,76 +378,26 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - assert_complete_results( - format!( - "select {} from public.one o join public.two on o.id = t.id;", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("a".to_string()), - CompletionAssertion::Label("b".to_string()), - CompletionAssertion::Label("c".to_string()), - CompletionAssertion::Label("d".to_string()), - CompletionAssertion::Label("e".to_string()), - ], - None, - &pool, - ) - .await; - - // "a" is already mentioned, so it jumps down - assert_complete_results( - format!( - "select a, {} from public.one o join public.two on o.id = t.id;", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("b".to_string()), - CompletionAssertion::Label("c".to_string()), - CompletionAssertion::Label("d".to_string()), - CompletionAssertion::Label("e".to_string()), - CompletionAssertion::Label("id".to_string()), - CompletionAssertion::Label("z".to_string()), - CompletionAssertion::Label("a".to_string()), - ], - None, - &pool, - ) - .await; - - // "id" of table one is mentioned, but table two isn't – - // its priority stays up - assert_complete_results( - format!( - "select o.id, a, b, c, d, e, {} from public.one o join public.two on o.id = t.id;", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::LabelAndDesc("id".to_string(), "public.two".to_string()), - CompletionAssertion::Label("z".to_string()), - ], - None, - &pool, - ) - .await; - - // "id" is ambiguous, so both "id" columns are lowered in priority - assert_complete_results( - format!( - "select id, a, b, c, d, e, {} from public.one o join public.two on o.id = t.id;", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![CompletionAssertion::Label("z".to_string())], - None, - &pool, - ) - .await; + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + " from public.one o join public.two on o.id = t.id;", + ) + .type_sql("select o.id, a, <1>b, c, d, e, <2>z") + .comment("Should have low priority for `a`, since it's already mentioned.") + .comment("Should have high priority of id of table two, but not one, since it's already mentioned.") + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + " from public.one o join public.two on o.id = t.id;", + ) + .type_sql("select id, a, b, c, d, e, <1>z") + .comment("`id` could be from both tables, so both priorities are lowered."), + ) + .snapshot("prefers_not_mentioned_columns") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -631,69 +416,25 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - // We should prefer the instrument columns, even though they - // are lower in the alphabet - - assert_complete_results( - format!( - "insert into instruments ({})", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("id".to_string()), - CompletionAssertion::Label("name".to_string()), - CompletionAssertion::Label("z".to_string()), - ], - None, - &pool, - ) - .await; - - assert_complete_results( - format!( - "insert into instruments (id, {})", - QueryWithCursorPosition::cursor_marker() + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .type_sql("insert into instruments (id, name) values (1, 'my_bass');"), ) - .as_str(), - vec![ - CompletionAssertion::Label("name".to_string()), - CompletionAssertion::Label("z".to_string()), - ], - None, - &pool, - ) - .await; - - assert_complete_results( - format!( - "insert into instruments (id, {}, name)", - QueryWithCursorPosition::cursor_marker() + .with_case( + TestCompletionsCase::new() + .type_sql(r#"insert into instruments ("id", "name") values (1, 'my_bass');"#), ) - .as_str(), - vec![CompletionAssertion::Label("z".to_string())], - None, - &pool, - ) - .await; - - // works with completed statement - assert_complete_results( - format!( - "insert into instruments (name, {}) values ('my_bass');", - QueryWithCursorPosition::cursor_marker() + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + r#"insert into instruments (, name) values ('my_bass');"#, + ) + .type_sql("id, <1>z") + .comment("`name` is already written, so z should be suggested."), ) - .as_str(), - vec![ - CompletionAssertion::Label("id".to_string()), - CompletionAssertion::Label("z".to_string()), - ], - None, - &pool, - ) - .await; + .snapshot("suggests_columns_in_insert_clause") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -713,74 +454,19 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - assert_complete_results( - format!( - "select name from instruments where {} ", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("created_at".into()), - CompletionAssertion::Label("id".into()), - CompletionAssertion::Label("name".into()), - CompletionAssertion::Label("z".into()), - ], - None, - &pool, - ) - .await; - - assert_complete_results( - format!( - "select name from instruments where z = 'something' and created_at > {}", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - // simply do not complete columns + schemas; functions etc. are ok - vec![ - CompletionAssertion::KindNotExists(CompletionItemKind::Column), - CompletionAssertion::KindNotExists(CompletionItemKind::Schema), - ], - None, - &pool, - ) - .await; - - // prefers not mentioned columns - assert_complete_results( - format!( - "select name from instruments where id = 'something' and {}", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("created_at".into()), - CompletionAssertion::Label("name".into()), - CompletionAssertion::Label("z".into()), - ], - None, - &pool, - ) - .await; - - // // uses aliases - assert_complete_results( - format!( - "select name from instruments i join others o on i.z = o.a where i.{}", - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("created_at".into()), - CompletionAssertion::Label("id".into()), - CompletionAssertion::Label("name".into()), - ], - None, - &pool, - ) - .await; + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + "select name from instruments i join others o on i.z = o.a ", + ) + .type_sql("where o.<1>a = <2>i.z and <3>i.id > 5;") + .comment("should respect alias speciifcation") + .comment("should not prioritize suggest columns or schemas (right side of binary expression)") + .comment("should prioritize columns that aren't already mentioned") + ) + .snapshot("suggests_columns_in_where_clause") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -792,65 +478,42 @@ mod tests { z text, created_at timestamp with time zone default now() ); - - create table others ( - a text, - b text, - c text - ); "#; - pool.execute(setup).await.unwrap(); - - let queries = vec![ - format!( - "alter table instruments drop column {}", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table instruments drop column if exists {}", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table instruments alter column {} set default", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table instruments alter {} set default", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table public.instruments alter column {}", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table instruments alter {}", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table instruments rename {} to new_col", - QueryWithCursorPosition::cursor_marker() - ), - format!( - "alter table public.instruments rename column {} to new_col", - QueryWithCursorPosition::cursor_marker() - ), - ]; - - for query in queries { - assert_complete_results( - query.as_str(), - vec![ - CompletionAssertion::Label("created_at".into()), - CompletionAssertion::Label("id".into()), - CompletionAssertion::Label("name".into()), - CompletionAssertion::Label("z".into()), - ], - None, - &pool, + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new().type_sql("alter table instruments drop column name"), + ) + .with_case( + TestCompletionsCase::new().type_sql("alter table instruments drop column name"), + ) + .with_case( + TestCompletionsCase::new() + .type_sql("alter table instruments drop column if exists name"), + ) + .with_case( + TestCompletionsCase::new() + .type_sql("alter table instruments alter column name set default"), + ) + .with_case( + TestCompletionsCase::new() + .type_sql("alter table instruments alter name set default"), + ) + .with_case( + TestCompletionsCase::new() + .type_sql("alter table public.instruments alter column name"), + ) + .with_case(TestCompletionsCase::new().type_sql("alter table instruments alter name")) + .with_case( + TestCompletionsCase::new() + .type_sql("alter table instruments rename name to new_col"), ) + .with_case( + TestCompletionsCase::new() + .type_sql("alter table public.instruments rename column name to new_col"), + ) + .snapshot("suggests_columns_in_alter_table_and_drop_table") .await; - } } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -864,41 +527,23 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - let col_queries = vec![ - format!( - r#"create policy "my_pol" on public.instruments for select using ({})"#, - QueryWithCursorPosition::cursor_marker() - ), - format!( - r#"create policy "my_pol" on public.instruments for insert with check ({})"#, - QueryWithCursorPosition::cursor_marker() - ), - format!( - r#"create policy "my_pol" on public.instruments for update using (id = 1 and {})"#, - QueryWithCursorPosition::cursor_marker() - ), - format!( - r#"create policy "my_pol" on public.instruments for insert with check (id = 1 and {})"#, - QueryWithCursorPosition::cursor_marker() - ), - ]; - - for query in col_queries { - assert_complete_results( - query.as_str(), - vec![ - CompletionAssertion::Label("created_at".into()), - CompletionAssertion::Label("id".into()), - CompletionAssertion::Label("name".into()), - CompletionAssertion::Label("z".into()), - ], - None, - &pool, - ) + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + r#"create policy "my_pol" on public.instruments for select using ()"#, + ) + .type_sql("id = 1 and created_at > '2025-01-01'"), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + r#"create policy "my_pol" on public.instruments for insert with check ()"#, + ) + .type_sql("id = 1 and created_at > '2025-01-01'"), + ) + .snapshot("suggests_columns_policy_using_clause") .await; - } } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -914,75 +559,12 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - // test completion inside quoted column name - assert_complete_results( - format!( - r#"select "em{}" from "private"."users""#, - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![CompletionAssertion::LabelAndDesc( - "email".to_string(), - "private.users".to_string(), - )], - None, - &pool, - ) - .await; - - // test completion for already quoted column - assert_complete_results( - format!( - r#"select "quoted_col{}" from "private"."users""#, - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![CompletionAssertion::LabelAndDesc( - "quoted_column".to_string(), - "private.users".to_string(), - )], - None, - &pool, - ) - .await; - - // test completion with empty quotes - assert_complete_results( - format!( - r#"select "{}" from "private"."users""#, - QueryWithCursorPosition::cursor_marker() - ) - .as_str(), - vec![ - CompletionAssertion::Label("email".to_string()), - CompletionAssertion::Label("id".to_string()), - CompletionAssertion::Label("name".to_string()), - CompletionAssertion::Label("quoted_column".to_string()), - ], - None, - &pool, - ) - .await; - - // test completion with partially opened quote - assert_complete_results( - format!( - r#"select "{} from "private"."users""#, - QueryWithCursorPosition::cursor_marker() + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new().type_sql(r#"select "email" from "private"."users";"#), ) - .as_str(), - vec![ - CompletionAssertion::Label("email".to_string()), - CompletionAssertion::Label("id".to_string()), - CompletionAssertion::Label("name".to_string()), - CompletionAssertion::Label("quoted_column".to_string()), - ], - None, - &pool, - ) - .await; + .snapshot("completes_quoted_columns") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] @@ -1003,240 +585,40 @@ mod tests { ); "#; - pool.execute(setup).await.unwrap(); - - { - // should suggest pr"."email and insert into existing quotes - let query = format!( - r#"select "e{}" from private.users "pr""#, - QueryWithCursorPosition::cursor_marker() - ); - - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - r#"pr"."email"#.into(), - // replaces the full `"e"` - TextRange::new(8.into(), 9.into()), - )], - None, - &pool, - ) - .await; - } - - { - // should suggest pr"."email and insert into existing quotes - let query = format!( - r#"select "{}" from private.users "pr""#, - QueryWithCursorPosition::cursor_marker() - ); - - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - r#"pr"."email"#.into(), - TextRange::new(8.into(), 8.into()), - )], - None, - &pool, - ) - .await; - } - - { - // should suggest email and insert into quotes - let query = format!( - r#"select pr."{}" from private.users "pr""#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - r#"email"#.into(), - TextRange::new(11.into(), 11.into()), - )], - None, - &pool, - ) + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new() + .type_sql(r#"select "pr"."email" from private.users "pr""#), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(r#" from private.users "pr""#) + .type_sql(r#"select "email""#), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(r#" from private.users "pr""#) + .type_sql(r#"select pr."email""#), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(r#" from private.users "pr""#) + .type_sql(r#"select "pr"."email""#), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement(r#" from private.users "pr""#) + .type_sql(r#"select pr.<1>email"#) + .comment("not quoted here, since the alias isn't."), + ) + .with_case( + TestCompletionsCase::new() + .inside_static_statement( + r#" from private.users "pr" join public.names n on pr.id = n.uid;"#, + ) + .type_sql(r#"select "pr"."email", n.uid"#), + ) + .snapshot("completes_quoted_columns_with_aliases") .await; - } - - { - // should suggest email - let query = format!( - r#"select "pr".{} from private.users "pr""#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - "email".into(), - TextRange::new(12.into(), 12.into()), - )], - None, - &pool, - ) - .await; - } - - { - // should suggest `email` - let query = format!( - r#"select pr.{} from private.users "pr""#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - "email".into(), - TextRange::new(10.into(), 10.into()), - )], - None, - &pool, - ) - .await; - } - - { - let query = format!( - r#"select {} from private.users "pr" join public.names n on pr.id = n.uid;"#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![ - CompletionAssertion::CompletionTextAndRange( - "n.name".into(), - TextRange::new(7.into(), 7.into()), - ), - CompletionAssertion::CompletionTextAndRange( - "n.uid".into(), - TextRange::new(7.into(), 7.into()), - ), - CompletionAssertion::CompletionTextAndRange( - r#""pr".email"#.into(), - TextRange::new(7.into(), 7.into()), - ), - CompletionAssertion::CompletionTextAndRange( - r#""pr".id"#.into(), - TextRange::new(7.into(), 7.into()), - ), - ], - None, - &pool, - ) - .await; - } - - { - // should suggest "pr"."email" - let query = format!( - r#"select "{}" from private.users "pr" join public.names "n" on pr.id = n.uid;"#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![ - CompletionAssertion::CompletionTextAndRange( - r#"n"."name"#.into(), - TextRange::new(8.into(), 8.into()), - ), - CompletionAssertion::CompletionTextAndRange( - r#"n"."uid"#.into(), - TextRange::new(8.into(), 8.into()), - ), - CompletionAssertion::CompletionTextAndRange( - r#"pr"."email"#.into(), - TextRange::new(8.into(), 8.into()), - ), - CompletionAssertion::CompletionTextAndRange( - r#"pr"."id"#.into(), - TextRange::new(8.into(), 8.into()), - ), - ], - None, - &pool, - ) - .await; - } - - { - // should suggest pr"."email" - let query = format!( - r#"select "{} from private.users "pr";"#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![ - CompletionAssertion::CompletionTextAndRange( - r#"pr"."email""#.into(), - TextRange::new(8.into(), 8.into()), - ), - CompletionAssertion::CompletionTextAndRange( - r#"pr"."id""#.into(), - TextRange::new(8.into(), 8.into()), - ), - ], - None, - &pool, - ) - .await; - } - - { - // should suggest email" - let query = format!( - r#"select pr."{} from private.users "pr";"#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - r#"email""#.into(), - TextRange::new(11.into(), 11.into()), - )], - None, - &pool, - ) - .await; - } - - { - // should suggest email" - let query = format!( - r#"select "pr"."{} from private.users "pr";"#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - r#"email""#.into(), - TextRange::new(13.into(), 13.into()), - )], - None, - &pool, - ) - .await; - } - - { - // should suggest "n".name - let query = format!( - r#"select {} from names "n";"#, - QueryWithCursorPosition::cursor_marker() - ); - assert_complete_results( - query.as_str(), - vec![CompletionAssertion::CompletionTextAndRange( - r#""n".name"#.into(), - TextRange::new(7.into(), 7.into()), - )], - None, - &pool, - ) - .await; - } } } diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new deleted file mode 100644 index 4cd0170d9..000000000 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__snapshot.snap.new +++ /dev/null @@ -1,90 +0,0 @@ ---- -source: crates/pgls_completions/src/test_helper.rs -assertion_line: 415 -expression: snapshot_result ---- -s| -from auth.users u join auth.posts p on u.id = p.user_id; - -No Results - --------------- - -select | -from auth.users u join auth.posts p on u.id = p.user_id; - -Results: -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) -u.name - auth.users.name (Column) -p.pid - auth.posts.pid (Column) - --------------- - -select u.| -from auth.users u join auth.posts p on u.id = p.user_id; - -Results: -email - auth.users.email (Column) -name - auth.users.name (Column) -uid - auth.users.uid (Column) - --------------- - -select u.i| -from auth.users u join auth.posts p on u.id = p.user_id; - -Results: -email - auth.users.email (Column) -uid - auth.users.uid (Column) -name - auth.users.name (Column) - --------------- - -select u.id, | -from auth.users u join auth.posts p on u.id = p.user_id; - -Results: -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) -u.name - auth.users.name (Column) -p.pid - auth.posts.pid (Column) - --------------- - -select u.id, p.| -from auth.users u join auth.posts p on u.id = p.user_id; - -**We should only get columns from the auth.posts table.** - -Results: -content - auth.posts.content (Column) -created_at - auth.posts.created_at (Column) -pid - auth.posts.pid (Column) -title - auth.posts.title (Column) -user_id - auth.posts.user_id (Column) - --------------- - -select u.id, p.p| -from auth.users u join auth.posts p on u.id = p.user_id; - -**We should only get columns from the auth.posts table.** - -Results: -pid - auth.posts.pid (Column) -content - auth.posts.content (Column) -created_at - auth.posts.created_at (Column) -title - auth.posts.title (Column) -user_id - auth.posts.user_id (Column) - --------------- - -select u.id, p.pid | -from auth.users u join auth.posts p on u.id = p.user_id; - -No Results - --------------- diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index 293b6d141..237076605 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -1,3 +1,4 @@ +use insta::assert_snapshot; use pgls_schema_cache::SchemaCache; use pgls_test_utils::QueryWithCursorPosition; use pgls_text_size::TextRange; @@ -77,8 +78,10 @@ pub(crate) fn get_test_params<'a>( pub(crate) enum CompletionAssertion { Label(String), LabelAndKind(String, CompletionItemKind), + #[allow(unused)] LabelAndDesc(String, String), LabelNotExists(String), + #[allow(unused)] KindNotExists(CompletionItemKind), CompletionTextAndRange(String, TextRange), } @@ -206,6 +209,7 @@ pub(crate) async fn assert_no_complete_results(query: &str, setup: Option<&str>, enum ChunkToType { WithCompletions(String), + #[allow(unused)] WithoutCompletions(String), } @@ -215,56 +219,37 @@ fn comment_regex() -> &'static Regex { COMMENT_RE.get_or_init(|| Regex::new(r"<\d+>").unwrap()) } -pub(crate) struct TestCompletionsBuilder<'a> { - prefixes: Vec, +pub(crate) struct TestCompletionsCase { tokens_to_type: Vec, - appendices: Vec, + surrounding_statement: String, comments: std::collections::HashMap, - pool: &'a PgPool, - setup: Option<&'a str>, comment_position: usize, } -impl<'a> TestCompletionsBuilder<'a> { - pub(crate) fn new(pool: &'a PgPool, setup: Option<&'a str>) -> Self { +impl TestCompletionsCase { + pub(crate) fn new() -> Self { Self { - prefixes: Vec::new(), tokens_to_type: Vec::new(), - appendices: Vec::new(), + surrounding_statement: String::new(), comments: HashMap::new(), - pool, - setup, comment_position: 0, } } - pub(crate) fn prefix_static(mut self, it: &str) -> Self { - self.prefixes.push(it.trim().to_string()); - self - } - - pub(crate) fn append_static(mut self, it: &str) -> Self { - self.appendices.push(it.trim().to_string()); + pub(crate) fn inside_static_statement(mut self, it: &str) -> Self { + assert!(it.contains("")); + self.surrounding_statement = it.trim().to_string(); self } pub(crate) fn type_sql(mut self, it: &str) -> Self { - assert_eq!( - self.appendices.len(), - 0, - "Make sure to call appendices LAST." - ); self.tokens_to_type .push(ChunkToType::WithCompletions(it.trim().to_string())); self } + #[allow(unused)] pub(crate) fn type_without_completions(mut self, it: &str) -> Self { - assert_eq!( - self.appendices.len(), - 0, - "Make sure to call appendices LAST." - ); self.tokens_to_type .push(ChunkToType::WithoutCompletions(it.trim().to_string())); self @@ -277,30 +262,14 @@ impl<'a> TestCompletionsBuilder<'a> { self } - pub(crate) async fn snapshot(self) { - if let Some(setup) = self.setup { - self.pool.execute(setup).await.expect("Invalid Setup!"); - } - - let schema_cache = SchemaCache::load(self.pool) - .await - .expect("Failed to load Schema Cache"); - - let mut parser = tree_sitter::Parser::new(); - parser - .set_language(&pgls_treesitter_grammar::LANGUAGE.into()) - .expect("Error loading sql language"); - - let mut joined_prefix = self.prefixes.join("\n"); - if joined_prefix.len() > 0 { - joined_prefix.push_str("\n"); - }; - - let mut joined_appendix = String::new(); - if self.appendices.len() > 0 { - joined_appendix.push_str("\n"); - } - joined_appendix.push_str(self.appendices.join("\n").as_str()); + async fn generate_snapshot( + &self, + schema_cache: &SchemaCache, + parser: &mut tree_sitter::Parser, + ) -> String { + let mut stmt_parts = self.surrounding_statement.split(""); + let mut pre_sql = stmt_parts.next().unwrap().to_string(); + let post_sql = stmt_parts.next().unwrap_or("").to_string(); let mut snapshot_result = String::new(); @@ -310,6 +279,8 @@ impl<'a> TestCompletionsBuilder<'a> { let whitespace_count = sql.chars().filter(|c| c.is_ascii_whitespace()).count(); let whitespace_split = sql.split_ascii_whitespace().enumerate(); + let mut should_close_with_paren = false; + for (whitespace_idx, token) in whitespace_split { let dot_count = token.chars().filter(|c| *c == '.').count(); let dotted_split = token.split(".").enumerate(); @@ -324,41 +295,145 @@ impl<'a> TestCompletionsBuilder<'a> { self.comments.get(&num).map(|s| s.as_str()) }); - let part = comment_regex().replace_all(og_part, ""); + let part_without_comments = comment_regex().replace_all(og_part, ""); + + let starts_with_paren = part_without_comments.starts_with('('); + let ends_with_paren = part_without_comments.ends_with(')'); - if joined_prefix.len() > 0 { + let part_without_parens = if starts_with_paren || ends_with_paren { + // we only want to sanitize when the token either starts or ends; that helps + // catch end tokens like `('something');` + part_without_comments.replace('(', "").replace(')', "") + } else { + part_without_comments.to_string() + }; + + let is_inside_quotes = part_without_parens.starts_with('"') + && part_without_parens.ends_with('"'); + + let part_without_quotes = part_without_parens.replace('"', ""); + + if pre_sql.len() > 0 { let query = format!( - "{}{}{}{}", - joined_prefix, + "{}{}{}{}{}", + pre_sql, if dot_idx <= dot_count { "" } else { "." }, QueryWithCursorPosition::cursor_marker(), - joined_appendix, + if should_close_with_paren { ")" } else { "" }, + post_sql, ); self.completions_snapshot( query.into(), &mut snapshot_result, &schema_cache, - &mut parser, + parser, + comment, + ) + .await; + } + + // try ` (|` and ` (|)` + if starts_with_paren { + let query1 = format!( + "{}{}({}{}", + pre_sql, + if dot_idx <= dot_count { "" } else { "." }, + QueryWithCursorPosition::cursor_marker(), + post_sql, + ); + + self.completions_snapshot( + query1.into(), + &mut snapshot_result, + &schema_cache, + parser, + comment, + ) + .await; + + let query2 = format!( + "{}{}({}){}", + pre_sql, + if dot_idx <= dot_count { "" } else { "." }, + QueryWithCursorPosition::cursor_marker(), + post_sql, + ); + + self.completions_snapshot( + query2.into(), + &mut snapshot_result, + &schema_cache, + parser, + comment, + ) + .await; + + pre_sql.push_str("("); + should_close_with_paren = true; + } + + // try ` "|` and ` "|"` + if is_inside_quotes { + let query1 = format!( + "{}{}\"{}{}{}", + pre_sql, + if dot_idx <= dot_count { "" } else { "." }, + QueryWithCursorPosition::cursor_marker(), + if should_close_with_paren { ")" } else { "" }, + post_sql, + ); + + self.completions_snapshot( + query1.into(), + &mut snapshot_result, + &schema_cache, + parser, + comment, + ) + .await; + + let query2 = format!( + "{}{}\"{}\"{}{}", + pre_sql, + if dot_idx <= dot_count { "" } else { "." }, + QueryWithCursorPosition::cursor_marker(), + if should_close_with_paren { ")" } else { "" }, + post_sql, + ); + + self.completions_snapshot( + query2.into(), + &mut snapshot_result, + &schema_cache, + parser, comment, ) .await; } - if part.len() > 1 { + if part_without_quotes.len() > 1 { + let first_token = &part_without_quotes[..1]; + let query = format!( - "{}{}{}{}", - joined_prefix, - &part[..1], + "{}{}{}{}{}{}", + pre_sql, + if is_inside_quotes { + format!(r#""{first_token}"#) + } else { + first_token.to_string() + }, QueryWithCursorPosition::cursor_marker(), - joined_appendix, + if is_inside_quotes { r#"""# } else { "" }, + if should_close_with_paren { ")" } else { "" }, + post_sql, ); self.completions_snapshot( query.into(), &mut snapshot_result, &schema_cache, - &mut parser, + parser, if comment_indicator .is_some_and(|txt| og_part.starts_with(txt.as_str())) { @@ -373,46 +448,55 @@ impl<'a> TestCompletionsBuilder<'a> { if whitespace_idx == whitespace_count && dot_idx == dot_count { let query = format!( "{}{} {}{}", - joined_prefix, - part, + pre_sql, + if is_inside_quotes { + format!(r#""{}""#, part_without_quotes.as_str()) + } else { + part_without_quotes.clone() + }, QueryWithCursorPosition::cursor_marker(), - joined_appendix, + post_sql, ); self.completions_snapshot( query.into(), &mut snapshot_result, &schema_cache, - &mut parser, + parser, None, ) .await; } - joined_prefix.push_str(&part); + pre_sql.push_str(&part_without_parens); if dot_idx < dot_count { - joined_prefix.push_str("."); + pre_sql.push_str("."); + } + + if ends_with_paren { + should_close_with_paren = false; + pre_sql.push_str(")"); } } if whitespace_idx < whitespace_count { // note: we're sanitizing the white_space of typed SQL to simple spaces. - joined_prefix.push_str(" "); + pre_sql.push_str(" "); } } - joined_prefix.push_str("\n"); + pre_sql.push_str("\n"); } ChunkToType::WithoutCompletions(sql) => { - joined_prefix.push_str(sql.as_str()); - joined_prefix.push_str("\n"); + pre_sql.push_str(sql.as_str()); + pre_sql.push_str("\n"); } } } - insta::assert_snapshot!(snapshot_result); + snapshot_result } async fn completions_snapshot( @@ -424,11 +508,6 @@ impl<'a> TestCompletionsBuilder<'a> { comment: Option<&str>, ) { let (pos, mut sql) = query.get_text_and_position(); - if sql.len() == 0 { - return; - } - - println!("'{sql}', {pos}"); let tree = parser.parse(&sql, None).expect("Invalid TS Tree!"); @@ -450,16 +529,13 @@ impl<'a> TestCompletionsBuilder<'a> { sql.push_str("|"); } writeln!(writer, "{sql}").unwrap(); - writeln!(writer).unwrap(); if let Some(c) = comment { writeln!(writer, "**{}**", c).unwrap(); - writeln!(writer).unwrap(); } - if items.len() == 0 { - writeln!(writer, "No Results").unwrap(); - } else { + if !items.is_empty() { + writeln!(writer).unwrap(); writeln!(writer, "Results:").unwrap(); let max_idx = std::cmp::min(items.len(), 5); @@ -469,6 +545,7 @@ impl<'a> TestCompletionsBuilder<'a> { "{}", item.completion_text .as_ref() + .filter(|c| !c.is_snippet) .map(|c| c.text.as_str()) .unwrap_or(item.label.as_str()) ) @@ -487,11 +564,74 @@ impl<'a> TestCompletionsBuilder<'a> { writeln!(writer).unwrap(); } + + writeln!(writer).unwrap(); + + writeln!(writer, "--------------").unwrap(); + writeln!(writer).unwrap(); } + } +} - writeln!(writer).unwrap(); +pub(crate) struct TestCompletionsSuite<'a> { + pool: &'a PgPool, + setup: Option<&'a str>, + cases: Vec, +} + +impl<'a> TestCompletionsSuite<'a> { + pub(crate) fn new(pool: &'a PgPool, setup: Option<&'a str>) -> Self { + Self { + pool, + setup, + cases: vec![], + } + } + + pub(crate) fn with_case(mut self, case: TestCompletionsCase) -> Self { + self.cases.push(case); + self + } + + pub(crate) async fn snapshot(self, snapshot_name: &str) { + assert!(!self.cases.is_empty(), "Needs at least one Snapshot case."); + + if let Some(setup) = self.setup { + self.pool.execute(setup).await.expect("Problem with Setup"); + } + + let cache = SchemaCache::load(self.pool) + .await + .expect("Problem loading SchemaCache"); + + let mut parser = tree_sitter::Parser::new(); + parser + .set_language(&pgls_treesitter_grammar::LANGUAGE.into()) + .expect("Problem with TreeSitter Grammar"); + + let mut final_snapshot = String::new(); + + let has_more_than_one_case = self.cases.len() > 1; + + for (idx, additional) in self.cases.iter().enumerate() { + if idx > 0 { + writeln!(final_snapshot).unwrap(); + writeln!(final_snapshot).unwrap(); + writeln!(final_snapshot).unwrap(); + + writeln!(final_snapshot).unwrap(); + } + + if has_more_than_one_case { + writeln!(final_snapshot, "***Case {}:***", idx + 1).unwrap(); + writeln!(final_snapshot).unwrap(); + } + + let snap = additional.generate_snapshot(&cache, &mut parser).await; + + write!(final_snapshot, "{snap}").unwrap(); + } - writeln!(writer, "--------------").unwrap(); - writeln!(writer).unwrap(); + assert_snapshot!(snapshot_name, final_snapshot) } } From 799f52d29fde4109c92c002e6a9393c5103db8f8 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 8 Nov 2025 18:29:56 +0100 Subject: [PATCH 03/38] changie --- .../pgls_completions/src/providers/columns.rs | 1 - ...t_helper__completes_in_join_on_clause.snap | 83 ++ ...test_helper__completes_quoted_columns.snap | 110 +++ ...completes_quoted_columns_with_aliases.snap | 462 +++++++++++ ...oes_not_complete_cols_in_join_clauses.snap | 202 +++++ ...er__filters_out_by_aliases_in_join_on.snap | 91 +++ ...per__filters_out_by_aliases_in_select.snap | 70 ++ ...__test_helper__handles_nested_queries.snap | 163 ++++ ...t_helper__ignores_cols_in_from_clause.snap | 43 + ...__prefers_columns_of_mentioned_tables.snap | 96 +++ ...helper__prefers_not_mentioned_columns.snap | 314 ++++++++ ...iple_columns_if_no_relation_specified.snap | 30 + ...columns_in_alter_table_and_drop_table.snap | 748 ++++++++++++++++++ ...er__suggests_columns_in_insert_clause.snap | 265 +++++++ ...per__suggests_columns_in_where_clause.snap | 131 +++ ..._suggests_columns_policy_using_clause.snap | 136 ++++ ...ests_relevant_columns_without_letters.snap | 52 ++ 17 files changed, 2996 insertions(+), 1 deletion(-) create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap create mode 100644 crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 6b2b03692..2e70b14ba 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -52,7 +52,6 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText mod tests { use indoc::indoc; - use pgls_text_size::TextRange; use sqlx::{Executor, PgPool}; use crate::{ diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap new file mode 100644 index 000000000..cfcd0eefe --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap @@ -0,0 +1,83 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +select u.id, auth.posts.content from auth.users u join auth.posts p on | +**Should prioritize primary keys here.** + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.| +**Should only consider columns from auth.posts here.** + +Results: +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.u| +**Should only consider columns from auth.posts here.** + +Results: +user_id - auth.posts.user_id (Column) +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id = | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id = u.| +**Should only consider columns from auth.users here.** + +Results: +uid - auth.users.uid (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id = u.u| +**Should only consider columns from auth.users here.** + +Results: +uid - auth.users.uid (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) + +-------------- + +select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id = u.uid; | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap new file mode 100644 index 000000000..c63e3fd40 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap @@ -0,0 +1,110 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +s| +select | + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) + +-------------- + +select "| + +Results: +email" - private.users.email (Column) +id" - private.users.id (Column) +name" - private.users.name (Column) +quoted_column" - private.users.quoted_column (Column) +abbrev" - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +select "|" + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +select "e|" + +Results: +email - private.users.email (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +id - private.users.id (Column) +elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) + +-------------- + +select "email" | +select "email" f| +select "email" from | + +Results: +public - public (Schema) +private - private (Schema) +private.users - private.users (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) + +-------------- + +select "email" from "| + +Results: +public - public (Schema) +private - private (Schema) +private"."users" - private.users (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) + +-------------- + +select "email" from "|" + +Results: +public - public (Schema) +private - private (Schema) +private"."users - private.users (Table) +information_schema"."_pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema"."_pg_foreign_servers - information_schema._pg_foreign_servers (Table) + +-------------- + +select "email" from "p|" + +Results: +public - public (Schema) +private - private (Schema) +private"."users - private.users (Table) +information_schema"."parameters - information_schema.parameters (Table) +pg_catalog"."pg_aggregate - pg_catalog.pg_aggregate (Table) + +-------------- + +select "email" from "private".| + +Results: +users - private.users (Table) + +-------------- + +select "email" from "private".u| + +Results: +users - private.users (Table) + +-------------- + +select "email" from "private".users; | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap new file mode 100644 index 000000000..110c63141 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -0,0 +1,462 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +***Case 1:*** + +s| +select | + +Results: +name - public.names.name (Column) +uid - public.names.uid (Column) +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) + +-------------- + +select "| + +Results: +name" - public.names.name (Column) +uid" - public.names.uid (Column) +email" - private.users.email (Column) +id" - private.users.id (Column) +name" - private.users.name (Column) + +-------------- + +select "|" + +Results: +name - public.names.name (Column) +uid - public.names.uid (Column) +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) + +-------------- + +select "p|" + +Results: +name - public.names.name (Column) +uid - public.names.uid (Column) +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) + +-------------- + +select "pr".| +select "pr"."| +select "pr"."|" +select "pr"."e|" +select "pr"."email" | +select "pr"."email" f| +select "pr"."email" from | + +Results: +public - public (Schema) +private - private (Schema) +names - public.names (Table) +private.users - private.users (Table) +information_schema - information_schema (Schema) + +-------------- + +select "pr"."email" from p| + +Results: +public - public (Schema) +private - private (Schema) +names - public.names (Table) +private.users - private.users (Table) +information_schema.parameters - information_schema.parameters (Table) + +-------------- + +select "pr"."email" from private.| + +Results: +users - private.users (Table) + +-------------- + +select "pr"."email" from private.u| + +Results: +users - private.users (Table) + +-------------- + +select "pr"."email" from private.users | +select "pr"."email" from private.users "| +select "pr"."email" from private.users "|" +select "pr"."email" from private.users "p|" +select "pr"."email" from private.users "pr" | + + + + +***Case 2:*** + +s| from private.users "pr" +select | from private.users "pr" + +Results: +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select "| from private.users "pr" + +Results: +pr"."email" - private.users.email (Column) +pr"."id" - private.users.id (Column) +pr"."name" - private.users.name (Column) +pr"."quoted_column" - private.users.quoted_column (Column) +name" - public.names.name (Column) + +-------------- + +select "|" from private.users "pr" + +Results: +pr"."email - private.users.email (Column) +pr"."id - private.users.id (Column) +pr"."name - private.users.name (Column) +pr"."quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select "e|" from private.users "pr" + +Results: +pr"."email - private.users.email (Column) +pr"."name - private.users.name (Column) +pr"."quoted_column - private.users.quoted_column (Column) +pr"."id - private.users.id (Column) +name - public.names.name (Column) + +-------------- + +select "email" | from private.users "pr" + + + + +***Case 3:*** + +s| from private.users "pr" +select | from private.users "pr" + +Results: +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select p| from private.users "pr" + +Results: +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select pr.| from private.users "pr" + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) + +-------------- + +select pr."| from private.users "pr" + +Results: +email" - private.users.email (Column) +id" - private.users.id (Column) +name" - private.users.name (Column) +quoted_column" - private.users.quoted_column (Column) + +-------------- + +select pr."|" from private.users "pr" + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) + +-------------- + +select pr."e|" from private.users "pr" + +Results: +email - private.users.email (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +id - private.users.id (Column) + +-------------- + +select pr."email" | from private.users "pr" + + + + +***Case 4:*** + +s| from private.users "pr" +select | from private.users "pr" + +Results: +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select "| from private.users "pr" + +Results: +pr"."email" - private.users.email (Column) +pr"."id" - private.users.id (Column) +pr"."name" - private.users.name (Column) +pr"."quoted_column" - private.users.quoted_column (Column) +name" - public.names.name (Column) + +-------------- + +select "|" from private.users "pr" + +Results: +pr"."email - private.users.email (Column) +pr"."id - private.users.id (Column) +pr"."name - private.users.name (Column) +pr"."quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select "p|" from private.users "pr" + +Results: +pr"."email - private.users.email (Column) +pr"."id - private.users.id (Column) +pr"."name - private.users.name (Column) +pr"."quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select "pr".| from private.users "pr" + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) + +-------------- + +select "pr"."| from private.users "pr" + +Results: +email" - private.users.email (Column) +id" - private.users.id (Column) +name" - private.users.name (Column) +quoted_column" - private.users.quoted_column (Column) + +-------------- + +select "pr"."|" from private.users "pr" + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) + +-------------- + +select "pr"."e|" from private.users "pr" + +Results: +email - private.users.email (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +id - private.users.id (Column) + +-------------- + +select "pr"."email" | from private.users "pr" + + + + +***Case 5:*** + +s| from private.users "pr" +select | from private.users "pr" + +Results: +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select p| from private.users "pr" + +Results: +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) +name - public.names.name (Column) + +-------------- + +select pr.| from private.users "pr" +**not quoted here, since the alias isn't.** + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) + +-------------- + +select pr.e| from private.users "pr" + +Results: +email - private.users.email (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +id - private.users.id (Column) + +-------------- + +select pr.email | from private.users "pr" + + + + +***Case 6:*** + +s| from private.users "pr" join public.names n on pr.id = n.uid; +select | from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +n.name - public.names.name (Column) +n.uid - public.names.uid (Column) +"pr".email - private.users.email (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) + +-------------- + +select "| from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +n"."name" - public.names.name (Column) +n"."uid" - public.names.uid (Column) +pr"."email" - private.users.email (Column) +pr"."id" - private.users.id (Column) +pr"."name" - private.users.name (Column) + +-------------- + +select "|" from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +n"."name - public.names.name (Column) +n"."uid - public.names.uid (Column) +pr"."email - private.users.email (Column) +pr"."id - private.users.id (Column) +pr"."name - private.users.name (Column) + +-------------- + +select "p|" from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +n"."name - public.names.name (Column) +n"."uid - public.names.uid (Column) +pr"."email - private.users.email (Column) +pr"."id - private.users.id (Column) +pr"."name - private.users.name (Column) + +-------------- + +select "pr".| from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +email - private.users.email (Column) +id - private.users.id (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) + +-------------- + +select "pr".e| from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +email - private.users.email (Column) +name - private.users.name (Column) +quoted_column - private.users.quoted_column (Column) +id - private.users.id (Column) + +-------------- + +select "pr"."email", | from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +n.name - public.names.name (Column) +n.uid - public.names.uid (Column) +"pr".id - private.users.id (Column) +"pr".name - private.users.name (Column) +"pr".quoted_column - private.users.quoted_column (Column) + +-------------- + +select "pr"."email", n.| from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +name - public.names.name (Column) +uid - public.names.uid (Column) + +-------------- + +select "pr"."email", n.u| from private.users "pr" join public.names n on pr.id = n.uid; + +Results: +uid - public.names.uid (Column) +name - public.names.name (Column) + +-------------- + +select "pr"."email", n.uid | from private.users "pr" join public.names n on pr.id = n.uid; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap new file mode 100644 index 000000000..47dad0792 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap @@ -0,0 +1,202 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +s| +select | + +Results: +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) +pid - auth.posts.pid (Column) + +-------------- + +select u.| +select u.u| +select u.uid, | + +Results: +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) +pid - auth.posts.pid (Column) + +-------------- + +select u.uid, p.| +select u.uid, p.c| +select u.uid, p.content | +select u.uid, p.content f| +select u.uid, p.content from | +**Schema suggestions should be prioritized, since we want to push users to specify them.** + +Results: +public - public (Schema) +auth - auth (Schema) +auth.posts - auth.posts (Table) +auth.users - auth.users (Table) +information_schema - information_schema (Schema) + +-------------- + +select u.uid, p.content from a| +**Schema suggestions should be prioritized, since we want to push users to specify them.** + +Results: +auth - auth (Schema) +public - public (Schema) +auth.posts - auth.posts (Table) +auth.users - auth.users (Table) +information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) + +-------------- + +select u.uid, p.content from auth.| +**Here, we shouldn't have schema completions.** + +Results: +posts - auth.posts (Table) +users - auth.users (Table) + +-------------- + +select u.uid, p.content from auth.u| +**Here, we shouldn't have schema completions.** + +Results: +users - auth.users (Table) +posts - auth.posts (Table) + +-------------- + +select u.uid, p.content from auth.users | +select u.uid, p.content from auth.users u | +select u.uid, p.content from auth.users u j| +select u.uid, p.content from auth.users u join | + +Results: +public - public (Schema) +auth - auth (Schema) +auth.posts - auth.posts (Table) +auth.users - auth.users (Table) +information_schema - information_schema (Schema) + +-------------- + +select u.uid, p.content from auth.users u join a| + +Results: +auth - auth (Schema) +public - public (Schema) +auth.posts - auth.posts (Table) +auth.users - auth.users (Table) +information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) + +-------------- + +select u.uid, p.content from auth.users u join auth.| + +Results: +posts - auth.posts (Table) +users - auth.users (Table) + +-------------- + +select u.uid, p.content from auth.users u join auth.p| + +Results: +posts - auth.posts (Table) +users - auth.users (Table) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts | +select u.uid, p.content from auth.users u join auth.posts p | +select u.uid, p.content from auth.users u join auth.posts p o| +select u.uid, p.content from auth.users u join auth.posts p on | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.| + +Results: +uid - auth.users.uid (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.u| + +Results: +uid - auth.users.uid (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.uid | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.uid = | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.uid = p.| + +Results: +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.uid = p.u| + +Results: +user_id - auth.posts.user_id (Column) +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) + +-------------- + +select u.uid, p.content from auth.users u join auth.posts p on u.uid = p.user_id | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap new file mode 100644 index 000000000..3e7740ed4 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap @@ -0,0 +1,91 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +select u.id, p.content from auth.users u join auth.posts p | +select u.id, p.content from auth.users u join auth.posts p o| +select u.id, p.content from auth.users u join auth.posts p on | +**Should prefer primary indices here.** + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.| + +Results: +uid - auth.users.uid (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.i| + +Results: +uid - auth.users.uid (Column) +email - auth.users.email (Column) +name - auth.users.name (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.id | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.id = | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.id = p.| +**We should only get columns from the auth.posts table.** + +Results: +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.id = p.u| + +Results: +user_id - auth.posts.user_id (Column) +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) + +-------------- + +select u.id, p.content from auth.users u join auth.posts p on u.id = p.user_id | + +Results: +p.pid - auth.posts.pid (Column) +u.uid - auth.users.uid (Column) +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) + +-------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap new file mode 100644 index 000000000..fb3f34286 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap @@ -0,0 +1,70 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +s| from auth.users u join auth.posts p on u.id = p.user_id; +select | from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) +u.name - auth.users.name (Column) +p.pid - auth.posts.pid (Column) + +-------------- + +select u.| from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +email - auth.users.email (Column) +name - auth.users.name (Column) +uid - auth.users.uid (Column) + +-------------- + +select u.i| from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +email - auth.users.email (Column) +uid - auth.users.uid (Column) +name - auth.users.name (Column) + +-------------- + +select u.id, | from auth.users u join auth.posts p on u.id = p.user_id; + +Results: +p.content - auth.posts.content (Column) +p.created_at - auth.posts.created_at (Column) +u.email - auth.users.email (Column) +u.name - auth.users.name (Column) +p.pid - auth.posts.pid (Column) + +-------------- + +select u.id, p.| from auth.users u join auth.posts p on u.id = p.user_id; +**We should only get columns from the auth.posts table.** + +Results: +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +pid - auth.posts.pid (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.id, p.p| from auth.users u join auth.posts p on u.id = p.user_id; +**We should only get columns from the auth.posts table.** + +Results: +pid - auth.posts.pid (Column) +content - auth.posts.content (Column) +created_at - auth.posts.created_at (Column) +title - auth.posts.title (Column) +user_id - auth.posts.user_id (Column) + +-------------- + +select u.id, p.pid | from auth.users u join auth.posts p on u.id = p.user_id; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap new file mode 100644 index 000000000..34f80a211 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap @@ -0,0 +1,163 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +select * from ( + | +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +audio_books - public.audio_books (Table) +users - public.users (Table) +private.audio_books - private.audio_books (Table) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) + +-------------- + +select * from ( + s| +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +audio_books - public.audio_books (Table) +users - public.users (Table) +private.audio_books - private.audio_books (Table) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) + +-------------- + +select * from ( + select | +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) +pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) +pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) +pg_catalog.RI_FKey_check_upd() - Schema: pg_catalog.RI_FKey_check_upd (Function) +pg_catalog.RI_FKey_noaction_del() - Schema: pg_catalog.RI_FKey_noaction_del (Function) + +-------------- + +select * from ( + select i| +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +iclikejoinsel - Schema: pg_catalog.iclikejoinsel (Function) +iclikesel - Schema: pg_catalog.iclikesel (Function) +icnlikejoinsel - Schema: pg_catalog.icnlikejoinsel (Function) +icnlikesel - Schema: pg_catalog.icnlikesel (Function) +icregexeqjoinsel - Schema: pg_catalog.icregexeqjoinsel (Function) + +-------------- + +select * from ( + select id, | +) as subquery +join public.users u +on u.id = subquery.id; +**Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.** + +Results: +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) +pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) +pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) +pg_catalog.RI_FKey_check_upd() - Schema: pg_catalog.RI_FKey_check_upd (Function) +pg_catalog.RI_FKey_noaction_del() - Schema: pg_catalog.RI_FKey_noaction_del (Function) + +-------------- + +select * from ( + select id, n| +) as subquery +join public.users u +on u.id = subquery.id; +**Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.** + +Results: +name - Schema: pg_catalog.name (Function) +nameconcatoid - Schema: pg_catalog.nameconcatoid (Function) +nameeq - Schema: pg_catalog.nameeq (Function) +nameeqtext - Schema: pg_catalog.nameeqtext (Function) +namege - Schema: pg_catalog.namege (Function) + +-------------- + +select * from ( + select id, narrator_id | +) as subquery +join public.users u +on u.id = subquery.id; +select * from ( + select id, narrator_id f| +) as subquery +join public.users u +on u.id = subquery.id; +select * from ( + select id, narrator_id from | +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +public - public (Schema) +private - private (Schema) +audio_books - public.audio_books (Table) +users - public.users (Table) +private.audio_books - private.audio_books (Table) + +-------------- + +select * from ( + select id, narrator_id from p| +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +public - public (Schema) +private - private (Schema) +audio_books - public.audio_books (Table) +users - public.users (Table) +private.audio_books - private.audio_books (Table) + +-------------- + +select * from ( + select id, narrator_id from private.| +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +audio_books - private.audio_books (Table) + +-------------- + +select * from ( + select id, narrator_id from private.a| +) as subquery +join public.users u +on u.id = subquery.id; + +Results: +audio_books - private.audio_books (Table) + +-------------- + +select * from ( + select id, narrator_id from private.audio_books | +) as subquery +join public.users u +on u.id = subquery.id; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap new file mode 100644 index 000000000..132e2a417 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap @@ -0,0 +1,43 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +select * from | +**No column suggestions.** + +Results: +public - public (Schema) +private - private (Schema) +private.users - private.users (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) + +-------------- + +select * from p| +**No column suggestions.** + +Results: +public - public (Schema) +private - private (Schema) +private.users - private.users (Table) +information_schema.parameters - information_schema.parameters (Table) +pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) + +-------------- + +select * from private.| + +Results: +users - private.users (Table) + +-------------- + +select * from private.u| + +Results: +users - private.users (Table) + +-------------- + +select * from private.users | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap new file mode 100644 index 000000000..9aee13885 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap @@ -0,0 +1,96 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +***Case 1:*** + +s| from public.users +select | from public.users +**Should suggest address 2 from public table** + +Results: +address2 - public.users.address2 (Column) +email2 - public.users.email2 (Column) +id2 - public.users.id2 (Column) +name2 - public.users.name2 (Column) +settings - public.users.settings (Column) + +-------------- + +select a| from public.users +**Should suggest address 2 from public table** + +Results: +address2 - public.users.address2 (Column) +email2 - public.users.email2 (Column) +name2 - public.users.name2 (Column) +id2 - public.users.id2 (Column) +settings - public.users.settings (Column) + +-------------- + +select address2 | from public.users + + + + +***Case 2:*** + +s| from private.users +select | from private.users +**Should suggest address 1 from private table** + +Results: +address1 - private.users.address1 (Column) +email1 - private.users.email1 (Column) +id1 - private.users.id1 (Column) +name1 - private.users.name1 (Column) +user_settings - private.users.user_settings (Column) + +-------------- + +select a| from private.users +**Should suggest address 1 from private table** + +Results: +address1 - private.users.address1 (Column) +email1 - private.users.email1 (Column) +name1 - private.users.name1 (Column) +id1 - private.users.id1 (Column) +user_settings - private.users.user_settings (Column) + +-------------- + +select address1 | from private.users + + + + +***Case 3:*** + +s| from private.users +select | from private.users +**Should prioritize columns starting with s** + +Results: +address1 - private.users.address1 (Column) +email1 - private.users.email1 (Column) +id1 - private.users.id1 (Column) +name1 - private.users.name1 (Column) +user_settings - private.users.user_settings (Column) + +-------------- + +select s| from private.users +**Should prioritize columns starting with s** + +Results: +user_settings - private.users.user_settings (Column) +address1 - private.users.address1 (Column) +email1 - private.users.email1 (Column) +id1 - private.users.id1 (Column) +name1 - private.users.name1 (Column) + +-------------- + +select settings | from private.users diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap new file mode 100644 index 000000000..82b8ffa88 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap @@ -0,0 +1,314 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +***Case 1:*** + +s| from public.one o join public.two on o.id = t.id; +select | from public.one o join public.two on o.id = t.id; + +Results: +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) + +-------------- + +select o.| from public.one o join public.two on o.id = t.id; + +Results: +a - public.one.a (Column) +b - public.one.b (Column) +id - public.one.id (Column) +z - public.one.z (Column) + +-------------- + +select o.i| from public.one o join public.two on o.id = t.id; + +Results: +id - public.one.id (Column) +a - public.one.a (Column) +b - public.one.b (Column) +z - public.one.z (Column) + +-------------- + +select o.id, | from public.one o join public.two on o.id = t.id; + +Results: +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) + +-------------- + +select o.id, a| from public.one o join public.two on o.id = t.id; + +Results: +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) + +-------------- + +select o.id, a, | from public.one o join public.two on o.id = t.id; +**Should have low priority for `a`, since it's already mentioned.** + +Results: +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +id - public.two.id (Column) + +-------------- + +select o.id, a, b| from public.one o join public.two on o.id = t.id; + +Results: +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +id - public.two.id (Column) + +-------------- + +select o.id, a, b, | from public.one o join public.two on o.id = t.id; + +Results: +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +id - public.two.id (Column) +o.z - public.one.z (Column) + +-------------- + +select o.id, a, b, c| from public.one o join public.two on o.id = t.id; + +Results: +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +id - public.two.id (Column) +o.z - public.one.z (Column) + +-------------- + +select o.id, a, b, c, | from public.one o join public.two on o.id = t.id; + +Results: +d - public.two.d (Column) +e - public.two.e (Column) +id - public.two.id (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) + +-------------- + +select o.id, a, b, c, d| from public.one o join public.two on o.id = t.id; + +Results: +id - public.two.id (Column) +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) +o.id - public.one.id (Column) + +-------------- + +select o.id, a, b, c, d, | from public.one o join public.two on o.id = t.id; + +Results: +e - public.two.e (Column) +id - public.two.id (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) + +-------------- + +select o.id, a, b, c, d, e| from public.one o join public.two on o.id = t.id; + +Results: +e - public.two.e (Column) +id - public.two.id (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) + +-------------- + +select o.id, a, b, c, d, e, | from public.one o join public.two on o.id = t.id; +**Should have high priority of id of table two, but not one, since it's already mentioned.** + +Results: +id - public.two.id (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) + +-------------- + +select o.id, a, b, c, d, e, z | from public.one o join public.two on o.id = t.id; + + + + +***Case 2:*** + +s| from public.one o join public.two on o.id = t.id; +select | from public.one o join public.two on o.id = t.id; + +Results: +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) + +-------------- + +select i| from public.one o join public.two on o.id = t.id; + +Results: +o.id - public.one.id (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) + +-------------- + +select id, | from public.one o join public.two on o.id = t.id; + +Results: +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) + +-------------- + +select id, a| from public.one o join public.two on o.id = t.id; + +Results: +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) + +-------------- + +select id, a, | from public.one o join public.two on o.id = t.id; + +Results: +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) + +-------------- + +select id, a, b| from public.one o join public.two on o.id = t.id; + +Results: +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) + +-------------- + +select id, a, b, | from public.one o join public.two on o.id = t.id; + +Results: +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) + +-------------- + +select id, a, b, c| from public.one o join public.two on o.id = t.id; + +Results: +c - public.two.c (Column) +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) + +-------------- + +select id, a, b, c, | from public.one o join public.two on o.id = t.id; + +Results: +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) + +-------------- + +select id, a, b, c, d| from public.one o join public.two on o.id = t.id; + +Results: +d - public.two.d (Column) +e - public.two.e (Column) +o.z - public.one.z (Column) +o.id - public.one.id (Column) +o.a - public.one.a (Column) + +-------------- + +select id, a, b, c, d, | from public.one o join public.two on o.id = t.id; + +Results: +e - public.two.e (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) + +-------------- + +select id, a, b, c, d, e| from public.one o join public.two on o.id = t.id; + +Results: +e - public.two.e (Column) +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) + +-------------- + +select id, a, b, c, d, e, | from public.one o join public.two on o.id = t.id; +**`id` could be from both tables, so both priorities are lowered.** + +Results: +o.z - public.one.z (Column) +o.a - public.one.a (Column) +o.b - public.one.b (Column) +c - public.two.c (Column) +d - public.two.d (Column) + +-------------- + +select id, a, b, c, d, e, z | from public.one o join public.two on o.id = t.id; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap new file mode 100644 index 000000000..5ece5410f --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap @@ -0,0 +1,30 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +s| +select | +**Should suggest all columns with n first** + +Results: +id - public.audio_books.id (Column) +name - public.users.name (Column) +narrator - public.audio_books.narrator (Column) +id - private.audio_books.id (Column) +narrator_id - private.audio_books.narrator_id (Column) + +-------------- + +select n| +**Should suggest all columns with n first** + +Results: +name - public.users.name (Column) +narrator - public.audio_books.narrator (Column) +narrator_id - private.audio_books.narrator_id (Column) +id - public.audio_books.id (Column) +name - Schema: pg_catalog.name (Function) + +-------------- + +select narrator_id | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap new file mode 100644 index 000000000..3898dfc62 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap @@ -0,0 +1,748 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +***Case 1:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments d| +alter table instruments drop | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop c| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +alter table instruments drop column | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop column n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments drop column name | + + + + +***Case 2:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments d| +alter table instruments drop | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop c| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +alter table instruments drop column | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop column n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments drop column name | + + + + +***Case 3:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments d| +alter table instruments drop | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop c| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +alter table instruments drop column | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop column i| + +Results: +id - public.instruments.id (Column) +created_at - public.instruments.created_at (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) + +-------------- + +alter table instruments drop column if | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop column if e| + +Results: +created_at - public.instruments.created_at (Column) +name - public.instruments.name (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) + +-------------- + +alter table instruments drop column if exists | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments drop column if exists n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments drop column if exists name | + + + + +***Case 4:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments a| +alter table instruments alter | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments alter c| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +alter table instruments alter column | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments alter column n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments alter column name | +alter table instruments alter column name s| +alter table instruments alter column name set | +alter table instruments alter column name set d| +alter table instruments alter column name set default | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + + + + + +***Case 5:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments a| +alter table instruments alter | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments alter n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments alter name | +alter table instruments alter name s| +alter table instruments alter name set | +alter table instruments alter name set d| +alter table instruments alter name set default | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + + + + + +***Case 6:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table p| + +Results: +public - public (Schema) +instruments - public.instruments (Table) +information_schema.parameters - information_schema.parameters (Table) +pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) +pg_catalog.pg_am - pg_catalog.pg_am (Table) + +-------------- + +alter table public.| + +Results: +instruments - public.instruments (Table) +_sqlx_migrations - public._sqlx_migrations (Table) + +-------------- + +alter table public.i| + +Results: +instruments - public.instruments (Table) +_sqlx_migrations - public._sqlx_migrations (Table) + +-------------- + +alter table public.instruments | +alter table public.instruments a| +alter table public.instruments alter | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table public.instruments alter c| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +alter table public.instruments alter column | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table public.instruments alter column n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table public.instruments alter column name | + + + + +***Case 7:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments a| +alter table instruments alter | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments alter n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments alter name | + + + + +***Case 8:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) + +-------------- + +alter table instruments | +alter table instruments r| +alter table instruments rename | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table instruments rename n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table instruments rename name | +alter table instruments rename name t| +alter table instruments rename name to | +alter table instruments rename name to n| +alter table instruments rename name to new_col | + + + + +***Case 9:*** + +a| +alter | +alter t| +alter table | + +Results: +instruments - public.instruments (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) + +-------------- + +alter table p| + +Results: +public - public (Schema) +instruments - public.instruments (Table) +information_schema.parameters - information_schema.parameters (Table) +pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) +pg_catalog.pg_am - pg_catalog.pg_am (Table) + +-------------- + +alter table public.| + +Results: +instruments - public.instruments (Table) +_sqlx_migrations - public._sqlx_migrations (Table) + +-------------- + +alter table public.i| + +Results: +instruments - public.instruments (Table) +_sqlx_migrations - public._sqlx_migrations (Table) + +-------------- + +alter table public.instruments | +alter table public.instruments r| +alter table public.instruments rename | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table public.instruments rename c| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +alter table public.instruments rename column | + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +alter table public.instruments rename column n| + +Results: +name - public.instruments.name (Column) +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) + +-------------- + +alter table public.instruments rename column name | +alter table public.instruments rename column name t| +alter table public.instruments rename column name to | +alter table public.instruments rename column name to n| +alter table public.instruments rename column name to new_col | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap new file mode 100644 index 000000000..82fdec2f1 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap @@ -0,0 +1,265 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +***Case 1:*** + +i| +insert | +insert i| +insert into | + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) + +-------------- + +insert into i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +others - public.others (Table) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) + +-------------- + +insert into instruments | +insert into instruments (| +insert into instruments (|) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (i|) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +id - public.others.id (Column) +a - public.others.a (Column) + +-------------- + +insert into instruments (id, |) + +Results: +name - public.instruments.name (Column) +z - public.instruments.z (Column) +id - public.instruments.id (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (id, n|) + +Results: +name - public.instruments.name (Column) +z - public.instruments.z (Column) +id - public.instruments.id (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (id, name) | +insert into instruments (id, name) v| +insert into instruments (id, name) values | +insert into instruments (id, name) values (| +insert into instruments (id, name) values (|) + +Results: +z - public.instruments.z (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (id, name) values (1|) +insert into instruments (id, name) values (1, |) + +Results: +z - public.instruments.z (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (id, name) values (1, '|) +insert into instruments (id, name) values (1, 'my_bass'); | + + + + +***Case 2:*** + +i| +insert | +insert i| +insert into | + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +public - public (Schema) +information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) +information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) + +-------------- + +insert into i| + +Results: +instruments - public.instruments (Table) +public - public (Schema) +others - public.others (Table) +information_schema - information_schema (Schema) +information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) + +-------------- + +insert into instruments | +insert into instruments (| +insert into instruments (|) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (i|) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +id - public.others.id (Column) +a - public.others.a (Column) + +-------------- + +insert into instruments ("id", |) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments ("id", "|) + +Results: +id" - public.instruments.id (Column) +name" - public.instruments.name (Column) +z" - public.instruments.z (Column) +a" - public.others.a (Column) +b" - public.others.b (Column) + +-------------- + +insert into instruments ("id", "|") + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments ("id", "n|") + +Results: +name - public.instruments.name (Column) +id - public.instruments.id (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments ("id", "name") | +insert into instruments ("id", "name") v| +insert into instruments ("id", "name") values | +insert into instruments ("id", "name") values (| +insert into instruments ("id", "name") values (|) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments ("id", "name") values (1|) +insert into instruments ("id", "name") values (1, |) + +Results: +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments ("id", "name") values (1, '|) +insert into instruments ("id", "name") values (1, 'my_bass'); | + + + + +***Case 3:*** + +insert into instruments (|, name) values ('my_bass'); + +Results: +id - public.instruments.id (Column) +z - public.instruments.z (Column) +name - public.instruments.name (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (i|, name) values ('my_bass'); +insert into instruments (id, |, name) values ('my_bass'); +**`name` is already written, so z should be suggested.** + +Results: +z - public.instruments.z (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +a - public.others.a (Column) +b - public.others.b (Column) + +-------------- + +insert into instruments (id, z |, name) values ('my_bass'); diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap new file mode 100644 index 000000000..601edcdeb --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap @@ -0,0 +1,131 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +select name from instruments i join others o on i.z = o.a | + +Results: +i.id - public.instruments.id (Column) +o.a - public.others.a (Column) +o.b - public.others.b (Column) +o.c - public.others.c (Column) +i.created_at - public.instruments.created_at (Column) + +-------------- + +select name from instruments i join others o on i.z = o.a w| + +Results: +i.id - public.instruments.id (Column) +o.a - public.others.a (Column) +o.b - public.others.b (Column) +o.c - public.others.c (Column) +i.created_at - public.instruments.created_at (Column) + +-------------- + +select name from instruments i join others o on i.z = o.a where | + +Results: +o.a - public.others.a (Column) +o.b - public.others.b (Column) +o.c - public.others.c (Column) +i.created_at - public.instruments.created_at (Column) +i.id - public.instruments.id (Column) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.| +**should respect alias speciifcation** + +Results: +a - public.others.a (Column) +b - public.others.b (Column) +c - public.others.c (Column) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a | + +Results: +o.a - public.others.a (Column) +o.b - public.others.b (Column) +o.c - public.others.c (Column) +i.created_at - public.instruments.created_at (Column) +i.id - public.instruments.id (Column) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = | +**should not prioritize suggest columns or schemas (right side of binary expression)** + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) +pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) +pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = i.| +select name from instruments i join others o on i.z = o.a where o.a = i.z | + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) +pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) +pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = i.z a| + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +abbrev - Schema: pg_catalog.abbrev (Function) +abs - Schema: pg_catalog.abs (Function) +aclcontains - Schema: pg_catalog.aclcontains (Function) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = i.z and | +**should prioritize columns that aren't already mentioned** + +Results: +o.b - public.others.b (Column) +o.c - public.others.c (Column) +i.created_at - public.instruments.created_at (Column) +i.id - public.instruments.id (Column) +i.name - public.instruments.name (Column) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = i.z and i.| +select name from instruments i join others o on i.z = o.a where o.a = i.z and i.i| +select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id | + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) +pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) +pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id > | + +Results: +instruments - public.instruments (Table) +others - public.others (Table) +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) +pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) +pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) + +-------------- + +select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id > 5| +select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id > 5; | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap new file mode 100644 index 000000000..8d1ef12a3 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap @@ -0,0 +1,136 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +***Case 1:*** + +create policy "my_pol" on public.instruments for select using (|) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +create policy "my_pol" on public.instruments for select using (i|) + +Results: +id - public.instruments.id (Column) +created_at - public.instruments.created_at (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) + +-------------- + +create policy "my_pol" on public.instruments for select using (id |) +create policy "my_pol" on public.instruments for select using (id = |) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +create policy "my_pol" on public.instruments for select using (id = 1 |) +create policy "my_pol" on public.instruments for select using (id = 1 a|) +create policy "my_pol" on public.instruments for select using (id = 1 and |) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +create policy "my_pol" on public.instruments for select using (id = 1 and c|) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +create policy "my_pol" on public.instruments for select using (id = 1 and created_at |) +create policy "my_pol" on public.instruments for select using (id = 1 and created_at > |) +create policy "my_pol" on public.instruments for select using (id = 1 and created_at > '|) +create policy "my_pol" on public.instruments for select using (id = 1 and created_at > '2025-01-01' |) + + + + +***Case 2:*** + +create policy "my_pol" on public.instruments for insert with check (|) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +create policy "my_pol" on public.instruments for insert with check (i|) + +Results: +id - public.instruments.id (Column) +created_at - public.instruments.created_at (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) + +-------------- + +create policy "my_pol" on public.instruments for insert with check (id |) +create policy "my_pol" on public.instruments for insert with check (id = |) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +create policy "my_pol" on public.instruments for insert with check (id = 1 |) +create policy "my_pol" on public.instruments for insert with check (id = 1 a|) +create policy "my_pol" on public.instruments for insert with check (id = 1 and |) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) + +-------------- + +create policy "my_pol" on public.instruments for insert with check (id = 1 and c|) + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) + +-------------- + +create policy "my_pol" on public.instruments for insert with check (id = 1 and created_at |) +create policy "my_pol" on public.instruments for insert with check (id = 1 and created_at > |) +create policy "my_pol" on public.instruments for insert with check (id = 1 and created_at > '|) +create policy "my_pol" on public.instruments for insert with check (id = 1 and created_at > '2025-01-01' |) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap new file mode 100644 index 000000000..ca7421a06 --- /dev/null +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap @@ -0,0 +1,52 @@ +--- +source: crates/pgls_completions/src/test_helper.rs +expression: final_snapshot +--- +s| +select | + +Results: +address - public.users.address (Column) +email - public.users.email (Column) +id - public.users.id (Column) +name - public.users.name (Column) +pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) + +-------------- + +select n| + +Results: +name - public.users.name (Column) +address - public.users.address (Column) +email - public.users.email (Column) +id - public.users.id (Column) +name - Schema: pg_catalog.name (Function) + +-------------- + +select name | +select name f| +select name from | + +Results: +public - public (Schema) +users - public.users (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) + +-------------- + +select name from u| + +Results: +users - public.users (Table) +public - public (Schema) +information_schema.udt_privileges - information_schema.udt_privileges (Table) +information_schema.usage_privileges - information_schema.usage_privileges (Table) +information_schema.user_defined_types - information_schema.user_defined_types (Table) + +-------------- + +select name from users | From f8ca4077065008c5d740542750d08943500a8ba6 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 8 Nov 2025 18:35:13 +0100 Subject: [PATCH 04/38] readied --- crates/pgls_completions/Cargo.toml | 6 ++-- .../pgls_completions/src/providers/columns.rs | 21 ++---------- crates/pgls_completions/src/test_helper.rs | 34 +++++++++---------- 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/crates/pgls_completions/Cargo.toml b/crates/pgls_completions/Cargo.toml index 32cc4cf20..73a5a7450 100644 --- a/crates/pgls_completions/Cargo.toml +++ b/crates/pgls_completions/Cargo.toml @@ -32,10 +32,10 @@ tokio = { version = "1.41.1", features = ["full"] } [dev-dependencies] criterion.workspace = true +indoc = "2.0.7" +insta.workspace = true pgls_test_utils.workspace = true -insta.workspace = true -regex = "1.12.2" -indoc = "2.0.7" +regex = "1.12.2" [lib] diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 2e70b14ba..a951cf04e 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -52,29 +52,12 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText mod tests { use indoc::indoc; - use sqlx::{Executor, PgPool}; + use sqlx::PgPool; - use crate::{ - CompletionItemKind, complete, - test_helper::{ - CompletionAssertion, TestCompletionsCase, TestCompletionsSuite, - assert_complete_results, get_test_deps, get_test_params, - }, - }; + use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; use pgls_test_utils::QueryWithCursorPosition; - struct TestCase { - query: String, - } - - impl TestCase { - fn get_input_query(&self) -> QueryWithCursorPosition { - let strs: Vec<&str> = self.query.split_whitespace().collect(); - strs.join(" ").as_str().into() - } - } - #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { let setup = r#" diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index 237076605..a4d9fe201 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -303,7 +303,7 @@ impl TestCompletionsCase { let part_without_parens = if starts_with_paren || ends_with_paren { // we only want to sanitize when the token either starts or ends; that helps // catch end tokens like `('something');` - part_without_comments.replace('(', "").replace(')', "") + part_without_comments.replace(['(', ')'], "") } else { part_without_comments.to_string() }; @@ -313,7 +313,7 @@ impl TestCompletionsCase { let part_without_quotes = part_without_parens.replace('"', ""); - if pre_sql.len() > 0 { + if !pre_sql.is_empty() { let query = format!( "{}{}{}{}{}", pre_sql, @@ -326,7 +326,7 @@ impl TestCompletionsCase { self.completions_snapshot( query.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, comment, ) @@ -346,7 +346,7 @@ impl TestCompletionsCase { self.completions_snapshot( query1.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, comment, ) @@ -363,13 +363,13 @@ impl TestCompletionsCase { self.completions_snapshot( query2.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, comment, ) .await; - pre_sql.push_str("("); + pre_sql.push('('); should_close_with_paren = true; } @@ -387,7 +387,7 @@ impl TestCompletionsCase { self.completions_snapshot( query1.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, comment, ) @@ -405,7 +405,7 @@ impl TestCompletionsCase { self.completions_snapshot( query2.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, comment, ) @@ -432,7 +432,7 @@ impl TestCompletionsCase { self.completions_snapshot( query.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, if comment_indicator .is_some_and(|txt| og_part.starts_with(txt.as_str())) @@ -461,7 +461,7 @@ impl TestCompletionsCase { self.completions_snapshot( query.into(), &mut snapshot_result, - &schema_cache, + schema_cache, parser, None, ) @@ -471,27 +471,27 @@ impl TestCompletionsCase { pre_sql.push_str(&part_without_parens); if dot_idx < dot_count { - pre_sql.push_str("."); + pre_sql.push('.'); } if ends_with_paren { should_close_with_paren = false; - pre_sql.push_str(")"); + pre_sql.push(')'); } } if whitespace_idx < whitespace_count { // note: we're sanitizing the white_space of typed SQL to simple spaces. - pre_sql.push_str(" "); + pre_sql.push(' '); } } - pre_sql.push_str("\n"); + pre_sql.push('\n'); } ChunkToType::WithoutCompletions(sql) => { pre_sql.push_str(sql.as_str()); - pre_sql.push_str("\n"); + pre_sql.push('\n'); } } } @@ -526,12 +526,12 @@ impl TestCompletionsCase { let diff = pos - sql.len(); sql.push_str(&" ".repeat(diff)); - sql.push_str("|"); + sql.push('|'); } writeln!(writer, "{sql}").unwrap(); if let Some(c) = comment { - writeln!(writer, "**{}**", c).unwrap(); + writeln!(writer, "**{c}**").unwrap(); } if !items.is_empty() { From 6365f80940314713acaedd86724d105cb75acb97 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 8 Nov 2025 18:36:11 +0100 Subject: [PATCH 05/38] more removed --- crates/pgls_completions/src/providers/columns.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index a951cf04e..6ef99ebad 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -56,8 +56,6 @@ mod tests { use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; - use pgls_test_utils::QueryWithCursorPosition; - #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { let setup = r#" From 4324377d43f575e1057e514d329baf042685d5b6 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 8 Nov 2025 18:38:58 +0100 Subject: [PATCH 06/38] minor changes --- crates/pgls_diagnostics/src/serde.rs | 1 - crates/pgls_hover/src/lib.rs | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/pgls_diagnostics/src/serde.rs b/crates/pgls_diagnostics/src/serde.rs index ab57d90fa..babb5d93d 100644 --- a/crates/pgls_diagnostics/src/serde.rs +++ b/crates/pgls_diagnostics/src/serde.rs @@ -164,7 +164,6 @@ impl From> for Location { #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(Eq, PartialEq))] - struct Advices { advices: Vec, } diff --git a/crates/pgls_hover/src/lib.rs b/crates/pgls_hover/src/lib.rs index 6276f7ba7..dbfdac68d 100644 --- a/crates/pgls_hover/src/lib.rs +++ b/crates/pgls_hover/src/lib.rs @@ -83,14 +83,15 @@ pub fn on_hover(params: OnHoverParams) -> Vec { _ => vec![], }, - HoveredNode::Function(node_identification) => match node_identification { - (maybe_schema, function_name) => params + HoveredNode::Function(node_identification) => { + let (maybe_schema, function_name) = node_identification; + params .schema_cache .find_functions(&function_name, maybe_schema.as_deref()) .into_iter() .map(Hoverable::from) - .collect(), - }, + .collect() + } HoveredNode::Role(role_name) => params .schema_cache From 9586d8cccc2b905dc514a5424ca9509c8368931a Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Sat, 8 Nov 2025 18:40:58 +0100 Subject: [PATCH 07/38] Update crates/pgls_completions/src/relevance/filtering.rs --- crates/pgls_completions/src/relevance/filtering.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index c0c3087d6..7aa3531a6 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -324,7 +324,7 @@ impl CompletionFilter<'_> { WrappingClause::DropRole | WrappingClause::AlterRole => true, WrappingClause::SetStatement => ctx - .before_cursor_matches_kind(&["eyword_role", "keyword_authorization"]), + .before_cursor_matches_kind(&["keyword_role", "keyword_authorization"]), WrappingClause::RevokeStatement | WrappingClause::GrantStatement => { ctx.history_ends_with(&["role_specification", "any_identifier"]) From c3201e7398920eefaf9a27b43038e8512770b835 Mon Sep 17 00:00:00 2001 From: Julian Date: Sun, 9 Nov 2025 09:24:01 +0100 Subject: [PATCH 08/38] use indoc, add setup --- Cargo.lock | 17 ++++++-------- crates/pgls_completions/Cargo.toml | 2 +- .../pgls_completions/src/providers/columns.rs | 6 ++--- ...t_helper__completes_in_join_on_clause.snap | 21 +++++++++++++++++ ...test_helper__completes_quoted_columns.snap | 14 +++++++++++ ...completes_quoted_columns_with_aliases.snap | 19 +++++++++++++++ ...oes_not_complete_cols_in_join_clauses.snap | 21 +++++++++++++++++ ...er__filters_out_by_aliases_in_join_on.snap | 21 +++++++++++++++++ ...per__filters_out_by_aliases_in_select.snap | 21 +++++++++++++++++ ...__test_helper__handles_nested_queries.snap | 22 ++++++++++++++++++ ...t_helper__ignores_cols_in_from_clause.snap | 15 ++++++++++++ ...__prefers_columns_of_mentioned_tables.snap | 23 +++++++++++++++++++ ...helper__prefers_not_mentioned_columns.snap | 21 +++++++++++++++++ ...iple_columns_if_no_relation_specified.snap | 22 ++++++++++++++++++ ...columns_in_alter_table_and_drop_table.snap | 12 ++++++++++ ...er__suggests_columns_in_insert_clause.snap | 17 ++++++++++++++ ...per__suggests_columns_in_where_clause.snap | 18 +++++++++++++++ ..._suggests_columns_policy_using_clause.snap | 12 ++++++++++ ...ests_relevant_columns_without_letters.snap | 12 ++++++++++ crates/pgls_completions/src/test_helper.rs | 14 ++++++++--- 20 files changed, 312 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46c1a2869..803c04887 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1999,15 +1999,6 @@ dependencies = [ "serde", ] -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - [[package]] name = "insta" version = "1.42.1" @@ -2726,7 +2717,6 @@ dependencies = [ "async-std", "criterion", "fuzzy-matcher", - "indoc", "insta", "pgls_schema_cache", "pgls_test_utils", @@ -2741,6 +2731,7 @@ dependencies = [ "tokio", "tracing", "tree-sitter", + "unindent", ] [[package]] @@ -5037,6 +5028,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/crates/pgls_completions/Cargo.toml b/crates/pgls_completions/Cargo.toml index 73a5a7450..6061faf60 100644 --- a/crates/pgls_completions/Cargo.toml +++ b/crates/pgls_completions/Cargo.toml @@ -32,7 +32,7 @@ tokio = { version = "1.41.1", features = ["full"] } [dev-dependencies] criterion.workspace = true -indoc = "2.0.7" +unindent = "0.2.4" insta.workspace = true pgls_test_utils.workspace = true regex = "1.12.2" diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 6ef99ebad..eda6fdcd1 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -50,8 +50,6 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText #[cfg(test)] mod tests { - use indoc::indoc; - use sqlx::PgPool; use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; @@ -79,13 +77,13 @@ mod tests { TestCompletionsSuite::new(&pool, Some(setup)).with_case( TestCompletionsCase::new() - .inside_static_statement(indoc! {r#" + .inside_static_statement(r#" select * from ( ) as subquery join public.users u on u.id = subquery.id; - "#}) + "#) .type_sql("select id, narrator_id<1> from private.audio_books") .comment("Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.") ) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap index cfcd0eefe..ed447c384 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap @@ -2,6 +2,27 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema auth; + +create table auth.users ( + uid serial primary key, + name text not null, + email text unique not null +); + +create table auth.posts ( + pid serial primary key, + user_id int not null references auth.users(uid), + title text not null, + content text, + created_at timestamp default now() +); + + +-------------- + select u.id, auth.posts.content from auth.users u join auth.posts p on | **Should prioritize primary keys here.** diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap index c63e3fd40..2cb3f114e 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap @@ -2,6 +2,20 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema if not exists private; + +create table private.users ( + id serial primary key, + email text unique not null, + name text not null, + "quoted_column" text +); + + +-------------- + s| select | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap index 110c63141..53a0d60c6 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -2,6 +2,25 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema if not exists private; + +create table private.users ( + id serial primary key, + email text unique not null, + name text not null, + "quoted_column" text +); + +create table public.names ( + uid serial references private.users(id), + name text +); + + +-------------- + ***Case 1:*** s| diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap index 47dad0792..95f59dec2 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap @@ -2,6 +2,27 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema auth; + +create table auth.users ( + uid serial primary key, + name text not null, + email text unique not null +); + +create table auth.posts ( + pid serial primary key, + user_id int not null references auth.users(uid), + title text not null, + content text, + created_at timestamp default now() +); + + +-------------- + s| select | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap index 3e7740ed4..8adcb3f56 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap @@ -2,6 +2,27 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema auth; + +create table auth.users ( + uid serial primary key, + name text not null, + email text unique not null +); + +create table auth.posts ( + pid serial primary key, + user_id int not null references auth.users(uid), + title text not null, + content text, + created_at timestamp default now() +); + + +-------------- + select u.id, p.content from auth.users u join auth.posts p | select u.id, p.content from auth.users u join auth.posts p o| select u.id, p.content from auth.users u join auth.posts p on | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap index fb3f34286..581ff6cdb 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap @@ -2,6 +2,27 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema auth; + +create table auth.users ( + uid serial primary key, + name text not null, + email text unique not null +); + +create table auth.posts ( + pid serial primary key, + user_id int not null references auth.users(uid), + title text not null, + content text, + created_at timestamp default now() +); + + +-------------- + s| from auth.users u join auth.posts p on u.id = p.user_id; select | from auth.users u join auth.posts p on u.id = p.user_id; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap index 34f80a211..9f0fcc088 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap @@ -2,6 +2,28 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema private; + +create table public.users ( + id serial primary key, + name text +); + +create table public.audio_books ( + id serial primary key, + narrator text +); + +create table private.audio_books ( + id serial primary key, + narrator_id text +); + + +-------------- + select * from ( | ) as subquery diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap index 132e2a417..8c8b135a9 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap @@ -2,6 +2,21 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema private; + +create table private.users ( + id serial primary key, + name text, + address text, + email text, + public boolean +); + + +-------------- + select * from | **No column suggestions.** diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap index 9aee13885..490549e94 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap @@ -2,6 +2,29 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema private; + +create table private.users ( + id1 serial primary key, + name1 text, + address1 text, + email1 text, + user_settings jsonb +); + +create table public.users ( + id2 serial primary key, + name2 text, + address2 text, + email2 text, + settings jsonb +); + + +-------------- + ***Case 1:*** s| from public.users diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap index 82b8ffa88..cfd1f380a 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap @@ -2,6 +2,27 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema auth; + +create table public.one ( + id serial primary key, + a text, + b text, + z text +); + +create table public.two ( + id serial primary key, + c text, + d text, + e text +); + + +-------------- + ***Case 1:*** s| from public.one o join public.two on o.id = t.id; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap index 5ece5410f..71823a4b6 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap @@ -2,6 +2,28 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create schema private; + +create table public.users ( + id serial primary key, + name text +); + +create table public.audio_books ( + id serial primary key, + narrator text +); + +create table private.audio_books ( + id serial primary key, + narrator_id text +); + + +-------------- + s| select | **Should suggest all columns with n first** diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap index 3898dfc62..e8af8cac0 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap @@ -2,6 +2,18 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create table instruments ( + id bigint primary key generated always as identity, + name text not null, + z text, + created_at timestamp with time zone default now() +); + + +-------------- + ***Case 1:*** a| diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap index 82fdec2f1..0d99a891f 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap @@ -2,6 +2,23 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create table instruments ( + id bigint primary key generated always as identity, + name text not null, + z text +); + +create table others ( + id serial primary key, + a text, + b text +); + + +-------------- + ***Case 1:*** i| diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap index 601edcdeb..90022579d 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap @@ -2,6 +2,24 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create table instruments ( + id bigint primary key generated always as identity, + name text not null, + z text, + created_at timestamp with time zone default now() +); + +create table others ( + a text, + b text, + c text +); + + +-------------- + select name from instruments i join others o on i.z = o.a | Results: diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap index 8d1ef12a3..1f04bb0f8 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap @@ -2,6 +2,18 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create table instruments ( + id bigint primary key generated always as identity, + name text not null, + z text, + created_at timestamp with time zone default now() +); + + +-------------- + ***Case 1:*** create policy "my_pol" on public.instruments for select using (|) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap index ca7421a06..12dcca6a4 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap @@ -2,6 +2,18 @@ source: crates/pgls_completions/src/test_helper.rs expression: final_snapshot --- +***Setup*** + +create table users ( + id serial primary key, + name text, + address text, + email text +); + + +-------------- + s| select | diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index a4d9fe201..7613f2815 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -5,6 +5,7 @@ use pgls_text_size::TextRange; use regex::Regex; use sqlx::{Executor, PgPool}; use std::{collections::HashMap, fmt::Write, sync::OnceLock}; +use unindent::unindent; use crate::{CompletionItem, CompletionItemKind, CompletionParams, complete}; @@ -238,7 +239,7 @@ impl TestCompletionsCase { pub(crate) fn inside_static_statement(mut self, it: &str) -> Self { assert!(it.contains("")); - self.surrounding_statement = it.trim().to_string(); + self.surrounding_statement = unindent(it); self } @@ -596,8 +597,17 @@ impl<'a> TestCompletionsSuite<'a> { pub(crate) async fn snapshot(self, snapshot_name: &str) { assert!(!self.cases.is_empty(), "Needs at least one Snapshot case."); + let mut final_snapshot = String::new(); + if let Some(setup) = self.setup { self.pool.execute(setup).await.expect("Problem with Setup"); + writeln!(final_snapshot, "***Setup***").unwrap(); + writeln!(final_snapshot).unwrap(); + write!(final_snapshot, "{}", unindent(setup)).unwrap(); + writeln!(final_snapshot).unwrap(); + writeln!(final_snapshot).unwrap(); + writeln!(final_snapshot, "--------------").unwrap(); + writeln!(final_snapshot).unwrap(); } let cache = SchemaCache::load(self.pool) @@ -609,8 +619,6 @@ impl<'a> TestCompletionsSuite<'a> { .set_language(&pgls_treesitter_grammar::LANGUAGE.into()) .expect("Problem with TreeSitter Grammar"); - let mut final_snapshot = String::new(); - let has_more_than_one_case = self.cases.len() > 1; for (idx, additional) in self.cases.iter().enumerate() { From 09664c1bb76ba2d6e9c273fa8ec9bcd28a0043fe Mon Sep 17 00:00:00 2001 From: Julian Date: Sun, 9 Nov 2025 09:44:24 +0100 Subject: [PATCH 09/38] ok --- crates/pgls_completions/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pgls_completions/Cargo.toml b/crates/pgls_completions/Cargo.toml index 6061faf60..43c0c3f4d 100644 --- a/crates/pgls_completions/Cargo.toml +++ b/crates/pgls_completions/Cargo.toml @@ -32,10 +32,10 @@ tokio = { version = "1.41.1", features = ["full"] } [dev-dependencies] criterion.workspace = true -unindent = "0.2.4" insta.workspace = true pgls_test_utils.workspace = true regex = "1.12.2" +unindent = "0.2.4" [lib] From 853929ccaed23ecc9ccf8b79b821ad38046eedb0 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 13:08:22 +0100 Subject: [PATCH 10/38] many fixings --- crates/pgls_completions/src/builder.rs | 12 +++++ .../pgls_completions/src/providers/columns.rs | 2 +- .../pgls_completions/src/relevance/scoring.rs | 49 +++++++++++++------ ...t_helper__completes_in_join_on_clause.snap | 6 --- crates/pgls_treesitter/src/context/mod.rs | 2 + todo.txt | 22 +++++++++ 6 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 todo.txt diff --git a/crates/pgls_completions/src/builder.rs b/crates/pgls_completions/src/builder.rs index db6be9e1c..1bb0075a7 100644 --- a/crates/pgls_completions/src/builder.rs +++ b/crates/pgls_completions/src/builder.rs @@ -2,6 +2,7 @@ use crate::{ CompletionItemKind, CompletionText, item::CompletionItem, relevance::{filtering::CompletionFilter, scoring::CompletionScore}, + sanitization, }; use pgls_treesitter::TreesitterContext; @@ -24,6 +25,12 @@ pub(crate) struct CompletionBuilder<'a> { impl<'a> CompletionBuilder<'a> { pub fn new(ctx: &'a TreesitterContext) -> Self { + println!( + "is sanitized: {:#?}", + ctx.get_node_under_cursor_content() + .map(|txt| sanitization::is_sanitized_token(txt.as_str())) + ); + CompletionBuilder { items: vec![], ctx } } @@ -42,6 +49,11 @@ impl<'a> CompletionBuilder<'a> { item.score.calc_score(self.ctx); } + items = items + .into_iter() + .filter(|i| !i.score.should_skip()) + .collect(); + items.sort_by(|a, b| { b.score .get_score() diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index eda6fdcd1..2e05c6144 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -52,7 +52,7 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText mod tests { use sqlx::PgPool; - use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; + use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite, assert_complete_results}; #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 68f157926..d1193e9dc 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -9,6 +9,7 @@ use super::CompletionRelevanceData; #[derive(Debug)] pub(crate) struct CompletionScore<'a> { score: i32, + skip: bool, data: CompletionRelevanceData<'a>, } @@ -16,12 +17,17 @@ impl<'a> From> for CompletionScore<'a> { fn from(value: CompletionRelevanceData<'a>) -> Self { Self { score: 0, + skip: false, data: value, } } } impl CompletionScore<'_> { + pub fn should_skip(&self) -> bool { + self.skip + } + pub fn get_score(&self) -> i32 { self.score } @@ -55,20 +61,35 @@ impl CompletionScore<'_> { let fz_matcher = SkimMatcherV2::default(); - if let Some(score) = - fz_matcher.fuzzy_match(name.as_str(), content.to_ascii_lowercase().as_str()) - { - let scorei32: i32 = score - .try_into() - .expect("The length of the input exceeds i32 capacity"); - - // the scoring value isn't linear. - // here are a couple of samples: - // - item: bytea_string_agg_transfn, input: n, score: 15 - // - item: numeric_uplus, input: n, score: 31 - // - item: settings, input: sett, score: 91 - // - item: user_settings, input: sett, score: 82 - self.score += scorei32 / 2; + let check_against = match ctx.identifier_qualifiers { + // If both qualifiers are already written out, we must check the item's name itself. + (Some(_), Some(_)) => content.to_ascii_lowercase(), + + _ => self + .get_table_name() + .and_then(|tbl| ctx.get_used_alias_for_table(tbl)) + .map(|alias| format!("{}.{}", alias, name)) + .unwrap_or(name), + }; + + match fz_matcher.fuzzy_match( + check_against.as_str(), + content.to_ascii_lowercase().as_str(), + ) { + Some(score) => { + let scorei32: i32 = score + .try_into() + .expect("The length of the input exceeds i32 capacity"); + + // the scoring value isn't linear. + // here are a couple of samples: + // - item: bytea_string_agg_transfn, input: n, score: 15 + // - item: numeric_uplus, input: n, score: 31 + // - item: settings, input: sett, score: 91 + // - item: user_settings, input: sett, score: 82 + self.score += scorei32 / 2; + } + None => self.skip = true, } } diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap index ed447c384..304b7306d 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap @@ -52,10 +52,6 @@ select u.id, auth.posts.content from auth.users u join auth.posts p on p.u| Results: user_id - auth.posts.user_id (Column) -pid - auth.posts.pid (Column) -content - auth.posts.content (Column) -created_at - auth.posts.created_at (Column) -title - auth.posts.title (Column) -------------- @@ -96,8 +92,6 @@ select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id Results: uid - auth.users.uid (Column) -email - auth.users.email (Column) -name - auth.users.name (Column) -------------- diff --git a/crates/pgls_treesitter/src/context/mod.rs b/crates/pgls_treesitter/src/context/mod.rs index d61fac7fe..b6df990eb 100644 --- a/crates/pgls_treesitter/src/context/mod.rs +++ b/crates/pgls_treesitter/src/context/mod.rs @@ -153,6 +153,8 @@ impl<'a> TreesitterContext<'a> { ctx.gather_tree_context(); ctx.gather_info_from_ts_queries(); + println!("{} {:#?}", ctx.text, ctx.get_node_under_cursor_content(),); + ctx } diff --git a/todo.txt b/todo.txt new file mode 100644 index 000000000..d97e85ea5 --- /dev/null +++ b/todo.txt @@ -0,0 +1,22 @@ + +#### Snapshot review +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap +- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap + +#### General Todos +- [ ] Should filter out any items where typed letters don't match at all +- [ ] From 0a9be8a91b8c6cdab89f7df1bb7cf2389bb487f8 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 13:18:53 +0100 Subject: [PATCH 11/38] remove all commata? --- bun.lock | 252 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 206 insertions(+), 46 deletions(-) diff --git a/bun.lock b/bun.lock index 718b63324..087c1a040 100644 --- a/bun.lock +++ b/bun.lock @@ -5,11 +5,11 @@ "name": "postgres_lsp", "devDependencies": { "@biomejs/biome": "1.9.4", - "@types/bun": "latest", + "@types/bun": "latest" }, "peerDependencies": { - "typescript": "^5", - }, + "typescript": "^5" + } }, "packages/@postgres-language-server/backend-jsonrpc": { "name": "@postgres-language-server/backend-jsonrpc", @@ -19,13 +19,13 @@ "@postgres-language-server/cli-linux-arm64": "", "@postgres-language-server/cli-linux-x64": "", "@postgres-language-server/cli-win32-arm64": "", - "@postgres-language-server/cli-win32-x64": "", - }, + "@postgres-language-server/cli-win32-x64": "" + } }, "packages/@postgres-language-server/cli": { "name": "@postgres-language-server/cli", "bin": { - "postgres-language-server": "bin/postgres-language-server", + "postgres-language-server": "bin/postgres-language-server" }, "optionalDependencies": { "@postgres-language-server/cli-aarch64-apple-darwin": "", @@ -34,8 +34,8 @@ "@postgres-language-server/cli-x86_64-apple-darwin": "", "@postgres-language-server/cli-x86_64-linux-gnu": "", "@postgres-language-server/cli-x86_64-linux-musl": "", - "@postgres-language-server/cli-x86_64-windows-msvc": "", - }, + "@postgres-language-server/cli-x86_64-windows-msvc": "" + } }, "packages/@postgrestools/backend-jsonrpc": { "name": "@postgrestools/backend-jsonrpc", @@ -45,13 +45,13 @@ "@postgrestools/cli-linux-arm64": "", "@postgrestools/cli-linux-x64": "", "@postgrestools/cli-win32-arm64": "", - "@postgrestools/cli-win32-x64": "", - }, + "@postgrestools/cli-win32-x64": "" + } }, "packages/@postgrestools/postgrestools": { "name": "@postgrestools/postgrestools", "bin": { - "postgrestools": "bin/postgrestools", + "postgrestools": "bin/postgrestools" }, "optionalDependencies": { "@postgrestools/cli-aarch64-apple-darwin": "", @@ -60,73 +60,233 @@ "@postgrestools/cli-x86_64-apple-darwin": "", "@postgrestools/cli-x86_64-linux-gnu": "", "@postgrestools/cli-x86_64-linux-musl": "", - "@postgrestools/cli-x86_64-windows-msvc": "", - }, - }, + "@postgrestools/cli-x86_64-windows-msvc": "" + } + } }, "packages": { - "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], + "@biomejs/biome": [ + "@biomejs/biome@1.9.4", + "", + { + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" + }, + "bin": { "biome": "bin/biome" } + }, + "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==" + ], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], + "@biomejs/cli-darwin-arm64": [ + "@biomejs/cli-darwin-arm64@1.9.4", + "", + { "os": "darwin", "cpu": "arm64" }, + "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==" + ], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], + "@biomejs/cli-darwin-x64": [ + "@biomejs/cli-darwin-x64@1.9.4", + "", + { "os": "darwin", "cpu": "x64" }, + "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==" + ], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], + "@biomejs/cli-linux-arm64": [ + "@biomejs/cli-linux-arm64@1.9.4", + "", + { "os": "linux", "cpu": "arm64" }, + "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==" + ], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], + "@biomejs/cli-linux-arm64-musl": [ + "@biomejs/cli-linux-arm64-musl@1.9.4", + "", + { "os": "linux", "cpu": "arm64" }, + "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==" + ], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], + "@biomejs/cli-linux-x64": [ + "@biomejs/cli-linux-x64@1.9.4", + "", + { "os": "linux", "cpu": "x64" }, + "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==" + ], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], + "@biomejs/cli-linux-x64-musl": [ + "@biomejs/cli-linux-x64-musl@1.9.4", + "", + { "os": "linux", "cpu": "x64" }, + "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==" + ], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], + "@biomejs/cli-win32-arm64": [ + "@biomejs/cli-win32-arm64@1.9.4", + "", + { "os": "win32", "cpu": "arm64" }, + "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==" + ], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + "@biomejs/cli-win32-x64": [ + "@biomejs/cli-win32-x64@1.9.4", + "", + { "os": "win32", "cpu": "x64" }, + "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==" + ], - "@postgres-language-server/backend-jsonrpc": ["@postgres-language-server/backend-jsonrpc@workspace:packages/@postgres-language-server/backend-jsonrpc"], + "@postgres-language-server/backend-jsonrpc": [ + "@postgres-language-server/backend-jsonrpc@workspace:packages/@postgres-language-server/backend-jsonrpc" + ], - "@postgres-language-server/cli": ["@postgres-language-server/cli@workspace:packages/@postgres-language-server/cli"], + "@postgres-language-server/cli": [ + "@postgres-language-server/cli@workspace:packages/@postgres-language-server/cli" + ], - "@postgres-language-server/cli-aarch64-apple-darwin": ["@postgres-language-server/cli-aarch64-apple-darwin@0.17.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0Umsb4yuHyI8D9oZkkpa6H4aZXMAAMLa7qbOrHRP9IMML4mKp5B9wkCiyvgd1ZKRP82UQBZnVjh53AGXky4Jdw=="], + "@postgres-language-server/cli-aarch64-apple-darwin": [ + "@postgres-language-server/cli-aarch64-apple-darwin@0.17.1", + "", + { "os": "darwin", "cpu": "arm64" }, + "sha512-0Umsb4yuHyI8D9oZkkpa6H4aZXMAAMLa7qbOrHRP9IMML4mKp5B9wkCiyvgd1ZKRP82UQBZnVjh53AGXky4Jdw==" + ], - "@postgres-language-server/cli-aarch64-linux-gnu": ["@postgres-language-server/cli-aarch64-linux-gnu@0.17.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-ye8IQLNLyJK4hR5tx8rKh1tEO1Sb+Dw574cbBG++K46LTZAdRq5/IQrNkIfDzFdgU+HMK3QYfm54cgudYeVVlQ=="], + "@postgres-language-server/cli-aarch64-linux-gnu": [ + "@postgres-language-server/cli-aarch64-linux-gnu@0.17.1", + "", + { "os": "linux", "cpu": "arm64" }, + "sha512-ye8IQLNLyJK4hR5tx8rKh1tEO1Sb+Dw574cbBG++K46LTZAdRq5/IQrNkIfDzFdgU+HMK3QYfm54cgudYeVVlQ==" + ], - "@postgres-language-server/cli-aarch64-windows-msvc": ["@postgres-language-server/cli-aarch64-windows-msvc@0.17.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-FkOjgvN7+vq0RvwVD/effFjKgH1mrAVMnV+3eO9bdZ6jCXpPesGOyCvyhf59IzoqmvIMyEeKJzjOUcXxWPmdZg=="], + "@postgres-language-server/cli-aarch64-windows-msvc": [ + "@postgres-language-server/cli-aarch64-windows-msvc@0.17.1", + "", + { "os": "win32", "cpu": "arm64" }, + "sha512-FkOjgvN7+vq0RvwVD/effFjKgH1mrAVMnV+3eO9bdZ6jCXpPesGOyCvyhf59IzoqmvIMyEeKJzjOUcXxWPmdZg==" + ], - "@postgres-language-server/cli-x86_64-apple-darwin": ["@postgres-language-server/cli-x86_64-apple-darwin@0.17.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-B79kZ9tz8fvGihv4W/cyCFzw5Wz95HlqF1hnNTBocJtf8Nvo9h6RDWoUEYliBjRp37gcdV945ZjV2RHHxdKMTw=="], + "@postgres-language-server/cli-x86_64-apple-darwin": [ + "@postgres-language-server/cli-x86_64-apple-darwin@0.17.1", + "", + { "os": "darwin", "cpu": "x64" }, + "sha512-B79kZ9tz8fvGihv4W/cyCFzw5Wz95HlqF1hnNTBocJtf8Nvo9h6RDWoUEYliBjRp37gcdV945ZjV2RHHxdKMTw==" + ], - "@postgres-language-server/cli-x86_64-linux-gnu": ["@postgres-language-server/cli-x86_64-linux-gnu@0.17.1", "", { "os": "linux", "cpu": "x64" }, "sha512-lh5Lg/Cc18+232NRd1OZqdpJzuJMW6lrwkC6RKYiiTzxUfRq7uRV8qI0NNnp5cyKiyDO90cpQQ8fjepTaMzh9A=="], + "@postgres-language-server/cli-x86_64-linux-gnu": [ + "@postgres-language-server/cli-x86_64-linux-gnu@0.17.1", + "", + { "os": "linux", "cpu": "x64" }, + "sha512-lh5Lg/Cc18+232NRd1OZqdpJzuJMW6lrwkC6RKYiiTzxUfRq7uRV8qI0NNnp5cyKiyDO90cpQQ8fjepTaMzh9A==" + ], - "@postgres-language-server/cli-x86_64-linux-musl": ["@postgres-language-server/cli-x86_64-linux-musl@0.17.1", "", { "os": "linux", "cpu": "x64" }, "sha512-YH5Znq0urRUxtdsbyDu6Dcg5x9qhgDqTl7gCVY/LmVrPnHfP2I6gU/TJT21B5sDTSadjwZ73AbXimLmlwG6HPA=="], + "@postgres-language-server/cli-x86_64-linux-musl": [ + "@postgres-language-server/cli-x86_64-linux-musl@0.17.1", + "", + { "os": "linux", "cpu": "x64" }, + "sha512-YH5Znq0urRUxtdsbyDu6Dcg5x9qhgDqTl7gCVY/LmVrPnHfP2I6gU/TJT21B5sDTSadjwZ73AbXimLmlwG6HPA==" + ], - "@postgres-language-server/cli-x86_64-windows-msvc": ["@postgres-language-server/cli-x86_64-windows-msvc@0.17.1", "", { "os": "win32", "cpu": "x64" }, "sha512-o2uIx0Vi3iEBFKKnz024JicgovJZWpM9G4WbQxf3r9IQlZPWvDXknlX7rNPQDnQTFRIUg8bpjZpjEvDFHMmuGg=="], + "@postgres-language-server/cli-x86_64-windows-msvc": [ + "@postgres-language-server/cli-x86_64-windows-msvc@0.17.1", + "", + { "os": "win32", "cpu": "x64" }, + "sha512-o2uIx0Vi3iEBFKKnz024JicgovJZWpM9G4WbQxf3r9IQlZPWvDXknlX7rNPQDnQTFRIUg8bpjZpjEvDFHMmuGg==" + ], - "@postgrestools/backend-jsonrpc": ["@postgrestools/backend-jsonrpc@workspace:packages/@postgrestools/backend-jsonrpc"], + "@postgrestools/backend-jsonrpc": [ + "@postgrestools/backend-jsonrpc@workspace:packages/@postgrestools/backend-jsonrpc" + ], - "@postgrestools/cli-aarch64-apple-darwin": ["@postgrestools/cli-aarch64-apple-darwin@0.13.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LqgpsiupuNR1m8rSqcMdwep1UsSG5z7RZ9MYHODBB0+AsN/e1YzcyutqGeKs+AfDBdKdv7QmSbpf7KruBYL+tg=="], + "@postgrestools/cli-aarch64-apple-darwin": [ + "@postgrestools/cli-aarch64-apple-darwin@0.13.0", + "", + { "os": "darwin", "cpu": "arm64" }, + "sha512-LqgpsiupuNR1m8rSqcMdwep1UsSG5z7RZ9MYHODBB0+AsN/e1YzcyutqGeKs+AfDBdKdv7QmSbpf7KruBYL+tg==" + ], - "@postgrestools/cli-aarch64-linux-gnu": ["@postgrestools/cli-aarch64-linux-gnu@0.13.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-QxZZBvg9qAo9raTA5cSuSXlrqo+eCUtYMWSR2dgDCEqogNlo/62ubWYgPi+SMEtfm9euqG1GOItxyRv6xUriEA=="], + "@postgrestools/cli-aarch64-linux-gnu": [ + "@postgrestools/cli-aarch64-linux-gnu@0.13.0", + "", + { "os": "linux", "cpu": "arm64" }, + "sha512-QxZZBvg9qAo9raTA5cSuSXlrqo+eCUtYMWSR2dgDCEqogNlo/62ubWYgPi+SMEtfm9euqG1GOItxyRv6xUriEA==" + ], - "@postgrestools/cli-aarch64-windows-msvc": ["@postgrestools/cli-aarch64-windows-msvc@0.13.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-C8BRZOPQA16eGL8Kk7D4M+9h2Y7x0GOJEBzjR3ZV54besoH8gcZqSxzaA8WbFczQ3dgEZ5CStny6asl3B5JMHA=="], + "@postgrestools/cli-aarch64-windows-msvc": [ + "@postgrestools/cli-aarch64-windows-msvc@0.13.0", + "", + { "os": "win32", "cpu": "arm64" }, + "sha512-C8BRZOPQA16eGL8Kk7D4M+9h2Y7x0GOJEBzjR3ZV54besoH8gcZqSxzaA8WbFczQ3dgEZ5CStny6asl3B5JMHA==" + ], - "@postgrestools/cli-x86_64-apple-darwin": ["@postgrestools/cli-x86_64-apple-darwin@0.13.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-stbU4+en82DofjzpAQlfgKemJe1lbkGhuOAxfWce4jyT25+cil6cEWBia0hZyrve9sVEJctE1MHy9os8s3EGww=="], + "@postgrestools/cli-x86_64-apple-darwin": [ + "@postgrestools/cli-x86_64-apple-darwin@0.13.0", + "", + { "os": "darwin", "cpu": "x64" }, + "sha512-stbU4+en82DofjzpAQlfgKemJe1lbkGhuOAxfWce4jyT25+cil6cEWBia0hZyrve9sVEJctE1MHy9os8s3EGww==" + ], - "@postgrestools/cli-x86_64-linux-gnu": ["@postgrestools/cli-x86_64-linux-gnu@0.13.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RPFdgcAiD+BWhSFw7suI0jTkJysHlHzrD4in27OGlKt9AvZ6TgKrCfDatUkn2VQfn6Ib8o/+1roFOKwFbfQIOw=="], + "@postgrestools/cli-x86_64-linux-gnu": [ + "@postgrestools/cli-x86_64-linux-gnu@0.13.0", + "", + { "os": "linux", "cpu": "x64" }, + "sha512-RPFdgcAiD+BWhSFw7suI0jTkJysHlHzrD4in27OGlKt9AvZ6TgKrCfDatUkn2VQfn6Ib8o/+1roFOKwFbfQIOw==" + ], - "@postgrestools/cli-x86_64-windows-msvc": ["@postgrestools/cli-x86_64-windows-msvc@0.13.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4S6mbvc9u6ipeMcZHwX3RqGMJaNR9B5utMQN/N3LBnvLx62N9MHU1bk3bnGxfHoALXQTMq+us7M9GvEe1izGHQ=="], + "@postgrestools/cli-x86_64-windows-msvc": [ + "@postgrestools/cli-x86_64-windows-msvc@0.13.0", + "", + { "os": "win32", "cpu": "x64" }, + "sha512-4S6mbvc9u6ipeMcZHwX3RqGMJaNR9B5utMQN/N3LBnvLx62N9MHU1bk3bnGxfHoALXQTMq+us7M9GvEe1izGHQ==" + ], - "@postgrestools/postgrestools": ["@postgrestools/postgrestools@workspace:packages/@postgrestools/postgrestools"], + "@postgrestools/postgrestools": [ + "@postgrestools/postgrestools@workspace:packages/@postgrestools/postgrestools" + ], - "@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="], + "@types/bun": [ + "@types/bun@1.2.5", + "", + { "dependencies": { "bun-types": "1.2.5" } }, + "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg==" + ], - "@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="], + "@types/node": [ + "@types/node@22.13.10", + "", + { "dependencies": { "undici-types": "~6.20.0" } }, + "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==" + ], - "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], + "@types/ws": [ + "@types/ws@8.5.14", + "", + { "dependencies": { "@types/node": "*" } }, + "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==" + ], - "bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="], + "bun-types": [ + "bun-types@1.2.5", + "", + { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, + "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg==" + ], - "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], + "typescript": [ + "typescript@5.8.2", + "", + { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, + "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==" + ], - "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], + "undici-types": [ + "undici-types@6.20.0", + "", + {}, + "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + ] } } From 6fabed2184f64e0a6b3fc26b0db5d40dc23bdb3b Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 13:33:10 +0100 Subject: [PATCH 12/38] use current version of bun --- bun.lock | 253 +++++++++++-------------------------------------------- 1 file changed, 47 insertions(+), 206 deletions(-) diff --git a/bun.lock b/bun.lock index 087c1a040..793040af6 100644 --- a/bun.lock +++ b/bun.lock @@ -1,15 +1,16 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "postgres_lsp", "devDependencies": { "@biomejs/biome": "1.9.4", - "@types/bun": "latest" + "@types/bun": "latest", }, "peerDependencies": { - "typescript": "^5" - } + "typescript": "^5", + }, }, "packages/@postgres-language-server/backend-jsonrpc": { "name": "@postgres-language-server/backend-jsonrpc", @@ -19,13 +20,13 @@ "@postgres-language-server/cli-linux-arm64": "", "@postgres-language-server/cli-linux-x64": "", "@postgres-language-server/cli-win32-arm64": "", - "@postgres-language-server/cli-win32-x64": "" - } + "@postgres-language-server/cli-win32-x64": "", + }, }, "packages/@postgres-language-server/cli": { "name": "@postgres-language-server/cli", "bin": { - "postgres-language-server": "bin/postgres-language-server" + "postgres-language-server": "bin/postgres-language-server", }, "optionalDependencies": { "@postgres-language-server/cli-aarch64-apple-darwin": "", @@ -34,8 +35,8 @@ "@postgres-language-server/cli-x86_64-apple-darwin": "", "@postgres-language-server/cli-x86_64-linux-gnu": "", "@postgres-language-server/cli-x86_64-linux-musl": "", - "@postgres-language-server/cli-x86_64-windows-msvc": "" - } + "@postgres-language-server/cli-x86_64-windows-msvc": "", + }, }, "packages/@postgrestools/backend-jsonrpc": { "name": "@postgrestools/backend-jsonrpc", @@ -45,13 +46,13 @@ "@postgrestools/cli-linux-arm64": "", "@postgrestools/cli-linux-x64": "", "@postgrestools/cli-win32-arm64": "", - "@postgrestools/cli-win32-x64": "" - } + "@postgrestools/cli-win32-x64": "", + }, }, "packages/@postgrestools/postgrestools": { "name": "@postgrestools/postgrestools", "bin": { - "postgrestools": "bin/postgrestools" + "postgrestools": "bin/postgrestools", }, "optionalDependencies": { "@postgrestools/cli-aarch64-apple-darwin": "", @@ -60,233 +61,73 @@ "@postgrestools/cli-x86_64-apple-darwin": "", "@postgrestools/cli-x86_64-linux-gnu": "", "@postgrestools/cli-x86_64-linux-musl": "", - "@postgrestools/cli-x86_64-windows-msvc": "" - } - } + "@postgrestools/cli-x86_64-windows-msvc": "", + }, + }, }, "packages": { - "@biomejs/biome": [ - "@biomejs/biome@1.9.4", - "", - { - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" - }, - "bin": { "biome": "bin/biome" } - }, - "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==" - ], + "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], - "@biomejs/cli-darwin-arm64": [ - "@biomejs/cli-darwin-arm64@1.9.4", - "", - { "os": "darwin", "cpu": "arm64" }, - "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==" - ], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], - "@biomejs/cli-darwin-x64": [ - "@biomejs/cli-darwin-x64@1.9.4", - "", - { "os": "darwin", "cpu": "x64" }, - "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==" - ], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], - "@biomejs/cli-linux-arm64": [ - "@biomejs/cli-linux-arm64@1.9.4", - "", - { "os": "linux", "cpu": "arm64" }, - "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==" - ], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], - "@biomejs/cli-linux-arm64-musl": [ - "@biomejs/cli-linux-arm64-musl@1.9.4", - "", - { "os": "linux", "cpu": "arm64" }, - "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==" - ], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], - "@biomejs/cli-linux-x64": [ - "@biomejs/cli-linux-x64@1.9.4", - "", - { "os": "linux", "cpu": "x64" }, - "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==" - ], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], - "@biomejs/cli-linux-x64-musl": [ - "@biomejs/cli-linux-x64-musl@1.9.4", - "", - { "os": "linux", "cpu": "x64" }, - "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==" - ], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], - "@biomejs/cli-win32-arm64": [ - "@biomejs/cli-win32-arm64@1.9.4", - "", - { "os": "win32", "cpu": "arm64" }, - "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==" - ], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], - "@biomejs/cli-win32-x64": [ - "@biomejs/cli-win32-x64@1.9.4", - "", - { "os": "win32", "cpu": "x64" }, - "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==" - ], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], - "@postgres-language-server/backend-jsonrpc": [ - "@postgres-language-server/backend-jsonrpc@workspace:packages/@postgres-language-server/backend-jsonrpc" - ], + "@postgres-language-server/backend-jsonrpc": ["@postgres-language-server/backend-jsonrpc@workspace:packages/@postgres-language-server/backend-jsonrpc"], - "@postgres-language-server/cli": [ - "@postgres-language-server/cli@workspace:packages/@postgres-language-server/cli" - ], + "@postgres-language-server/cli": ["@postgres-language-server/cli@workspace:packages/@postgres-language-server/cli"], - "@postgres-language-server/cli-aarch64-apple-darwin": [ - "@postgres-language-server/cli-aarch64-apple-darwin@0.17.1", - "", - { "os": "darwin", "cpu": "arm64" }, - "sha512-0Umsb4yuHyI8D9oZkkpa6H4aZXMAAMLa7qbOrHRP9IMML4mKp5B9wkCiyvgd1ZKRP82UQBZnVjh53AGXky4Jdw==" - ], + "@postgres-language-server/cli-aarch64-apple-darwin": ["@postgres-language-server/cli-aarch64-apple-darwin@0.17.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0Umsb4yuHyI8D9oZkkpa6H4aZXMAAMLa7qbOrHRP9IMML4mKp5B9wkCiyvgd1ZKRP82UQBZnVjh53AGXky4Jdw=="], - "@postgres-language-server/cli-aarch64-linux-gnu": [ - "@postgres-language-server/cli-aarch64-linux-gnu@0.17.1", - "", - { "os": "linux", "cpu": "arm64" }, - "sha512-ye8IQLNLyJK4hR5tx8rKh1tEO1Sb+Dw574cbBG++K46LTZAdRq5/IQrNkIfDzFdgU+HMK3QYfm54cgudYeVVlQ==" - ], + "@postgres-language-server/cli-aarch64-linux-gnu": ["@postgres-language-server/cli-aarch64-linux-gnu@0.17.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-ye8IQLNLyJK4hR5tx8rKh1tEO1Sb+Dw574cbBG++K46LTZAdRq5/IQrNkIfDzFdgU+HMK3QYfm54cgudYeVVlQ=="], - "@postgres-language-server/cli-aarch64-windows-msvc": [ - "@postgres-language-server/cli-aarch64-windows-msvc@0.17.1", - "", - { "os": "win32", "cpu": "arm64" }, - "sha512-FkOjgvN7+vq0RvwVD/effFjKgH1mrAVMnV+3eO9bdZ6jCXpPesGOyCvyhf59IzoqmvIMyEeKJzjOUcXxWPmdZg==" - ], + "@postgres-language-server/cli-aarch64-windows-msvc": ["@postgres-language-server/cli-aarch64-windows-msvc@0.17.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-FkOjgvN7+vq0RvwVD/effFjKgH1mrAVMnV+3eO9bdZ6jCXpPesGOyCvyhf59IzoqmvIMyEeKJzjOUcXxWPmdZg=="], - "@postgres-language-server/cli-x86_64-apple-darwin": [ - "@postgres-language-server/cli-x86_64-apple-darwin@0.17.1", - "", - { "os": "darwin", "cpu": "x64" }, - "sha512-B79kZ9tz8fvGihv4W/cyCFzw5Wz95HlqF1hnNTBocJtf8Nvo9h6RDWoUEYliBjRp37gcdV945ZjV2RHHxdKMTw==" - ], + "@postgres-language-server/cli-x86_64-apple-darwin": ["@postgres-language-server/cli-x86_64-apple-darwin@0.17.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-B79kZ9tz8fvGihv4W/cyCFzw5Wz95HlqF1hnNTBocJtf8Nvo9h6RDWoUEYliBjRp37gcdV945ZjV2RHHxdKMTw=="], - "@postgres-language-server/cli-x86_64-linux-gnu": [ - "@postgres-language-server/cli-x86_64-linux-gnu@0.17.1", - "", - { "os": "linux", "cpu": "x64" }, - "sha512-lh5Lg/Cc18+232NRd1OZqdpJzuJMW6lrwkC6RKYiiTzxUfRq7uRV8qI0NNnp5cyKiyDO90cpQQ8fjepTaMzh9A==" - ], + "@postgres-language-server/cli-x86_64-linux-gnu": ["@postgres-language-server/cli-x86_64-linux-gnu@0.17.1", "", { "os": "linux", "cpu": "x64" }, "sha512-lh5Lg/Cc18+232NRd1OZqdpJzuJMW6lrwkC6RKYiiTzxUfRq7uRV8qI0NNnp5cyKiyDO90cpQQ8fjepTaMzh9A=="], - "@postgres-language-server/cli-x86_64-linux-musl": [ - "@postgres-language-server/cli-x86_64-linux-musl@0.17.1", - "", - { "os": "linux", "cpu": "x64" }, - "sha512-YH5Znq0urRUxtdsbyDu6Dcg5x9qhgDqTl7gCVY/LmVrPnHfP2I6gU/TJT21B5sDTSadjwZ73AbXimLmlwG6HPA==" - ], + "@postgres-language-server/cli-x86_64-linux-musl": ["@postgres-language-server/cli-x86_64-linux-musl@0.17.1", "", { "os": "linux", "cpu": "x64" }, "sha512-YH5Znq0urRUxtdsbyDu6Dcg5x9qhgDqTl7gCVY/LmVrPnHfP2I6gU/TJT21B5sDTSadjwZ73AbXimLmlwG6HPA=="], - "@postgres-language-server/cli-x86_64-windows-msvc": [ - "@postgres-language-server/cli-x86_64-windows-msvc@0.17.1", - "", - { "os": "win32", "cpu": "x64" }, - "sha512-o2uIx0Vi3iEBFKKnz024JicgovJZWpM9G4WbQxf3r9IQlZPWvDXknlX7rNPQDnQTFRIUg8bpjZpjEvDFHMmuGg==" - ], + "@postgres-language-server/cli-x86_64-windows-msvc": ["@postgres-language-server/cli-x86_64-windows-msvc@0.17.1", "", { "os": "win32", "cpu": "x64" }, "sha512-o2uIx0Vi3iEBFKKnz024JicgovJZWpM9G4WbQxf3r9IQlZPWvDXknlX7rNPQDnQTFRIUg8bpjZpjEvDFHMmuGg=="], - "@postgrestools/backend-jsonrpc": [ - "@postgrestools/backend-jsonrpc@workspace:packages/@postgrestools/backend-jsonrpc" - ], + "@postgrestools/backend-jsonrpc": ["@postgrestools/backend-jsonrpc@workspace:packages/@postgrestools/backend-jsonrpc"], - "@postgrestools/cli-aarch64-apple-darwin": [ - "@postgrestools/cli-aarch64-apple-darwin@0.13.0", - "", - { "os": "darwin", "cpu": "arm64" }, - "sha512-LqgpsiupuNR1m8rSqcMdwep1UsSG5z7RZ9MYHODBB0+AsN/e1YzcyutqGeKs+AfDBdKdv7QmSbpf7KruBYL+tg==" - ], + "@postgrestools/cli-aarch64-apple-darwin": ["@postgrestools/cli-aarch64-apple-darwin@0.13.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LqgpsiupuNR1m8rSqcMdwep1UsSG5z7RZ9MYHODBB0+AsN/e1YzcyutqGeKs+AfDBdKdv7QmSbpf7KruBYL+tg=="], - "@postgrestools/cli-aarch64-linux-gnu": [ - "@postgrestools/cli-aarch64-linux-gnu@0.13.0", - "", - { "os": "linux", "cpu": "arm64" }, - "sha512-QxZZBvg9qAo9raTA5cSuSXlrqo+eCUtYMWSR2dgDCEqogNlo/62ubWYgPi+SMEtfm9euqG1GOItxyRv6xUriEA==" - ], + "@postgrestools/cli-aarch64-linux-gnu": ["@postgrestools/cli-aarch64-linux-gnu@0.13.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-QxZZBvg9qAo9raTA5cSuSXlrqo+eCUtYMWSR2dgDCEqogNlo/62ubWYgPi+SMEtfm9euqG1GOItxyRv6xUriEA=="], - "@postgrestools/cli-aarch64-windows-msvc": [ - "@postgrestools/cli-aarch64-windows-msvc@0.13.0", - "", - { "os": "win32", "cpu": "arm64" }, - "sha512-C8BRZOPQA16eGL8Kk7D4M+9h2Y7x0GOJEBzjR3ZV54besoH8gcZqSxzaA8WbFczQ3dgEZ5CStny6asl3B5JMHA==" - ], + "@postgrestools/cli-aarch64-windows-msvc": ["@postgrestools/cli-aarch64-windows-msvc@0.13.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-C8BRZOPQA16eGL8Kk7D4M+9h2Y7x0GOJEBzjR3ZV54besoH8gcZqSxzaA8WbFczQ3dgEZ5CStny6asl3B5JMHA=="], - "@postgrestools/cli-x86_64-apple-darwin": [ - "@postgrestools/cli-x86_64-apple-darwin@0.13.0", - "", - { "os": "darwin", "cpu": "x64" }, - "sha512-stbU4+en82DofjzpAQlfgKemJe1lbkGhuOAxfWce4jyT25+cil6cEWBia0hZyrve9sVEJctE1MHy9os8s3EGww==" - ], + "@postgrestools/cli-x86_64-apple-darwin": ["@postgrestools/cli-x86_64-apple-darwin@0.13.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-stbU4+en82DofjzpAQlfgKemJe1lbkGhuOAxfWce4jyT25+cil6cEWBia0hZyrve9sVEJctE1MHy9os8s3EGww=="], - "@postgrestools/cli-x86_64-linux-gnu": [ - "@postgrestools/cli-x86_64-linux-gnu@0.13.0", - "", - { "os": "linux", "cpu": "x64" }, - "sha512-RPFdgcAiD+BWhSFw7suI0jTkJysHlHzrD4in27OGlKt9AvZ6TgKrCfDatUkn2VQfn6Ib8o/+1roFOKwFbfQIOw==" - ], + "@postgrestools/cli-x86_64-linux-gnu": ["@postgrestools/cli-x86_64-linux-gnu@0.13.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RPFdgcAiD+BWhSFw7suI0jTkJysHlHzrD4in27OGlKt9AvZ6TgKrCfDatUkn2VQfn6Ib8o/+1roFOKwFbfQIOw=="], - "@postgrestools/cli-x86_64-windows-msvc": [ - "@postgrestools/cli-x86_64-windows-msvc@0.13.0", - "", - { "os": "win32", "cpu": "x64" }, - "sha512-4S6mbvc9u6ipeMcZHwX3RqGMJaNR9B5utMQN/N3LBnvLx62N9MHU1bk3bnGxfHoALXQTMq+us7M9GvEe1izGHQ==" - ], + "@postgrestools/cli-x86_64-windows-msvc": ["@postgrestools/cli-x86_64-windows-msvc@0.13.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4S6mbvc9u6ipeMcZHwX3RqGMJaNR9B5utMQN/N3LBnvLx62N9MHU1bk3bnGxfHoALXQTMq+us7M9GvEe1izGHQ=="], - "@postgrestools/postgrestools": [ - "@postgrestools/postgrestools@workspace:packages/@postgrestools/postgrestools" - ], + "@postgrestools/postgrestools": ["@postgrestools/postgrestools@workspace:packages/@postgrestools/postgrestools"], - "@types/bun": [ - "@types/bun@1.2.5", - "", - { "dependencies": { "bun-types": "1.2.5" } }, - "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg==" - ], + "@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="], - "@types/node": [ - "@types/node@22.13.10", - "", - { "dependencies": { "undici-types": "~6.20.0" } }, - "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==" - ], + "@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="], - "@types/ws": [ - "@types/ws@8.5.14", - "", - { "dependencies": { "@types/node": "*" } }, - "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==" - ], + "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], - "bun-types": [ - "bun-types@1.2.5", - "", - { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, - "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg==" - ], + "bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="], - "typescript": [ - "typescript@5.8.2", - "", - { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, - "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==" - ], + "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], - "undici-types": [ - "undici-types@6.20.0", - "", - {}, - "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" - ] + "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], } } From c0e2d359865eb256bbce30f41190d3c6b0e738c3 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 14:32:04 +0100 Subject: [PATCH 13/38] fixed aaaaaaaaaaaaaaat --- .../pgls_completions/src/providers/columns.rs | 2 +- .../pgls_completions/src/relevance/scoring.rs | 60 ++++++++++++++++--- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 2e05c6144..eda6fdcd1 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -52,7 +52,7 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText mod tests { use sqlx::PgPool; - use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite, assert_complete_results}; + use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index d1193e9dc..360a77afe 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -59,24 +59,53 @@ impl CompletionScore<'_> { CompletionRelevanceData::Role(r) => r.name.as_str().to_ascii_lowercase(), }; + let is_case = + matches!(self.data, CompletionRelevanceData::Column(_)) && content.as_str() == "u"; + let fz_matcher = SkimMatcherV2::default(); - let check_against = match ctx.identifier_qualifiers { + let check_against = match &ctx.identifier_qualifiers { // If both qualifiers are already written out, we must check the item's name itself. - (Some(_), Some(_)) => content.to_ascii_lowercase(), + (Some(_), Some(_)) => name, + + // If only one qualifier is written out, we might look at a schema, a table, or an alias. + (None, Some(qualifier)) => { + if self.get_schema_name().is_some_and(|s| s == qualifier) { + self.get_table_name() + .map(|t| format!("{}.{}", t, name)) + .unwrap_or(name) + } else if self.get_table_name().is_some_and(|t| t == qualifier) { + name + } else if ctx + .get_mentioned_table_for_alias(&qualifier) + .is_some_and(|alias_tbl| { + self.get_table_name() + .is_some_and(|item_tbl| alias_tbl == item_tbl) + }) + { + name + } else { + // the qualifier does not match schema, table, or alias. + // what the hell is it? + // probably a typo. + self.skip = true; + String::new() + } + } - _ => self - .get_table_name() - .and_then(|tbl| ctx.get_used_alias_for_table(tbl)) - .map(|alias| format!("{}.{}", alias, name)) - .unwrap_or(name), + // no qualifier whatsoever, we'll check the full qualified name of the item. + _ => self.get_fully_qualified_name(), }; match fz_matcher.fuzzy_match( - check_against.as_str(), + check_against.to_ascii_lowercase().as_str(), content.to_ascii_lowercase().as_str(), ) { Some(score) => { + if is_case { + println!("check {}, score {}", check_against, score); + } + let scorei32: i32 = score .try_into() .expect("The length of the input exceeds i32 capacity"); @@ -258,6 +287,21 @@ impl CompletionScore<'_> { } } + fn get_fully_qualified_name(&self) -> String { + match self.data { + CompletionRelevanceData::Schema(s) => s.name.clone(), + CompletionRelevanceData::Column(c) => { + format!("{}.{}.{}", c.schema_name, c.table_name, c.name) + } + CompletionRelevanceData::Table(t) => format!("{}.{}", t.schema, t.name), + CompletionRelevanceData::Function(f) => format!("{}.{}", f.schema, f.name), + CompletionRelevanceData::Policy(p) => { + format!("{}.{}.{}", p.schema_name, p.table_name, p.name) + } + CompletionRelevanceData::Role(r) => r.name.clone(), + } + } + fn get_table_name(&self) -> Option<&str> { match self.data { CompletionRelevanceData::Column(c) => Some(c.table_name.as_str()), From 324c4f5e79581c6cf26cec00106fc716cb12bcf2 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 15:54:28 +0100 Subject: [PATCH 14/38] commit --- .../pgls_completions/src/providers/columns.rs | 6 ++++- .../src/relevance/filtering.rs | 10 ++++++++ .../pgls_completions/src/relevance/scoring.rs | 25 +++++++++---------- crates/pgls_completions/src/sanitization.rs | 3 ++- ...t_helper__completes_in_join_on_clause.snap | 10 -------- ...completes_quoted_columns_with_aliases.snap | 9 ++----- crates/pgls_completions/src/test_helper.rs | 2 ++ 7 files changed, 33 insertions(+), 32 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index eda6fdcd1..8a02b0244 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -50,9 +50,13 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText #[cfg(test)] mod tests { + use pgls_test_utils::QueryWithCursorPosition; use sqlx::PgPool; - use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; + use crate::test_helper::{ + TestCompletionsCase, TestCompletionsSuite, assert_complete_results, + assert_no_complete_results, + }; #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index 7aa3531a6..8b2cb9248 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -1,6 +1,8 @@ use pgls_schema_cache::ProcKind; use pgls_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode}; +use crate::is_sanitized_token; + use super::CompletionRelevanceData; #[derive(Debug)] @@ -43,6 +45,14 @@ impl CompletionFilter<'_> { return None; } + // if ctx + // .get_node_under_cursor_content() + // .is_some_and(|c| is_sanitized_token(c.as_str())) + // && ctx.before_cursor_matches_kind(&["ERROR"]) + // { + // return None; + // } + // "literal" nodes can be identfiers wrapped in quotes: // `select "email" from auth.users;` // Here, "email" is a literal node. diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 360a77afe..ff7746445 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -47,7 +47,9 @@ impl CompletionScore<'_> { fn check_matches_query_input(&mut self, ctx: &TreesitterContext) { let content = match ctx.get_node_under_cursor_content() { Some(c) if !sanitization::is_sanitized_token(c.as_str()) => c.replace('"', ""), - _ => return, + _ => { + return; + } }; let name = match self.data { @@ -59,23 +61,20 @@ impl CompletionScore<'_> { CompletionRelevanceData::Role(r) => r.name.as_str().to_ascii_lowercase(), }; - let is_case = - matches!(self.data, CompletionRelevanceData::Column(_)) && content.as_str() == "u"; - let fz_matcher = SkimMatcherV2::default(); let check_against = match &ctx.identifier_qualifiers { // If both qualifiers are already written out, we must check the item's name itself. - (Some(_), Some(_)) => name, + (Some(_), Some(_)) => name.clone(), // If only one qualifier is written out, we might look at a schema, a table, or an alias. (None, Some(qualifier)) => { if self.get_schema_name().is_some_and(|s| s == qualifier) { self.get_table_name() .map(|t| format!("{}.{}", t, name)) - .unwrap_or(name) + .unwrap_or(name.clone()) } else if self.get_table_name().is_some_and(|t| t == qualifier) { - name + name.clone() } else if ctx .get_mentioned_table_for_alias(&qualifier) .is_some_and(|alias_tbl| { @@ -83,7 +82,7 @@ impl CompletionScore<'_> { .is_some_and(|item_tbl| alias_tbl == item_tbl) }) { - name + name.clone() } else { // the qualifier does not match schema, table, or alias. // what the hell is it? @@ -102,10 +101,6 @@ impl CompletionScore<'_> { content.to_ascii_lowercase().as_str(), ) { Some(score) => { - if is_case { - println!("check {}, score {}", check_against, score); - } - let scorei32: i32 = score .try_into() .expect("The length of the input exceeds i32 capacity"); @@ -116,7 +111,11 @@ impl CompletionScore<'_> { // - item: numeric_uplus, input: n, score: 31 // - item: settings, input: sett, score: 91 // - item: user_settings, input: sett, score: 82 - self.score += scorei32 / 2; + self.score += if check_against == name { + scorei32 / 2 + } else { + scorei32 / 3 + }; } None => self.skip = true, } diff --git a/crates/pgls_completions/src/sanitization.rs b/crates/pgls_completions/src/sanitization.rs index 045eaae65..2b10ee01d 100644 --- a/crates/pgls_completions/src/sanitization.rs +++ b/crates/pgls_completions/src/sanitization.rs @@ -26,7 +26,8 @@ pub(crate) fn remove_sanitized_token(it: &str) -> String { } pub(crate) fn is_sanitized_token(node_under_cursor_txt: &str) -> bool { - node_under_cursor_txt == SANITIZED_TOKEN || is_sanitized_token_with_quote(node_under_cursor_txt) + node_under_cursor_txt.replace('"', "") == SANITIZED_TOKEN + || is_sanitized_token_with_quote(node_under_cursor_txt) } pub(crate) fn is_sanitized_token_with_quote(node_under_cursor_txt: &str) -> bool { diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap index 304b7306d..73e340190 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap @@ -56,16 +56,6 @@ user_id - auth.posts.user_id (Column) -------------- select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id | - -Results: -p.pid - auth.posts.pid (Column) -u.uid - auth.users.uid (Column) -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) - --------------- - select u.id, auth.posts.content from auth.users u join auth.posts p on p.user_id = | Results: diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap index 53a0d60c6..5f1bc74b7 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -92,7 +92,7 @@ public - public (Schema) private - private (Schema) names - public.names (Table) private.users - private.users (Table) -information_schema.parameters - information_schema.parameters (Table) +pg_catalog - pg_catalog (Schema) -------------- @@ -159,9 +159,9 @@ select "e|" from private.users "pr" Results: pr"."email - private.users.email (Column) +pr"."id - private.users.id (Column) pr"."name - private.users.name (Column) pr"."quoted_column - private.users.quoted_column (Column) -pr"."id - private.users.id (Column) name - public.names.name (Column) -------------- @@ -232,7 +232,6 @@ Results: email - private.users.email (Column) name - private.users.name (Column) quoted_column - private.users.quoted_column (Column) -id - private.users.id (Column) -------------- @@ -324,7 +323,6 @@ Results: email - private.users.email (Column) name - private.users.name (Column) quoted_column - private.users.quoted_column (Column) -id - private.users.id (Column) -------------- @@ -375,7 +373,6 @@ Results: email - private.users.email (Column) name - private.users.name (Column) quoted_column - private.users.quoted_column (Column) -id - private.users.id (Column) -------------- @@ -447,7 +444,6 @@ Results: email - private.users.email (Column) name - private.users.name (Column) quoted_column - private.users.quoted_column (Column) -id - private.users.id (Column) -------------- @@ -474,7 +470,6 @@ select "pr"."email", n.u| from private.users "pr" join public.names n on pr.id = Results: uid - public.names.uid (Column) -name - public.names.name (Column) -------------- diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index 7613f2815..f048d5649 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -205,6 +205,8 @@ pub(crate) async fn assert_no_complete_results(query: &str, setup: Option<&str>, let params = get_test_params(&tree, &cache, query.into()); let items = complete(params); + println!("Items: {:#?}", &items[..3]); + assert_eq!(items.len(), 0) } From 9c07d91a34dc0021f1c183755468f53c9d0ccd2a Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 16:44:51 +0100 Subject: [PATCH 15/38] accept snaps --- .../pgls_completions/src/providers/columns.rs | 19 +++++++++---- .../src/relevance/filtering.rs | 14 +++++----- .../pgls_completions/src/relevance/scoring.rs | 13 +++++++-- ...test_helper__completes_quoted_columns.snap | 18 ++++-------- ...completes_quoted_columns_with_aliases.snap | 28 +++++++++---------- 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 8a02b0244..44a33a8fb 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -541,12 +541,19 @@ mod tests { ); "#; - TestCompletionsSuite::new(&pool, Some(setup)) - .with_case( - TestCompletionsCase::new().type_sql(r#"select "email" from "private"."users";"#), - ) - .snapshot("completes_quoted_columns") - .await; + assert_no_complete_results( + format!(r#"select "e{}""#, QueryWithCursorPosition::cursor_marker()).as_str(), + Some(setup), + &pool, + ) + .await; + + // TestCompletionsSuite::new(&pool, Some(setup)) + // .with_case( + // TestCompletionsCase::new().type_sql(r#"select "email" from "private"."users";"#), + // ) + // .snapshot("completes_quoted_columns") + // .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index 8b2cb9248..4207ad3a3 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -45,13 +45,13 @@ impl CompletionFilter<'_> { return None; } - // if ctx - // .get_node_under_cursor_content() - // .is_some_and(|c| is_sanitized_token(c.as_str())) - // && ctx.before_cursor_matches_kind(&["ERROR"]) - // { - // return None; - // } + if ctx + .get_node_under_cursor_content() + .is_some_and(|c| is_sanitized_token(c.as_str())) + && ctx.before_cursor_matches_kind(&["ERROR"]) + { + return None; + } // "literal" nodes can be identfiers wrapped in quotes: // `select "email" from auth.users;` diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index ff7746445..756245d82 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -92,8 +92,17 @@ impl CompletionScore<'_> { } } - // no qualifier whatsoever, we'll check the full qualified name of the item. - _ => self.get_fully_qualified_name(), + _ => match self.data { + // for columns and functions, we fuzzy match with a possible alias. + CompletionRelevanceData::Column(_) | CompletionRelevanceData::Policy(_) => self + .get_table_name() + .and_then(|tbl| ctx.get_used_alias_for_table(tbl)) + .map(|t| format!("{}.{}", t, name)) + .unwrap_or(name.clone()), + + // everything else is just fuzzy matched against its name. + _ => name.clone(), + }, }; match fz_matcher.fuzzy_match( diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap index 2cb3f114e..a1b9b0627 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap @@ -54,10 +54,10 @@ select "e|" Results: email - private.users.email (Column) +id - private.users.id (Column) name - private.users.name (Column) quoted_column - private.users.quoted_column (Column) -id - private.users.id (Column) -elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) +apply_error_count - pg_catalog.pg_stat_subscription_stats.apply_error_count (Column) -------------- @@ -91,8 +91,8 @@ Results: public - public (Schema) private - private (Schema) private"."users - private.users (Table) -information_schema"."_pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema"."_pg_foreign_servers - information_schema._pg_foreign_servers (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) -------------- @@ -102,8 +102,8 @@ Results: public - public (Schema) private - private (Schema) private"."users - private.users (Table) -information_schema"."parameters - information_schema.parameters (Table) -pg_catalog"."pg_aggregate - pg_catalog.pg_aggregate (Table) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -115,10 +115,4 @@ users - private.users (Table) -------------- select "email" from "private".u| - -Results: -users - private.users (Table) - --------------- - select "email" from "private".users; | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap index 5f1bc74b7..a580c43d4 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -60,11 +60,11 @@ name - private.users.name (Column) select "p|" Results: -name - public.names.name (Column) -uid - public.names.uid (Column) -email - private.users.email (Column) -id - private.users.id (Column) -name - private.users.name (Column) +pad_attribute - information_schema.collations.pad_attribute (Column) +page - pg_catalog.pg_locks.page (Column) +pageno - pg_catalog.pg_largeobject.pageno (Column) +paracl - pg_catalog.pg_parameter_acl.paracl (Column) +parameter_default - information_schema.parameters.parameter_default (Column) -------------- @@ -90,9 +90,9 @@ select "pr"."email" from p| Results: public - public (Schema) private - private (Schema) -names - public.names (Table) -private.users - private.users (Table) -pg_catalog - pg_catalog (Schema) +information_schema.parameters - information_schema.parameters (Table) +pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) +pg_catalog.pg_am - pg_catalog.pg_am (Table) -------------- @@ -159,10 +159,10 @@ select "e|" from private.users "pr" Results: pr"."email - private.users.email (Column) -pr"."id - private.users.id (Column) pr"."name - private.users.name (Column) pr"."quoted_column - private.users.quoted_column (Column) name - public.names.name (Column) +elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) -------------- @@ -192,7 +192,7 @@ Results: "pr".id - private.users.id (Column) "pr".name - private.users.name (Column) "pr".quoted_column - private.users.quoted_column (Column) -name - public.names.name (Column) +pad_attribute - information_schema.collations.pad_attribute (Column) -------------- @@ -283,7 +283,7 @@ pr"."email - private.users.email (Column) pr"."id - private.users.id (Column) pr"."name - private.users.name (Column) pr"."quoted_column - private.users.quoted_column (Column) -name - public.names.name (Column) +pad_attribute - information_schema.collations.pad_attribute (Column) -------------- @@ -352,7 +352,7 @@ Results: "pr".id - private.users.id (Column) "pr".name - private.users.name (Column) "pr".quoted_column - private.users.quoted_column (Column) -name - public.names.name (Column) +pad_attribute - information_schema.collations.pad_attribute (Column) -------------- @@ -420,11 +420,11 @@ pr"."name - private.users.name (Column) select "p|" from private.users "pr" join public.names n on pr.id = n.uid; Results: -n"."name - public.names.name (Column) -n"."uid - public.names.uid (Column) pr"."email - private.users.email (Column) pr"."id - private.users.id (Column) pr"."name - private.users.name (Column) +pr"."quoted_column - private.users.quoted_column (Column) +pad_attribute - information_schema.collations.pad_attribute (Column) -------------- From 359a8ff40c4f9744378c5ae9eef30bdcc97ecf5b Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 16:58:15 +0100 Subject: [PATCH 16/38] ok? --- .../pgls_completions/src/providers/columns.rs | 28 +++-- .../pgls_completions/src/relevance/scoring.rs | 58 ++++++++- ...test_helper__completes_quoted_columns.snap | 6 +- ...completes_quoted_columns_with_aliases.snap | 4 +- ...oes_not_complete_cols_in_join_clauses.snap | 39 +----- ...er__filters_out_by_aliases_in_join_on.snap | 24 ---- ...per__filters_out_by_aliases_in_select.snap | 5 - ...__test_helper__handles_nested_queries.snap | 26 +--- ...t_helper__ignores_cols_in_from_clause.snap | 4 +- ...__prefers_columns_of_mentioned_tables.snap | 14 +-- ...helper__prefers_not_mentioned_columns.snap | 89 +++++++------- ...iple_columns_if_no_relation_specified.snap | 2 +- ...columns_in_alter_table_and_drop_table.snap | 114 ++++++++---------- ...er__suggests_columns_in_insert_clause.snap | 36 +++--- ...per__suggests_columns_in_where_clause.snap | 54 ++------- ..._suggests_columns_policy_using_clause.snap | 24 ++-- ...ests_relevant_columns_without_letters.snap | 10 +- crates/pgls_completions/src/test_helper.rs | 2 +- 18 files changed, 231 insertions(+), 308 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 44a33a8fb..863a6a8ed 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -541,19 +541,23 @@ mod tests { ); "#; - assert_no_complete_results( - format!(r#"select "e{}""#, QueryWithCursorPosition::cursor_marker()).as_str(), - Some(setup), - &pool, - ) - .await; - - // TestCompletionsSuite::new(&pool, Some(setup)) - // .with_case( - // TestCompletionsCase::new().type_sql(r#"select "email" from "private"."users";"#), + // assert_no_complete_results( + // format!( + // r#"select "email" from "p{}""#, + // QueryWithCursorPosition::cursor_marker() // ) - // .snapshot("completes_quoted_columns") - // .await; + // .as_str(), + // Some(setup), + // &pool, + // ) + // .await; + + TestCompletionsSuite::new(&pool, Some(setup)) + .with_case( + TestCompletionsCase::new().type_sql(r#"select "email" from "private"."users";"#), + ) + .snapshot("completes_quoted_columns") + .await; } #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 756245d82..4f1dfbcf6 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -33,15 +33,52 @@ impl CompletionScore<'_> { } pub fn calc_score(&mut self, ctx: &TreesitterContext) { + let case = (matches!(self.data, CompletionRelevanceData::Schema(_)) + && self.get_schema_name().is_some_and(|s| s == "pg_catalog")) + || (matches!(self.data, CompletionRelevanceData::Table(_)) + && self.get_table_name().is_some_and(|s| s == "parameters")); + + if case { + println!("checking {}", self.get_fully_qualified_name()) + } + self.check_is_user_defined(); + if case { + println!("{} after user-defined check", self.score); + } + self.check_matches_schema(ctx); + if case { + println!("{} after schema match check", self.score); + } self.check_matches_query_input(ctx); + if case { + println!("{} after query input match check", self.score); + } self.check_is_invocation(ctx); + if case { + println!("{} after invocation check", self.score); + } self.check_matching_clause_type(ctx); + if case { + println!("{} after clause type check", self.score); + } self.check_matching_wrapping_node(ctx); + if case { + println!("{} after wrapping node check", self.score); + } self.check_relations_in_stmt(ctx); + if case { + println!("{} after relations in stmt check", self.score); + } self.check_columns_in_stmt(ctx); + if case { + println!("{} after columns in stmt check", self.score); + } self.check_is_not_wellknown_migration(ctx); + if case { + println!("{} after well-known migration check", self.score); + } } fn check_matches_query_input(&mut self, ctx: &TreesitterContext) { @@ -125,6 +162,21 @@ impl CompletionScore<'_> { } else { scorei32 / 3 }; + + if matches!(self.data, CompletionRelevanceData::Schema(_)) + && self.get_schema_name().is_some_and(|s| s == "pg_catalog") + { + println!("Debug: Schema pg_catalog match score {}", self.score); + } + + if matches!(self.data, CompletionRelevanceData::Table(_)) + && self.get_table_name().is_some_and(|s| s == "parameters") + { + println!( + "Debug: Table information_schema.parameters match score {}", + self.score + ); + } } None => self.skip = true, } @@ -202,9 +254,6 @@ impl CompletionScore<'_> { }; let has_qualifier = ctx.has_any_qualifier(); - let has_node_text = ctx - .get_node_under_cursor_content() - .is_some_and(|txt| !sanitization::is_sanitized_token(txt.as_str())); self.score += match self.data { CompletionRelevanceData::Table(_) => match wrapping_node { @@ -224,8 +273,7 @@ impl CompletionScore<'_> { _ => -15, }, CompletionRelevanceData::Schema(_) => match wrapping_node { - WrappingNode::Relation if !has_qualifier && !has_node_text => 15, - WrappingNode::Relation if !has_qualifier && has_node_text => 0, + WrappingNode::Relation if !has_qualifier => 15, _ => -50, }, CompletionRelevanceData::Policy(_) => 0, diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap index a1b9b0627..5444ede22 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap @@ -54,10 +54,10 @@ select "e|" Results: email - private.users.email (Column) -id - private.users.id (Column) name - private.users.name (Column) quoted_column - private.users.quoted_column (Column) -apply_error_count - pg_catalog.pg_stat_subscription_stats.apply_error_count (Column) +elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) +encoding - pg_catalog.pg_database.encoding (Column) -------------- @@ -101,9 +101,9 @@ select "email" from "p|" Results: public - public (Schema) private - private (Schema) -private"."users - private.users (Table) pg_catalog - pg_catalog (Schema) pg_toast - pg_toast (Schema) +information_schema"."parameters - information_schema.parameters (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap index a580c43d4..94846f498 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -90,9 +90,9 @@ select "pr"."email" from p| Results: public - public (Schema) private - private (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) information_schema.parameters - information_schema.parameters (Table) -pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) -pg_catalog.pg_am - pg_catalog.pg_am (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap index 95f59dec2..7aa565e5f 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap @@ -69,9 +69,9 @@ select u.uid, p.content from a| Results: auth - auth (Schema) -public - public (Schema) -auth.posts - auth.posts (Table) -auth.users - auth.users (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) -------------- @@ -90,7 +90,6 @@ select u.uid, p.content from auth.u| Results: users - auth.users (Table) -posts - auth.posts (Table) -------------- @@ -112,9 +111,9 @@ select u.uid, p.content from auth.users u join a| Results: auth - auth (Schema) -public - public (Schema) -auth.posts - auth.posts (Table) -auth.users - auth.users (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) -------------- @@ -131,7 +130,6 @@ select u.uid, p.content from auth.users u join auth.p| Results: posts - auth.posts (Table) -users - auth.users (Table) -------------- @@ -162,22 +160,10 @@ select u.uid, p.content from auth.users u join auth.posts p on u.u| Results: uid - auth.users.uid (Column) -email - auth.users.email (Column) -name - auth.users.name (Column) -------------- select u.uid, p.content from auth.users u join auth.posts p on u.uid | - -Results: -p.pid - auth.posts.pid (Column) -u.uid - auth.users.uid (Column) -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) - --------------- - select u.uid, p.content from auth.users u join auth.posts p on u.uid = | Results: @@ -204,20 +190,7 @@ select u.uid, p.content from auth.users u join auth.posts p on u.uid = p.u| Results: user_id - auth.posts.user_id (Column) -pid - auth.posts.pid (Column) -content - auth.posts.content (Column) -created_at - auth.posts.created_at (Column) -title - auth.posts.title (Column) -------------- select u.uid, p.content from auth.users u join auth.posts p on u.uid = p.user_id | - -Results: -p.pid - auth.posts.pid (Column) -u.uid - auth.users.uid (Column) -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) - --------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap index 8adcb3f56..37c635810 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap @@ -51,21 +51,10 @@ select u.id, p.content from auth.users u join auth.posts p on u.i| Results: uid - auth.users.uid (Column) email - auth.users.email (Column) -name - auth.users.name (Column) -------------- select u.id, p.content from auth.users u join auth.posts p on u.id | - -Results: -p.pid - auth.posts.pid (Column) -u.uid - auth.users.uid (Column) -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) - --------------- - select u.id, p.content from auth.users u join auth.posts p on u.id = | Results: @@ -93,20 +82,7 @@ select u.id, p.content from auth.users u join auth.posts p on u.id = p.u| Results: user_id - auth.posts.user_id (Column) -pid - auth.posts.pid (Column) -content - auth.posts.content (Column) -created_at - auth.posts.created_at (Column) -title - auth.posts.title (Column) -------------- select u.id, p.content from auth.users u join auth.posts p on u.id = p.user_id | - -Results: -p.pid - auth.posts.pid (Column) -u.uid - auth.users.uid (Column) -p.content - auth.posts.content (Column) -p.created_at - auth.posts.created_at (Column) -u.email - auth.users.email (Column) - --------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap index 581ff6cdb..0a80debe3 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap @@ -49,7 +49,6 @@ select u.i| from auth.users u join auth.posts p on u.id = p.user_id; Results: email - auth.users.email (Column) uid - auth.users.uid (Column) -name - auth.users.name (Column) -------------- @@ -81,10 +80,6 @@ select u.id, p.p| from auth.users u join auth.posts p on u.id = p.user_id; Results: pid - auth.posts.pid (Column) -content - auth.posts.content (Column) -created_at - auth.posts.created_at (Column) -title - auth.posts.title (Column) -user_id - auth.posts.user_id (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap index 9f0fcc088..bbef4c4d4 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap @@ -29,31 +29,11 @@ select * from ( ) as subquery join public.users u on u.id = subquery.id; - -Results: -audio_books - public.audio_books (Table) -users - public.users (Table) -private.audio_books - private.audio_books (Table) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) - --------------- - select * from ( s| ) as subquery join public.users u on u.id = subquery.id; - -Results: -audio_books - public.audio_books (Table) -users - public.users (Table) -private.audio_books - private.audio_books (Table) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) - --------------- - select * from ( select | ) as subquery @@ -150,9 +130,9 @@ on u.id = subquery.id; Results: public - public (Schema) private - private (Schema) -audio_books - public.audio_books (Table) -users - public.users (Table) -private.audio_books - private.audio_books (Table) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +information_schema.parameters - information_schema.parameters (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap index 8c8b135a9..ddd575abb 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap @@ -35,9 +35,9 @@ select * from p| Results: public - public (Schema) private - private (Schema) -private.users - private.users (Table) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) information_schema.parameters - information_schema.parameters (Table) -pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap index 490549e94..ca024d628 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap @@ -47,8 +47,8 @@ Results: address2 - public.users.address2 (Column) email2 - public.users.email2 (Column) name2 - public.users.name2 (Column) -id2 - public.users.id2 (Column) -settings - public.users.settings (Column) +address1 - private.users.address1 (Column) +email1 - private.users.email1 (Column) -------------- @@ -79,8 +79,8 @@ Results: address1 - private.users.address1 (Column) email1 - private.users.email1 (Column) name1 - private.users.name1 (Column) -id1 - private.users.id1 (Column) -user_settings - private.users.user_settings (Column) +address2 - public.users.address2 (Column) +email2 - public.users.email2 (Column) -------------- @@ -110,9 +110,9 @@ select s| from private.users Results: user_settings - private.users.user_settings (Column) address1 - private.users.address1 (Column) -email1 - private.users.email1 (Column) -id1 - private.users.id1 (Column) -name1 - private.users.name1 (Column) +settings - public.users.settings (Column) +address2 - public.users.address2 (Column) +safe_wal_size - pg_catalog.pg_replication_slots.safe_wal_size (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap index cfd1f380a..52b19b693 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap @@ -51,9 +51,6 @@ select o.i| from public.one o join public.two on o.id = t.id; Results: id - public.one.id (Column) -a - public.one.a (Column) -b - public.one.b (Column) -z - public.one.z (Column) -------------- @@ -72,10 +69,10 @@ select o.id, a| from public.one o join public.two on o.id = t.id; Results: o.a - public.one.a (Column) -o.b - public.one.b (Column) -c - public.two.c (Column) -d - public.two.d (Column) -e - public.two.e (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) +action_condition - information_schema.triggers.action_condition (Column) +action_order - information_schema.triggers.action_order (Column) +action_orientation - information_schema.triggers.action_orientation (Column) -------------- @@ -95,10 +92,10 @@ select o.id, a, b| from public.one o join public.two on o.id = t.id; Results: o.b - public.one.b (Column) -c - public.two.c (Column) -d - public.two.d (Column) -e - public.two.e (Column) -id - public.two.id (Column) +backend_start - pg_catalog.pg_stat_activity.backend_start (Column) +backend_type - pg_catalog.pg_stat_activity.backend_type (Column) +backend_xid - pg_catalog.pg_stat_activity.backend_xid (Column) +backend_xmin - pg_catalog.pg_stat_activity.backend_xmin (Column) -------------- @@ -117,10 +114,10 @@ select o.id, a, b, c| from public.one o join public.two on o.id = t.id; Results: c - public.two.c (Column) -d - public.two.d (Column) -e - public.two.e (Column) -id - public.two.id (Column) -o.z - public.one.z (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -140,9 +137,9 @@ select o.id, a, b, c, d| from public.one o join public.two on o.id = t.id; Results: id - public.two.id (Column) d - public.two.d (Column) -e - public.two.e (Column) -o.z - public.one.z (Column) o.id - public.one.id (Column) +data - pg_catalog.pg_largeobject.data (Column) +data_type - pg_catalog.pg_sequences.data_type (Column) -------------- @@ -161,10 +158,10 @@ select o.id, a, b, c, d, e| from public.one o join public.two on o.id = t.id; Results: e - public.two.e (Column) -id - public.two.id (Column) -o.z - public.one.z (Column) -o.a - public.one.a (Column) -o.b - public.one.b (Column) +elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) +encoding - pg_catalog.pg_database.encoding (Column) +encrypted - pg_catalog.pg_stat_gssapi.encrypted (Column) +enforced - information_schema.table_constraints.enforced (Column) -------------- @@ -202,11 +199,11 @@ e - public.two.e (Column) select i| from public.one o join public.two on o.id = t.id; Results: -o.id - public.one.id (Column) -o.a - public.one.a (Column) -o.b - public.one.b (Column) -c - public.two.c (Column) -d - public.two.d (Column) +id - public.two.id (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) -------------- @@ -225,10 +222,10 @@ select id, a| from public.one o join public.two on o.id = t.id; Results: o.a - public.one.a (Column) -o.b - public.one.b (Column) -c - public.two.c (Column) -d - public.two.d (Column) -e - public.two.e (Column) +abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) +action_condition - information_schema.triggers.action_condition (Column) +action_order - information_schema.triggers.action_order (Column) +action_orientation - information_schema.triggers.action_orientation (Column) -------------- @@ -247,10 +244,10 @@ select id, a, b| from public.one o join public.two on o.id = t.id; Results: o.b - public.one.b (Column) -c - public.two.c (Column) -d - public.two.d (Column) -e - public.two.e (Column) -o.z - public.one.z (Column) +backend_start - pg_catalog.pg_stat_activity.backend_start (Column) +backend_type - pg_catalog.pg_stat_activity.backend_type (Column) +backend_xid - pg_catalog.pg_stat_activity.backend_xid (Column) +backend_xmin - pg_catalog.pg_stat_activity.backend_xmin (Column) -------------- @@ -269,10 +266,10 @@ select id, a, b, c| from public.one o join public.two on o.id = t.id; Results: c - public.two.c (Column) -d - public.two.d (Column) -e - public.two.e (Column) -o.z - public.one.z (Column) -o.a - public.one.a (Column) +cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -291,10 +288,10 @@ select id, a, b, c, d| from public.one o join public.two on o.id = t.id; Results: d - public.two.d (Column) -e - public.two.e (Column) -o.z - public.one.z (Column) -o.id - public.one.id (Column) -o.a - public.one.a (Column) +id - public.two.id (Column) +data - pg_catalog.pg_largeobject.data (Column) +data_type - pg_catalog.pg_sequences.data_type (Column) +database - pg_catalog.pg_hba_file_rules.database (Column) -------------- @@ -313,10 +310,10 @@ select id, a, b, c, d, e| from public.one o join public.two on o.id = t.id; Results: e - public.two.e (Column) -o.z - public.one.z (Column) -o.a - public.one.a (Column) -o.b - public.one.b (Column) -c - public.two.c (Column) +elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) +encoding - pg_catalog.pg_database.encoding (Column) +encrypted - pg_catalog.pg_stat_gssapi.encrypted (Column) +enforced - information_schema.table_constraints.enforced (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap index 71823a4b6..463bca8f4 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap @@ -44,8 +44,8 @@ Results: name - public.users.name (Column) narrator - public.audio_books.narrator (Column) narrator_id - private.audio_books.narrator_id (Column) -id - public.audio_books.id (Column) name - Schema: pg_catalog.name (Function) +nameconcatoid - Schema: pg_catalog.nameconcatoid (Function) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap index e8af8cac0..4853ab4ca 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap @@ -58,10 +58,10 @@ alter table instruments drop c| Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -80,10 +80,10 @@ alter table instruments drop column n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -136,10 +136,10 @@ alter table instruments drop c| Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -158,10 +158,10 @@ alter table instruments drop column n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -214,10 +214,10 @@ alter table instruments drop c| Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -236,32 +236,22 @@ alter table instruments drop column i| Results: id - public.instruments.id (Column) -created_at - public.instruments.created_at (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) -------------- alter table instruments drop column if | - -Results: -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) -abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) - --------------- - alter table instruments drop column if e| Results: created_at - public.instruments.created_at (Column) name - public.instruments.name (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) +encoding - pg_catalog.pg_database.encoding (Column) +encrypted - pg_catalog.pg_stat_gssapi.encrypted (Column) -------------- @@ -280,10 +270,10 @@ alter table instruments drop column if exists n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -336,10 +326,10 @@ alter table instruments alter c| Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -358,10 +348,10 @@ alter table instruments alter column n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -428,10 +418,10 @@ alter table instruments alter n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -474,10 +464,10 @@ alter table p| Results: public - public (Schema) -instruments - public.instruments (Table) information_schema.parameters - information_schema.parameters (Table) pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) pg_catalog.pg_am - pg_catalog.pg_am (Table) +pg_catalog.pg_amop - pg_catalog.pg_amop (Table) -------------- @@ -514,10 +504,10 @@ alter table public.instruments alter c| Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -536,10 +526,10 @@ alter table public.instruments alter column n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -592,10 +582,10 @@ alter table instruments alter n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -648,10 +638,10 @@ alter table instruments rename n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -684,10 +674,10 @@ alter table p| Results: public - public (Schema) -instruments - public.instruments (Table) information_schema.parameters - information_schema.parameters (Table) pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) pg_catalog.pg_am - pg_catalog.pg_am (Table) +pg_catalog.pg_amop - pg_catalog.pg_amop (Table) -------------- @@ -724,10 +714,10 @@ alter table public.instruments rename c| Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -746,10 +736,10 @@ alter table public.instruments rename column n| Results: name - public.instruments.name (Column) -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap index 0d99a891f..b7dcd79a5 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap @@ -40,9 +40,9 @@ insert into i| Results: instruments - public.instruments (Table) public - public (Schema) -others - public.others (Table) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -63,10 +63,10 @@ insert into instruments (i|) Results: id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) -id - public.others.id (Column) -a - public.others.a (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) -------------- @@ -85,10 +85,10 @@ insert into instruments (id, n|) Results: name - public.instruments.name (Column) -z - public.instruments.z (Column) -id - public.instruments.id (Column) -a - public.others.a (Column) -b - public.others.b (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- @@ -146,9 +146,9 @@ insert into i| Results: instruments - public.instruments (Table) public - public (Schema) -others - public.others (Table) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -169,10 +169,10 @@ insert into instruments (i|) Results: id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) -id - public.others.id (Column) -a - public.others.a (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) -------------- @@ -213,10 +213,10 @@ insert into instruments ("id", "n|") Results: name - public.instruments.name (Column) -id - public.instruments.id (Column) -z - public.instruments.z (Column) -a - public.others.a (Column) -b - public.others.b (Column) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) +n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) +n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap index 90022579d..40c42e8c9 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap @@ -21,24 +21,14 @@ create table others ( -------------- select name from instruments i join others o on i.z = o.a | - -Results: -i.id - public.instruments.id (Column) -o.a - public.others.a (Column) -o.b - public.others.b (Column) -o.c - public.others.c (Column) -i.created_at - public.instruments.created_at (Column) - --------------- - select name from instruments i join others o on i.z = o.a w| Results: -i.id - public.instruments.id (Column) -o.a - public.others.a (Column) -o.b - public.others.b (Column) -o.c - public.others.c (Column) -i.created_at - public.instruments.created_at (Column) +wait_event - pg_catalog.pg_stat_activity.wait_event (Column) +wait_event_type - pg_catalog.pg_stat_activity.wait_event_type (Column) +waitstart - pg_catalog.pg_locks.waitstart (Column) +wal_buffers_full - pg_catalog.pg_stat_wal.wal_buffers_full (Column) +wal_bytes - pg_catalog.pg_stat_wal.wal_bytes (Column) -------------- @@ -64,16 +54,6 @@ c - public.others.c (Column) -------------- select name from instruments i join others o on i.z = o.a where o.a | - -Results: -o.a - public.others.a (Column) -o.b - public.others.b (Column) -o.c - public.others.c (Column) -i.created_at - public.instruments.created_at (Column) -i.id - public.instruments.id (Column) - --------------- - select name from instruments i join others o on i.z = o.a where o.a = | **should not prioritize suggest columns or schemas (right side of binary expression)** @@ -88,24 +68,14 @@ pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) select name from instruments i join others o on i.z = o.a where o.a = i.| select name from instruments i join others o on i.z = o.a where o.a = i.z | - -Results: -instruments - public.instruments (Table) -others - public.others (Table) -pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) -pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) - --------------- - select name from instruments i join others o on i.z = o.a where o.a = i.z a| Results: -instruments - public.instruments (Table) -others - public.others (Table) abbrev - Schema: pg_catalog.abbrev (Function) abs - Schema: pg_catalog.abs (Function) aclcontains - Schema: pg_catalog.aclcontains (Function) +acldefault - Schema: pg_catalog.acldefault (Function) +aclexplode - Schema: pg_catalog.aclexplode (Function) -------------- @@ -124,16 +94,6 @@ i.name - public.instruments.name (Column) select name from instruments i join others o on i.z = o.a where o.a = i.z and i.| select name from instruments i join others o on i.z = o.a where o.a = i.z and i.i| select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id | - -Results: -instruments - public.instruments (Table) -others - public.others (Table) -pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) -pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) - --------------- - select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id > | Results: diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap index 1f04bb0f8..d3db86856 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap @@ -31,10 +31,10 @@ create policy "my_pol" on public.instruments for select using (i|) Results: id - public.instruments.id (Column) -created_at - public.instruments.created_at (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) -------------- @@ -67,10 +67,10 @@ create policy "my_pol" on public.instruments for select using (id = 1 and c|) Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- @@ -99,10 +99,10 @@ create policy "my_pol" on public.instruments for insert with check (i|) Results: id - public.instruments.id (Column) -created_at - public.instruments.created_at (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) -------------- @@ -135,10 +135,10 @@ create policy "my_pol" on public.instruments for insert with check (id = 1 and c Results: created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) cache_size - pg_catalog.pg_sequences.cache_size (Column) +calls - pg_catalog.pg_stat_user_functions.calls (Column) +castcontext - pg_catalog.pg_cast.castcontext (Column) +castfunc - pg_catalog.pg_cast.castfunc (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap index 12dcca6a4..2b903c41f 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap @@ -30,10 +30,10 @@ select n| Results: name - public.users.name (Column) -address - public.users.address (Column) -email - public.users.email (Column) -id - public.users.id (Column) -name - Schema: pg_catalog.name (Function) +nameconcatoid - Schema: pg_catalog.nameconcatoid (Function) +nameeq - Schema: pg_catalog.nameeq (Function) +nameeqtext - Schema: pg_catalog.nameeqtext (Function) +namege - Schema: pg_catalog.namege (Function) -------------- @@ -53,8 +53,8 @@ pg_toast - pg_toast (Schema) select name from u| Results: -users - public.users (Table) public - public (Schema) +users - public.users (Table) information_schema.udt_privileges - information_schema.udt_privileges (Table) information_schema.usage_privileges - information_schema.usage_privileges (Table) information_schema.user_defined_types - information_schema.user_defined_types (Table) diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index f048d5649..51769c652 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -205,7 +205,7 @@ pub(crate) async fn assert_no_complete_results(query: &str, setup: Option<&str>, let params = get_test_params(&tree, &cache, query.into()); let items = complete(params); - println!("Items: {:#?}", &items[..3]); + println!("Items: {:#?}", &items[..8]); assert_eq!(items.len(), 0) } From 195c7063ae01e48f7fabeabeb320859446130014 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 17:36:43 +0100 Subject: [PATCH 17/38] do not prefer public schema --- .../pgls_completions/src/relevance/scoring.rs | 70 +++++++++++-------- ...test_helper__completes_quoted_columns.snap | 12 ++-- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 4f1dfbcf6..77295e048 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -63,7 +63,7 @@ impl CompletionScore<'_> { if case { println!("{} after clause type check", self.score); } - self.check_matching_wrapping_node(ctx); + self.check_without_content(ctx); if case { println!("{} after wrapping node check", self.score); } @@ -247,37 +247,49 @@ impl CompletionScore<'_> { } } - fn check_matching_wrapping_node(&mut self, ctx: &TreesitterContext) { - let wrapping_node = match ctx.wrapping_node_kind.as_ref() { - None => return, - Some(wn) => wn, - }; + // ok i think we need a rule set first. + // generally, we want to prefer columns that match a mentioned relation. that's already handled elsewhere. same with schema matches. - let has_qualifier = ctx.has_any_qualifier(); + // so, what here? we want to handle the *no content* case. + // in that case, we want to check the current node_kind. - self.score += match self.data { - CompletionRelevanceData::Table(_) => match wrapping_node { - WrappingNode::Relation if has_qualifier => 15, - WrappingNode::Relation if !has_qualifier => 10, - WrappingNode::BinaryExpression => 5, - _ => -50, - }, - CompletionRelevanceData::Function(_) => match wrapping_node { - WrappingNode::BinaryExpression => 15, - WrappingNode::Relation => 10, - _ => -50, - }, - CompletionRelevanceData::Column(_) => match wrapping_node { - WrappingNode::BinaryExpression => 15, - WrappingNode::Assignment => 15, - _ => -15, - }, - CompletionRelevanceData::Schema(_) => match wrapping_node { - WrappingNode::Relation if !has_qualifier => 15, - _ => -50, + fn check_without_content(&mut self, ctx: &TreesitterContext) { + // the function is only concerned with cases where the user hasn't typed anything yet. + if ctx + .get_node_under_cursor_content() + .is_some_and(|c| !sanitization::is_sanitized_token(c.as_str())) + { + return; + } + + match ctx.node_under_cursor.kind() { + "function_identifier" | "table_identifier" + if self.get_schema_name().is_some_and(|s| s == "public") => + { + self.score += 10; + } + + "schema_identifier" if self.get_schema_name().is_some_and(|s| s == "public") => { + self.score += 10; + } + + "any_identifier" => match self.data { + CompletionRelevanceData::Table(table) => { + if table.schema == "public" { + self.score += 10; + } + } + CompletionRelevanceData::Schema(schema) => { + if schema.name != "public" { + self.score += 10; + } else { + self.score -= 20; + } + } + _ => {} }, - CompletionRelevanceData::Policy(_) => 0, - CompletionRelevanceData::Role(_) => 0, + + _ => return, } } diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap index 5444ede22..4adad6c64 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap @@ -66,33 +66,33 @@ select "email" f| select "email" from | Results: -public - public (Schema) private - private (Schema) -private.users - private.users (Table) information_schema - information_schema (Schema) pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +private.users - private.users (Table) -------------- select "email" from "| Results: -public - public (Schema) private - private (Schema) -private"."users" - private.users (Table) information_schema - information_schema (Schema) pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +private"."users" - private.users (Table) -------------- select "email" from "|" Results: -public - public (Schema) private - private (Schema) -private"."users - private.users (Table) information_schema - information_schema (Schema) pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +private"."users - private.users (Table) -------------- From 9f8435fc9a51d620df809f130b42799f562ba658 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 17:38:55 +0100 Subject: [PATCH 18/38] more adjustments --- crates/pgls_completions/src/relevance/scoring.rs | 4 ++-- ..._test_helper__completes_quoted_columns_with_aliases.snap | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 77295e048..e7cd2b097 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -269,14 +269,14 @@ impl CompletionScore<'_> { self.score += 10; } - "schema_identifier" if self.get_schema_name().is_some_and(|s| s == "public") => { + "schema_identifier" if self.get_schema_name().is_some_and(|s| s != "public") => { self.score += 10; } "any_identifier" => match self.data { CompletionRelevanceData::Table(table) => { if table.schema == "public" { - self.score += 10; + self.score += 20; } } CompletionRelevanceData::Schema(schema) => { diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap index 94846f498..20c8bc5af 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -77,11 +77,11 @@ select "pr"."email" f| select "pr"."email" from | Results: -public - public (Schema) -private - private (Schema) names - public.names (Table) -private.users - private.users (Table) +private - private (Schema) information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- From 61b02ff2e5438c2872bf862634046efdcaf04e2d Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 17:41:28 +0100 Subject: [PATCH 19/38] snaps --- .../pgls_completions/src/providers/columns.rs | 17 +---- ...oes_not_complete_cols_in_join_clauses.snap | 12 +-- ...__test_helper__handles_nested_queries.snap | 32 ++++---- ...t_helper__ignores_cols_in_from_clause.snap | 4 +- ...columns_in_alter_table_and_drop_table.snap | 76 +++++++++---------- ...er__suggests_columns_in_insert_clause.snap | 12 +-- ...per__suggests_columns_in_where_clause.snap | 22 +++--- ...ests_relevant_columns_without_letters.snap | 2 +- 8 files changed, 81 insertions(+), 96 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 863a6a8ed..eda6fdcd1 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -50,13 +50,9 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText #[cfg(test)] mod tests { - use pgls_test_utils::QueryWithCursorPosition; use sqlx::PgPool; - use crate::test_helper::{ - TestCompletionsCase, TestCompletionsSuite, assert_complete_results, - assert_no_complete_results, - }; + use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { @@ -541,17 +537,6 @@ mod tests { ); "#; - // assert_no_complete_results( - // format!( - // r#"select "email" from "p{}""#, - // QueryWithCursorPosition::cursor_marker() - // ) - // .as_str(), - // Some(setup), - // &pool, - // ) - // .await; - TestCompletionsSuite::new(&pool, Some(setup)) .with_case( TestCompletionsCase::new().type_sql(r#"select "email" from "private"."users";"#), diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap index 7aa565e5f..811e32219 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap @@ -56,11 +56,11 @@ select u.uid, p.content from | **Schema suggestions should be prioritized, since we want to push users to specify them.** Results: -public - public (Schema) auth - auth (Schema) -auth.posts - auth.posts (Table) -auth.users - auth.users (Table) information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +auth.posts - auth.posts (Table) -------------- @@ -99,11 +99,11 @@ select u.uid, p.content from auth.users u j| select u.uid, p.content from auth.users u join | Results: -public - public (Schema) auth - auth (Schema) -auth.posts - auth.posts (Table) -auth.users - auth.users (Table) information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +auth.posts - auth.posts (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap index bbef4c4d4..11e812a9d 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap @@ -41,11 +41,11 @@ join public.users u on u.id = subquery.id; Results: -pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) -pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) -pg_catalog.RI_FKey_check_upd() - Schema: pg_catalog.RI_FKey_check_upd (Function) -pg_catalog.RI_FKey_noaction_del() - Schema: pg_catalog.RI_FKey_noaction_del (Function) +id - public.audio_books.id (Column) +name - public.users.name (Column) +narrator - public.audio_books.narrator (Column) +id - private.audio_books.id (Column) +narrator_id - private.audio_books.narrator_id (Column) -------------- @@ -56,11 +56,11 @@ join public.users u on u.id = subquery.id; Results: +id - public.audio_books.id (Column) +narrator_id - private.audio_books.narrator_id (Column) iclikejoinsel - Schema: pg_catalog.iclikejoinsel (Function) iclikesel - Schema: pg_catalog.iclikesel (Function) icnlikejoinsel - Schema: pg_catalog.icnlikejoinsel (Function) -icnlikesel - Schema: pg_catalog.icnlikesel (Function) -icregexeqjoinsel - Schema: pg_catalog.icregexeqjoinsel (Function) -------------- @@ -72,11 +72,11 @@ on u.id = subquery.id; **Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.** Results: +name - public.users.name (Column) +narrator - public.audio_books.narrator (Column) +narrator_id - private.audio_books.narrator_id (Column) +id - public.audio_books.id (Column) pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) -pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) -pg_catalog.RI_FKey_check_upd() - Schema: pg_catalog.RI_FKey_check_upd (Function) -pg_catalog.RI_FKey_noaction_del() - Schema: pg_catalog.RI_FKey_noaction_del (Function) -------------- @@ -88,11 +88,11 @@ on u.id = subquery.id; **Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.** Results: +name - public.users.name (Column) +narrator - public.audio_books.narrator (Column) +narrator_id - private.audio_books.narrator_id (Column) name - Schema: pg_catalog.name (Function) nameconcatoid - Schema: pg_catalog.nameconcatoid (Function) -nameeq - Schema: pg_catalog.nameeq (Function) -nameeqtext - Schema: pg_catalog.nameeqtext (Function) -namege - Schema: pg_catalog.namege (Function) -------------- @@ -113,11 +113,11 @@ join public.users u on u.id = subquery.id; Results: -public - public (Schema) -private - private (Schema) audio_books - public.audio_books (Table) users - public.users (Table) +private - private (Schema) private.audio_books - private.audio_books (Table) +information_schema - information_schema (Schema) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap index ddd575abb..12200f57e 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap @@ -21,11 +21,11 @@ select * from | **No column suggestions.** Results: -public - public (Schema) private - private (Schema) -private.users - private.users (Table) information_schema - information_schema (Schema) pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) +private.users - private.users (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap index 4853ab4ca..06135cb3e 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap @@ -23,10 +23,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -101,10 +101,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -179,10 +179,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -291,10 +291,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -366,7 +366,7 @@ created_at - public.instruments.created_at (Column) id - public.instruments.id (Column) name - public.instruments.name (Column) z - public.instruments.z (Column) -abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) +instruments - public.instruments (Table) -------------- @@ -383,10 +383,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -436,7 +436,7 @@ created_at - public.instruments.created_at (Column) id - public.instruments.id (Column) name - public.instruments.name (Column) z - public.instruments.z (Column) -abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) +instruments - public.instruments (Table) -------------- @@ -453,10 +453,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -547,10 +547,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -603,10 +603,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- @@ -663,10 +663,10 @@ alter table | Results: instruments - public.instruments (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) -information_schema._pg_foreign_table_columns - information_schema._pg_foreign_table_columns (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) +pg_toast - pg_toast (Schema) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap index b7dcd79a5..6098c27bf 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap @@ -29,9 +29,9 @@ insert into | Results: instruments - public.instruments (Table) others - public.others (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) -------------- @@ -135,9 +135,9 @@ insert into | Results: instruments - public.instruments (Table) others - public.others (Table) -public - public (Schema) -information_schema._pg_foreign_data_wrappers - information_schema._pg_foreign_data_wrappers (Table) -information_schema._pg_foreign_servers - information_schema._pg_foreign_servers (Table) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap index 40c42e8c9..5bfb1f78a 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap @@ -60,9 +60,9 @@ select name from instruments i join others o on i.z = o.a where o.a = | Results: instruments - public.instruments (Table) others - public.others (Table) -pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) -pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) -------------- @@ -71,11 +71,11 @@ select name from instruments i join others o on i.z = o.a where o.a = i.z | select name from instruments i join others o on i.z = o.a where o.a = i.z a| Results: -abbrev - Schema: pg_catalog.abbrev (Function) -abs - Schema: pg_catalog.abs (Function) -aclcontains - Schema: pg_catalog.aclcontains (Function) -acldefault - Schema: pg_catalog.acldefault (Function) -aclexplode - Schema: pg_catalog.aclexplode (Function) +information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) +information_schema.applicable_roles - information_schema.applicable_roles (Table) +information_schema.attributes - information_schema.attributes (Table) +information_schema.collation_character_set_applicability - information_schema.collation_character_set_applicability (Table) +pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) -------------- @@ -99,9 +99,9 @@ select name from instruments i join others o on i.z = o.a where o.a = i.z and i. Results: instruments - public.instruments (Table) others - public.others (Table) -pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -pg_catalog.RI_FKey_cascade_upd() - Schema: pg_catalog.RI_FKey_cascade_upd (Function) -pg_catalog.RI_FKey_check_ins() - Schema: pg_catalog.RI_FKey_check_ins (Function) +_sqlx_migrations - public._sqlx_migrations (Table) +information_schema - information_schema (Schema) +pg_catalog - pg_catalog (Schema) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap index 2b903c41f..6868b5648 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap @@ -42,11 +42,11 @@ select name f| select name from | Results: -public - public (Schema) users - public.users (Table) information_schema - information_schema (Schema) pg_catalog - pg_catalog (Schema) pg_toast - pg_toast (Schema) +_sqlx_migrations - public._sqlx_migrations (Table) -------------- From 0dc7f2b0447b622e1a35fdb7bfcbba3a2aea8c07 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 18:00:00 +0100 Subject: [PATCH 20/38] ok --- .../pgls_completions/src/providers/columns.rs | 4 +- .../pgls_completions/src/relevance/scoring.rs | 11 ++- ...__test_helper__handles_nested_queries.snap | 98 ++++--------------- ...t_helper__ignores_cols_in_from_clause.snap | 2 +- 4 files changed, 30 insertions(+), 85 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index eda6fdcd1..dc852c9f6 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -79,12 +79,12 @@ mod tests { TestCompletionsCase::new() .inside_static_statement(r#" select * from ( - + from private.audio_books ) as subquery join public.users u on u.id = subquery.id; "#) - .type_sql("select id, narrator_id<1> from private.audio_books") + .type_sql("select id, narrator_id<1>") .comment("Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.") ) .snapshot("handles_nested_queries") diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index e7cd2b097..67d123545 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -444,9 +444,16 @@ impl CompletionScore<'_> { } // "public" is the default postgres schema where users - // create objects. Prefer it by a slight bit. + // create objects. Prefer it by a slight bit, but do not suggest the literal one. if schema_name.as_str() == "public" { - self.score += 2; + match self.data { + CompletionRelevanceData::Schema(_) => { + self.score -= 2; + } + _ => { + self.score += 2; + } + } } let item_name = self.get_item_name().to_string(); diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap index 11e812a9d..0d203a4c1 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap @@ -25,141 +25,79 @@ create table private.audio_books ( -------------- select * from ( - | + | from private.audio_books ) as subquery join public.users u on u.id = subquery.id; select * from ( - s| + s| from private.audio_books ) as subquery join public.users u on u.id = subquery.id; select * from ( - select | + select | from private.audio_books ) as subquery join public.users u on u.id = subquery.id; Results: +id - private.audio_books.id (Column) +narrator_id - private.audio_books.narrator_id (Column) id - public.audio_books.id (Column) name - public.users.name (Column) narrator - public.audio_books.narrator (Column) -id - private.audio_books.id (Column) -narrator_id - private.audio_books.narrator_id (Column) -------------- select * from ( - select i| + select i| from private.audio_books ) as subquery join public.users u on u.id = subquery.id; Results: -id - public.audio_books.id (Column) +id - private.audio_books.id (Column) narrator_id - private.audio_books.narrator_id (Column) -iclikejoinsel - Schema: pg_catalog.iclikejoinsel (Function) -iclikesel - Schema: pg_catalog.iclikesel (Function) -icnlikejoinsel - Schema: pg_catalog.icnlikejoinsel (Function) +id - public.audio_books.id (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) -------------- select * from ( - select id, | + select id, | from private.audio_books ) as subquery join public.users u on u.id = subquery.id; **Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.** Results: +narrator_id - private.audio_books.narrator_id (Column) +id - private.audio_books.id (Column) name - public.users.name (Column) narrator - public.audio_books.narrator (Column) -narrator_id - private.audio_books.narrator_id (Column) id - public.audio_books.id (Column) -pg_catalog.RI_FKey_cascade_del() - Schema: pg_catalog.RI_FKey_cascade_del (Function) -------------- select * from ( - select id, n| + select id, n| from private.audio_books ) as subquery join public.users u on u.id = subquery.id; **Should prefer the one from private.audio_audiobooks, since the other tables are out of scope.** Results: +narrator_id - private.audio_books.narrator_id (Column) name - public.users.name (Column) narrator - public.audio_books.narrator (Column) -narrator_id - private.audio_books.narrator_id (Column) -name - Schema: pg_catalog.name (Function) -nameconcatoid - Schema: pg_catalog.nameconcatoid (Function) - --------------- - -select * from ( - select id, narrator_id | -) as subquery -join public.users u -on u.id = subquery.id; -select * from ( - select id, narrator_id f| -) as subquery -join public.users u -on u.id = subquery.id; -select * from ( - select id, narrator_id from | -) as subquery -join public.users u -on u.id = subquery.id; - -Results: -audio_books - public.audio_books (Table) -users - public.users (Table) -private - private (Schema) -private.audio_books - private.audio_books (Table) -information_schema - information_schema (Schema) - --------------- - -select * from ( - select id, narrator_id from p| -) as subquery -join public.users u -on u.id = subquery.id; - -Results: -public - public (Schema) -private - private (Schema) -pg_catalog - pg_catalog (Schema) -pg_toast - pg_toast (Schema) -information_schema.parameters - information_schema.parameters (Table) - --------------- - -select * from ( - select id, narrator_id from private.| -) as subquery -join public.users u -on u.id = subquery.id; - -Results: -audio_books - private.audio_books (Table) - --------------- - -select * from ( - select id, narrator_id from private.a| -) as subquery -join public.users u -on u.id = subquery.id; - -Results: -audio_books - private.audio_books (Table) +n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) +n_distinct - pg_catalog.pg_stats.n_distinct (Column) -------------- select * from ( - select id, narrator_id from private.audio_books | + select id, narrator_id | from private.audio_books ) as subquery join public.users u on u.id = subquery.id; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap index 12200f57e..a6c1b9f57 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap @@ -33,8 +33,8 @@ select * from p| **No column suggestions.** Results: -public - public (Schema) private - private (Schema) +public - public (Schema) pg_catalog - pg_catalog (Schema) pg_toast - pg_toast (Schema) information_schema.parameters - information_schema.parameters (Table) From 46c2596fd923597254ac64e03d49a507e8e6b1f7 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 22 Nov 2025 18:01:19 +0100 Subject: [PATCH 21/38] slight improvement --- ...pgls_completions__test_helper__completes_quoted_columns.snap | 2 +- ...ons__test_helper__completes_quoted_columns_with_aliases.snap | 2 +- ..._test_helper__suggests_relevant_columns_without_letters.snap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap index 4adad6c64..8ef51386a 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap @@ -99,8 +99,8 @@ private"."users - private.users (Table) select "email" from "p|" Results: -public - public (Schema) private - private (Schema) +public - public (Schema) pg_catalog - pg_catalog (Schema) pg_toast - pg_toast (Schema) information_schema"."parameters - information_schema.parameters (Table) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap index 20c8bc5af..a21fe79fe 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap @@ -88,8 +88,8 @@ pg_toast - pg_toast (Schema) select "pr"."email" from p| Results: -public - public (Schema) private - private (Schema) +public - public (Schema) pg_catalog - pg_catalog (Schema) pg_toast - pg_toast (Schema) information_schema.parameters - information_schema.parameters (Table) diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap index 6868b5648..758932c93 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap @@ -53,8 +53,8 @@ _sqlx_migrations - public._sqlx_migrations (Table) select name from u| Results: -public - public (Schema) users - public.users (Table) +public - public (Schema) information_schema.udt_privileges - information_schema.udt_privileges (Table) information_schema.usage_privileges - information_schema.usage_privileges (Table) information_schema.user_defined_types - information_schema.user_defined_types (Table) From faf379302186460614fd6c0719a221dded0794ef Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 29 Nov 2025 14:00:38 +0100 Subject: [PATCH 22/38] intermediary --- postgrestools.jsonc | 2 +- test.sql | 35 +---------------------------------- todo.txt | 14 +++++++------- 3 files changed, 9 insertions(+), 42 deletions(-) diff --git a/postgrestools.jsonc b/postgrestools.jsonc index 47d08c729..fd1149e81 100644 --- a/postgrestools.jsonc +++ b/postgrestools.jsonc @@ -17,7 +17,7 @@ // YOU CAN COMMENT ME OUT :) "db": { "host": "127.0.0.1", - "port": 5432, + "port": 54322, "username": "postgres", "password": "postgres", "database": "postgres", diff --git a/test.sql b/test.sql index b21a2e8dc..74eb49db5 100644 --- a/test.sql +++ b/test.sql @@ -1,34 +1 @@ -create table - unknown_users (id serial primary key, address text, email text); - -drop table unknown_users; - -select - * -from - unknown_users; - -sel 1; - - - -create function test_organisation_id () - returns setof text - language plpgsql - security invoker - as $$ - declre - v_organisation_id uuid; -begin - return next is(private.organisation_id(), v_organisation_id, 'should return organisation_id of token'); -end -$$; - - -create function f1() -returns void as $$ -declare b constant int; -begin - call p1(10, b); -end; -$$ language plpgsql; +select email REPLACED_TOKE pff \ No newline at end of file diff --git a/todo.txt b/todo.txt index d97e85ea5..199ea5d01 100644 --- a/todo.txt +++ b/todo.txt @@ -1,12 +1,12 @@ #### Snapshot review -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap - [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap - [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap - [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap From 4cf53b4c47ecb22c7fbb4e6cc43108ddb4654bce Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 12 Dec 2025 20:43:08 +0100 Subject: [PATCH 23/38] many changes --- .../pgls_completions/src/providers/columns.rs | 3 - .../src/relevance/filtering.rs | 6 +- .../pgls_completions/src/relevance/scoring.rs | 16 ++- ...oes_not_complete_cols_in_join_clauses.snap | 12 +- ...helper__prefers_not_mentioned_columns.snap | 4 +- ...columns_in_alter_table_and_drop_table.snap | 110 ++---------------- ...er__suggests_columns_in_insert_clause.snap | 4 +- ...per__suggests_columns_in_where_clause.snap | 20 ---- todo.txt | 8 +- 9 files changed, 37 insertions(+), 146 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index dc852c9f6..fcdedac90 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -459,9 +459,6 @@ mod tests { "#; TestCompletionsSuite::new(&pool, Some(setup)) - .with_case( - TestCompletionsCase::new().type_sql("alter table instruments drop column name"), - ) .with_case( TestCompletionsCase::new().type_sql("alter table instruments drop column name"), ) diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index 4207ad3a3..f5f185502 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -45,11 +45,7 @@ impl CompletionFilter<'_> { return None; } - if ctx - .get_node_under_cursor_content() - .is_some_and(|c| is_sanitized_token(c.as_str())) - && ctx.before_cursor_matches_kind(&["ERROR"]) - { + if ctx.before_cursor_matches_kind(&["ERROR"]) { return None; } diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 67d123545..4be0db815 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -142,15 +142,21 @@ impl CompletionScore<'_> { }, }; - match fz_matcher.fuzzy_match( - check_against.to_ascii_lowercase().as_str(), - content.to_ascii_lowercase().as_str(), - ) { + let content_lower = content.to_ascii_lowercase(); + let check_against_lower = check_against.to_ascii_lowercase(); + + match fz_matcher.fuzzy_match(check_against_lower.as_str(), content_lower.as_str()) { Some(score) => { - let scorei32: i32 = score + let mut scorei32: i32 = score .try_into() .expect("The length of the input exceeds i32 capacity"); + // give a significant bonus for prefix matches since these are much more + // likely what the user is looking for + if check_against_lower.starts_with(&content_lower) { + scorei32 += 20; + } + // the scoring value isn't linear. // here are a couple of samples: // - item: bytea_string_agg_transfn, input: n, score: 15 diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap index 811e32219..629e98bc6 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap @@ -69,10 +69,10 @@ select u.uid, p.content from a| Results: auth - auth (Schema) -information_schema - information_schema (Schema) -pg_catalog - pg_catalog (Schema) -pg_toast - pg_toast (Schema) information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) +information_schema.applicable_roles - information_schema.applicable_roles (Table) +information_schema.attributes - information_schema.attributes (Table) +information_schema - information_schema (Schema) -------------- @@ -111,10 +111,10 @@ select u.uid, p.content from auth.users u join a| Results: auth - auth (Schema) -information_schema - information_schema (Schema) -pg_catalog - pg_catalog (Schema) -pg_toast - pg_toast (Schema) information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) +information_schema.applicable_roles - information_schema.applicable_roles (Table) +information_schema.attributes - information_schema.attributes (Table) +information_schema - information_schema (Schema) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap index 52b19b693..9ccf98a9a 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap @@ -135,11 +135,11 @@ o.a - public.one.a (Column) select o.id, a, b, c, d| from public.one o join public.two on o.id = t.id; Results: -id - public.two.id (Column) d - public.two.d (Column) -o.id - public.one.id (Column) +id - public.two.id (Column) data - pg_catalog.pg_largeobject.data (Column) data_type - pg_catalog.pg_sequences.data_type (Column) +database - pg_catalog.pg_hba_file_rules.database (Column) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap index 06135cb3e..76dc39d7e 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap @@ -34,9 +34,9 @@ alter table i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -112,87 +112,9 @@ alter table i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) -pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) - --------------- - -alter table instruments | -alter table instruments d| -alter table instruments drop | - -Results: -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) -abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) - --------------- - -alter table instruments drop c| - -Results: -created_at - public.instruments.created_at (Column) -cache_size - pg_catalog.pg_sequences.cache_size (Column) -calls - pg_catalog.pg_stat_user_functions.calls (Column) -castcontext - pg_catalog.pg_cast.castcontext (Column) -castfunc - pg_catalog.pg_cast.castfunc (Column) - --------------- - -alter table instruments drop column | - -Results: -created_at - public.instruments.created_at (Column) -id - public.instruments.id (Column) -name - public.instruments.name (Column) -z - public.instruments.z (Column) -abbrev - pg_catalog.pg_timezone_abbrevs.abbrev (Column) - --------------- - -alter table instruments drop column n| - -Results: -name - public.instruments.name (Column) -n_dead_tup - pg_catalog.pg_stat_all_tables.n_dead_tup (Column) -n_distinct - pg_catalog.pg_stats.n_distinct (Column) -n_ins_since_vacuum - pg_catalog.pg_stat_all_tables.n_ins_since_vacuum (Column) -n_live_tup - pg_catalog.pg_stat_all_tables.n_live_tup (Column) - --------------- - -alter table instruments drop column name | - - - - -***Case 3:*** - -a| -alter | -alter t| -alter table | - -Results: -instruments - public.instruments (Table) -_sqlx_migrations - public._sqlx_migrations (Table) -information_schema - information_schema (Schema) -pg_catalog - pg_catalog (Schema) -pg_toast - pg_toast (Schema) - --------------- - -alter table i| - -Results: -instruments - public.instruments (Table) public - public (Schema) -information_schema - information_schema (Schema) -information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -245,16 +167,6 @@ identity_increment - information_schema.columns.identity_increment (Column) alter table instruments drop column if | alter table instruments drop column if e| - -Results: -created_at - public.instruments.created_at (Column) -name - public.instruments.name (Column) -elem_count_histogram - pg_catalog.pg_stats.elem_count_histogram (Column) -encoding - pg_catalog.pg_database.encoding (Column) -encrypted - pg_catalog.pg_stat_gssapi.encrypted (Column) - --------------- - alter table instruments drop column if exists | Results: @@ -282,7 +194,7 @@ alter table instruments drop column if exists name | -***Case 4:*** +***Case 3:*** a| alter | @@ -302,9 +214,9 @@ alter table i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -374,7 +286,7 @@ instruments - public.instruments (Table) -***Case 5:*** +***Case 4:*** a| alter | @@ -394,9 +306,9 @@ alter table i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -444,7 +356,7 @@ instruments - public.instruments (Table) -***Case 6:*** +***Case 5:*** a| alter | @@ -538,7 +450,7 @@ alter table public.instruments alter column name | -***Case 7:*** +***Case 6:*** a| alter | @@ -558,9 +470,9 @@ alter table i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -594,7 +506,7 @@ alter table instruments alter name | -***Case 8:*** +***Case 7:*** a| alter | @@ -614,9 +526,9 @@ alter table i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -654,7 +566,7 @@ alter table instruments rename name to new_col | -***Case 9:*** +***Case 8:*** a| alter | diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap index 6098c27bf..466411c8f 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap @@ -39,9 +39,9 @@ insert into i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- @@ -145,9 +145,9 @@ insert into i| Results: instruments - public.instruments (Table) -public - public (Schema) information_schema - information_schema (Schema) information_schema.information_schema_catalog_name - information_schema.information_schema_catalog_name (Table) +public - public (Schema) pg_catalog.pg_ident_file_mappings - pg_catalog.pg_ident_file_mappings (Table) -------------- diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap index 5bfb1f78a..8e5b540ae 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap @@ -22,16 +22,6 @@ create table others ( select name from instruments i join others o on i.z = o.a | select name from instruments i join others o on i.z = o.a w| - -Results: -wait_event - pg_catalog.pg_stat_activity.wait_event (Column) -wait_event_type - pg_catalog.pg_stat_activity.wait_event_type (Column) -waitstart - pg_catalog.pg_locks.waitstart (Column) -wal_buffers_full - pg_catalog.pg_stat_wal.wal_buffers_full (Column) -wal_bytes - pg_catalog.pg_stat_wal.wal_bytes (Column) - --------------- - select name from instruments i join others o on i.z = o.a where | Results: @@ -69,16 +59,6 @@ pg_catalog - pg_catalog (Schema) select name from instruments i join others o on i.z = o.a where o.a = i.| select name from instruments i join others o on i.z = o.a where o.a = i.z | select name from instruments i join others o on i.z = o.a where o.a = i.z a| - -Results: -information_schema.administrable_role_authorizations - information_schema.administrable_role_authorizations (Table) -information_schema.applicable_roles - information_schema.applicable_roles (Table) -information_schema.attributes - information_schema.attributes (Table) -information_schema.collation_character_set_applicability - information_schema.collation_character_set_applicability (Table) -pg_catalog.pg_aggregate - pg_catalog.pg_aggregate (Table) - --------------- - select name from instruments i join others o on i.z = o.a where o.a = i.z and | **should prioritize columns that aren't already mentioned** diff --git a/todo.txt b/todo.txt index 199ea5d01..93c1050c9 100644 --- a/todo.txt +++ b/todo.txt @@ -7,10 +7,10 @@ - [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap - [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap - [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap - [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap - [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap - [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap From cf0c98debabde55f2068a80e3571374c4784f208 Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 12 Dec 2025 21:20:50 +0100 Subject: [PATCH 24/38] this is gret --- .../pgls_completions/src/providers/columns.rs | 3 ++- ...er__suggests_columns_in_insert_clause.snap | 10 +++++++++ crates/pgls_treesitter/src/context/mod.rs | 21 ++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index fcdedac90..d24c38dd5 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -50,9 +50,10 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText #[cfg(test)] mod tests { + use pgls_test_utils::QueryWithCursorPosition; use sqlx::PgPool; - use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; + use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite, assert_complete_results}; #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap index 466411c8f..0d300f984 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap @@ -267,6 +267,16 @@ b - public.others.b (Column) -------------- insert into instruments (i|, name) values ('my_bass'); + +Results: +id - public.instruments.id (Column) +ident - pg_catalog.pg_backend_memory_contexts.ident (Column) +identity_cycle - information_schema.columns.identity_cycle (Column) +identity_generation - information_schema.columns.identity_generation (Column) +identity_increment - information_schema.columns.identity_increment (Column) + +-------------- + insert into instruments (id, |, name) values ('my_bass'); **`name` is already written, so z should be suggested.** diff --git a/crates/pgls_treesitter/src/context/mod.rs b/crates/pgls_treesitter/src/context/mod.rs index b6df990eb..4d1020cfe 100644 --- a/crates/pgls_treesitter/src/context/mod.rs +++ b/crates/pgls_treesitter/src/context/mod.rs @@ -153,7 +153,11 @@ impl<'a> TreesitterContext<'a> { ctx.gather_tree_context(); ctx.gather_info_from_ts_queries(); - println!("{} {:#?}", ctx.text, ctx.get_node_under_cursor_content(),); + println!("TreesitterContext: {:#?}", ctx); + println!( + "NodeUnderCursor: {:#?}", + ctx.get_node_under_cursor_content() + ); ctx } @@ -359,6 +363,10 @@ impl<'a> TreesitterContext<'a> { } } + "insert_columns" => { + self.wrapping_node_kind = Some(WrappingNode::List); + } + _ => { if let Some(clause_type) = self.get_wrapping_clause_from_current_node(current_node, &mut cursor) @@ -372,6 +380,17 @@ impl<'a> TreesitterContext<'a> { if current_node.child_count() == 0 || current_node.first_child_for_byte(self.position).is_none() { + // if the cursor is exactly at the start of a punctuation node, + // prefer the previous sibling (e.g., when cursor is at "i|," prefer "i" over ",") + let is_punctuation = matches!(current_node.kind(), "," | ")" | ";"); + if is_punctuation && current_node.start_byte() == self.position { + if let Some(prev) = current_node.prev_sibling() { + if prev.end_byte() == self.position { + self.node_under_cursor = prev; + return; + } + } + } self.node_under_cursor = current_node; return; } From 21217b52b8247ea5f7fd81728e18d627f6ac5c63 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 08:55:02 +0100 Subject: [PATCH 25/38] commitered --- .../pgls_completions/src/providers/columns.rs | 8 +++---- .../src/relevance/filtering.rs | 4 ++-- .../pgls_completions/src/relevance/scoring.rs | 2 +- ...per__suggests_columns_in_where_clause.snap | 24 +++++++++++++++++++ crates/pgls_treesitter/src/context/mod.rs | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index d24c38dd5..2e1732bab 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -53,7 +53,7 @@ mod tests { use pgls_test_utils::QueryWithCursorPosition; use sqlx::PgPool; - use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite, assert_complete_results}; + use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; #[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")] async fn handles_nested_queries(pool: PgPool) { @@ -440,9 +440,9 @@ mod tests { "select name from instruments i join others o on i.z = o.a ", ) .type_sql("where o.<1>a = <2>i.z and <3>i.id > 5;") - .comment("should respect alias speciifcation") - .comment("should not prioritize suggest columns or schemas (right side of binary expression)") - .comment("should prioritize columns that aren't already mentioned") + .comment("should respect alias speciifcation") + .comment("should not prioritize suggest columns or schemas (right side of binary expression)") + .comment("should prioritize columns that aren't already mentioned"), ) .snapshot("suggests_columns_in_where_clause") .await; diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index f5f185502..3a598c2bd 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -1,7 +1,6 @@ use pgls_schema_cache::ProcKind; use pgls_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode}; -use crate::is_sanitized_token; use super::CompletionRelevanceData; @@ -117,7 +116,8 @@ impl CompletionFilter<'_> { "column_reference_1of1", "column_reference_2of2", "column_reference_3of3", - ]) && !ctx.node_under_cursor_is_within_field(&["binary_expr_right"]) + ]) && (!ctx.node_under_cursor_is_within_field(&["binary_expr_right"]) + || ctx.has_any_qualifier()) } CompletionRelevanceData::Schema(_) => ctx.node_under_cursor_is_within_field(&[ diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 4be0db815..77f50e7a4 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -1,6 +1,6 @@ use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2}; -use pgls_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode}; +use pgls_treesitter::context::{TreesitterContext, WrappingClause}; use crate::sanitization; diff --git a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap index 8e5b540ae..a291aad9e 100644 --- a/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +++ b/crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap @@ -57,6 +57,15 @@ pg_catalog - pg_catalog (Schema) -------------- select name from instruments i join others o on i.z = o.a where o.a = i.| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) + +-------------- + select name from instruments i join others o on i.z = o.a where o.a = i.z | select name from instruments i join others o on i.z = o.a where o.a = i.z a| select name from instruments i join others o on i.z = o.a where o.a = i.z and | @@ -72,7 +81,22 @@ i.name - public.instruments.name (Column) -------------- select name from instruments i join others o on i.z = o.a where o.a = i.z and i.| + +Results: +created_at - public.instruments.created_at (Column) +id - public.instruments.id (Column) +name - public.instruments.name (Column) +z - public.instruments.z (Column) + +-------------- + select name from instruments i join others o on i.z = o.a where o.a = i.z and i.i| + +Results: +id - public.instruments.id (Column) + +-------------- + select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id | select name from instruments i join others o on i.z = o.a where o.a = i.z and i.id > | diff --git a/crates/pgls_treesitter/src/context/mod.rs b/crates/pgls_treesitter/src/context/mod.rs index 4d1020cfe..9bb3b2d0a 100644 --- a/crates/pgls_treesitter/src/context/mod.rs +++ b/crates/pgls_treesitter/src/context/mod.rs @@ -382,7 +382,7 @@ impl<'a> TreesitterContext<'a> { { // if the cursor is exactly at the start of a punctuation node, // prefer the previous sibling (e.g., when cursor is at "i|," prefer "i" over ",") - let is_punctuation = matches!(current_node.kind(), "," | ")" | ";"); + let is_punctuation = matches!(current_node.kind(), "," | ")"); if is_punctuation && current_node.start_byte() == self.position { if let Some(prev) = current_node.prev_sibling() { if prev.end_byte() == self.position { From 0accdf2e33d7f9250c801fc8351dda883c33bf3c Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 09:04:37 +0100 Subject: [PATCH 26/38] many cheeeenges --- crates/pgls_completions/src/providers/columns.rs | 1 - crates/pgls_completions/src/test_helper.rs | 2 -- todo.txt | 13 +++++-------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/crates/pgls_completions/src/providers/columns.rs b/crates/pgls_completions/src/providers/columns.rs index 2e1732bab..248c8d087 100644 --- a/crates/pgls_completions/src/providers/columns.rs +++ b/crates/pgls_completions/src/providers/columns.rs @@ -50,7 +50,6 @@ fn get_completion_text(ctx: &TreesitterContext, col: &Column) -> CompletionText #[cfg(test)] mod tests { - use pgls_test_utils::QueryWithCursorPosition; use sqlx::PgPool; use crate::test_helper::{TestCompletionsCase, TestCompletionsSuite}; diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index 51769c652..7613f2815 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -205,8 +205,6 @@ pub(crate) async fn assert_no_complete_results(query: &str, setup: Option<&str>, let params = get_test_params(&tree, &cache, query.into()); let items = complete(params); - println!("Items: {:#?}", &items[..8]); - assert_eq!(items.len(), 0) } diff --git a/todo.txt b/todo.txt index 93c1050c9..74493115c 100644 --- a/todo.txt +++ b/todo.txt @@ -11,12 +11,9 @@ - [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap - [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap - [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap -- [ ] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap +- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap -#### General Todos -- [ ] Should filter out any items where typed letters don't match at all -- [ ] From f8daaf3fa913e87d8ade61a690a65e14dd605564 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 13:44:37 +0100 Subject: [PATCH 27/38] fix: rest of tests --- .../pgls_completions/src/providers/tables.rs | 33 ++++---- .../src/relevance/filtering.rs | 36 ++++++--- crates/pgls_completions/src/test_helper.rs | 5 ++ crates/pgls_treesitter/src/context/mod.rs | 81 ++++++++++++++----- crates/pgls_treesitter_grammar/grammar.js | 2 - .../pgls_treesitter_grammar/src/grammar.json | 25 ------ 6 files changed, 106 insertions(+), 76 deletions(-) diff --git a/crates/pgls_completions/src/providers/tables.rs b/crates/pgls_completions/src/providers/tables.rs index ccfc66374..2068577fb 100644 --- a/crates/pgls_completions/src/providers/tables.rs +++ b/crates/pgls_completions/src/providers/tables.rs @@ -273,8 +273,8 @@ mod tests { assert_complete_results( format!("update {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![CompletionAssertion::LabelAndKind( - "public".into(), - CompletionItemKind::Schema, + "coos".into(), + CompletionItemKind::Table, )], None, &pool, @@ -354,10 +354,10 @@ mod tests { assert_complete_results( format!("delete from {}", QueryWithCursorPosition::cursor_marker()).as_str(), - vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), - CompletionAssertion::LabelAndKind("coos".into(), CompletionItemKind::Table), - ], + vec![CompletionAssertion::LabelAndKind( + "coos".into(), + CompletionItemKind::Table, + )], None, &pool, ) @@ -418,8 +418,13 @@ mod tests { ) .as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), + CompletionAssertion::LabelAndKind( + "information_schema".into(), + CompletionItemKind::Schema, + ), + CompletionAssertion::LabelAndKind("pg_catalog".into(), CompletionItemKind::Schema), + CompletionAssertion::LabelAndKind("pg_toast".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("posts".into(), CompletionItemKind::Table), // self-join CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), ], @@ -454,7 +459,6 @@ mod tests { assert_complete_results( format!("alter table {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("posts".into(), CompletionItemKind::Table), CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), @@ -471,7 +475,6 @@ mod tests { ) .as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("posts".into(), CompletionItemKind::Table), CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), @@ -484,7 +487,6 @@ mod tests { assert_complete_results( format!("drop table {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("posts".into(), CompletionItemKind::Table), CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), @@ -501,7 +503,6 @@ mod tests { ) .as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("posts".into(), CompletionItemKind::Table), // self-join CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), @@ -529,7 +530,6 @@ mod tests { assert_complete_results( format!("insert into {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), ], @@ -561,7 +561,6 @@ mod tests { ) .as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), ], @@ -686,8 +685,14 @@ mod tests { ) .as_str(), vec![ - CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), + CompletionAssertion::LabelAndKind( + "information_schema".into(), + CompletionItemKind::Schema, + ), + CompletionAssertion::LabelAndKind("pg_catalog".into(), CompletionItemKind::Schema), + CompletionAssertion::LabelAndKind("pg_toast".into(), CompletionItemKind::Schema), + CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), ], None, &pool, diff --git a/crates/pgls_completions/src/relevance/filtering.rs b/crates/pgls_completions/src/relevance/filtering.rs index 3a598c2bd..f273d0591 100644 --- a/crates/pgls_completions/src/relevance/filtering.rs +++ b/crates/pgls_completions/src/relevance/filtering.rs @@ -1,7 +1,6 @@ use pgls_schema_cache::ProcKind; use pgls_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode}; - use super::CompletionRelevanceData; #[derive(Debug)] @@ -144,17 +143,23 @@ impl CompletionFilter<'_> { ]) && matches!(f.kind, ProcKind::Aggregate)) } - CompletionRelevanceData::Table(_) => ctx.node_under_cursor_is_within_field(&[ - "object_reference_1of1", - "object_reference_1of2", - "object_reference_2of2", - "object_reference_2of3", - "table_reference_1of1", - "column_reference_1of1", - "column_reference_1of2", - "column_reference_2of2", - ]), - + CompletionRelevanceData::Table(_) => { + ctx.node_under_cursor_is_within_field(&[ + "object_reference_1of1", + "object_reference_1of2", + "object_reference_2of2", + "object_reference_2of3", + "table_reference_1of1", + "column_reference_1of1", + "column_reference_1of2", + "column_reference_2of2", + ]) && !ctx.history_ends_with(&[ + "update", + "assignment", + "column_reference", + "any_identifier", + ]) + } _ => false, }, @@ -170,7 +175,12 @@ impl CompletionFilter<'_> { .map(|clause| { match self.data { CompletionRelevanceData::Table(_) => match clause { - WrappingClause::From | WrappingClause::Update => true, + WrappingClause::From => true, + + WrappingClause::Update => ctx + .wrapping_node_kind + .as_ref() + .is_none_or(|n| n != &WrappingNode::Assignment), WrappingClause::RevokeStatement | WrappingClause::GrantStatement => ctx .history_ends_with(&[ diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index 7613f2815..ff53c6afa 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -186,6 +186,11 @@ pub(crate) async fn assert_complete_results( items.len() ); + if !items.is_empty() { + let max_len = std::cmp::min(items.len(), 5); + println!("Completion Items: {:#?}", &items[..max_len]); + } + for item in &items { for assertion in ¬_existing { assertion.assert(item); diff --git a/crates/pgls_treesitter/src/context/mod.rs b/crates/pgls_treesitter/src/context/mod.rs index 9bb3b2d0a..92ea2cf0a 100644 --- a/crates/pgls_treesitter/src/context/mod.rs +++ b/crates/pgls_treesitter/src/context/mod.rs @@ -153,12 +153,6 @@ impl<'a> TreesitterContext<'a> { ctx.gather_tree_context(); ctx.gather_info_from_ts_queries(); - println!("TreesitterContext: {:#?}", ctx); - println!( - "NodeUnderCursor: {:#?}", - ctx.get_node_under_cursor_content() - ); - ctx } @@ -274,14 +268,14 @@ impl<'a> TreesitterContext<'a> { if chars .nth(self.position) - .is_some_and(|c| !c.is_ascii_whitespace() && !&[';', ')'].contains(&c)) + .is_some_and(|c| c.is_ascii_whitespace() || [';', ')', ',', '('].contains(&c)) { - self.position = cmp::min(self.position, self.text.len().saturating_sub(1)); - } else { self.position = cmp::min( self.position.saturating_sub(1), self.text.len().saturating_sub(1), ); + } else { + self.position = cmp::min(self.position, self.text.len().saturating_sub(1)); } cursor.goto_first_child_for_byte(self.position); @@ -380,17 +374,6 @@ impl<'a> TreesitterContext<'a> { if current_node.child_count() == 0 || current_node.first_child_for_byte(self.position).is_none() { - // if the cursor is exactly at the start of a punctuation node, - // prefer the previous sibling (e.g., when cursor is at "i|," prefer "i" over ",") - let is_punctuation = matches!(current_node.kind(), "," | ")"); - if is_punctuation && current_node.start_byte() == self.position { - if let Some(prev) = current_node.prev_sibling() { - if prev.end_byte() == self.position { - self.node_under_cursor = prev; - return; - } - } - } self.node_under_cursor = current_node; return; } @@ -641,8 +624,8 @@ impl<'a> TreesitterContext<'a> { /// Verifies whether the node_under_cursor has the passed in ancestors in the right order. /// Note that you need to pass in the ancestors in the order as they would appear in the tree: /// - /// If the tree shows `relation > object_reference > any_identifier` and the "any_identifier" is a leaf node, - /// you need to pass `&["relation", "object_reference"]`. + /// If the tree shows `relation > object_reference > any_identifier` + /// you need to pass `&["relation", "object_reference", "any_identifier"]`. pub fn history_ends_with(&self, expected_ancestors: &[&'static str]) -> bool { self.scope_tracker .current() @@ -1115,4 +1098,58 @@ mod tests { // should simply not panic let _ = TreesitterContext::new(params); } + + #[test] + fn corrects_the_position_accordingly() { + let query = "select id, email from some_foo(param1, param2);"; + // 01234567890123456789012345678901234567890123456 + // 0 10 20 30 40 + + let tree = get_tree(query); + + struct TestCase { + cursor_position: u32, + context_position: usize, + } + + let cases: Vec = vec![ + TestCase { + // moves from ',' to 'l' of 'email' + cursor_position: 9, + context_position: 8, + }, + TestCase { + // stays on 'e' of 'email' + cursor_position: 11, + context_position: 11, + }, + TestCase { + // moves from '(' to 'o' of 'some_foo' + cursor_position: 30, + context_position: 29, + }, + TestCase { + // moves from ')' to '2' of 'param2' + cursor_position: 45, + context_position: 44, + }, + ]; + + for case in cases { + let params = TreeSitterContextParams { + position: case.cursor_position.into(), + text: &query, + tree: &tree, + }; + + // should simply not panic + let ctx = TreesitterContext::new(params); + + assert_eq!( + ctx.position, case.context_position, + "received {} but expected {}", + ctx.position, case.context_position + ); + } + } } diff --git a/crates/pgls_treesitter_grammar/grammar.js b/crates/pgls_treesitter_grammar/grammar.js index 8a1f91c52..1b2a0e3df 100644 --- a/crates/pgls_treesitter_grammar/grammar.js +++ b/crates/pgls_treesitter_grammar/grammar.js @@ -2241,8 +2241,6 @@ module.exports = grammar({ ) ), - assignment_list: ($) => seq($.assignment, repeat(seq(",", $.assignment))), - _set_values: ($) => seq($.keyword_set, comma_list($.assignment, true)), _column_list: ($) => paren_list(alias($._column, $.column), true), diff --git a/crates/pgls_treesitter_grammar/src/grammar.json b/crates/pgls_treesitter_grammar/src/grammar.json index 683c519ec..600f9a3e0 100644 --- a/crates/pgls_treesitter_grammar/src/grammar.json +++ b/crates/pgls_treesitter_grammar/src/grammar.json @@ -13838,31 +13838,6 @@ } ] }, - "assignment_list": { - "type": "SEQ", - "members": [ - { - "type": "SYMBOL", - "name": "assignment" - }, - { - "type": "REPEAT", - "content": { - "type": "SEQ", - "members": [ - { - "type": "STRING", - "value": "," - }, - { - "type": "SYMBOL", - "name": "assignment" - } - ] - } - } - ] - }, "_set_values": { "type": "SEQ", "members": [ From 7ce97c65dc0780383e0f69850efadf56cde6d037 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 13:53:09 +0100 Subject: [PATCH 28/38] removed many stuffs --- crates/pgls_completions/src/builder.rs | 6 --- .../pgls_completions/src/providers/schemas.rs | 8 +-- .../pgls_completions/src/relevance/scoring.rs | 52 ------------------- crates/pgls_completions/src/test_helper.rs | 5 -- 4 files changed, 4 insertions(+), 67 deletions(-) diff --git a/crates/pgls_completions/src/builder.rs b/crates/pgls_completions/src/builder.rs index 1bb0075a7..8a54b185e 100644 --- a/crates/pgls_completions/src/builder.rs +++ b/crates/pgls_completions/src/builder.rs @@ -25,12 +25,6 @@ pub(crate) struct CompletionBuilder<'a> { impl<'a> CompletionBuilder<'a> { pub fn new(ctx: &'a TreesitterContext) -> Self { - println!( - "is sanitized: {:#?}", - ctx.get_node_under_cursor_content() - .map(|txt| sanitization::is_sanitized_token(txt.as_str())) - ); - CompletionBuilder { items: vec![], ctx } } diff --git a/crates/pgls_completions/src/providers/schemas.rs b/crates/pgls_completions/src/providers/schemas.rs index 410205f89..a2f263fe8 100644 --- a/crates/pgls_completions/src/providers/schemas.rs +++ b/crates/pgls_completions/src/providers/schemas.rs @@ -59,7 +59,8 @@ mod tests { assert_complete_results( format!("select * from {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ - CompletionAssertion::LabelAndKind("public".to_string(), CompletionItemKind::Schema), + // users table still preferred over schemas + CompletionAssertion::LabelAndKind("users".to_string(), CompletionItemKind::Table), CompletionAssertion::LabelAndKind("auth".to_string(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind( "internal".to_string(), @@ -69,8 +70,7 @@ mod tests { "private".to_string(), CompletionItemKind::Schema, ), - // users table still preferred over system schemas - CompletionAssertion::LabelAndKind("users".to_string(), CompletionItemKind::Table), + // system schemas have lowest preference CompletionAssertion::LabelAndKind( "information_schema".to_string(), CompletionItemKind::Schema, @@ -110,8 +110,8 @@ mod tests { ) .as_str(), vec![ - CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), CompletionAssertion::LabelAndKind("ultimate".into(), CompletionItemKind::Schema), + CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), ], Some(setup), &pool, diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 77f50e7a4..0fd3a4c49 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -33,52 +33,15 @@ impl CompletionScore<'_> { } pub fn calc_score(&mut self, ctx: &TreesitterContext) { - let case = (matches!(self.data, CompletionRelevanceData::Schema(_)) - && self.get_schema_name().is_some_and(|s| s == "pg_catalog")) - || (matches!(self.data, CompletionRelevanceData::Table(_)) - && self.get_table_name().is_some_and(|s| s == "parameters")); - - if case { - println!("checking {}", self.get_fully_qualified_name()) - } - self.check_is_user_defined(); - if case { - println!("{} after user-defined check", self.score); - } - self.check_matches_schema(ctx); - if case { - println!("{} after schema match check", self.score); - } self.check_matches_query_input(ctx); - if case { - println!("{} after query input match check", self.score); - } self.check_is_invocation(ctx); - if case { - println!("{} after invocation check", self.score); - } self.check_matching_clause_type(ctx); - if case { - println!("{} after clause type check", self.score); - } self.check_without_content(ctx); - if case { - println!("{} after wrapping node check", self.score); - } self.check_relations_in_stmt(ctx); - if case { - println!("{} after relations in stmt check", self.score); - } self.check_columns_in_stmt(ctx); - if case { - println!("{} after columns in stmt check", self.score); - } self.check_is_not_wellknown_migration(ctx); - if case { - println!("{} after well-known migration check", self.score); - } } fn check_matches_query_input(&mut self, ctx: &TreesitterContext) { @@ -168,21 +131,6 @@ impl CompletionScore<'_> { } else { scorei32 / 3 }; - - if matches!(self.data, CompletionRelevanceData::Schema(_)) - && self.get_schema_name().is_some_and(|s| s == "pg_catalog") - { - println!("Debug: Schema pg_catalog match score {}", self.score); - } - - if matches!(self.data, CompletionRelevanceData::Table(_)) - && self.get_table_name().is_some_and(|s| s == "parameters") - { - println!( - "Debug: Table information_schema.parameters match score {}", - self.score - ); - } } None => self.skip = true, } diff --git a/crates/pgls_completions/src/test_helper.rs b/crates/pgls_completions/src/test_helper.rs index ff53c6afa..7613f2815 100644 --- a/crates/pgls_completions/src/test_helper.rs +++ b/crates/pgls_completions/src/test_helper.rs @@ -186,11 +186,6 @@ pub(crate) async fn assert_complete_results( items.len() ); - if !items.is_empty() { - let max_len = std::cmp::min(items.len(), 5); - println!("Completion Items: {:#?}", &items[..max_len]); - } - for item in &items { for assertion in ¬_existing { assertion.assert(item); From 9e29117d4dcdcac8e7965e66527badc089da934a Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 14:00:32 +0100 Subject: [PATCH 29/38] squieliex --- ...d5a53712d68afd273423f9310cd793586f532.json | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 .sqlx/query-b869d517301aaf69d382f092c09d5a53712d68afd273423f9310cd793586f532.json diff --git a/.sqlx/query-b869d517301aaf69d382f092c09d5a53712d68afd273423f9310cd793586f532.json b/.sqlx/query-b869d517301aaf69d382f092c09d5a53712d68afd273423f9310cd793586f532.json deleted file mode 100644 index c460f458d..000000000 --- a/.sqlx/query-b869d517301aaf69d382f092c09d5a53712d68afd273423f9310cd793586f532.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n(\nwith foreign_keys as (\n select\n cl.relnamespace::regnamespace::text as schema_name,\n cl.relname as table_name,\n cl.oid as table_oid,\n ct.conname as fkey_name,\n ct.conkey as col_attnums\n from\n pg_catalog.pg_constraint ct\n join pg_catalog.pg_class cl -- fkey owning table\n on ct.conrelid = cl.oid\n left join pg_catalog.pg_depend d\n on d.objid = cl.oid\n and d.deptype = 'e'\n where\n ct.contype = 'f' -- foreign key constraints\n and d.objid is null -- exclude tables that are dependencies of extensions\n and cl.relnamespace::regnamespace::text not in (\n 'pg_catalog', 'information_schema', 'auth', 'storage', 'vault', 'extensions'\n )\n),\nindex_ as (\n select\n pi.indrelid as table_oid,\n indexrelid::regclass as index_,\n string_to_array(indkey::text, ' ')::smallint[] as col_attnums\n from\n pg_catalog.pg_index pi\n where\n indisvalid\n)\nselect\n 'unindexed_foreign_keys' as \"name!\",\n 'Unindexed foreign keys' as \"title!\",\n 'INFO' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Identifies foreign key constraints without a covering index, which can impact database performance.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has a foreign key \\`%s\\` without a covering index. This can lead to suboptimal query performance.',\n fk.schema_name,\n fk.table_name,\n fk.fkey_name\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys' as \"remediation!\",\n jsonb_build_object(\n 'schema', fk.schema_name,\n 'name', fk.table_name,\n 'type', 'table',\n 'fkey_name', fk.fkey_name,\n 'fkey_columns', fk.col_attnums\n ) as \"metadata!\",\n format('unindexed_foreign_keys_%s_%s_%s', fk.schema_name, fk.table_name, fk.fkey_name) as \"cache_key!\"\nfrom\n foreign_keys fk\n left join index_ idx\n on fk.table_oid = idx.table_oid\n and fk.col_attnums = idx.col_attnums[1:array_length(fk.col_attnums, 1)]\n left join pg_catalog.pg_depend dep\n on idx.table_oid = dep.objid\n and dep.deptype = 'e'\nwhere\n idx.index_ is null\n and fk.schema_name not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null -- exclude tables owned by extensions\norder by\n fk.schema_name,\n fk.table_name,\n fk.fkey_name)\nunion all\n(\nselect\n 'auth_users_exposed' as \"name!\",\n 'Exposed Auth Users' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects if auth.users is exposed to anon or authenticated roles via a view or materialized view in schemas exposed to PostgREST, potentially compromising user data security.' as \"description!\",\n format(\n 'View/Materialized View \"%s\" in the public schema may expose \\`auth.users\\` data to anon or authenticated roles.',\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0002_auth_users_exposed' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'view',\n 'exposed_to', array_remove(array_agg(DISTINCT case when pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') then 'anon' when pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') then 'authenticated' end), null)\n ) as \"metadata!\",\n format('auth_users_exposed_%s_%s', n.nspname, c.relname) as \"cache_key!\"\nfrom\n -- Identify the oid for auth.users\n pg_catalog.pg_class auth_users_pg_class\n join pg_catalog.pg_namespace auth_users_pg_namespace\n on auth_users_pg_class.relnamespace = auth_users_pg_namespace.oid\n and auth_users_pg_class.relname = 'users'\n and auth_users_pg_namespace.nspname = 'auth'\n -- Depends on auth.users\n join pg_catalog.pg_depend d\n on d.refobjid = auth_users_pg_class.oid\n join pg_catalog.pg_rewrite r\n on r.oid = d.objid\n join pg_catalog.pg_class c\n on c.oid = r.ev_class\n join pg_catalog.pg_namespace n\n on n.oid = c.relnamespace\n join pg_catalog.pg_class pg_class_auth_users\n on d.refobjid = pg_class_auth_users.oid\nwhere\n d.deptype = 'n'\n and (\n pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')\n or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT')\n )\n and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ',')))))\n -- Exclude self\n and c.relname <> '0002_auth_users_exposed'\n -- There are 3 insecure configurations\n and\n (\n -- Materialized views don't support RLS so this is insecure by default\n (c.relkind in ('m')) -- m for materialized view\n or\n -- Standard View, accessible to anon or authenticated that is security_definer\n (\n c.relkind = 'v' -- v for view\n -- Exclude security invoker views\n and not (\n lower(coalesce(c.reloptions::text,'{}'))::text[]\n && array[\n 'security_invoker=1',\n 'security_invoker=true',\n 'security_invoker=yes',\n 'security_invoker=on'\n ]\n )\n )\n or\n -- Standard View, security invoker, but no RLS enabled on auth.users\n (\n c.relkind in ('v') -- v for view\n -- is security invoker\n and (\n lower(coalesce(c.reloptions::text,'{}'))::text[]\n && array[\n 'security_invoker=1',\n 'security_invoker=true',\n 'security_invoker=yes',\n 'security_invoker=on'\n ]\n )\n and not pg_class_auth_users.relrowsecurity\n )\n )\ngroup by\n n.nspname,\n c.relname,\n c.oid)\nunion all\n(\nwith policies as (\n select\n nsp.nspname as schema_name,\n pb.tablename as table_name,\n pc.relrowsecurity as is_rls_active,\n polname as policy_name,\n polpermissive as is_permissive, -- if not, then restrictive\n (select array_agg(r::regrole) from unnest(polroles) as x(r)) as roles,\n case polcmd\n when 'r' then 'SELECT'\n when 'a' then 'INSERT'\n when 'w' then 'UPDATE'\n when 'd' then 'DELETE'\n when '*' then 'ALL'\n end as command,\n qual,\n with_check\n from\n pg_catalog.pg_policy pa\n join pg_catalog.pg_class pc\n on pa.polrelid = pc.oid\n join pg_catalog.pg_namespace nsp\n on pc.relnamespace = nsp.oid\n join pg_catalog.pg_policies pb\n on pc.relname = pb.tablename\n and nsp.nspname = pb.schemaname\n and pa.polname = pb.policyname\n)\nselect\n 'auth_rls_initplan' as \"name!\",\n 'Auth RLS Initialization Plan' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Detects if calls to \\`current_setting()\\` and \\`auth.()\\` in RLS policies are being unnecessarily re-evaluated for each row' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has a row level security policy \\`%s\\` that re-evaluates current_setting() or auth.() for each row. This produces suboptimal query performance at scale. Resolve the issue by replacing \\`auth.()\\` with \\`(select auth.())\\`. See [docs](https://supabase.com/docs/guides/database/postgres/row-level-security#call-functions-with-select) for more info.',\n schema_name,\n table_name,\n policy_name\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0003_auth_rls_initplan' as \"remediation!\",\n jsonb_build_object(\n 'schema', schema_name,\n 'name', table_name,\n 'type', 'table'\n ) as \"metadata!\",\n format('auth_rls_init_plan_%s_%s_%s', schema_name, table_name, policy_name) as \"cache_key!\"\nfrom\n policies\nwhere\n is_rls_active\n -- NOTE: does not include realtime in support of monitoring policies on realtime.messages\n and schema_name not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and (\n -- Example: auth.uid()\n (\n qual like '%auth.uid()%'\n and lower(qual) not like '%select auth.uid()%'\n )\n or (\n qual like '%auth.jwt()%'\n and lower(qual) not like '%select auth.jwt()%'\n )\n or (\n qual like '%auth.role()%'\n and lower(qual) not like '%select auth.role()%'\n )\n or (\n qual like '%auth.email()%'\n and lower(qual) not like '%select auth.email()%'\n )\n or (\n qual like '%current\\_setting(%)%'\n and lower(qual) not like '%select current\\_setting(%)%'\n )\n or (\n with_check like '%auth.uid()%'\n and lower(with_check) not like '%select auth.uid()%'\n )\n or (\n with_check like '%auth.jwt()%'\n and lower(with_check) not like '%select auth.jwt()%'\n )\n or (\n with_check like '%auth.role()%'\n and lower(with_check) not like '%select auth.role()%'\n )\n or (\n with_check like '%auth.email()%'\n and lower(with_check) not like '%select auth.email()%'\n )\n or (\n with_check like '%current\\_setting(%)%'\n and lower(with_check) not like '%select current\\_setting(%)%'\n )\n ))\nunion all\n(\nselect\n 'no_primary_key' as \"name!\",\n 'No Primary Key' as \"title!\",\n 'INFO' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Detects if a table does not have a primary key. Tables without a primary key can be inefficient to interact with at scale.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` does not have a primary key',\n pgns.nspname,\n pgc.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key' as \"remediation!\",\n jsonb_build_object(\n 'schema', pgns.nspname,\n 'name', pgc.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'no_primary_key_%s_%s',\n pgns.nspname,\n pgc.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class pgc\n join pg_catalog.pg_namespace pgns\n on pgns.oid = pgc.relnamespace\n left join pg_catalog.pg_index pgi\n on pgi.indrelid = pgc.oid\n left join pg_catalog.pg_depend dep\n on pgc.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n pgc.relkind = 'r' -- regular tables\n and pgns.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null -- exclude tables owned by extensions\ngroup by\n pgc.oid,\n pgns.nspname,\n pgc.relname\nhaving\n max(coalesce(pgi.indisprimary, false)::int) = 0)\nunion all\n(\nselect\n 'unused_index' as \"name!\",\n 'Unused Index' as \"title!\",\n 'INFO' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Detects if an index has never been used and may be a candidate for removal.' as \"description!\",\n format(\n 'Index \\`%s\\` on table \\`%s.%s\\` has not been used',\n psui.indexrelname,\n psui.schemaname,\n psui.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0005_unused_index' as \"remediation!\",\n jsonb_build_object(\n 'schema', psui.schemaname,\n 'name', psui.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'unused_index_%s_%s_%s',\n psui.schemaname,\n psui.relname,\n psui.indexrelname\n ) as \"cache_key!\"\n\nfrom\n pg_catalog.pg_stat_user_indexes psui\n join pg_catalog.pg_index pi\n on psui.indexrelid = pi.indexrelid\n left join pg_catalog.pg_depend dep\n on psui.relid = dep.objid\n and dep.deptype = 'e'\nwhere\n psui.idx_scan = 0\n and not pi.indisunique\n and not pi.indisprimary\n and dep.objid is null -- exclude tables owned by extensions\n and psui.schemaname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n ))\nunion all\n(\nselect\n 'multiple_permissive_policies' as \"name!\",\n 'Multiple Permissive Policies' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Detects if multiple permissive row level security policies are present on a table for the same \\`role\\` and \\`action\\` (e.g. insert). Multiple permissive policies are suboptimal for performance as each policy must be executed for every relevant query.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has multiple permissive policies for role \\`%s\\` for action \\`%s\\`. Policies include \\`%s\\`',\n n.nspname,\n c.relname,\n r.rolname,\n act.cmd,\n array_agg(p.polname order by p.polname)\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0006_multiple_permissive_policies' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'multiple_permissive_policies_%s_%s_%s_%s',\n n.nspname,\n c.relname,\n r.rolname,\n act.cmd\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_policy p\n join pg_catalog.pg_class c\n on p.polrelid = c.oid\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\n join pg_catalog.pg_roles r\n on p.polroles @> array[r.oid]\n or p.polroles = array[0::oid]\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e',\n lateral (\n select x.cmd\n from unnest((\n select\n case p.polcmd\n when 'r' then array['SELECT']\n when 'a' then array['INSERT']\n when 'w' then array['UPDATE']\n when 'd' then array['DELETE']\n when '*' then array['SELECT', 'INSERT', 'UPDATE', 'DELETE']\n else array['ERROR']\n end as actions\n )) x(cmd)\n ) act(cmd)\nwhere\n c.relkind = 'r' -- regular tables\n and p.polpermissive -- policy is permissive\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and r.rolname not like 'pg_%'\n and r.rolname not like 'supabase%admin'\n and not r.rolbypassrls\n and dep.objid is null -- exclude tables owned by extensions\ngroup by\n n.nspname,\n c.relname,\n r.rolname,\n act.cmd\nhaving\n count(1) > 1)\nunion all\n(\nselect\n 'policy_exists_rls_disabled' as \"name!\",\n 'Policy Exists RLS Disabled' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects cases where row level security (RLS) policies have been created, but RLS has not been enabled for the underlying table.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has RLS policies but RLS is not enabled on the table. Policies include %s.',\n n.nspname,\n c.relname,\n array_agg(p.polname order by p.polname)\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0007_policy_exists_rls_disabled' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'policy_exists_rls_disabled_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_policy p\n join pg_catalog.pg_class c\n on p.polrelid = c.oid\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n c.relkind = 'r' -- regular tables\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n -- RLS is disabled\n and not c.relrowsecurity\n and dep.objid is null -- exclude tables owned by extensions\ngroup by\n n.nspname,\n c.relname)\nunion all\n(\nselect\n 'rls_enabled_no_policy' as \"name!\",\n 'RLS Enabled No Policy' as \"title!\",\n 'INFO' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects cases where row level security (RLS) has been enabled on a table but no RLS policies have been created.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has RLS enabled, but no policies exist',\n n.nspname,\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0008_rls_enabled_no_policy' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'rls_enabled_no_policy_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class c\n left join pg_catalog.pg_policy p\n on p.polrelid = c.oid\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n c.relkind = 'r' -- regular tables\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n -- RLS is enabled\n and c.relrowsecurity\n and p.polname is null\n and dep.objid is null -- exclude tables owned by extensions\ngroup by\n n.nspname,\n c.relname)\nunion all\n(\nselect\n 'duplicate_index' as \"name!\",\n 'Duplicate Index' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Detects cases where two ore more identical indexes exist.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has identical indexes %s. Drop all except one of them',\n n.nspname,\n c.relname,\n array_agg(pi.indexname order by pi.indexname)\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0009_duplicate_index' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', case\n when c.relkind = 'r' then 'table'\n when c.relkind = 'm' then 'materialized view'\n else 'ERROR'\n end,\n 'indexes', array_agg(pi.indexname order by pi.indexname)\n ) as \"metadata!\",\n format(\n 'duplicate_index_%s_%s_%s',\n n.nspname,\n c.relname,\n array_agg(pi.indexname order by pi.indexname)\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_indexes pi\n join pg_catalog.pg_namespace n\n on n.nspname = pi.schemaname\n join pg_catalog.pg_class c\n on pi.tablename = c.relname\n and n.oid = c.relnamespace\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n c.relkind in ('r', 'm') -- tables and materialized views\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null -- exclude tables owned by extensions\ngroup by\n n.nspname,\n c.relkind,\n c.relname,\n replace(pi.indexdef, pi.indexname, '')\nhaving\n count(*) > 1)\nunion all\n(\nselect\n 'security_definer_view' as \"name!\",\n 'Security Definer View' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects views defined with the SECURITY DEFINER property. These views enforce Postgres permissions and row level security policies (RLS) of the view creator, rather than that of the querying user' as \"description!\",\n format(\n 'View \\`%s.%s\\` is defined with the SECURITY DEFINER property',\n n.nspname,\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0010_security_definer_view' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'view'\n ) as \"metadata!\",\n format(\n 'security_definer_view_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on n.oid = c.relnamespace\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n c.relkind = 'v'\n and (\n pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')\n or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT')\n )\n and substring(pg_catalog.version() from 'PostgreSQL ([0-9]+)') >= '15' -- security invoker was added in pg15\n and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ',')))))\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null -- exclude views owned by extensions\n and not (\n lower(coalesce(c.reloptions::text,'{}'))::text[]\n && array[\n 'security_invoker=1',\n 'security_invoker=true',\n 'security_invoker=yes',\n 'security_invoker=on'\n ]\n ))\nunion all\n(\nselect\n 'function_search_path_mutable' as \"name!\",\n 'Function Search Path Mutable' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects functions where the search_path parameter is not set.' as \"description!\",\n format(\n 'Function \\`%s.%s\\` has a role mutable search_path',\n n.nspname,\n p.proname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', p.proname,\n 'type', 'function'\n ) as \"metadata!\",\n format(\n 'function_search_path_mutable_%s_%s_%s',\n n.nspname,\n p.proname,\n md5(p.prosrc) -- required when function is polymorphic\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_proc p\n join pg_catalog.pg_namespace n\n on p.pronamespace = n.oid\n left join pg_catalog.pg_depend dep\n on p.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null -- exclude functions owned by extensions\n -- Search path not set\n and not exists (\n select 1\n from unnest(coalesce(p.proconfig, '{}')) as config\n where config like 'search_path=%'\n ))\nunion all\n(\nselect\n 'rls_disabled_in_public' as \"name!\",\n 'RLS Disabled in Public' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects cases where row level security (RLS) has not been enabled on tables in schemas exposed to PostgREST' as \"description!\",\n format(\n 'Table \\`%s.%s\\` is public, but RLS has not been enabled.',\n n.nspname,\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0013_rls_disabled_in_public' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'rls_disabled_in_public_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\nwhere\n c.relkind = 'r' -- regular tables\n -- RLS is disabled\n and not c.relrowsecurity\n and (\n pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')\n or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT')\n )\n and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ',')))))\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n ))\nunion all\n(\nselect\n 'extension_in_public' as \"name!\",\n 'Extension in Public' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects extensions installed in the \\`public\\` schema.' as \"description!\",\n format(\n 'Extension \\`%s\\` is installed in the public schema. Move it to another schema.',\n pe.extname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0014_extension_in_public' as \"remediation!\",\n jsonb_build_object(\n 'schema', pe.extnamespace::regnamespace,\n 'name', pe.extname,\n 'type', 'extension'\n ) as \"metadata!\",\n format(\n 'extension_in_public_%s',\n pe.extname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_extension pe\nwhere\n -- plpgsql is installed by default in public and outside user control\n -- confirmed safe\n pe.extname not in ('plpgsql')\n -- Scoping this to public is not optimal. Ideally we would use the postgres\n -- search path. That currently isn't available via SQL. In other lints\n -- we have used has_schema_privilege('anon', 'extensions', 'USAGE') but that\n -- is not appropriate here as it would evaluate true for the extensions schema\n and pe.extnamespace::regnamespace::text = 'public')\nunion all\n(\nwith policies as (\n select\n nsp.nspname as schema_name,\n pb.tablename as table_name,\n polname as policy_name,\n qual,\n with_check\n from\n pg_catalog.pg_policy pa\n join pg_catalog.pg_class pc\n on pa.polrelid = pc.oid\n join pg_catalog.pg_namespace nsp\n on pc.relnamespace = nsp.oid\n join pg_catalog.pg_policies pb\n on pc.relname = pb.tablename\n and nsp.nspname = pb.schemaname\n and pa.polname = pb.policyname\n)\nselect\n 'rls_references_user_metadata' as \"name!\",\n 'RLS references user metadata' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects when Supabase Auth user_metadata is referenced insecurely in a row level security (RLS) policy.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has a row level security policy \\`%s\\` that references Supabase Auth \\`user_metadata\\`. \\`user_metadata\\` is editable by end users and should never be used in a security context.',\n schema_name,\n table_name,\n policy_name\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0015_rls_references_user_metadata' as \"remediation!\",\n jsonb_build_object(\n 'schema', schema_name,\n 'name', table_name,\n 'type', 'table'\n ) as \"metadata!\",\n format('rls_references_user_metadata_%s_%s_%s', schema_name, table_name, policy_name) as \"cache_key!\"\nfrom\n policies\nwhere\n schema_name not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and (\n -- Example: auth.jwt() -> 'user_metadata'\n -- False positives are possible, but it isn't practical to string match\n -- If false positive rate is too high, this expression can iterate\n qual like '%auth.jwt()%user_metadata%'\n or qual like '%current_setting(%request.jwt.claims%)%user_metadata%'\n or with_check like '%auth.jwt()%user_metadata%'\n or with_check like '%current_setting(%request.jwt.claims%)%user_metadata%'\n ))\nunion all\n(\nselect\n 'materialized_view_in_api' as \"name!\",\n 'Materialized View in API' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects materialized views that are accessible over the Data APIs.' as \"description!\",\n format(\n 'Materialized view \\`%s.%s\\` is selectable by anon or authenticated roles',\n n.nspname,\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0016_materialized_view_in_api' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'materialized view'\n ) as \"metadata!\",\n format(\n 'materialized_view_in_api_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on n.oid = c.relnamespace\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n c.relkind = 'm'\n and (\n pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')\n or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT')\n )\n and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ',')))))\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null)\nunion all\n(\nselect\n 'foreign_table_in_api' as \"name!\",\n 'Foreign Table in API' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects foreign tables that are accessible over APIs. Foreign tables do not respect row level security policies.' as \"description!\",\n format(\n 'Foreign table \\`%s.%s\\` is accessible over APIs',\n n.nspname,\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0017_foreign_table_in_api' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'foreign table'\n ) as \"metadata!\",\n format(\n 'foreign_table_in_api_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on n.oid = c.relnamespace\n left join pg_catalog.pg_depend dep\n on c.oid = dep.objid\n and dep.deptype = 'e'\nwhere\n c.relkind = 'f'\n and (\n pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')\n or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT')\n )\n and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ',')))))\n and n.nspname not in (\n '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgmq', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault'\n )\n and dep.objid is null)\nunion all\n(\nselect\n 'unsupported_reg_types' as \"name!\",\n 'Unsupported reg types' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Identifies columns using unsupported reg* types outside pg_catalog schema, which prevents database upgrades using pg_upgrade.' as \"description!\",\n format(\n 'Table \\`%s.%s\\` has a column \\`%s\\` with unsupported reg* type \\`%s\\`.',\n n.nspname,\n c.relname,\n a.attname,\n t.typname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=unsupported_reg_types' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'column', a.attname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'unsupported_reg_types_%s_%s_%s',\n n.nspname,\n c.relname,\n a.attname\n ) AS cache_key\nfrom\n pg_catalog.pg_attribute a\n join pg_catalog.pg_class c\n on a.attrelid = c.oid\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\n join pg_catalog.pg_type t\n on a.atttypid = t.oid\n join pg_catalog.pg_namespace tn\n on t.typnamespace = tn.oid\nwhere\n tn.nspname = 'pg_catalog'\n and t.typname in ('regcollation', 'regconfig', 'regdictionary', 'regnamespace', 'regoper', 'regoperator', 'regproc', 'regprocedure')\n and n.nspname not in ('pg_catalog', 'information_schema', 'pgsodium'))\nunion all\n(\nselect\n 'insecure_queue_exposed_in_api' as \"name!\",\n 'Insecure Queue Exposed in API' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects cases where an insecure Queue is exposed over Data APIs' as \"description!\",\n format(\n 'Table \\`%s.%s\\` is public, but RLS has not been enabled.',\n n.nspname,\n c.relname\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0019_insecure_queue_exposed_in_api' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as \"metadata!\",\n format(\n 'rls_disabled_in_public_%s_%s',\n n.nspname,\n c.relname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\nwhere\n c.relkind in ('r', 'I') -- regular or partitioned tables\n and not c.relrowsecurity -- RLS is disabled\n and (\n pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')\n or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT')\n )\n and n.nspname = 'pgmq' -- tables in the pgmq schema\n and c.relname like 'q_%' -- only queue tables\n -- Constant requirements\n and 'pgmq_public' = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ','))))))\nunion all\n(\nwith constants as (\n select current_setting('block_size')::numeric as bs, 23 as hdr, 4 as ma\n),\n\nbloat_info as (\n select\n ma,\n bs,\n schemaname,\n tablename,\n (datawidth + (hdr + ma - (case when hdr % ma = 0 then ma else hdr % ma end)))::numeric as datahdr,\n (maxfracsum * (nullhdr + ma - (case when nullhdr % ma = 0 then ma else nullhdr % ma end))) as nullhdr2\n from (\n select\n schemaname,\n tablename,\n hdr,\n ma,\n bs,\n sum((1 - null_frac) * avg_width) as datawidth,\n max(null_frac) as maxfracsum,\n hdr + (\n select 1 + count(*) / 8\n from pg_stats s2\n where\n null_frac <> 0\n and s2.schemaname = s.schemaname\n and s2.tablename = s.tablename\n ) as nullhdr\n from pg_stats s, constants\n group by 1, 2, 3, 4, 5\n ) as foo\n),\n\ntable_bloat as (\n select\n schemaname,\n tablename,\n cc.relpages,\n bs,\n ceil((cc.reltuples * ((datahdr + ma -\n (case when datahdr % ma = 0 then ma else datahdr % ma end)) + nullhdr2 + 4)) / (bs - 20::float)) as otta\n from\n bloat_info\n join pg_class cc\n on cc.relname = bloat_info.tablename\n join pg_namespace nn\n on cc.relnamespace = nn.oid\n and nn.nspname = bloat_info.schemaname\n and nn.nspname <> 'information_schema'\n where\n cc.relkind = 'r'\n and cc.relam = (select oid from pg_am where amname = 'heap')\n),\n\nbloat_data as (\n select\n 'table' as type,\n schemaname,\n tablename as object_name,\n round(case when otta = 0 then 0.0 else table_bloat.relpages / otta::numeric end, 1) as bloat,\n case when relpages < otta then 0 else (bs * (table_bloat.relpages - otta)::bigint)::bigint end as raw_waste\n from\n table_bloat\n)\n\nselect\n 'table_bloat' as \"name!\",\n 'Table Bloat' as \"title!\",\n 'INFO' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['PERFORMANCE'] as \"categories!\",\n 'Detects if a table has excess bloat and may benefit from maintenance operations like vacuum full or cluster.' as \"description!\",\n format(\n 'Table `%s`.`%s` has excessive bloat',\n bloat_data.schemaname,\n bloat_data.object_name\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0020_table_bloat' as \"remediation!\",\n jsonb_build_object(\n 'schema', bloat_data.schemaname,\n 'name', bloat_data.object_name,\n 'type', bloat_data.type\n ) as \"metadata!\",\n format(\n 'table_bloat_%s_%s',\n bloat_data.schemaname,\n bloat_data.object_name\n ) as \"cache_key!\"\nfrom\n bloat_data\nwhere\n bloat > 70.0\n and raw_waste > (20 * 1024 * 1024) -- filter for waste > 200 MB\norder by\n schemaname,\n object_name)\nunion all\n(\nselect\n 'fkey_to_auth_unique' as \"name!\",\n 'Foreign Key to Auth Unique Constraint' as \"title!\",\n 'ERROR' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects user defined foreign keys to unique constraints in the auth schema.' as \"description!\",\n format(\n 'Table `%s`.`%s` has a foreign key `%s` referencing an auth unique constraint',\n n.nspname, -- referencing schema\n c_rel.relname, -- referencing table\n c.conname -- fkey name\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0021_fkey_to_auth_unique' as \"remediation!\",\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c_rel.relname,\n 'foreign_key', c.conname\n ) as \"metadata!\",\n format(\n 'fkey_to_auth_unique_%s_%s_%s',\n n.nspname, -- referencing schema\n c_rel.relname, -- referencing table\n c.conname\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_constraint c\n join pg_catalog.pg_class c_rel\n on c.conrelid = c_rel.oid\n join pg_catalog.pg_namespace n\n on c_rel.relnamespace = n.oid\n join pg_catalog.pg_class ref_rel\n on c.confrelid = ref_rel.oid\n join pg_catalog.pg_namespace cn\n on ref_rel.relnamespace = cn.oid\n join pg_catalog.pg_index i\n on c.conindid = i.indexrelid\nwhere c.contype = 'f'\n and cn.nspname = 'auth'\n and i.indisunique\n and not i.indisprimary)\nunion all\n(\nselect\n 'extension_versions_outdated' as \"name!\",\n 'Extension Versions Outdated' as \"title!\",\n 'WARN' as \"level!\",\n 'EXTERNAL' as \"facing!\",\n array['SECURITY'] as \"categories!\",\n 'Detects extensions that are not using the default (recommended) version.' as \"description!\",\n format(\n 'Extension `%s` is using version `%s` but version `%s` is available. Using outdated extension versions may expose the database to security vulnerabilities.',\n ext.name,\n ext.installed_version,\n ext.default_version\n ) as \"detail!\",\n 'https://supabase.com/docs/guides/database/database-linter?lint=0022_extension_versions_outdated' as \"remediation!\",\n jsonb_build_object(\n 'extension_name', ext.name,\n 'installed_version', ext.installed_version,\n 'default_version', ext.default_version\n ) as \"metadata!\",\n format(\n 'extension_versions_outdated_%s_%s',\n ext.name,\n ext.installed_version\n ) as \"cache_key!\"\nfrom\n pg_catalog.pg_available_extensions ext\njoin\n -- ignore versions not in pg_available_extension_versions\n -- e.g. residue of pg_upgrade\n pg_catalog.pg_available_extension_versions extv\n on extv.name = ext.name and extv.installed\nwhere\n ext.installed_version is not null\n and ext.default_version is not null\n and ext.installed_version != ext.default_version\norder by\n ext.name)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "name!", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "title!", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "level!", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "facing!", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "categories!", - "type_info": "TextArray" - }, - { - "ordinal": 5, - "name": "description!", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "detail!", - "type_info": "Text" - }, - { - "ordinal": 7, - "name": "remediation!", - "type_info": "Text" - }, - { - "ordinal": 8, - "name": "metadata!", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "cache_key!", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ] - }, - "hash": "b869d517301aaf69d382f092c09d5a53712d68afd273423f9310cd793586f532" -} From 2f97f7f79258692c2827caecb4db2320cce1d68a Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 14:05:00 +0100 Subject: [PATCH 30/38] readied --- crates/pgls_completions/src/builder.rs | 6 +----- crates/pgls_completions/src/relevance/scoring.rs | 8 ++++---- crates/pgls_treesitter/src/context/mod.rs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/crates/pgls_completions/src/builder.rs b/crates/pgls_completions/src/builder.rs index 8a54b185e..e7992d7c3 100644 --- a/crates/pgls_completions/src/builder.rs +++ b/crates/pgls_completions/src/builder.rs @@ -2,7 +2,6 @@ use crate::{ CompletionItemKind, CompletionText, item::CompletionItem, relevance::{filtering::CompletionFilter, scoring::CompletionScore}, - sanitization, }; use pgls_treesitter::TreesitterContext; @@ -43,10 +42,7 @@ impl<'a> CompletionBuilder<'a> { item.score.calc_score(self.ctx); } - items = items - .into_iter() - .filter(|i| !i.score.should_skip()) - .collect(); + items.retain(|i| !i.score.should_skip()); items.sort_by(|a, b| { b.score diff --git a/crates/pgls_completions/src/relevance/scoring.rs b/crates/pgls_completions/src/relevance/scoring.rs index 0fd3a4c49..e4da05565 100644 --- a/crates/pgls_completions/src/relevance/scoring.rs +++ b/crates/pgls_completions/src/relevance/scoring.rs @@ -71,12 +71,12 @@ impl CompletionScore<'_> { (None, Some(qualifier)) => { if self.get_schema_name().is_some_and(|s| s == qualifier) { self.get_table_name() - .map(|t| format!("{}.{}", t, name)) + .map(|t| format!("{t}.{name}")) .unwrap_or(name.clone()) } else if self.get_table_name().is_some_and(|t| t == qualifier) { name.clone() } else if ctx - .get_mentioned_table_for_alias(&qualifier) + .get_mentioned_table_for_alias(qualifier) .is_some_and(|alias_tbl| { self.get_table_name() .is_some_and(|item_tbl| alias_tbl == item_tbl) @@ -97,7 +97,7 @@ impl CompletionScore<'_> { CompletionRelevanceData::Column(_) | CompletionRelevanceData::Policy(_) => self .get_table_name() .and_then(|tbl| ctx.get_used_alias_for_table(tbl)) - .map(|t| format!("{}.{}", t, name)) + .map(|t| format!("{t}.{name}")) .unwrap_or(name.clone()), // everything else is just fuzzy matched against its name. @@ -243,7 +243,7 @@ impl CompletionScore<'_> { _ => {} }, - _ => return, + _ => (), } } diff --git a/crates/pgls_treesitter/src/context/mod.rs b/crates/pgls_treesitter/src/context/mod.rs index 92ea2cf0a..2761d32da 100644 --- a/crates/pgls_treesitter/src/context/mod.rs +++ b/crates/pgls_treesitter/src/context/mod.rs @@ -1138,7 +1138,7 @@ mod tests { for case in cases { let params = TreeSitterContextParams { position: case.cursor_position.into(), - text: &query, + text: query, tree: &tree, }; From d4e8b487a418271b28c5199fd6e4449b615f4af1 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 14:13:41 +0100 Subject: [PATCH 31/38] only install if no cache --- .github/workflows/pull_request.yml | 8 ++++---- .github/workflows/release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 16a6015ee..77c0c288a 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -121,7 +121,7 @@ jobs: key: ${{ runner.os }}-tree-sitter-${{ hashFiles('rust-toolchain.toml') }} - name: Setup tree-sitter - run: cargo install tree-sitter-cli + run: command -v tree-sitter || cargo install tree-sitter-cli - name: Setup sqlx-cli run: cargo install sqlx-cli @@ -186,7 +186,7 @@ jobs: key: ${{ runner.os }}-tree-sitter-${{ hashFiles('rust-toolchain.toml') }} - name: Setup Postgres - run: cargo install tree-sitter-cli + run: command -v tree-sitter || cargo install tree-sitter-cli - name: Run tests run: cargo test --workspace @@ -222,7 +222,7 @@ jobs: ~/.cargo/bin/tree-sitter key: ${{ runner.os }}-tree-sitter-${{ hashFiles('rust-toolchain.toml') }} - name: setup tree-sitter - run: cargo install tree-sitter-cli + run: command -v tree-sitter || cargo install tree-sitter-cli - name: Build main binary run: cargo build -p pgls_cli --release - name: Setup Bun @@ -281,7 +281,7 @@ jobs: ~/.cargo/bin/tree-sitter key: ${{ runner.os }}-tree-sitter-${{ hashFiles('rust-toolchain.toml') }} - name: setup tree-sitter - run: cargo install tree-sitter-cli + run: command -v tree-sitter || cargo install tree-sitter-cli - name: Ensure RustFMT on nightly toolchain run: rustup component add rustfmt --toolchain nightly - name: echo toolchain diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1c4b00aea..87daee5bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,7 +79,7 @@ jobs: key: ${{ runner.os }}-tree-sitter-${{ hashFiles('rust-toolchain.toml') }} - name: Setup tree-sitter - run: cargo install tree-sitter-cli + run: command -v tree-sitter || cargo install tree-sitter-cli - name: Setup Postgres uses: ./.github/actions/setup-postgres From 71969bb9d407374f3a31a2d4b33060d5d7d90398 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 14:55:37 +0100 Subject: [PATCH 32/38] rename test bin? --- .../@postgrestools/backend-jsonrpc/tests/workspace.test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@postgrestools/backend-jsonrpc/tests/workspace.test.mjs b/packages/@postgrestools/backend-jsonrpc/tests/workspace.test.mjs index cf0f13ae6..3c32d574c 100644 --- a/packages/@postgrestools/backend-jsonrpc/tests/workspace.test.mjs +++ b/packages/@postgrestools/backend-jsonrpc/tests/workspace.test.mjs @@ -10,7 +10,7 @@ describe("Workspace API", () => { const command = resolve( fileURLToPath(import.meta.url), "../../../../..", - `target/release/postgrestools${extension}`, + `target/release/postgres-language-server${extension}`, ); const workspace = await createWorkspaceWithBinary(command); From c484d0b7def659e84729447bf53b693edd816922 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 14:59:34 +0100 Subject: [PATCH 33/38] that one, too --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 77c0c288a..8d5bccd9c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -124,7 +124,7 @@ jobs: run: command -v tree-sitter || cargo install tree-sitter-cli - name: Setup sqlx-cli - run: cargo install sqlx-cli + run: command -v sqlx ||cargo install sqlx-cli - name: Setup Bun uses: oven-sh/setup-bun@v2 From a6cbde637bf23fddaf5d03b21079059e51fcb835 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 15:07:57 +0100 Subject: [PATCH 34/38] aha! --- .github/workflows/pull_request.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8d5bccd9c..cabb02351 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -118,6 +118,7 @@ jobs: path: | ~/.cargo/bin/tree-sitter ~/.cargo/bin/sqlx + ~/.cargo/bin/cargo-sqlx key: ${{ runner.os }}-tree-sitter-${{ hashFiles('rust-toolchain.toml') }} - name: Setup tree-sitter From 78cecce20c4e02a378ca8b67522863e4d4092503 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 15:08:46 +0100 Subject: [PATCH 35/38] remove depr tests --- .github/workflows/pull_request.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index cabb02351..45d4f9879 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -231,16 +231,6 @@ jobs: - name: Install JS dependencies run: bun install - - name: Build backend-jsonrpc (deprecated) - working-directory: packages/@postgrestools/backend-jsonrpc - run: bun run build - - name: Run backend-jsonrpc test (deprecated) - working-directory: packages/@postgrestools/backend-jsonrpc - run: bun run test - - name: Run cli test (deprecated) - working-directory: packages/@postgrestools/postgrestools - run: bun run test - - name: Build backend-jsonrpc working-directory: packages/@postgres-language-server/backend-jsonrpc run: bun run build From 0068c2ae624f0e85dd3fb99b07e219b05d989238 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 21:25:43 +0100 Subject: [PATCH 36/38] disable-db --- .../@postgres-language-server/cli/test/bin.test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/@postgres-language-server/cli/test/bin.test.js b/packages/@postgres-language-server/cli/test/bin.test.js index 921869e53..7c743e629 100644 --- a/packages/@postgres-language-server/cli/test/bin.test.js +++ b/packages/@postgres-language-server/cli/test/bin.test.js @@ -8,12 +8,15 @@ const binPath = join(__dirname, "../bin/postgres-language-server"); const testSqlPath = join(__dirname, "test.sql"); describe("postgres-language-server bin", () => { - it("should check a SQL file successfully", async () => { const result = await new Promise((resolve) => { - const proc = spawn("node", [binPath, "check", testSqlPath], { - env: { ...process.env }, - }); + const proc = spawn( + "node", + [binPath, "check", "--disable-db", testSqlPath], + { + env: { ...process.env }, + } + ); let stdout = ""; let stderr = ""; From 87925f2d686f64354a786d46a58b02635b99fd7c Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 21:36:51 +0100 Subject: [PATCH 37/38] clean --- test.sql | 35 ++++++++++++++++++++++++++++++++++- todo.txt | 19 ------------------- 2 files changed, 34 insertions(+), 20 deletions(-) delete mode 100644 todo.txt diff --git a/test.sql b/test.sql index 74eb49db5..b21a2e8dc 100644 --- a/test.sql +++ b/test.sql @@ -1 +1,34 @@ -select email REPLACED_TOKE pff \ No newline at end of file +create table + unknown_users (id serial primary key, address text, email text); + +drop table unknown_users; + +select + * +from + unknown_users; + +sel 1; + + + +create function test_organisation_id () + returns setof text + language plpgsql + security invoker + as $$ + declre + v_organisation_id uuid; +begin + return next is(private.organisation_id(), v_organisation_id, 'should return organisation_id of token'); +end +$$; + + +create function f1() +returns void as $$ +declare b constant int; +begin + call p1(10, b); +end; +$$ language plpgsql; diff --git a/todo.txt b/todo.txt deleted file mode 100644 index 74493115c..000000000 --- a/todo.txt +++ /dev/null @@ -1,19 +0,0 @@ - -#### Snapshot review -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_in_join_on_clause.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns_with_aliases.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__completes_quoted_columns.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__does_not_complete_cols_in_join_clauses.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_join_on.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__filters_out_by_aliases_in_select.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__handles_nested_queries.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__ignores_cols_in_from_clause.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_columns_of_mentioned_tables.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__prefers_not_mentioned_columns.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__shows_multiple_columns_if_no_relation_specified.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_alter_table_and_drop_table.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_insert_clause.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_in_where_clause.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_columns_policy_using_clause.snap -- [x] crates/pgls_completions/src/snapshots/pgls_completions__test_helper__suggests_relevant_columns_without_letters.snap - From c4e57478e1f94b2aaa9b84ce0555eac955e994c9 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 13 Dec 2025 21:37:18 +0100 Subject: [PATCH 38/38] ack --- postgrestools.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgrestools.jsonc b/postgrestools.jsonc index fd1149e81..47d08c729 100644 --- a/postgrestools.jsonc +++ b/postgrestools.jsonc @@ -17,7 +17,7 @@ // YOU CAN COMMENT ME OUT :) "db": { "host": "127.0.0.1", - "port": 54322, + "port": 5432, "username": "postgres", "password": "postgres", "database": "postgres",