mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-03-23 18:40:24 +00:00
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:
@@ -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() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>>()?;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user