diff --git a/crates/pgt_hover/src/lib.rs b/crates/pgt_hover/src/lib.rs index d20adc3c6..b690d7f05 100644 --- a/crates/pgt_hover/src/lib.rs +++ b/crates/pgt_hover/src/lib.rs @@ -48,7 +48,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec { .map(Hoverable::from) .collect(), - hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![], + _ => vec![], }, HoveredNode::Column(node_identification) => match node_identification { @@ -74,7 +74,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec { .collect() } - hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![], + _ => vec![], }, HoveredNode::Function(node_identification) => match node_identification { @@ -92,7 +92,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec { .map(Hoverable::from) .collect(), - hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![], + _ => vec![], }, HoveredNode::Role(node_identification) => match node_identification { @@ -103,8 +103,7 @@ pub fn on_hover(params: OnHoverParams) -> Vec { .map(Hoverable::from) .collect(), - hovered_node::NodeIdentification::SchemaAndName(_) => vec![], - hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![], + _ => vec![], }, HoveredNode::Schema(node_identification) => match node_identification { diff --git a/crates/pgt_text_size/src/size.rs b/crates/pgt_text_size/src/size.rs index 1082485e7..b72dfe72b 100644 --- a/crates/pgt_text_size/src/size.rs +++ b/crates/pgt_text_size/src/size.rs @@ -109,6 +109,13 @@ impl TryFrom for TextSize { } } +impl From for isize { + #[inline] + fn from(value: TextSize) -> Self { + value.raw as isize + } +} + impl From for usize { #[inline] fn from(value: TextSize) -> Self { diff --git a/crates/pgt_text_size/src/text_range_replacement.rs b/crates/pgt_text_size/src/text_range_replacement.rs index 22b17a6e3..a288dfeb3 100644 --- a/crates/pgt_text_size/src/text_range_replacement.rs +++ b/crates/pgt_text_size/src/text_range_replacement.rs @@ -1,6 +1,6 @@ use crate::{TextRange, TextSize}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum AdjustmentDirection { Lengthened, Shortened, @@ -8,13 +8,10 @@ enum AdjustmentDirection { #[derive(Debug)] struct AdjustmentMarker { - #[allow(dead_code)] original_range: TextRange, adjusted_range: TextRange, replacement_txt: String, - registered_offset_at_start: TextSize, - - #[allow(dead_code)] + registered_offset_at_start: isize, adjustment_direction: AdjustmentDirection, range_size_difference: TextSize, } @@ -44,7 +41,7 @@ impl AdjustmentMarker { // will be calculated during `.build()` adjusted_range: original_range, - registered_offset_at_start: 0.into(), + registered_offset_at_start: 0, } } @@ -105,33 +102,50 @@ impl TextRangeReplacementBuilder { /// The adjustments are processed in order of their starting positions in the original text. /// Currently only supports lengthening adjustments (where replacement text is longer /// than the original range). - /// - /// # Panics - /// - /// Panics if any adjustment involves shortening the text, as this is not yet implemented. pub fn build(mut self) -> TextRangeReplacement { self.markers.sort_by_key(|r| r.original_range.start()); - let mut total_offset: usize = 0; + let mut total_offset: isize = 0; for marker in self.markers.iter_mut() { match marker.adjustment_direction { AdjustmentDirection::Lengthened => { - marker.registered_offset_at_start = total_offset.try_into().unwrap(); - - marker.adjusted_range = TextRange::new( - marker.original_range.start() + marker.registered_offset_at_start, - marker.original_range.end() - + marker.registered_offset_at_start - + marker.range_size_difference, - ); - - total_offset += usize::from(marker.range_size_difference); + marker.adjusted_range = if total_offset >= 0 { + let to_add = TextSize::new(total_offset.abs().try_into().unwrap()); + TextRange::new( + marker.original_range.start() + to_add, + marker.original_range.end() + to_add + marker.range_size_difference, + ) + } else { + let to_sub = TextSize::new(total_offset.abs().try_into().unwrap()); + TextRange::new( + marker.original_range.start().checked_sub(to_sub).unwrap(), + marker.original_range.end().checked_sub(to_sub).unwrap() + + marker.range_size_difference, + ) + }; + + marker.registered_offset_at_start = total_offset; + total_offset += isize::from(marker.range_size_difference); } AdjustmentDirection::Shortened => { - unimplemented!( - "So far, we've only ever lengthened TextRanges. Consider filling up your range with spaces" - ) + marker.adjusted_range = if total_offset >= 0 { + let to_add = TextSize::new(total_offset.abs().try_into().unwrap()); + TextRange::new( + marker.original_range.start() + to_add, + marker.original_range.end() + to_add - marker.range_size_difference, + ) + } else { + let to_sub = TextSize::new(total_offset.abs().try_into().unwrap()); + TextRange::new( + marker.original_range.start().checked_sub(to_sub).unwrap(), + marker.original_range.end().checked_sub(to_sub).unwrap() + - marker.range_size_difference, + ) + }; + + marker.registered_offset_at_start = total_offset; + total_offset -= isize::from(marker.range_size_difference); } } } @@ -193,16 +207,20 @@ impl TextRangeReplacement { .rev() .find(|marker| adjusted_position >= marker.adjusted_range.start()) { - if adjusted_position >= marker.adjusted_range.end() { - adjusted_position - .checked_sub(marker.registered_offset_at_start) - .unwrap() - .checked_sub(marker.range_size_difference) - .unwrap() + if marker.adjustment_direction == AdjustmentDirection::Lengthened { + if adjusted_position >= marker.adjusted_range.end() { + offset_reverted(adjusted_position, marker.registered_offset_at_start) + .checked_sub(marker.range_size_difference) + .unwrap() + } else { + let clamped = marker.adjusted_end_within_clamped_range(adjusted_position); + offset_reverted(clamped, marker.registered_offset_at_start) + } + } else if adjusted_position < marker.adjusted_range.end() { + offset_reverted(adjusted_position, marker.registered_offset_at_start) } else { - let clamped = marker.adjusted_end_within_clamped_range(adjusted_position); - clamped - .checked_sub(marker.registered_offset_at_start) + offset_reverted(adjusted_position, marker.registered_offset_at_start) + .checked_add(marker.range_size_difference) .unwrap() } } else { @@ -221,6 +239,15 @@ impl TextRangeReplacement { } } +fn offset_reverted(position: TextSize, offset: isize) -> TextSize { + let absolute = TextSize::new(offset.abs().try_into().unwrap()); + if offset >= 0 { + position.checked_sub(absolute).unwrap() + } else { + position.checked_add(absolute).unwrap() + } +} + #[cfg(test)] mod tests { use crate::TextSize; @@ -228,7 +255,7 @@ mod tests { use crate::text_range_replacement::TextRangeReplacementBuilder; #[test] - fn tracks_adjustments() { + fn tracks_lengthening_adjustments() { let sql = "select $1 from $2 where $3 = $4 limit 5;"; let range_1: std::ops::Range = 7..9; // $1 @@ -343,6 +370,250 @@ mod tests { // select email from auth.users where id = '00000000-0000-0000-0000-000000000000'| limit 5;| // maps to // select $1 from $2 where $3 = $4| limit 5;| + for i in repl_range_4.end..text_replacement.text.len() { + let between_og_4_end = range_4.end..og_end; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_4_end.contains(&usize::from(adjusted))); + } + } + + #[test] + fn tracks_shortening_adjustments() { + let sql = "select :my_param1 from :my_param2 where :my_param3 = :my_param4 limit 5;"; + + let range_1: std::ops::Range = 7..17; // :my_param1 + let range_2: std::ops::Range = 23..33; // :my_param2 + let range_3: std::ops::Range = 40..50; // :my_param3 + let range_4: std::ops::Range = 53..63; // :my_param4 + let og_end = sql.len(); + + let mut replacement_builder = TextRangeReplacementBuilder::new(sql); + + let replacement_1 = /* select */ "email"; + let replacement_2 = /* from */ "auth.users"; // (same length) + let replacement_3 = /* where */ "name"; + let replacement_4 = /* = */ "timo"; /* limit 5; */ + + // start in the middle – the builder can deal with unordered replacements + replacement_builder.replace_range(range_2.clone().try_into().unwrap(), replacement_2); + replacement_builder.replace_range(range_4.clone().try_into().unwrap(), replacement_4); + replacement_builder.replace_range(range_1.clone().try_into().unwrap(), replacement_1); + replacement_builder.replace_range(range_3.clone().try_into().unwrap(), replacement_3); + + let text_replacement = replacement_builder.build(); + + assert_eq!( + text_replacement.text(), + "select email from auth.users where name = timo limit 5;" + ); + + let repl_range_1 = 7..12; // email + let repl_range_2 = 18..28; // auth.users + let repl_range_3 = 35..39; // name + let repl_range_4 = 42..46; // timo + + // |select |email from auth.users where name = timo limit 5; + // maps to + // |select |:my_param1 from :my_param2 where :my_param3 = :my_param4 limit 5; + for i in 0..repl_range_1.clone().start { + let between_og_0_1 = 0..range_1.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_0_1.contains(&usize::from(adjusted))); + } + + // select |email| from auth.users where name = timo limit 5; + // maps to + // select |:my_param1| from :my_param2 where :my_param3 = :my_param4 limit 5; + for i in repl_range_1.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_1.contains(&usize::from(og_pos))); + } + + // select email| from |auth.users where name = timo limit 5; + // maps to + // select :my_param1| from |:my_param2 where :my_param3 = :my_param4 limit 5; + for i in repl_range_1.end..repl_range_2.clone().start { + let between_og_1_2 = range_1.end..range_2.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_1_2.contains(&usize::from(adjusted))); + } + + // select email from |auth.users| where name = timo limit 5; + // maps to + // select :my_param1 from |:my_param2| where :my_param3 = :my_param4 limit 5; + for i in repl_range_2.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_2.contains(&usize::from(og_pos))); + } + + // select email from auth.users| where |name = timo limit 5; + // maps to + // select :my_param1 from :my_param2| where |:my_param3 = :my_param4 limit 5; + for i in repl_range_2.end..repl_range_3.clone().start { + let between_og_2_3 = range_2.end..range_3.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_2_3.contains(&usize::from(adjusted))); + } + + // select email from auth.users where |name| = timo limit 5; + // maps to + // select :my_param1 from :my_param2 where |:my_param3| = :my_param4 limit 5; + for i in repl_range_3.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_3.contains(&usize::from(og_pos))); + } + + // select email from auth.users where name| = |timo limit 5; + // maps to + // select :my_param1 from :my_param2 where :my_param3| = |:my_param4 limit 5; + for i in repl_range_3.end..repl_range_4.clone().start { + let between_og_3_4 = range_3.end..range_4.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_3_4.contains(&usize::from(adjusted))); + } + + // select email from auth.users where name = |timo| limit 5; + // maps to + // select :my_param1 from :my_param2 where :my_param3 = |:my_param4| limit 5; + for i in repl_range_4.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_4.contains(&usize::from(og_pos))); + } + + // select email from auth.users where name = timo| limit 5;| + // maps to + // select :my_param1 from :my_param2 where :my_param3 = :my_param4| limit 5;| + for i in repl_range_4.end..text_replacement.text.len() { + let between_og_4_end = range_4.end..og_end; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_4_end.contains(&usize::from(adjusted))); + } + } + + #[test] + fn tracks_mixed_adjustments() { + let sql = "select :my_param1 from $2 where $3 = :my_param4 limit 5;"; + + let range_1: std::ops::Range = 7..17; // :my_param1 + let range_2: std::ops::Range = 23..25; // $2 + let range_3: std::ops::Range = 32..34; // $3 + let range_4: std::ops::Range = 37..47; // :my_param4 + let og_end = sql.len(); + + let mut replacement_builder = TextRangeReplacementBuilder::new(sql); + + let replacement_1 = "email"; // replacement is shorter + let replacement_2 = "auth.users"; // replacement is longer + let replacement_3 = "id"; // replacement is same length + let replacement_4 = "'00000000-0000-0000-0000-000000000000'"; // replacement is longer + + // start in the middle – the builder can deal with unordered replacements + replacement_builder.replace_range(range_2.clone().try_into().unwrap(), replacement_2); + replacement_builder.replace_range(range_4.clone().try_into().unwrap(), replacement_4); + replacement_builder.replace_range(range_1.clone().try_into().unwrap(), replacement_1); + replacement_builder.replace_range(range_3.clone().try_into().unwrap(), replacement_3); + + let text_replacement = replacement_builder.build(); + + assert_eq!( + text_replacement.text(), + "select email from auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5;" + ); + + let repl_range_1 = 7..12; // email + let repl_range_2 = 18..28; // auth.users + let repl_range_3 = 35..37; // id + let repl_range_4 = 40..78; // '00000000-0000-0000-0000-000000000000' + + // |select |email from auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // |select |:my_param1 from $2 where $3 = :my_param4 limit 5; + for i in 0..repl_range_1.clone().start { + let between_og_0_1 = 0..range_1.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_0_1.contains(&usize::from(adjusted))); + } + + // select |email| from auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // select |:my_param1| from $2 where $3 = :my_param4 limit 5; + for i in repl_range_1.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_1.contains(&usize::from(og_pos))); + } + + // select email| from |auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // select :my_param1| from |$2 where $3 = :my_param4 limit 5; + for i in repl_range_1.end..repl_range_2.clone().start { + let between_og_1_2 = range_1.end..range_2.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_1_2.contains(&usize::from(adjusted))); + } + + // select email from |auth.users| where id = '00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // select :my_param1 from |$2| where $3 = :my_param4 limit 5; + for i in repl_range_2.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_2.contains(&usize::from(og_pos))); + } + + // select email from auth.users| where |id = '00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // select :my_param1 from $2| where |$3 = :my_param4 limit 5; + for i in repl_range_2.end..repl_range_3.clone().start { + let between_og_2_3 = range_2.end..range_3.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_2_3.contains(&usize::from(adjusted))); + } + + // select email from auth.users where |id| = '00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // select :my_param1 from $2 where |$3| = :my_param4 limit 5; + for i in repl_range_3.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_3.contains(&usize::from(og_pos))); + } + + // select email from auth.users where id| = |'00000000-0000-0000-0000-000000000000' limit 5; + // maps to + // select :my_param1 from $2 where $3| = |:my_param4 limit 5; + for i in repl_range_3.end..repl_range_4.clone().start { + let between_og_3_4 = range_3.end..range_4.start; + let adjusted = + text_replacement.to_original_position(TextSize::new(i.try_into().unwrap())); + assert!(between_og_3_4.contains(&usize::from(adjusted))); + } + + // select email from auth.users where id = |'00000000-0000-0000-0000-000000000000'| limit 5; + // maps to + // select :my_param1 from $2 where $3 = |:my_param4| limit 5; + for i in repl_range_4.clone() { + let pos = TextSize::new(i.try_into().unwrap()); + let og_pos = text_replacement.to_original_position(pos); + assert!(range_4.contains(&usize::from(og_pos))); + } + + // select email from auth.users where id = '00000000-0000-0000-0000-000000000000'| limit 5;| + // maps to + // select :my_param1 from $2 where $3 = :my_param4| limit 5;| for i in repl_range_4.end..sql.len() { let between_og_4_end = range_4.end..og_end; let adjusted = diff --git a/crates/pgt_typecheck/src/typed_identifier.rs b/crates/pgt_typecheck/src/typed_identifier.rs index ca355dfc6..b51774e50 100644 --- a/crates/pgt_typecheck/src/typed_identifier.rs +++ b/crates/pgt_typecheck/src/typed_identifier.rs @@ -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!("{:, 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, +} - 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 TestSetup<'_> { + 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("~~~"); + result.push_str(self.query); + result.push_str("~~~"); + } + + 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, @@ -74,7 +114,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 *shorter* 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; } diff --git a/crates/pgt_typecheck/tests/snapshots/invalid_column.snap b/crates/pgt_typecheck/tests/snapshots/invalid_column.snap index aa29a5290..b5f9244f1 100644 --- a/crates/pgt_typecheck/tests/snapshots/invalid_column.snap +++ b/crates/pgt_typecheck/tests/snapshots/invalid_column.snap @@ -1,10 +1,7 @@ --- source: crates/pgt_typecheck/tests/diagnostics.rs -expression: normalized -snapshot_kind: text +expression: content --- -typecheck ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +select id, ~~~unknown~~~ from contacts; - column "unknown" does not exist - - Error Code: 42703 +column "unknown" does not exist diff --git a/crates/pgt_typecheck/tests/snapshots/invalid_type_in_function_longer_default.snap b/crates/pgt_typecheck/tests/snapshots/invalid_type_in_function_longer_default.snap new file mode 100644 index 000000000..c1494513b --- /dev/null +++ b/crates/pgt_typecheck/tests/snapshots/invalid_type_in_function_longer_default.snap @@ -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: "00000000-0000-0000-0000-000000000000" diff --git a/crates/pgt_typecheck/tests/snapshots/invalid_type_in_function_shorter_default.snap b/crates/pgt_typecheck/tests/snapshots/invalid_type_in_function_shorter_default.snap new file mode 100644 index 000000000..8be816b2f --- /dev/null +++ b/crates/pgt_typecheck/tests/snapshots/invalid_type_in_function_shorter_default.snap @@ -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: ""