Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c0ed76c
ok?
juleswritescode Sep 19, 2025
fd0d198
ok?
juleswritescode Sep 26, 2025
d4ece2e
testie testie
juleswritescode Sep 26, 2025
dae4b9a
ok so far
juleswritescode Sep 26, 2025
c35c596
Merge branch 'main' of https://github.com/supabase-community/postgres…
juleswritescode Sep 26, 2025
2823ca0
ack
juleswritescode Sep 26, 2025
a2ae8f0
OKJ
juleswritescode Sep 26, 2025
802a0d2
install em toolz
juleswritescode Sep 26, 2025
934a44b
oke then
juleswritescode Sep 26, 2025
e3b5e30
amazing!
juleswritescode Sep 27, 2025
79cb776
Merge branch 'main' of https://github.com/supabase-community/postgres…
juleswritescode Sep 27, 2025
b472454
remove unneeded
juleswritescode Sep 27, 2025
48ade40
better api
juleswritescode Sep 27, 2025
38741b4
almost there…
juleswritescode Sep 27, 2025
7e63009
ok?
juleswritescode Sep 27, 2025
4e0ef81
ack ack
juleswritescode Sep 27, 2025
5f1d273
comment
juleswritescode Sep 27, 2025
d503333
Update crates/pgt_text_size/src/text_range_replacement.rs
juleswritescode Sep 27, 2025
bb9bfba
ok
juleswritescode Sep 27, 2025
3eb2f61
simple
juleswritescode Sep 27, 2025
79109a0
Merge branch 'main' into feat/longer-type-replacements
juleswritescode Oct 3, 2025
52cd007
intermediary
juleswritescode Oct 3, 2025
00fe8ef
resolve conflicts
juleswritescode Oct 3, 2025
e669094
ok then…
juleswritescode Oct 3, 2025
0cf105e
its not dead
juleswritescode Oct 3, 2025
7c7549c
no more spaces
juleswritescode Oct 10, 2025
53e2429
ok
juleswritescode Oct 10, 2025
41405a6
we got em tests
juleswritescode Oct 10, 2025
8cfef05
Update crates/pgt_typecheck/tests/diagnostics.rs
juleswritescode Oct 10, 2025
f653ea5
Merge branch 'main' into feat/shorter-type-replacements
juleswritescode Oct 10, 2025
6ec8115
better
juleswritescode Oct 10, 2025
1ddbc5e
Merge branch 'feat/shorter-type-replacements' of https://github.com/s…
juleswritescode Oct 10, 2025
36345c6
ack
juleswritescode Oct 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions crates/pgt_hover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec<String> {
.map(Hoverable::from)
.collect(),

hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![],
_ => vec![],
},

HoveredNode::Column(node_identification) => match node_identification {
Expand All @@ -74,7 +74,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec<String> {
.collect()
}

hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![],
_ => vec![],
},

