Skip to content

Commit 6a68d24

Browse files
committed
fix(any): rewrite ? placeholders to $N for Postgres backend (#3000)
1 parent 1d674f5 commit 6a68d24

4 files changed

Lines changed: 328 additions & 1 deletion

File tree

sqlx-core/src/any/arguments.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,27 @@ use std::sync::Arc;
1010
pub struct AnyArguments {
1111
#[doc(hidden)]
1212
pub values: AnyArgumentBuffer,
13+
14+
/// Byte offsets, into the query string being built by
15+
/// [`QueryBuilder`][crate::query_builder::QueryBuilder], of each `?` placeholder written by
16+
/// [`push_bind()`][crate::query_builder::QueryBuilder::push_bind], in the order they were
17+
/// added.
18+
///
19+
/// This is empty unless the query was built with `QueryBuilder<Any>`; e.g. a plain
20+
/// `sqlx::query()` call with a hand-written `?` in the SQL string has no way to report
21+
/// where that `?` is, since the string is opaque to us. Backends that can't use `?`
22+
/// natively (namely Postgres) use this, when available, to rewrite placeholders precisely
23+
/// instead of re-parsing the query string.
24+
#[doc(hidden)]
25+
pub placeholder_offsets: Vec<usize>,
1326
}
1427

1528
impl Arguments for AnyArguments {
1629
type Database = Any;
1730

1831
fn reserve(&mut self, additional: usize, _size: usize) {
1932
self.values.0.reserve(additional);
33+
self.placeholder_offsets.reserve(additional);
2034
}
2135

2236
fn add<'t, T>(&mut self, value: T) -> Result<(), BoxDynError>
@@ -30,6 +44,10 @@ impl Arguments for AnyArguments {
3044
fn len(&self) -> usize {
3145
self.values.0.len()
3246
}
47+
48+
fn note_placeholder_offset(&mut self, offset: usize) {
49+
self.placeholder_offsets.push(offset);
50+
}
3351
}
3452

3553
#[derive(Default)]

sqlx-core/src/arguments.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ pub trait Arguments: Send + Sized + Default {
2727
fn format_placeholder<W: Write>(&self, writer: &mut W) -> fmt::Result {
2828
writer.write_str("?")
2929
}
30+
31+
/// Called by [`QueryBuilder::push_bind()`][crate::query_builder::QueryBuilder::push_bind]
32+
/// with the byte offset, into the query string being built, at which the placeholder for
33+
/// this argument is about to be written by [`format_placeholder()`][Self::format_placeholder].
34+
///
35+
/// Most backends write an unambiguous placeholder immediately (Postgres writes `$1`, `$2`,
36+
/// ...) and so have no need to remember where it ended up; the default implementation does
37+
/// nothing.
38+
///
39+
/// The `Any` driver overrides this: it always writes a plain `?`, since the real backend
40+
/// isn't known yet when `QueryBuilder<Any>` is being built. Recording the exact offset of
41+
/// each `?` lets the backend-specific driver (e.g. Postgres) rewrite them precisely once the
42+
/// backend *is* known, instead of re-parsing the finished SQL string to guess which `?`
43+
/// characters are placeholders as opposed to, say, part of a string literal or comment.
44+
fn note_placeholder_offset(&mut self, _offset: usize) {}
3045
}
3146

3247
pub trait IntoArguments<DB: Database>: Sized + Send {

sqlx-core/src/query_builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ where
165165
arguments.add(value).expect("Failed to add argument");
166166

167167
let query: &mut String = Arc::get_mut(&mut self.query).expect(ERROR);
168+
arguments.note_placeholder_offset(query.len());
168169
arguments
169170
.format_placeholder(query)
170171
.expect("error in format_placeholder");

0 commit comments

Comments
 (0)