Skip to content

Commit 0fdb875

Browse files
committed
support arbitrary numbers of bind parameters in query!() et al
1 parent da5c538 commit 0fdb875

File tree

10 files changed

+66
-124
lines changed

10 files changed

+66
-124
lines changed

sqlx-core/src/arguments.rs

Lines changed: 9 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -38,123 +38,22 @@ where
3838
fn into_arguments(self) -> DB::Arguments;
3939
}
4040

41-
impl<DB> IntoArguments<DB> for DB::Arguments
41+
impl<A> IntoArguments<A::Database> for A
4242
where
43-
DB: Database,
43+
A: Arguments,
44+
A::Database: Database<Arguments = Self> + Sized,
4445
{
4546
#[inline]
46-
fn into_arguments(self) -> DB::Arguments {
47+
fn into_arguments(self) -> Self {
4748
self
4849
}
4950
}
5051

51-
#[allow(unused)]
52-
macro_rules! impl_into_arguments {
53-
($B:ident: $( ($idx:tt) -> $T:ident );+;) => {
54-
impl<$($T,)+> crate::arguments::IntoArguments<$B> for ($($T,)+)
55-
where
56-
$($B: crate::types::HasSqlType<$T>,)+
57-
$($T: crate::encode::Encode<$B>,)+
58-
{
59-
fn into_arguments(self) -> <$B as crate::database::Database>::Arguments {
60-
use crate::arguments::Arguments;
61-
62-
let mut arguments = <$B as crate::database::Database>::Arguments::default();
63-
64-
let binds = 0 $(+ { $idx; 1 } )+;
65-
let bytes = 0 $(+ crate::encode::Encode::size_hint(&self.$idx))+;
66-
67-
arguments.reserve(binds, bytes);
68-
69-
$(crate::arguments::Arguments::add(&mut arguments, self.$idx);)+
70-
71-
arguments
72-
}
73-
}
74-
};
75-
}
76-
77-
#[allow(unused)]
78-
macro_rules! impl_into_arguments_for_database {
79-
($B:ident) => {
80-
impl crate::arguments::IntoArguments<$B> for ()
81-
{
82-
#[inline]
83-
fn into_arguments(self) -> <$B as crate::database::Database>::Arguments {
84-
Default::default()
85-
}
86-
}
87-
88-
impl_into_arguments!($B:
89-
(0) -> T1;
90-
);
91-
92-
impl_into_arguments!($B:
93-
(0) -> T1;
94-
(1) -> T2;
95-
);
96-
97-
impl_into_arguments!($B:
98-
(0) -> T1;
99-
(1) -> T2;
100-
(2) -> T3;
101-
);
102-
103-
impl_into_arguments!($B:
104-
(0) -> T1;
105-
(1) -> T2;
106-
(2) -> T3;
107-
(3) -> T4;
108-
);
109-
110-
impl_into_arguments!($B:
111-
(0) -> T1;
112-
(1) -> T2;
113-
(2) -> T3;
114-
(3) -> T4;
115-
(4) -> T5;
116-
);
117-
118-
impl_into_arguments!($B:
119-
(0) -> T1;
120-
(1) -> T2;
121-
(2) -> T3;
122-
(3) -> T4;
123-
(4) -> T5;
124-
(5) -> T6;
125-
);
126-
127-
impl_into_arguments!($B:
128-
(0) -> T1;
129-
(1) -> T2;
130-
(2) -> T3;
131-
(3) -> T4;
132-
(4) -> T5;
133-
(5) -> T6;
134-
(6) -> T7;
135-
);
136-
137-
impl_into_arguments!($B:
138-
(0) -> T1;
139-
(1) -> T2;
140-
(2) -> T3;
141-
(3) -> T4;
142-
(4) -> T5;
143-
(5) -> T6;
144-
(6) -> T7;
145-
(7) -> T8;
146-
);
52+
#[doc(hidden)]
53+
pub struct ImmutableArguments<DB: Database>(pub DB::Arguments);
14754

148-
impl_into_arguments!($B:
149-
(0) -> T1;
150-
(1) -> T2;
151-
(2) -> T3;
152-
(3) -> T4;
153-
(4) -> T5;
154-
(5) -> T6;
155-
(6) -> T7;
156-
(7) -> T8;
157-
(8) -> T9;
158-
);
55+
impl<DB: Database> IntoArguments<DB> for ImmutableArguments<DB> {
56+
fn into_arguments(self) -> <DB as Database>::Arguments {
57+
self.0
15958
}
16059
}

sqlx-core/src/mysql/database.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,3 @@ impl Database for MySql {
1414

1515
type TableId = Box<str>;
1616
}
17-
18-
impl_into_arguments_for_database!(MySql);

sqlx-core/src/postgres/arguments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use byteorder::{ByteOrder, NetworkEndian};
22

3-
use crate::arguments::Arguments;
3+
use crate::arguments::{Arguments, IntoArguments};
44
use crate::encode::{Encode, IsNull};
55
use crate::io::BufMut;
66
use crate::types::HasSqlType;

sqlx-core/src/postgres/database.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,3 @@ impl Database for Postgres {
1414

1515
type TableId = u32;
1616
}
17-
18-
impl_into_arguments_for_database!(Postgres);

sqlx-core/src/query_as.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use futures_core::Stream;
22
use futures_util::{future, TryStreamExt};
33

4-
use crate::arguments::Arguments;
4+
use crate::arguments::{Arguments, ImmutableArguments};
55
use crate::{
66
arguments::IntoArguments, database::Database, encode::Encode, executor::Executor, row::FromRow,
77
types::HasSqlType,
@@ -128,13 +128,10 @@ where
128128

129129
// used by query!() and friends
130130
#[doc(hidden)]
131-
pub fn bind_all<I>(self, values: I) -> QueryAs<'q, DB, R, I>
132-
where
133-
I: IntoArguments<DB>,
134-
{
131+
pub fn bind_all(self, values: DB::Arguments) -> QueryAs<'q, DB, R, ImmutableArguments<DB>> {
135132
QueryAs {
136133
query: self.query,
137-
args: values,
134+
args: ImmutableArguments(values),
138135
map_row: self.map_row,
139136
}
140137
}

sqlx-macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ macro_rules! async_macro (
8585
macro_result(parse_err.to_compile_error())
8686
} else {
8787
let msg = format!("{:?}", e);
88-
macro_result(quote!(compile_error(#msg)))
88+
macro_result(quote!(compile_error!(#msg)))
8989
}
9090
}
9191
}

sqlx-macros/src/query_macros/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub fn quote_args<DB: DatabaseExt>(
5959
let args = input.arg_names.iter();
6060

6161
Ok(quote! {
62+
// emit as a tuple first so each expression is only evaluated once
6263
let args = (#(&$#args),*,);
6364
#args_check
6465
})

sqlx-macros/src/query_macros/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,26 @@ where
5555
&columns,
5656
);
5757

58+
let db_path = <C::Database as DatabaseExt>::quotable_path();
59+
let args_count = arg_names.len();
60+
let arg_indices = (0..args_count).map(|i| syn::Index::from(i));
61+
let arg_indices_2 = arg_indices.clone();
62+
5863
Ok(quote! {
5964
macro_rules! macro_result {
6065
(#($#arg_names:expr),*) => {{
66+
use sqlx::arguments::Arguments as _;
67+
6168
#args_tokens
62-
#output.bind_all(args)
69+
70+
let mut query_args = <#db_path as sqlx::Database>::Arguments::default();
71+
query_args.reserve(
72+
#args_count,
73+
0 #(+ sqlx::encode::Encode::<#db_path>::size_hint(args.#arg_indices))*
74+
);
75+
#(query_args.add(args.#arg_indices_2);)*
76+
77+
#output.bind_all(query_args)
6378
}}
6479
}
6580
})

sqlx-macros/src/query_macros/query.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,32 @@ where
5151

5252
let output = output::quote_query_as::<C::Database>(sql, &record_type, &columns);
5353
let arg_names = &input.arg_names;
54+
let args_count = arg_names.len();
55+
let arg_indices = (0..args_count).map(|i| syn::Index::from(i));
56+
let arg_indices_2 = arg_indices.clone();
57+
let db_path = <C::Database as DatabaseExt>::quotable_path();
5458

5559
Ok(quote! {
5660
macro_rules! macro_result {
5761
(#($#arg_names:expr),*) => {{
62+
use sqlx::arguments::Arguments as _;
63+
5864
#[derive(Debug)]
5965
struct #record_type {
6066
#record_fields
6167
}
6268

6369
#args
6470

65-
#output.bind_all(args)
71+
let mut query_args = <#db_path as sqlx::Database>::Arguments::default();
72+
query_args.reserve(
73+
#args_count,
74+
0 #(+ sqlx::encode::Encode::<#db_path>::size_hint(args.#arg_indices))*
75+
);
76+
77+
#(query_args.add(args.#arg_indices_2);)*
78+
79+
#output.bind_all(query_args)
6680
}
6781
}}
6882
})

tests/postgres-macros.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,23 @@ async fn test_nullable_err() -> sqlx::Result<()> {
108108
panic!("expected `UnexpectedNull`, got {}", err)
109109
}
110110
}
111+
112+
#[async_std::test]
113+
async fn test_many_args() -> sqlx::Result<()> {
114+
let mut conn = sqlx::postgres::connect(&dotenv::var("DATABASE_URL").unwrap()).await?;
115+
116+
// previous implementation would only have supported 10 bind parameters
117+
// (this is really gross to test in MySQL)
118+
let rows = sqlx::query!(
119+
"SELECT * from unnest(array[$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12]::int[]) ids(id);",
120+
0i32, 1i32, 2i32, 3i32, 4i32, 5i32, 6i32, 7i32, 8i32, 9i32, 10i32, 11i32
121+
)
122+
.fetch_all(&mut conn)
123+
.await?;
124+
125+
for (i, row) in rows.iter().enumerate() {
126+
assert_eq!(i as i32, row.id);
127+
}
128+
129+
Ok(())
130+
}

0 commit comments

Comments
 (0)