HoveredNode::Function(node_identification) => match node_identification {
Expand All @@ -92,7 +92,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec<String> {
.map(Hoverable::from)
.collect(),

hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![],
_ => vec![],
},

HoveredNode::Role(node_identification) => match node_identification {
Expand All @@ -103,8 +103,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec<String> {
.map(Hoverable::from)
.collect(),

hovered_node::NodeIdentification::SchemaAndName(_) => vec![],
hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![],
_ => vec![],
},

HoveredNode::Schema(node_identification) => match node_identification {
Expand Down
7 changes: 7 additions & 0 deletions crates/pgt_text_size/src/size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ impl TryFrom<usize> for TextSize {
}
}

impl From<TextSize> for isize {
#[inline]
fn from(value: TextSize) -> Self {
value.raw as isize
}
}

impl From<TextSize> for usize {
#[inline]
fn from(value: TextSize) -> Self {
Expand Down
339 changes: 305 additions & 34 deletions crates/pgt_text_size/src/text_range_replacement.rs

Large diffs are not rendered by default.

10 changes: 2 additions & 8 deletions crates/pgt_typecheck/src/typed_identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ pub fn apply_identifiers<'a>(
for (range, type_, is_array) in replacements {
let default_value = get_formatted_default_value(type_, is_array);

let range_size = range.end - range.start;

// if the default_value is shorter than "range", fill it up with spaces
let default_value = format!("{:<range_size$}", default_value);

text_range_replacement_builder
.replace_range(range.clone().try_into().unwrap(), &default_value);
}
Expand Down Expand Up @@ -314,7 +309,7 @@ mod tests {
assert_eq!(
replacement.text(),
// the numeric parameters are filled with 0;
"select 0 + 0 + 0 + 0 + 0 + 'critical'"
"select 0 + 0 + 0 + 0 + 0 + 'critical'"
);
}

Expand Down Expand Up @@ -363,8 +358,7 @@ mod tests {

assert_eq!(
replacement.text(),
// two spaces at the end because mail is longer than ''
r#"select id from auth.users where email_change_confirm_status = '00000000-0000-0000-0000-000000000000' and email = '' ;"#
r#"select id from auth.users where email_change_confirm_status = '00000000-0000-0000-0000-000000000000' and email = '';"#
);
}
}
213 changes: 155 additions & 58 deletions crates/pgt_typecheck/tests/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,70 +1,108 @@
use pgt_console::{
fmt::{Formatter, HTML},
markup,
};
use pgt_diagnostics::PrintDiagnostic;
use pgt_typecheck::{TypecheckParams, check_sql};
use pgt_console::fmt::{Formatter, HTML};
use pgt_diagnostics::Diagnostic;
use pgt_typecheck::{IdentifierType, TypecheckParams, TypedIdentifier, check_sql};
use sqlx::{Executor, PgPool};
use std::fmt::Write;

async fn test(name: &str, query: &str, setup: Option<&str>, test_db: &PgPool) {
if let Some(setup) = setup {
test_db
.execute(setup)
.await
.expect("Failed to setup test database");
}
struct TestSetup<'a> {
name: &'a str,
query: &'a str,
setup: Option<&'a str>,
test_db: &'a PgPool,
typed_identifiers: Vec<TypedIdentifier>,
}

let mut parser = tree_sitter::Parser::new();
parser
.set_language(&pgt_treesitter_grammar::LANGUAGE.into())
.expect("Error loading sql language");

let schema_cache = pgt_schema_cache::SchemaCache::load(test_db)
.await
.expect("Failed to load Schema Cache");

let root = pgt_query::parse(query)
.unwrap()
.into_root()
.expect("Failed to parse query");
let tree = parser.parse(query, None).unwrap();

let conn = &test_db;
let result = check_sql(TypecheckParams {
conn,
sql: query,
ast: &root,
tree: &tree,
schema_cache: &schema_cache,
search_path_patterns: vec![],
identifiers: vec![],
})
.await;
impl<'a> TestSetup<'a> {
async fn test(self) {
if let Some(setup) = self.setup {
self.test_db
.execute(setup)
.await
.expect("Failed to setup test selfbase");
}

let mut content = vec![];
let mut writer = HTML::new(&mut content);
let mut parser = tree_sitter::Parser::new();
parser
.set_language(&pgt_treesitter_grammar::LANGUAGE.into())
.expect("Error loading sql language");

Formatter::new(&mut writer)
.write_markup(markup! {
{PrintDiagnostic::simple(&result.unwrap().unwrap())}
let schema_cache = pgt_schema_cache::SchemaCache::load(&self.test_db)
.await
.expect("Failed to load Schema Cache");

let root = pgt_query::parse(self.query)
.unwrap()
.into_root()
.expect("Failed to parse query");
let tree = parser.parse(self.query, None).unwrap();

let result = check_sql(TypecheckParams {
conn: self.test_db,
sql: self.query,
ast: &root,
tree: &tree,
schema_cache: &schema_cache,
identifiers: self.typed_identifiers,
search_path_patterns: vec![],
})
.unwrap();
.await;

assert!(
result.is_ok(),
"Got Typechecking error: {}",
result.unwrap_err()
);

let content = String::from_utf8(content).unwrap();
let maybe_diagnostic = result.unwrap();

insta::with_settings!({
prepend_module_to_snapshot => false,
}, {
insta::assert_snapshot!(name, content);
});
let content = match maybe_diagnostic {
Some(d) => {
let mut result = String::new();

if let Some(span) = d.location().span {
for (idx, c) in self.query.char_indices() {
if pgt_text_size::TextSize::new(idx.try_into().unwrap()) == span.start() {
result.push_str("~~~");
}
if pgt_text_size::TextSize::new(idx.try_into().unwrap()) == span.end() {
result.push_str("~~~");
}
result.push(c);
}
} else {
result.push_str(self.query);
}

writeln!(&mut result).unwrap();
writeln!(&mut result).unwrap();

let mut msg_content = vec![];
let mut writer = HTML::new(&mut msg_content);
let mut formatter = Formatter::new(&mut writer);
d.message(&mut formatter).unwrap();

result.push_str(String::from_utf8(msg_content).unwrap().as_str());

result
}
None => String::from("No Diagnostic"),
};

insta::with_settings!({
prepend_module_to_snapshot => false,
}, {
insta::assert_snapshot!(self.name, content);

});
}
}

#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")]
async fn invalid_column(pool: PgPool) {
test(
"invalid_column",
"select id, unknown from contacts;",
Some(
async fn invalid_column(test_db: PgPool) {
TestSetup {
name: "invalid_column",
query: "select id, unknown from contacts;",
setup: Some(
r#"
create table public.contacts (
id serial primary key,
Expand All @@ -74,7 +112,66 @@ async fn invalid_column(pool: PgPool) {
);
"#,
),
&pool,
)
test_db: &test_db,
typed_identifiers: vec![],
}
.test()
.await;
}

#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")]
async fn invalid_type_in_function(test_db: PgPool) {
// create or replace function clean_up(uid uuid)
// returns void
// language sql
// as $$
// delete from public.contacts where id = uid;
// $$;

let setup = r#"
create table public.contacts (
id serial primary key,
name text not null,
is_vegetarian bool default false,
middle_name varchar(255)
);
"#;

/* NOTE: The replaced type default value is *longer* than the param name. */
TestSetup {
name: "invalid_type_in_function_longer_default",
setup: Some(setup),
query: r#"delete from public.contacts where id = uid;"#,
test_db: &test_db,
typed_identifiers: vec![TypedIdentifier {
path: "clean_up".to_string(),
name: Some("uid".to_string()),
type_: IdentifierType {
schema: None,
name: "uuid".to_string(),
is_array: false,
},
}],
}
.test()
.await;

/* NOTE: The replaced type default value is *longer* than the param name. */
TestSetup {
name: "invalid_type_in_function_shorter_default",
setup: None,
query: r#"delete from public.contacts where id = contact_name;"#,
test_db: &test_db,
typed_identifiers: vec![TypedIdentifier {
path: "clean_up".to_string(),
name: Some("contact_name".to_string()),
type_: IdentifierType {
schema: None,
name: "text".to_string(),
is_array: false,
},
}],
}
.test()
.await;
}
9 changes: 3 additions & 6 deletions crates/pgt_typecheck/tests/snapshots/invalid_column.snap
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
---
source: crates/pgt_typecheck/tests/diagnostics.rs
expression: normalized
snapshot_kind: text
expression: content
---
typecheck ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
select id, ~~~unknown~~~ from contacts;

<strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">column &quot;unknown&quot; does not exist</span>

<strong><span style="color: Tomato;">✖</span></strong> <span style="color: Tomato;">Error Code: </span><span style="color: Tomato;"><strong>42703</strong></span>
column &quot;unknown&quot; does not exist
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
source: crates/pgt_typecheck/tests/diagnostics.rs
expression: content
---
delete from public.contacts where id = ~~~uid~~~;

invalid input syntax for type integer: &quot;00000000-0000-0000-0000-000000000000&quot;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
source: crates/pgt_typecheck/tests/diagnostics.rs
expression: content
---
delete from public.contacts where id = ~~~contact_name~~~;

invalid input syntax for type integer: &quot;&quot;
Loading