breaking: add SqlStr (#3723)

* refactor: introduce `SqlSafeStr` API

* rebase main

* Add SqlStr + remove Statement lifetime

* Update the definition of Executor and AnyConnectionBackend + update Postgres driver

* Update MySql driver

* Update Sqlite driver

* remove debug clone count

* Reduce the amount of SqlStr clones

* improve QueryBuilder error message

* cargo fmt

* fix clippy warnings

* fix doc test

* Avoid panic in `QueryBuilder::reset`

* Use `QueryBuilder` when removing all test db's

* Add comment to `SqlStr`

Co-authored-by: Austin Bonander <austin.bonander@gmail.com>

* Update sqlx-core/src/query_builder.rs

Co-authored-by: Austin Bonander <austin.bonander@gmail.com>

* Add `Clone` as supertrait to `Statement`

* Move `Connection`, `AnyConnectionBackend` and `TransactionManager` to `SqlStr`

* Replace `sql_cloned` with `sql` in `Statement`

* Update `Executor` trait

* Update unit tests + QueryBuilder changes

* Remove code in comments

* Update comment in `QueryBuilder`

* Fix clippy warnings

* Update `Migrate` comment

* Small changes

* Move `Migration` to `SqlStr`

---------

Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
This commit is contained in:
Joey de Waal
2025-07-07 09:35:54 +02:00
committed by GitHub
parent 2702b9851a
commit 469f22788e
74 changed files with 901 additions and 625 deletions

View File

@@ -1,5 +1,3 @@
use std::borrow::Cow;
use crate::{
Either, Sqlite, SqliteArgumentValue, SqliteArguments, SqliteColumn, SqliteConnectOptions,
SqliteConnection, SqliteQueryResult, SqliteRow, SqliteTransactionManager, SqliteTypeInfo,
@@ -12,6 +10,7 @@ use sqlx_core::any::{
Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow,
AnyStatement, AnyTypeInfo, AnyTypeInfoKind, AnyValueKind,
};
use sqlx_core::sql_str::SqlStr;
use crate::type_info::DataType;
use sqlx_core::connection::{ConnectOptions, Connection};
@@ -40,10 +39,7 @@ impl AnyConnectionBackend for SqliteConnection {
Connection::ping(self).boxed()
}
fn begin(
&mut self,
statement: Option<Cow<'static, str>>,
) -> BoxFuture<'_, sqlx_core::Result<()>> {
fn begin(&mut self, statement: Option<SqlStr>) -> BoxFuture<'_, sqlx_core::Result<()>> {
SqliteTransactionManager::begin(self, statement).boxed()
}
@@ -84,7 +80,7 @@ impl AnyConnectionBackend for SqliteConnection {
fn fetch_many<'q>(
&'q mut self,
query: &'q str,
query: SqlStr,
persistent: bool,
arguments: Option<AnyArguments<'q>>,
) -> BoxStream<'q, sqlx_core::Result<Either<AnyQueryResult, AnyRow>>> {
@@ -107,7 +103,7 @@ impl AnyConnectionBackend for SqliteConnection {
fn fetch_optional<'q>(
&'q mut self,
query: &'q str,
query: SqlStr,
persistent: bool,
arguments: Option<AnyArguments<'q>>,
) -> BoxFuture<'q, sqlx_core::Result<Option<AnyRow>>> {
@@ -132,16 +128,17 @@ impl AnyConnectionBackend for SqliteConnection {
fn prepare_with<'c, 'q: 'c>(
&'c mut self,
sql: &'q str,
sql: SqlStr,
_parameters: &[AnyTypeInfo],
) -> BoxFuture<'c, sqlx_core::Result<AnyStatement<'q>>> {
) -> BoxFuture<'c, sqlx_core::Result<AnyStatement>> {
Box::pin(async move {
let statement = Executor::prepare_with(self, sql, &[]).await?;
AnyStatement::try_from_statement(sql, &statement, statement.column_names.clone())
let column_names = statement.column_names.clone();
AnyStatement::try_from_statement(statement, column_names)
})
}
fn describe<'q>(&'q mut self, sql: &'q str) -> BoxFuture<'q, sqlx_core::Result<Describe<Any>>> {
fn describe(&mut self, sql: SqlStr) -> BoxFuture<'_, sqlx_core::Result<Describe<Any>>> {
Box::pin(async move { Executor::describe(self, sql).await?.try_into_any() })
}
}

View File

