Skip to content

Add SqlStr #3723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3eeb165
refactor: introduce `SqlSafeStr` API
abonander Apr 23, 2024
35e368c
rebase main
joeydewaal Feb 17, 2025
cb9b7d0
Add SqlStr + remove Statement lifetime
joeydewaal Feb 1, 2025
6dd8fd6
Update the definition of Executor and AnyConnectionBackend + update P…
joeydewaal Feb 1, 2025
d836f65
Update MySql driver
joeydewaal Feb 1, 2025
88d9f8f
Update Sqlite driver
joeydewaal Feb 1, 2025
db51756
remove debug clone count
joeydewaal Feb 1, 2025
694682d
Reduce the amount of SqlStr clones
joeydewaal Feb 1, 2025
a6aa63d
improve QueryBuilder error message
joeydewaal Feb 1, 2025
28bc322
cargo fmt
joeydewaal Feb 17, 2025
a0f122b
fix clippy warnings
joeydewaal Feb 17, 2025
a0b6739
fix doc test
joeydewaal Feb 17, 2025
3a90efc
Merge branch 'main' into sqlstr
joeydewaal Jul 5, 2025
09018d0
Avoid panic in `QueryBuilder::reset`
joeydewaal Jul 5, 2025
99fc942
Use `QueryBuilder` when removing all test db's
joeydewaal Jul 5, 2025
211aa78
Add comment to `SqlStr`
joeydewaal Jul 6, 2025
afb3cc5
Update sqlx-core/src/query_builder.rs
joeydewaal Jul 6, 2025
e89de06
Add `Clone` as supertrait to `Statement`
joeydewaal Jul 6, 2025
d962d70
Move `Connection`, `AnyConnectionBackend` and `TransactionManager` to…
joeydewaal Jul 6, 2025
4e09194
Replace `sql_cloned` with `sql` in `Statement`
joeydewaal Jul 6, 2025
af52820
Update `Executor` trait
joeydewaal Jul 6, 2025
d13a184
Update unit tests + QueryBuilder changes
joeydewaal Jul 6, 2025
abe2b2f
Remove code in comments
joeydewaal Jul 6, 2025
d098847
Update comment in `QueryBuilder`
joeydewaal Jul 6, 2025
627c2fa
Merge branch 'main' into sqlstr
joeydewaal Jul 6, 2025
ad522e5
Fix clippy warnings
joeydewaal Jul 6, 2025
61f4f43
Update `Migrate` comment
joeydewaal Jul 6, 2025
fa2910f
Small changes
joeydewaal Jul 6, 2025
1607a11
Move `Migration` to `SqlStr`
joeydewaal Jul 6, 2025
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
11 changes: 6 additions & 5 deletions sqlx-core/src/any/connection/backend.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::any::{Any, AnyArguments, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo};
use crate::describe::Describe;
use crate::sql_str::SqlStr;
use either::Either;
use futures_core::future::BoxFuture;
use futures_core::stream::BoxStream;
Expand Down Expand Up @@ -96,23 +97,23 @@ pub trait AnyConnectionBackend: std::any::Any + Debug + Send + 'static {

fn fetch_many<'q>(
&'q mut self,
query: &'q str,
query: SqlStr,
persistent: bool,
arguments: Option<AnyArguments<'q>>,
) -> BoxStream<'q, crate::Result<Either<AnyQueryResult, AnyRow>>>;

fn fetch_optional<'q>(
&'q mut self,
query: &'q str,
query: SqlStr,
persistent: bool,
arguments: Option<AnyArguments<'q>>,
) -> BoxFuture<'q, crate::Result<Option<AnyRow>>>;

fn prepare_with<'c, 'q: 'c>(
&'c mut self,
sql: &'q str,
sql: SqlStr,
parameters: &[AnyTypeInfo],
) -> BoxFuture<'c, crate::Result<AnyStatement<'q>>>;
) -> BoxFuture<'c, crate::Result<AnyStatement>>;

fn describe<'q>(&'q mut self, sql: &'q str) -> BoxFuture<'q, crate::Result<Describe<Any>>>;
fn describe(&mut self, sql: SqlStr) -> BoxFuture<'_, crate::Result<Describe<Any>>>;
}
22 changes: 12 additions & 10 deletions sqlx-core/src/any/connection/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::any::{Any, AnyConnection, AnyQueryResult, AnyRow, AnyStatement, AnyTy
use crate::describe::Describe;
use crate::error::Error;
use crate::executor::{Execute, Executor};
use crate::sql_str::SqlSafeStr;
use either::Either;
use futures_core::future::BoxFuture;
use futures_core::stream::BoxStream;
Expand All @@ -23,8 +24,8 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
Ok(arguments) => arguments,
Err(error) => return stream::once(future::ready(Err(error))).boxed(),
};
self.backend
.fetch_many(query.sql(), query.persistent(), arguments)
let persistent = query.persistent();
self.backend.fetch_many(query.sql(), persistent, arguments)
}

