Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
better api
  • Loading branch information
juleswritescode committed Sep 27, 2025
commit 48ade40b87f261ea7ca94c1c2d3f9b9993aeebe3
4 changes: 2 additions & 2 deletions crates/pgt_text_size/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
#![warn(missing_debug_implementations, missing_docs)]

mod range;
mod range_adjustment_tracker;
mod size;
mod text_range_replacement;
mod traits;

#[cfg(feature = "serde")]
Expand All @@ -32,8 +32,8 @@ mod schemars_impls;

pub use crate::{
range::TextRange,
range_adjustment_tracker::{RangeAdjustmentsTracker, RangeAdjustmentsTrackerBuilder},
size::TextSize,
text_range_replacement::{TextRangeReplacement, TextRangeReplacementBuilder},
traits::TextLen,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct AdjustmentMarker {
#[allow(dead_code)]
original_range: TextRange,
adjusted_range: TextRange,
replacement_txt: String,
registered_offset_at_start: TextSize,

#[allow(dead_code)]
Expand Down Expand Up @@ -38,6 +39,7 @@ impl AdjustmentMarker {
AdjustmentMarker {
original_range,
adjustment_direction,
replacement_txt: replacement_txt.into(),
range_size_difference: TextSize::new(range_size_difference.try_into().unwrap()),

// will be calculated during `.build()`
Expand Down Expand Up @@ -65,20 +67,18 @@ impl AdjustmentMarker {
/// This builder allows you to register multiple text replacements and their effects on ranges,
/// then build a tracker that can map positions between the original and adjusted text.
#[derive(Debug)]
pub struct RangeAdjustmentsTrackerBuilder {
pub struct TextRangeReplacementBuilder {
markers: Vec<AdjustmentMarker>,
text: String,
}

impl Default for RangeAdjustmentsTrackerBuilder {
fn default() -> Self {
Self::new()
}
}

impl RangeAdjustmentsTrackerBuilder {
impl TextRangeReplacementBuilder {
/// Creates a new empty builder for range adjustments tracking.
pub fn new() -> Self {
Self { markers: vec![] }
pub fn new(text: &str) -> Self {
Self {
markers: vec![],
text: text.to_string(),
}
}

/// Registers a text replacement that affects range positions.
Expand All @@ -87,7 +87,7 @@ impl RangeAdjustmentsTrackerBuilder {
///
/// * `original_range` - The range in the original text that will be replaced
/// * `replacement_text` - The text that will replace the content in the original range
pub fn register_adjustment(&mut self, original_range: TextRange, replacement_text: &str) {
pub fn replace_range(&mut self, original_range: TextRange, replacement_text: &str) {
if usize::from(original_range.len()) == replacement_text.len() {
return;
}
Expand All @@ -105,7 +105,7 @@ impl RangeAdjustmentsTrackerBuilder {
/// # Panics
///
/// Panics if any adjustment involves shortening the text, as this is not yet implemented.
pub fn build(mut self) -> RangeAdjustmentsTracker {
pub fn build(mut self) -> TextRangeReplacement {
self.markers.sort_by_key(|r| r.original_range.start());

let mut total_offset: usize = 0;
Expand All @@ -132,8 +132,15 @@ impl RangeAdjustmentsTrackerBuilder {
}
}

RangeAdjustmentsTracker {
for marker in self.markers.iter().rev() {
let std_range: std::ops::Range<usize> = marker.original_range.into();
self.text
.replace_range(std_range, marker.replacement_txt.as_str());
}

TextRangeReplacement {
markers: self.markers,
text: self.text,
}
}
}
Expand All @@ -150,11 +157,17 @@ impl RangeAdjustmentsTrackerBuilder {
/// `$2` with `auth.users`, this tracker can map positions in the adjusted text
/// `"select email from auth.users"` back to positions in the original text.
#[derive(Debug)]
pub struct RangeAdjustmentsTracker {
pub struct TextRangeReplacement {
markers: Vec<AdjustmentMarker>,
text: String,
}

impl RangeAdjustmentsTracker {
impl TextRangeReplacement {
/// Returns the adjusted text.
pub fn text(&self) -> &str {
&self.text
}

/// Maps a position in the adjusted text back to its corresponding position in the original text.
///
///
Expand Down Expand Up @@ -208,68 +221,50 @@ impl RangeAdjustmentsTracker {
mod tests {
use crate::TextSize;

use crate::range_adjustment_tracker::RangeAdjustmentsTrackerBuilder;

fn range_of_substr(full_str: &str, sub_str: &str) -> Option<std::ops::Range<usize>> {
for (i, _) in full_str.char_indices() {
if &full_str[i..i + sub_str.len()] == sub_str {
return Some(i..i + sub_str.len());
}
}
None
}
use crate::text_range_replacement::TextRangeReplacementBuilder;

#[test]
fn tracks_adjustments() {
let mut sql = "select $1 from $2 where $3 = $4 limit 5;".to_string();
let sql = "select $1 from $2 where $3 = $4 limit 5;";

let range_1: std::ops::Range<usize> = 7..9;
let range_2: std::ops::Range<usize> = 15..17;
let range_3: std::ops::Range<usize> = 24..26;
let range_4: std::ops::Range<usize> = 29..31;
let og_end = sql.len();

let mut adjustment_tracker_builder = RangeAdjustmentsTrackerBuilder::new();
let mut replacement_builder = TextRangeReplacementBuilder::new(sql);

let replacement_4 = "'00000000-0000-0000-0000-000000000000'";
adjustment_tracker_builder
.register_adjustment(range_4.clone().try_into().unwrap(), replacement_4);
sql.replace_range(range_4.clone(), replacement_4);

let replacement_3 = "id";
adjustment_tracker_builder
.register_adjustment(range_3.clone().try_into().unwrap(), replacement_3);
sql.replace_range(range_3.clone(), replacement_3);

let replacement_2 = "auth.users";
adjustment_tracker_builder
.register_adjustment(range_2.clone().try_into().unwrap(), replacement_2);
sql.replace_range(range_2.clone(), replacement_2);

let replacement_1 = "email";
adjustment_tracker_builder
.register_adjustment(range_1.clone().try_into().unwrap(), replacement_1);
sql.replace_range(range_1.clone(), replacement_1);

// start in the middle – the tracker builder can deal with unordered registers
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!(
sql.as_str(),
text_replacement.text(),
"select email from auth.users where id = '00000000-0000-0000-0000-000000000000' limit 5;"
);

let adjustment_tracker = adjustment_tracker_builder.build();

let repl_range_1 = range_of_substr(sql.as_str(), replacement_1).unwrap();
let repl_range_2 = range_of_substr(sql.as_str(), replacement_2).unwrap();
let repl_range_3 = range_of_substr(sql.as_str(), replacement_3).unwrap();
let repl_range_4 = range_of_substr(sql.as_str(), replacement_4).unwrap();
let repl_range_1 = 7..12;
let repl_range_2 = 18..28;
let repl_range_3 = 35..37;
let repl_range_4 = 40..78;

// |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 0..repl_range_1.clone().start {
let between_og_0_1 = 0..range_1.start;
let adjusted =
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
assert!(between_og_0_1.contains(&usize::from(adjusted)));
}

Expand All @@ -278,7 +273,7 @@ mod tests {
// select |$1| from $2 where $3 = $4 limit 5;
for i in repl_range_1.clone() {
let pos = TextSize::new(i.try_into().unwrap());
let og_pos = adjustment_tracker.to_original_position(pos);
let og_pos = text_replacement.to_original_position(pos);
assert!(range_1.contains(&usize::from(og_pos)));
}

Expand All @@ -288,7 +283,7 @@ mod tests {
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 =
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
assert!(between_og_1_2.contains(&usize::from(adjusted)));
}

Expand All @@ -297,7 +292,7 @@ mod tests {
// select $1 from |$2| where $3 = $4 limit 5;
for i in repl_range_2.clone() {
let pos = TextSize::new(i.try_into().unwrap());
let og_pos = adjustment_tracker.to_original_position(pos);
let og_pos = text_replacement.to_original_position(pos);
assert!(range_2.contains(&usize::from(og_pos)));
}

Expand All @@ -307,7 +302,7 @@ mod tests {
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 =
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
assert!(between_og_2_3.contains(&usize::from(adjusted)));
}

Expand All @@ -318,7 +313,7 @@ mod tests {
// NOTE: this isn't even hanlded by the tracker, since `id` has the same length as `$3`
for i in repl_range_3.clone() {
let pos = TextSize::new(i.try_into().unwrap());
let og_pos = adjustment_tracker.to_original_position(pos);
let og_pos = text_replacement.to_original_position(pos);
assert!(range_3.contains(&usize::from(og_pos)));
}

Expand All @@ -328,7 +323,7 @@ mod tests {
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 =
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
assert!(between_og_3_4.contains(&usize::from(adjusted)));
}

Expand All @@ -337,7 +332,7 @@ mod tests {
// select $1 from $2 where $3 = |$4| limit 5;
for i in repl_range_4.clone() {
let pos = TextSize::new(i.try_into().unwrap());
let og_pos = adjustment_tracker.to_original_position(pos);
let og_pos = text_replacement.to_original_position(pos);
assert!(range_4.contains(&usize::from(og_pos)));
}

Expand All @@ -347,7 +342,7 @@ mod tests {
for i in repl_range_4.end..sql.len() {
let between_og_4_end = range_4.end..og_end;
let adjusted =
adjustment_tracker.to_original_position(TextSize::new(i.try_into().unwrap()));
text_replacement.to_original_position(TextSize::new(i.try_into().unwrap()));
assert!(between_og_4_end.contains(&usize::from(adjusted)));
}
}
Expand Down
7 changes: 3 additions & 4 deletions crates/pgt_typecheck/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io;

use pgt_console::markup;
use pgt_diagnostics::{Advices, Diagnostic, LogCategory, MessageAndDescription, Severity, Visit};
use pgt_text_size::{RangeAdjustmentsTracker, TextRange, TextSize};
use pgt_text_size::{TextRange, TextRangeReplacement, TextSize};
use sqlx::postgres::{PgDatabaseError, PgSeverity};

/// A specialized diagnostic for the typechecker.
Expand Down Expand Up @@ -97,16 +97,15 @@ impl Advices for TypecheckAdvices {
pub(crate) fn create_type_error(
pg_err: &PgDatabaseError,
ts: &tree_sitter::Tree,
range_adjustments: RangeAdjustmentsTracker,
txt_replacement: TextRangeReplacement,
) -> TypecheckDiagnostic {
let position = pg_err.position().and_then(|pos| match pos {
sqlx::postgres::PgErrorPosition::Original(pos) => Some(pos - 1),
_ => None,
});

let range = position.and_then(|pos| {
let adjusted =
range_adjustments.to_original_position(TextSize::new(pos.try_into().unwrap()));
let adjusted = txt_replacement.to_original_position(TextSize::new(pos.try_into().unwrap()));

ts.root_node()
.named_descendant_for_byte_range(adjusted.into(), adjusted.into())
Expand Down
10 changes: 3 additions & 7 deletions crates/pgt_typecheck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub async fn check_sql(
// each typecheck operation.
conn.close_on_drop();

let (prepared, range_adjustments) = apply_identifiers(
let replacement = apply_identifiers(
params.identifiers,
params.schema_cache,
params.tree,
Expand All @@ -68,17 +68,13 @@ pub async fn check_sql(
conn.execute(&*search_path_query).await?;
}

let res = conn.prepare(&prepared).await;
let res = conn.prepare(&replacement.text()).await;

match res {
Ok(_) => Ok(None),
Err(sqlx::Error::Database(err)) => {
let pg_err = err.downcast_ref::<PgDatabaseError>();
Ok(Some(create_type_error(
pg_err,
params.tree,
range_adjustments,
)))
Ok(Some(create_type_error(pg_err, params.tree, replacement)))
}
Err(err) => Err(err),
}
Expand Down
Loading
Loading