@@ -5,14 +5,18 @@ use crate::error::Error;
use crate::statement::VirtualStatement;
use crate::type_info::DataType;
use crate::{Sqlite, SqliteColumn};
use sqlx_core::sql_str::SqlStr;
use sqlx_core::Either;
use std::convert::identity;
pub(crate) fn describe(conn: &mut ConnectionState, query: &str) -> Result<Describe<Sqlite>, Error> {
pub(crate) fn describe(
conn: &mut ConnectionState,
query: SqlStr,
) -> Result<Describe<Sqlite>, Error> {
// describing a statement from SQLite can be involved
// each SQLx statement is comprised of multiple SQL statements
let mut statement = VirtualStatement::new(query, false)?;
let mut statement = VirtualStatement::new(query.as_str(), false)?;
let mut columns = Vec::new();
let mut nullable = Vec::new();

View File

@@ -3,12 +3,13 @@ use crate::error::Error;
use crate::logger::QueryLogger;
use crate::statement::{StatementHandle, VirtualStatement};
use crate::{SqliteArguments, SqliteQueryResult, SqliteRow};
use sqlx_core::sql_str::SqlSafeStr;
use sqlx_core::Either;
pub struct ExecuteIter<'a> {
handle: &'a mut ConnectionHandle,
statement: &'a mut VirtualStatement,
logger: QueryLogger<'a>,
logger: QueryLogger,
args: Option<SqliteArguments<'a>>,
/// since a `VirtualStatement` can encompass multiple actual statements,
@@ -20,12 +21,13 @@ pub struct ExecuteIter<'a> {
pub(crate) fn iter<'a>(
conn: &'a mut ConnectionState,
query: &'a str,
query: impl SqlSafeStr,
args: Option<SqliteArguments<'a>>,
persistent: bool,
) -> Result<ExecuteIter<'a>, Error> {
let query = query.into_sql_str();
// fetch the cached statement or allocate a new one
let statement = conn.statements.get(query, persistent)?;
let statement = conn.statements.get(query.as_str(), persistent)?;
let logger = QueryLogger::new(query, conn.log_settings.clone());

View File

@@ -7,6 +7,7 @@ use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt};
use sqlx_core::describe::Describe;
use sqlx_core::error::Error;
use sqlx_core::executor::{Execute, Executor};
use sqlx_core::sql_str::SqlStr;
use sqlx_core::Either;
use std::{future, pin::pin};
@@ -23,12 +24,12 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
'q: 'e,
E: 'q,
{
let sql = query.sql();
let arguments = match query.take_arguments().map_err(Error::Encode) {
Ok(arguments) => arguments,
Err(error) => return stream::once(future::ready(Err(error))).boxed(),
};
let persistent = query.persistent() && arguments.is_some();
let sql = query.sql();
Box::pin(
self.worker
@@ -48,7 +49,6 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
'q: 'e,
E: 'q,
{
let sql = query.sql();
let arguments = match query.take_arguments().map_err(Error::Encode) {
Ok(arguments) => arguments,
Err(error) => return future::ready(Err(error)).boxed(),
@@ -56,6 +56,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
let persistent = query.persistent() && arguments.is_some();
Box::pin(async move {
let sql = query.sql();
let mut stream = pin!(self
.worker
.execute(sql, arguments, self.row_channel_size, persistent, Some(1))
@@ -72,29 +73,26 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection {
})
}
fn prepare_with<'e, 'q: 'e>(
fn prepare_with<'e>(
self,
sql: &'q str,
sql: SqlStr,
_parameters: &[SqliteTypeInfo],
) -> BoxFuture<'e, Result<SqliteStatement<'q>, Error>>
) -> BoxFuture<'e, Result<SqliteStatement, Error>>
where
'c: 'e,
{
Box::pin(async move {
let statement = self.worker.prepare(sql).await?;
Ok(SqliteStatement {
sql: sql.into(),
..statement
})
Ok(statement)
})
}
#[doc(hidden)]
fn describe<'e, 'q: 'e>(self, sql: &'q str) -> BoxFuture<'e, Result<Describe<Sqlite>, Error>>
fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result<Describe<Sqlite>, Error>>
where
'c: 'e,
{
Box::pin(self.worker.describe(sql))
Box::pin(async move { self.worker.describe(sql).await })
}
}

View File

@@ -12,6 +12,7 @@ use crate::from_row::FromRow;
use crate::logger::{BranchParent, BranchResult, DebugDiff};
use crate::type_info::DataType;
use crate::SqliteTypeInfo;
use sqlx_core::sql_str::AssertSqlSafe;
use sqlx_core::{hash_map, HashMap};
use std::fmt::Debug;
use std::str::from_utf8;
@@ -567,7 +568,7 @@ pub(super) fn explain(
) -> Result<(Vec<SqliteTypeInfo>, Vec<Option<bool>>), Error> {
let root_block_cols = root_block_columns(conn)?;
let program: Vec<(i64, String, i64, i64, i64, Vec<u8>)> =
execute::iter(conn, &format!("EXPLAIN {query}"), None, false)?
execute::iter(conn, AssertSqlSafe(format!("EXPLAIN {query}")), None, false)?
.filter_map(|res| res.map(|either| either.right()).transpose())
.map(|row| FromRow::from_row(&row?))
.collect::<Result<Vec<_>, Error>>()?;

View File

@@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::cmp::Ordering;
use std::ffi::CStr;
use std::fmt::Write;
@@ -22,6 +21,7 @@ use sqlx_core::common::StatementCache;
pub(crate) use sqlx_core::connection::*;
use sqlx_core::error::Error;
use sqlx_core::executor::Executor;
use sqlx_core::sql_str::{AssertSqlSafe, SqlSafeStr};
use sqlx_core::transaction::Transaction;
use crate::connection::establish::EstablishParams;
@@ -222,7 +222,7 @@ impl Connection for SqliteConnection {
write!(pragma_string, "PRAGMA analysis_limit = {limit}; ").ok();
}
pragma_string.push_str("PRAGMA optimize;");
self.execute(&*pragma_string).await?;
self.execute(AssertSqlSafe(pragma_string)).await?;
}
let shutdown = self.worker.shutdown();
// Drop the statement worker, which should
@@ -250,12 +250,12 @@ impl Connection for SqliteConnection {
fn begin_with(
&mut self,
statement: impl Into<Cow<'static, str>>,
statement: impl SqlSafeStr,
) -> impl Future<Output = Result<Transaction<'_, Self::Database>, Error>> + Send + '_
where
Self: Sized,
{
Transaction::begin(self, Some(statement.into()))
Transaction::begin(self, Some(statement.into_sql_str()))
}
fn cached_statements_size(&self) -> usize {

View File

@@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::future::Future;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
@@ -6,6 +5,7 @@ use std::thread;
use futures_channel::oneshot;
use futures_intrusive::sync::{Mutex, MutexGuard};
use sqlx_core::sql_str::SqlStr;
use tracing::span::Span;
use sqlx_core::describe::Describe;
@@ -53,15 +53,15 @@ impl WorkerSharedState {
enum Command {
Prepare {
query: Box<str>,
tx: oneshot::Sender<Result<SqliteStatement<'static>, Error>>,
query: SqlStr,
tx: oneshot::Sender<Result<SqliteStatement, Error>>,
},
Describe {
query: Box<str>,
query: SqlStr,
tx: oneshot::Sender<Result<Describe<Sqlite>, Error>>,
},
Execute {
query: Box<str>,
query: SqlStr,
arguments: Option<SqliteArguments<'static>>,
persistent: bool,
tx: flume::Sender<Result<Either<SqliteQueryResult, SqliteRow>, Error>>,
@@ -79,7 +79,7 @@ enum Command {
},
Begin {
tx: rendezvous_oneshot::Sender<Result<(), Error>>,
statement: Option<Cow<'static, str>>,
statement: Option<SqlStr>,
},
Commit {
tx: rendezvous_oneshot::Sender<Result<(), Error>>,
@@ -145,7 +145,7 @@ impl ConnectionWorker {
let _guard = span.enter();
match cmd {
Command::Prepare { query, tx } => {
tx.send(prepare(&mut conn, &query)).ok();
tx.send(prepare(&mut conn, query)).ok();
// This may issue an unnecessary write on failure,
// but it doesn't matter in the grand scheme of things.
@@ -155,7 +155,7 @@ impl ConnectionWorker {
);
}
Command::Describe { query, tx } => {
tx.send(describe(&mut conn, &query)).ok();
tx.send(describe(&mut conn, query)).ok();
}
Command::Execute {
query,
@@ -164,7 +164,7 @@ impl ConnectionWorker {
tx,
limit
} => {
let iter = match execute::iter(&mut conn, &query, arguments, persistent)
let iter = match execute::iter(&mut conn, query, arguments, persistent)
{
Ok(iter) => iter,
Err(e) => {
@@ -225,7 +225,7 @@ impl ConnectionWorker {
};
let res =
conn.handle
.exec(statement)
.exec(statement.as_str())
.map(|_| {
shared.transaction_depth.fetch_add(1, Ordering::Release);
});
@@ -238,7 +238,7 @@ impl ConnectionWorker {
// immediately otherwise it would remain started forever.
if let Err(error) = conn
.handle
.exec(rollback_ansi_transaction_sql(depth + 1))
.exec(rollback_ansi_transaction_sql(depth + 1).as_str())
.map(|_| {
shared.transaction_depth.fetch_sub(1, Ordering::Release);
})
@@ -256,7 +256,7 @@ impl ConnectionWorker {
let res = if depth > 0 {
conn.handle
.exec(commit_ansi_transaction_sql(depth))
.exec(commit_ansi_transaction_sql(depth).as_str())
.map(|_| {
shared.transaction_depth.fetch_sub(1, Ordering::Release);
})
@@ -282,7 +282,7 @@ impl ConnectionWorker {
let res = if depth > 0 {
conn.handle
.exec(rollback_ansi_transaction_sql(depth))
.exec(rollback_ansi_transaction_sql(depth).as_str())
.map(|_| {
shared.transaction_depth.fetch_sub(1, Ordering::Release);
})
@@ -335,25 +335,19 @@ impl ConnectionWorker {
establish_rx.await.map_err(|_| Error::WorkerCrashed)?
}
pub(crate) async fn prepare(&mut self, query: &str) -> Result<SqliteStatement<'static>, Error> {
self.oneshot_cmd(|tx| Command::Prepare {
query: query.into(),
tx,
})
.await?
pub(crate) async fn prepare(&mut self, query: SqlStr) -> Result<SqliteStatement, Error> {
self.oneshot_cmd(|tx| Command::Prepare { query, tx })
.await?
}
pub(crate) async fn describe(&mut self, query: &str) -> Result<Describe<Sqlite>, Error> {
self.oneshot_cmd(|tx| Command::Describe {
query: query.into(),
tx,
})
.await?
pub(crate) async fn describe(&mut self, query: SqlStr) -> Result<Describe<Sqlite>, Error> {
self.oneshot_cmd(|tx| Command::Describe { query, tx })
.await?
}
pub(crate) async fn execute(
&mut self,
query: &str,
query: SqlStr,
args: Option<SqliteArguments<'_>>,
chan_size: usize,
persistent: bool,
@@ -364,7 +358,7 @@ impl ConnectionWorker {
self.command_tx
.send_async((
Command::Execute {
query: query.into(),
query,
arguments: args.map(SqliteArguments::into_static),
persistent,
tx,
@@ -378,10 +372,7 @@ impl ConnectionWorker {
Ok(rx)
}
pub(crate) async fn begin(
&mut self,
statement: Option<Cow<'static, str>>,
) -> Result<(), Error> {
pub(crate) async fn begin(&mut self, statement: Option<SqlStr>) -> Result<(), Error> {
self.oneshot_cmd_with_ack(|tx| Command::Begin { tx, statement })
.await?
}
@@ -495,9 +486,9 @@ impl ConnectionWorker {
}
}
fn prepare(conn: &mut ConnectionState, query: &str) -> Result<SqliteStatement<'static>, Error> {
fn prepare(conn: &mut ConnectionState, query: SqlStr) -> Result<SqliteStatement, Error> {
// prepare statement object (or checkout from cache)
let statement = conn.statements.get(query, true)?;
let statement = conn.statements.get(query.as_str(), true)?;
let mut parameters = 0;
let mut columns = None;
@@ -514,7 +505,7 @@ fn prepare(conn: &mut ConnectionState, query: &str) -> Result<SqliteStatement<'s
}
Ok(SqliteStatement {
sql: Cow::Owned(query.to_string()),
sql: query,
columns: columns.unwrap_or_default(),
column_names: column_names.unwrap_or_default(),
parameters,

View File

@@ -29,7 +29,7 @@ impl Database for Sqlite {
type Arguments<'q> = SqliteArguments<'q>;
type ArgumentBuffer<'q> = Vec<SqliteArgumentValue<'q>>;
type Statement<'q> = SqliteStatement<'q>;
type Statement = SqliteStatement;
const NAME: &'static str = "SQLite";

View File

@@ -57,6 +57,7 @@ pub use options::{
};
pub use query_result::SqliteQueryResult;
pub use row::SqliteRow;
use sqlx_core::sql_str::{AssertSqlSafe, SqlSafeStr};
pub use statement::SqliteStatement;
pub use transaction::SqliteTransactionManager;
pub use type_info::SqliteTypeInfo;
@@ -132,9 +133,10 @@ pub fn describe_blocking(query: &str, database_url: &str) -> Result<Describe<Sql
let mut conn = params.establish()?;
// Execute any ancillary `PRAGMA`s
connection::execute::iter(&mut conn, &opts.pragma_string(), None, false)?.finish()?;
connection::execute::iter(&mut conn, AssertSqlSafe(opts.pragma_string()), None, false)?
.finish()?;
connection::describe::describe(&mut conn, query)
connection::describe::describe(&mut conn, AssertSqlSafe(query.to_string()).into_sql_str())
// SQLite database is closed immediately when `conn` is dropped
}

View File

@@ -9,6 +9,7 @@ use crate::query::query;
use crate::query_as::query_as;
use crate::{Sqlite, SqliteConnectOptions, SqliteConnection, SqliteJournalMode};
use futures_core::future::BoxFuture;
use sqlx_core::sql_str::AssertSqlSafe;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::time::Duration;
@@ -65,10 +66,11 @@ impl Migrate for SqliteConnection {
) -> BoxFuture<'e, Result<(), MigrateError>> {
Box::pin(async move {
// Check if the schema already exists; if so, don't error.
let schema_version: Option<i64> =
query_scalar(&format!("PRAGMA {schema_name}.schema_version"))
.fetch_optional(&mut *self)
.await?;
let schema_version: Option<i64> = query_scalar(AssertSqlSafe(format!(
"PRAGMA {schema_name}.schema_version"
)))
.fetch_optional(&mut *self)
.await?;
if schema_version.is_some() {
return Ok(());
@@ -86,7 +88,7 @@ impl Migrate for SqliteConnection {
) -> BoxFuture<'e, Result<(), MigrateError>> {
Box::pin(async move {
// language=SQLite
self.execute(&*format!(
self.execute(AssertSqlSafe(format!(
r#"
CREATE TABLE IF NOT EXISTS {table_name} (
version BIGINT PRIMARY KEY,
@@ -97,7 +99,7 @@ CREATE TABLE IF NOT EXISTS {table_name} (
execution_time BIGINT NOT NULL
);
"#
))
)))
.await?;
Ok(())
@@ -110,9 +112,9 @@ CREATE TABLE IF NOT EXISTS {table_name} (
) -> BoxFuture<'e, Result<Option<i64>, MigrateError>> {
Box::pin(async move {
// language=SQLite
let row: Option<(i64,)> = query_as(&format!(
let row: Option<(i64,)> = query_as(AssertSqlSafe(format!(
"SELECT version FROM {table_name} WHERE success = false ORDER BY version LIMIT 1"
))
)))
.fetch_optional(self)
.await?;
@@ -126,9 +128,9 @@ CREATE TABLE IF NOT EXISTS {table_name} (
) -> BoxFuture<'e, Result<Vec<AppliedMigration>, MigrateError>> {
Box::pin(async move {
// language=SQLite
let rows: Vec<(i64, Vec<u8>)> = query_as(&format!(
let rows: Vec<(i64, Vec<u8>)> = query_as(AssertSqlSafe(format!(
"SELECT version, checksum FROM {table_name} ORDER BY version"
))
)))
.fetch_all(self)
.await?;
@@ -167,17 +169,17 @@ CREATE TABLE IF NOT EXISTS {table_name} (
// data lineage and debugging reasons, so it is not super important if it is lost. So we initialize it to -1
// and update it once the actual transaction completed.
let _ = tx
.execute(&*migration.sql)
.execute(migration.sql.clone())
.await
.map_err(|e| MigrateError::ExecuteMigration(e, migration.version))?;
// language=SQL
let _ = query(&format!(
let _ = query(AssertSqlSafe(format!(
r#"
INSERT INTO {table_name} ( version, description, success, checksum, execution_time )
VALUES ( ?1, ?2, TRUE, ?3, -1 )
"#
))
)))
.bind(migration.version)
.bind(&*migration.description)
.bind(&*migration.checksum)
@@ -194,13 +196,13 @@ CREATE TABLE IF NOT EXISTS {table_name} (
// language=SQL
#[allow(clippy::cast_possible_truncation)]
let _ = query(&format!(
let _ = query(AssertSqlSafe(format!(
r#"
UPDATE {table_name}
SET execution_time = ?1
WHERE version = ?2
"#
))
)))
.bind(elapsed.as_nanos() as i64)
.bind(migration.version)
.execute(self)
@@ -221,13 +223,15 @@ CREATE TABLE IF NOT EXISTS {table_name} (
let mut tx = self.begin().await?;
let start = Instant::now();
let _ = tx.execute(&*migration.sql).await?;
let _ = tx.execute(migration.sql.clone()).await?;
// language=SQLite
let _ = query(&format!(r#"DELETE FROM {table_name} WHERE version = ?1"#))
.bind(migration.version)
.execute(&mut *tx)
.await?;
let _ = query(AssertSqlSafe(format!(
r#"DELETE FROM {table_name} WHERE version = ?1"#
)))
.bind(migration.version)
.execute(&mut *tx)
.await?;
tx.commit().await?;

View File

@@ -3,6 +3,7 @@ use log::LevelFilter;
use sqlx_core::connection::ConnectOptions;
use sqlx_core::error::Error;
use sqlx_core::executor::Executor;
use sqlx_core::sql_str::AssertSqlSafe;
use std::fmt::Write;
use std::str::FromStr;
use std::time::Duration;
@@ -34,7 +35,7 @@ impl ConnectOptions for SqliteConnectOptions {
let mut conn = SqliteConnection::establish(self).await?;
// Execute PRAGMAs
conn.execute(&*self.pragma_string()).await?;
conn.execute(AssertSqlSafe(self.pragma_string())).await?;
if !self.collations.is_empty() {
let mut locked = conn.lock_handle().await?;

View File

@@ -2,8 +2,8 @@ use crate::column::ColumnIndex;
use crate::error::Error;
use crate::ext::ustr::UStr;
use crate::{Sqlite, SqliteArguments, SqliteColumn, SqliteTypeInfo};
use sqlx_core::sql_str::SqlStr;
use sqlx_core::{Either, HashMap};
use std::borrow::Cow;
use std::sync::Arc;
pub(crate) use sqlx_core::statement::*;
@@ -17,26 +17,21 @@ pub(crate) use r#virtual::VirtualStatement;
#[derive(Debug, Clone)]
#[allow(clippy::rc_buffer)]
pub struct SqliteStatement<'q> {
pub(crate) sql: Cow<'q, str>,
pub struct SqliteStatement {
pub(crate) sql: SqlStr,
pub(crate) parameters: usize,
pub(crate) columns: Arc<Vec<SqliteColumn>>,
pub(crate) column_names: Arc<HashMap<UStr, usize>>,
}
impl<'q> Statement<'q> for SqliteStatement<'q> {
impl Statement for SqliteStatement {
type Database = Sqlite;
fn to_owned(&self) -> SqliteStatement<'static> {
SqliteStatement::<'static> {
sql: Cow::Owned(self.sql.clone().into_owned()),
parameters: self.parameters,
columns: Arc::clone(&self.columns),
column_names: Arc::clone(&self.column_names),
}
fn into_sql(self) -> SqlStr {
self.sql
}
fn sql(&self) -> &str {
fn sql(&self) -> &SqlStr {
&self.sql
}
@@ -51,8 +46,8 @@ impl<'q> Statement<'q> for SqliteStatement<'q> {
impl_statement_query!(SqliteArguments<'_>);
}
impl ColumnIndex<SqliteStatement<'_>> for &'_ str {
fn index(&self, statement: &SqliteStatement<'_>) -> Result<usize, Error> {
impl ColumnIndex<SqliteStatement> for &'_ str {
fn index(&self, statement: &SqliteStatement) -> Result<usize, Error> {
statement
.column_names
.get(*self)

View File

@@ -1,7 +1,7 @@
use std::{borrow::Cow, future::Future};
use std::future::Future;
use sqlx_core::error::Error;
use sqlx_core::transaction::TransactionManager;
use sqlx_core::{error::Error, sql_str::SqlStr};
use crate::{Sqlite, SqliteConnection};
@@ -11,10 +11,7 @@ pub struct SqliteTransactionManager;
impl TransactionManager for SqliteTransactionManager {
type Database = Sqlite;
async fn begin(
conn: &mut SqliteConnection,
statement: Option<Cow<'static, str>>,
) -> Result<(), Error> {
async fn begin(conn: &mut SqliteConnection, statement: Option<SqlStr>) -> Result<(), Error> {
let is_custom_statement = statement.is_some();
conn.worker.begin(statement).await?;
if is_custom_statement {