fn fetch_optional<'e, 'q: 'e, E>(
Expand All @@ -39,28 +40,29 @@ impl<'c> Executor<'c> for &'c mut AnyConnection {
Ok(arguments) => arguments,
Err(error) => return future::ready(Err(error)).boxed(),
};
let persistent = query.persistent();
self.backend
.fetch_optional(query.sql(), query.persistent(), arguments)
.fetch_optional(query.sql(), persistent, arguments)
}

fn prepare_with<'e, 'q: 'e>(
fn prepare_with<'e>(
self,
sql: &'q str,
sql: impl SqlSafeStr,
parameters: &[AnyTypeInfo],
) -> BoxFuture<'e, Result<AnyStatement<'q>, Error>>
) -> BoxFuture<'e, Result<AnyStatement, Error>>
where
'c: 'e,
{
self.backend.prepare_with(sql, parameters)
self.backend.prepare_with(sql.into_sql_str(), parameters)
}

fn describe<'e, 'q: 'e>(
fn describe<'e>(
self,
sql: &'q str,
sql: impl SqlSafeStr,
) -> BoxFuture<'e, Result<Describe<Self::Database>, Error>>
where
'c: 'e,
{
self.backend.describe(sql)
self.backend.describe(sql.into_sql_str())
}
}
2 changes: 1 addition & 1 deletion sqlx-core/src/any/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Database for Any {
type Arguments<'q> = AnyArguments<'q>;
type ArgumentBuffer<'q> = AnyArgumentBuffer<'q>;

type Statement<'q> = AnyStatement<'q>;
type Statement = AnyStatement;

const NAME: &'static str = "Any";

Expand Down
35 changes: 19 additions & 16 deletions sqlx-core/src/any/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use crate::column::ColumnIndex;
use crate::database::Database;
use crate::error::Error;
use crate::ext::ustr::UStr;
use crate::sql_str::SqlStr;
use crate::statement::Statement;
use crate::HashMap;
use either::Either;
use std::borrow::Cow;
use std::sync::Arc;

pub struct AnyStatement<'q> {
pub struct AnyStatement {
#[doc(hidden)]
pub sql: Cow<'q, str>,
pub sql: SqlStr,
#[doc(hidden)]
pub parameters: Option<Either<Vec<AnyTypeInfo>, usize>>,
#[doc(hidden)]
Expand All @@ -20,20 +20,24 @@ pub struct AnyStatement<'q> {
pub columns: Vec<AnyColumn>,
}

impl<'q> Statement<'q> for AnyStatement<'q> {
impl Statement for AnyStatement {
type Database = Any;

fn to_owned(&self) -> AnyStatement<'static> {
AnyStatement::<'static> {
sql: Cow::Owned(self.sql.clone().into_owned()),
fn to_owned(&self) -> AnyStatement {
AnyStatement {
sql: self.sql.clone(),
column_names: self.column_names.clone(),
parameters: self.parameters.clone(),
columns: self.columns.clone(),
}
}

fn sql(&self) -> &str {
&self.sql
fn sql_cloned(&self) -> SqlStr {
self.sql.clone()
}

fn into_sql(self) -> SqlStr {
self.sql
}

fn parameters(&self) -> Option<Either<&[AnyTypeInfo], usize>> {
Expand All @@ -51,8 +55,8 @@ impl<'q> Statement<'q> for AnyStatement<'q> {
impl_statement_query!(AnyArguments<'_>);
}

impl ColumnIndex<AnyStatement<'_>> for &'_ str {
fn index(&self, statement: &AnyStatement<'_>) -> Result<usize, Error> {
impl ColumnIndex<AnyStatement> for &'_ str {
fn index(&self, statement: &AnyStatement) -> Result<usize, Error> {
statement
.column_names
.get(*self)
Expand All @@ -61,15 +65,14 @@ impl ColumnIndex<AnyStatement<'_>> for &'_ str {
}
}

impl<'q> AnyStatement<'q> {
impl AnyStatement {
#[doc(hidden)]
pub fn try_from_statement<S>(
query: &'q str,
statement: &S,
statement: S,
column_names: Arc<HashMap<UStr, usize>>,
) -> crate::Result<Self>
where
S: Statement<'q>,
S: Statement,
AnyTypeInfo: for<'a> TryFrom<&'a <S::Database as Database>::TypeInfo, Error = Error>,
AnyColumn: for<'a> TryFrom<&'a <S::Database as Database>::Column, Error = Error>,
{
Expand All @@ -91,7 +94,7 @@ impl<'q> AnyStatement<'q> {
.collect::<Result<Vec<_>, _>>()?;

Ok(Self {
sql: query.into(),
sql: statement.into_sql(),
columns,
column_names,
parameters,
Expand Down
4 changes: 2 additions & 2 deletions sqlx-core/src/column.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ macro_rules! impl_column_index_for_row {
#[macro_export]
macro_rules! impl_column_index_for_statement {
($S:ident) => {
impl $crate::column::ColumnIndex<$S<'_>> for usize {
fn index(&self, statement: &$S<'_>) -> Result<usize, $crate::error::Error> {
impl $crate::column::ColumnIndex<$S> for usize {
fn index(&self, statement: &$S) -> Result<usize, $crate::error::Error> {
let len = $crate::statement::Statement::columns(statement).len();

if *self >= len {
Expand Down
2 changes: 1 addition & 1 deletion sqlx-core/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub trait Database: 'static + Sized + Send + Debug {
type ArgumentBuffer<'q>;

/// The concrete `Statement` implementation for this database.
type Statement<'q>: Statement<'q, Database = Self>;
type Statement: Statement<Database = Self>;

/// The display name for this database driver.
const NAME: &'static str;
Expand Down
49 changes: 27 additions & 22 deletions sqlx-core/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::database::Database;
use crate::describe::Describe;
use crate::error::{BoxDynError, Error};
use crate::sql_str::{SqlSafeStr, SqlStr};

use either::Either;
use futures_core::future::BoxFuture;
Expand Down Expand Up @@ -148,10 +149,10 @@ pub trait Executor<'c>: Send + Debug + Sized {
/// This explicit API is provided to allow access to the statement metadata available after
/// it prepared but before the first row is returned.
#[inline]
fn prepare<'e, 'q: 'e>(
fn prepare<'e>(
self,
query: &'q str,
) -> BoxFuture<'e, Result<<Self::Database as Database>::Statement<'q>, Error>>
query: impl SqlSafeStr,
) -> BoxFuture<'e, Result<<Self::Database as Database>::Statement, Error>>
where
'c: 'e,
{
Expand All @@ -163,11 +164,11 @@ pub trait Executor<'c>: Send + Debug + Sized {
///
/// Only some database drivers (PostgreSQL, MSSQL) can take advantage of
/// this extra information to influence parameter type inference.
fn prepare_with<'e, 'q: 'e>(
fn prepare_with<'e>(
self,
sql: &'q str,
sql: impl SqlSafeStr,
parameters: &'e [<Self::Database as Database>::TypeInfo],
) -> BoxFuture<'e, Result<<Self::Database as Database>::Statement<'q>, Error>>
) -> BoxFuture<'e, Result<<Self::Database as Database>::Statement, Error>>
where
'c: 'e;

Expand All @@ -177,9 +178,9 @@ pub trait Executor<'c>: Send + Debug + Sized {
/// This is used by compile-time verification in the query macros to
/// power their type inference.
#[doc(hidden)]
fn describe<'e, 'q: 'e>(
fn describe<'e>(
self,
sql: &'q str,
sql: impl SqlSafeStr,
) -> BoxFuture<'e, Result<Describe<Self::Database>, Error>>
where
'c: 'e;
Expand All @@ -194,10 +195,10 @@ pub trait Executor<'c>: Send + Debug + Sized {
///
pub trait Execute<'q, DB: Database>: Send + Sized {
/// Gets the SQL that will be executed.
fn sql(&self) -> &'q str;
fn sql(self) -> SqlStr;

/// Gets the previously cached statement, if available.
fn statement(&self) -> Option<&DB::Statement<'q>>;
fn statement(&self) -> Option<&DB::Statement>;

/// Returns the arguments to be bound against the query string.
///
Expand All @@ -212,22 +213,23 @@ pub trait Execute<'q, DB: Database>: Send + Sized {
fn persistent(&self) -> bool;
}

// NOTE: `Execute` is explicitly not implemented for String and &String to make it slightly more
// involved to write `conn.execute(format!("SELECT {val}"))`
impl<'q, DB: Database> Execute<'q, DB> for &'q str {
impl<'q, DB: Database, T> Execute<'q, DB> for (T, Option<<DB as Database>::Arguments<'q>>)
where
T: SqlSafeStr + Send,
{
#[inline]
fn sql(&self) -> &'q str {
self
fn sql(self) -> SqlStr {
self.0.into_sql_str()
}

#[inline]
fn statement(&self) -> Option<&DB::Statement<'q>> {
fn statement(&self) -> Option<&DB::Statement> {
None
}

#[inline]
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
Ok(None)
Ok(self.1.take())
}

#[inline]
Expand All @@ -236,20 +238,23 @@ impl<'q, DB: Database> Execute<'q, DB> for &'q str {
}
}

impl<'q, DB: Database> Execute<'q, DB> for (&'q str, Option<<DB as Database>::Arguments<'q>>) {
impl<'q, DB: Database, T> Execute<'q, DB> for T
where
T: SqlSafeStr + Send,
{
#[inline]
fn sql(&self) -> &'q str {
self.0
fn sql(self) -> SqlStr {
self.into_sql_str()
}

#[inline]
fn statement(&self) -> Option<&DB::Statement<'q>> {
fn statement(&self) -> Option<&DB::Statement> {
None
}

#[inline]
fn take_arguments(&mut self) -> Result<Option<<DB as Database>::Arguments<'q>>, BoxDynError> {
Ok(self.1.take())
Ok(None)
}

#[inline]
Expand Down
1 change: 1 addition & 0 deletions sqlx-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub mod net;
pub mod query_as;
pub mod query_builder;
pub mod query_scalar;
pub mod sql_str;

pub mod raw_sql;
pub mod row;
Expand Down
Loading