Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions sqlx-core/src/any/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,27 @@ use std::sync::Arc;
pub struct AnyArguments {
#[doc(hidden)]
pub values: AnyArgumentBuffer,

/// Byte offsets, into the query string being built by
/// [`QueryBuilder`][crate::query_builder::QueryBuilder], of each `?` placeholder written by
/// [`push_bind()`][crate::query_builder::QueryBuilder::push_bind], in the order they were
/// added.
///
/// This is empty unless the query was built with `QueryBuilder<Any>`; e.g. a plain
/// `sqlx::query()` call with a hand-written `?` in the SQL string has no way to report
/// where that `?` is, since the string is opaque to us. Backends that can't use `?`
/// natively (namely Postgres) use this, when available, to rewrite placeholders precisely
/// instead of re-parsing the query string.
#[doc(hidden)]
pub placeholder_offsets: Vec<usize>,
}

impl Arguments for AnyArguments {
type Database = Any;

fn reserve(&mut self, additional: usize, _size: usize) {
self.values.0.reserve(additional);
self.placeholder_offsets.reserve(additional);
}

fn add<'t, T>(&mut self, value: T) -> Result<(), BoxDynError>
Expand All @@ -30,6 +44,10 @@ impl Arguments for AnyArguments {
fn len(&self) -> usize {
self.values.0.len()
}

fn note_placeholder_offset(&mut self, offset: usize) {
self.placeholder_offsets.push(offset);
}
}

#[derive(Default)]
Expand Down
15 changes: 15 additions & 0 deletions sqlx-core/src/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ pub trait Arguments: Send + Sized + Default {
fn format_placeholder<W: Write>(&self, writer: &mut W) -> fmt::Result {
writer.write_str("?")
}

/// Called by [`QueryBuilder::push_bind()`][crate::query_builder::QueryBuilder::push_bind]
/// with the byte offset, into the query string being built, at which the placeholder for
/// this argument is about to be written by [`format_placeholder()`][Self::format_placeholder].
///
/// Most backends write an unambiguous placeholder immediately (Postgres writes `$1`, `$2`,
/// ...) and so have no need to remember where it ended up; the default implementation does
/// nothing.
///
/// The `Any` driver overrides this: it always writes a plain `?`, since the real backend
/// isn't known yet when `QueryBuilder<Any>` is being built. Recording the exact offset of
/// each `?` lets the backend-specific driver (e.g. Postgres) rewrite them precisely once the
/// backend *is* known, instead of re-parsing the finished SQL string to guess which `?`
/// characters are placeholders as opposed to, say, part of a string literal or comment.
fn note_placeholder_offset(&mut self, _offset: usize) {}
}

pub trait IntoArguments<DB: Database>: Sized + Send {
Expand Down
1 change: 1 addition & 0 deletions sqlx-core/src/query_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ where
arguments.add(value).expect("Failed to add argument");

let query: &mut String = Arc::get_mut(&mut self.query).expect(ERROR);
arguments.note_placeholder_offset(query.len());
arguments
.format_placeholder(query)
.expect("error in format_placeholder");
Expand Down
Loading
Loading