refactor(sqlite): do not borrow bound values, delete lifetime on SqliteArguments (#3957)

* bug(sqlite): query macro argument lifetime use inconsistent with other db platforms

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>

* refactor(sqlite): Improve support for references as query macro bind arguments by removing lifetime parameter from SqliteArguments

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>

* refactor(sqlite): Introduce SqliteArgumentsBuffer type

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>

* refactor(sqlite): Improve cloning of SqliteArgumentValue, SqliteArguments, and SqliteArgumentsBuffer

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>

* refactor(any): Simplify AnyArguments::convert_to to convert_into

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>

---------

Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com>
This commit is contained in:
iamjpotts 2025-08-19 16:59:37 -04:00 committed by GitHub
parent e77f32ea5e
commit ff93aa017a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 221 additions and 275 deletions

View File

@ -42,7 +42,7 @@ impl Default for AnyArguments<'_> {
impl<'q> AnyArguments<'q> {
#[doc(hidden)]
pub fn convert_to<'a, A: Arguments<'a>>(&'a self) -> Result<A, BoxDynError>
pub fn convert_into<'a, A: Arguments<'a>>(self) -> Result<A, BoxDynError>
where
'q: 'a,
Option<i32>: Type<A::Database> + Encode<'a, A::Database>,
@ -60,12 +60,12 @@ impl<'q> AnyArguments<'q> {
i64: Type<A::Database> + Encode<'a, A::Database>,
f32: Type<A::Database> + Encode<'a, A::Database>,
f64: Type<A::Database> + Encode<'a, A::Database>,
&'a str: Type<A::Database> + Encode<'a, A::Database>,
&'a [u8]: Type<A::Database> + Encode<'a, A::Database>,
String: Type<A::Database> + Encode<'a, A::Database>,
Vec<u8>: Type<A::Database> + Encode<'a, A::Database>,
{
let mut out = A::default();
for arg in &self.values.0 {
for arg in self.values.0 {
match arg {
AnyValueKind::Null(AnyTypeInfoKind::Null) => out.add(Option::<i32>::None),
AnyValueKind::Null(AnyTypeInfoKind::Bool) => out.add(Option::<bool>::None),
@ -82,8 +82,8 @@ impl<'q> AnyArguments<'q> {
AnyValueKind::BigInt(i) => out.add(i),
AnyValueKind::Real(r) => out.add(r),
AnyValueKind::Double(d) => out.add(d),
AnyValueKind::Text(t) => out.add(&**t),
AnyValueKind::Blob(b) => out.add(&**b),
AnyValueKind::Text(t) => out.add(String::from(t)),
AnyValueKind::Blob(b) => out.add(Vec::from(b)),
}?
}
Ok(out)

View File

@ -84,7 +84,7 @@ impl AnyConnectionBackend for MySqlConnection {
arguments: Option<AnyArguments<'q>>,
) -> BoxStream<'q, sqlx_core::Result<Either<AnyQueryResult, AnyRow>>> {
let persistent = persistent && arguments.is_some();
let arguments = match arguments.as_ref().map(AnyArguments::convert_to).transpose() {
let arguments = match arguments.map(AnyArguments::convert_into).transpose() {
Ok(arguments) => arguments,
Err(error) => {
return stream::once(future::ready(Err(sqlx_core::Error::Encode(error)))).boxed()
@ -111,8 +111,7 @@ impl AnyConnectionBackend for MySqlConnection {
) -> BoxFuture<'q, sqlx_core::Result<Option<AnyRow>>> {
let persistent = persistent && arguments.is_some();
let arguments = arguments
.as_ref()
.map(AnyArguments::convert_to)
.map(AnyArguments::convert_into)
.transpose()
.map_err(sqlx_core::Error::Encode);

View File

@ -86,7 +86,7 @@ impl AnyConnectionBackend for PgConnection {
arguments: Option<AnyArguments<'q>>,
) -> BoxStream<'q, sqlx_core::Result<Either<AnyQueryResult, AnyRow>>> {
let persistent = persistent && arguments.is_some();
let arguments = match arguments.as_ref().map(AnyArguments::convert_to).transpose() {
let arguments = match arguments.map(AnyArguments::convert_into).transpose() {
Ok(arguments) => arguments,
Err(error) => {
return stream::once(future::ready(Err(sqlx_core::Error::Encode(error)))).boxed()
@ -113,8 +113,7 @@ impl AnyConnectionBackend for PgConnection {
) -> BoxFuture<'q, sqlx_core::Result<Option<AnyRow>>> {
let persistent = persistent && arguments.is_some();
let arguments = arguments
.as_ref()
.map(AnyArguments::convert_to)
.map(AnyArguments::convert_into)
.transpose()
.map_err(sqlx_core::Error::Encode);

View File

@ -12,6 +12,7 @@ use sqlx_core::any::{
};
use sqlx_core::sql_str::SqlStr;
use crate::arguments::SqliteArgumentsBuffer;
use crate::type_info::DataType;
use sqlx_core::connection::{ConnectOptions, Connection};
use sqlx_core::database::Database;
@ -19,6 +20,7 @@ use sqlx_core::describe::Describe;
use sqlx_core::executor::Executor;
use sqlx_core::transaction::TransactionManager;
use std::pin::pin;
use std::sync::Arc;
sqlx_core::declare_driver_with_optional_migrate!(DRIVER = Sqlite);
@ -203,27 +205,29 @@ impl<'a> TryFrom<&'a AnyConnectOptions> for SqliteConnectOptions {
}
}
/// Instead of `AnyArguments::convert_into()`, we can do a direct mapping and preserve the lifetime.
fn map_arguments(args: AnyArguments<'_>) -> SqliteArguments<'_> {
// Infallible alternative to AnyArguments::convert_into()
fn map_arguments(args: AnyArguments<'_>) -> SqliteArguments {
let values = args
.values
.0
.into_iter()
.map(|val| match val {
AnyValueKind::Null(_) => SqliteArgumentValue::Null,
AnyValueKind::Bool(b) => SqliteArgumentValue::Int(b as i32),
AnyValueKind::SmallInt(i) => SqliteArgumentValue::Int(i as i32),
AnyValueKind::Integer(i) => SqliteArgumentValue::Int(i),
AnyValueKind::BigInt(i) => SqliteArgumentValue::Int64(i),
AnyValueKind::Real(r) => SqliteArgumentValue::Double(r as f64),
AnyValueKind::Double(d) => SqliteArgumentValue::Double(d),
AnyValueKind::Text(t) => SqliteArgumentValue::Text(Arc::new(t.to_string())),
AnyValueKind::Blob(b) => SqliteArgumentValue::Blob(Arc::new(b.to_vec())),
// AnyValueKind is `#[non_exhaustive]` but we should have covered everything
_ => unreachable!("BUG: missing mapping for {val:?}"),
})
.collect();
SqliteArguments {
values: args
.values
.0
.into_iter()
.map(|val| match val {
AnyValueKind::Null(_) => SqliteArgumentValue::Null,
AnyValueKind::Bool(b) => SqliteArgumentValue::Int(b as i32),
AnyValueKind::SmallInt(i) => SqliteArgumentValue::Int(i as i32),
AnyValueKind::Integer(i) => SqliteArgumentValue::Int(i),
AnyValueKind::BigInt(i) => SqliteArgumentValue::Int64(i),
AnyValueKind::Real(r) => SqliteArgumentValue::Double(r as f64),
AnyValueKind::Double(d) => SqliteArgumentValue::Double(d),
AnyValueKind::Text(t) => SqliteArgumentValue::Text(t),
AnyValueKind::Blob(b) => SqliteArgumentValue::Blob(b),
// AnyValueKind is `#[non_exhaustive]` but we should have covered everything
_ => unreachable!("BUG: missing mapping for {val:?}"),
})
.collect(),
values: SqliteArgumentsBuffer::new(values),
}
}

View File

@ -4,62 +4,56 @@ use crate::statement::StatementHandle;
use crate::Sqlite;
use atoi::atoi;
use libsqlite3_sys::SQLITE_OK;
use std::borrow::Cow;
use std::sync::Arc;
pub(crate) use sqlx_core::arguments::*;
use sqlx_core::error::BoxDynError;
#[derive(Debug, Clone)]
pub enum SqliteArgumentValue<'q> {
pub enum SqliteArgumentValue {
Null,
Text(Cow<'q, str>),
Blob(Cow<'q, [u8]>),
Text(Arc<String>),
TextSlice(Arc<str>),
Blob(Arc<Vec<u8>>),
Double(f64),
Int(i32),
Int64(i64),
}
#[derive(Default, Debug, Clone)]
pub struct SqliteArguments<'q> {
pub(crate) values: Vec<SqliteArgumentValue<'q>>,
pub struct SqliteArguments {
pub(crate) values: SqliteArgumentsBuffer,
}
impl<'q> SqliteArguments<'q> {
#[derive(Default, Debug, Clone)]
pub struct SqliteArgumentsBuffer(Vec<SqliteArgumentValue>);
impl<'q> SqliteArguments {
pub(crate) fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
where
T: Encode<'q, Sqlite>,
{
let value_length_before_encoding = self.values.len();
let value_length_before_encoding = self.values.0.len();
match value.encode(&mut self.values) {
Ok(IsNull::Yes) => self.values.push(SqliteArgumentValue::Null),
Ok(IsNull::Yes) => self.values.0.push(SqliteArgumentValue::Null),
Ok(IsNull::No) => {}
Err(error) => {
// reset the value buffer to its previous value if encoding failed so we don't leave a half-encoded value behind
self.values.truncate(value_length_before_encoding);
self.values.0.truncate(value_length_before_encoding);
return Err(error);
}
};
Ok(())
}
pub(crate) fn into_static(self) -> SqliteArguments<'static> {
SqliteArguments {
values: self
.values
.into_iter()
.map(SqliteArgumentValue::into_static)
.collect(),
}
}
}
impl<'q> Arguments<'q> for SqliteArguments<'q> {
impl<'q> Arguments<'q> for SqliteArguments {
type Database = Sqlite;
fn reserve(&mut self, len: usize, _size_hint: usize) {
self.values.reserve(len);
self.values.0.reserve(len);
}
fn add<T>(&mut self, value: T) -> Result<(), BoxDynError>
@ -70,11 +64,11 @@ impl<'q> Arguments<'q> for SqliteArguments<'q> {
}
fn len(&self) -> usize {
self.values.len()
self.values.0.len()
}
}
impl SqliteArguments<'_> {
impl SqliteArguments {
pub(super) fn bind(&self, handle: &mut StatementHandle, offset: usize) -> Result<usize, Error> {
let mut arg_i = offset;
// for handle in &statement.handles {
@ -103,7 +97,7 @@ impl SqliteArguments<'_> {
arg_i
};
if n > self.values.len() {
if n > self.values.0.len() {
// SQLite treats unbound variables as NULL
// we reproduce this here
// If you are reading this and think this should be an error, open an issue and we can
@ -113,32 +107,31 @@ impl SqliteArguments<'_> {
break;
}
self.values[n - 1].bind(handle, param_i)?;
self.values.0[n - 1].bind(handle, param_i)?;
}
Ok(arg_i - offset)
}
}
impl SqliteArgumentValue<'_> {
fn into_static(self) -> SqliteArgumentValue<'static> {
use SqliteArgumentValue::*;
match self {
Null => Null,
Text(text) => Text(text.into_owned().into()),
Blob(blob) => Blob(blob.into_owned().into()),
Int(v) => Int(v),
Int64(v) => Int64(v),
Double(v) => Double(v),
}
impl SqliteArgumentsBuffer {
#[allow(dead_code)] // clippy incorrectly reports this as unused
pub(crate) fn new(values: Vec<SqliteArgumentValue>) -> SqliteArgumentsBuffer {
Self(values)
}
pub(crate) fn push(&mut self, value: SqliteArgumentValue) {
self.0.push(value);
}
}
impl SqliteArgumentValue {
fn bind(&self, handle: &mut StatementHandle, i: usize) -> Result<(), Error> {
use SqliteArgumentValue::*;
let status = match self {
Text(v) => handle.bind_text(i, v),
TextSlice(v) => handle.bind_text(i, v),
Blob(v) => handle.bind_blob(i, v),
Int(v) => handle.bind_int(i, *v),
Int64(v) => handle.bind_int64(i, *v),

View File

@ -10,7 +10,7 @@ pub struct ExecuteIter<'a> {
handle: &'a mut ConnectionHandle,
statement: &'a mut VirtualStatement,
logger: QueryLogger,
args: Option<SqliteArguments<'a>>,
args: Option<SqliteArguments>,
/// since a `VirtualStatement` can encompass multiple actual statements,
/// this keeps track of the number of arguments so far
@ -19,12 +19,12 @@ pub struct ExecuteIter<'a> {
goto_next: bool,
}
pub(crate) fn iter<'a>(
conn: &'a mut ConnectionState,
pub(crate) fn iter(
conn: &mut ConnectionState,
query: impl SqlSafeStr,
args: Option<SqliteArguments<'a>>,
args: Option<SqliteArguments>,
persistent: bool,
) -> Result<ExecuteIter<'a>, Error> {
) -> Result<ExecuteIter<'_>, Error> {
let query = query.into_sql_str();
// fetch the cached statement or allocate a new one
let statement = conn.statements.get(query.as_str(), persistent)?;
@ -43,7 +43,7 @@ pub(crate) fn iter<'a>(
fn bind(
statement: &mut StatementHandle,
arguments: &Option<SqliteArguments<'_>>,
arguments: &Option<SqliteArguments>,
offset: usize,
) -> Result<usize, Error> {
let mut n = 0;
@ -56,7 +56,7 @@ fn bind(
}
impl ExecuteIter<'_> {
pub fn finish(&mut self) -> Result<(), Error> {
pub fn finish(self) -> Result<(), Error> {
for res in self {
let _ = res?;
}

View File

@ -63,7 +63,7 @@ enum Command {
},
Execute {
query: SqlStr,
arguments: Option<SqliteArguments<'static>>,
arguments: Option<SqliteArguments>,
persistent: bool,
tx: flume::Sender<Result<Either<SqliteQueryResult, SqliteRow>, Error>>,
limit: Option<usize>,
@ -360,7 +360,7 @@ impl ConnectionWorker {
pub(crate) async fn execute(
&mut self,
query: SqlStr,
args: Option<SqliteArguments<'_>>,
args: Option<SqliteArguments>,
chan_size: usize,
persistent: bool,
limit: Option<usize>,
@ -371,7 +371,7 @@ impl ConnectionWorker {
.send_async((
Command::Execute {
query,
arguments: args.map(SqliteArguments::into_static),
arguments: args,
persistent,
tx,
limit,

View File

@ -1,9 +1,9 @@
pub(crate) use sqlx_core::database::{Database, HasStatementCache};
use crate::arguments::SqliteArgumentsBuffer;
use crate::{
SqliteArgumentValue, SqliteArguments, SqliteColumn, SqliteConnection, SqliteQueryResult,
SqliteRow, SqliteStatement, SqliteTransactionManager, SqliteTypeInfo, SqliteValue,
SqliteValueRef,
SqliteArguments, SqliteColumn, SqliteConnection, SqliteQueryResult, SqliteRow, SqliteStatement,
SqliteTransactionManager, SqliteTypeInfo, SqliteValue, SqliteValueRef,
};
/// Sqlite database driver.
@ -26,8 +26,8 @@ impl Database for Sqlite {
type Value = SqliteValue;
type ValueRef<'r> = SqliteValueRef<'r>;
type Arguments<'q> = SqliteArguments<'q>;
type ArgumentBuffer<'q> = Vec<SqliteArgumentValue<'q>>;
type Arguments<'q> = SqliteArguments;
type ArgumentBuffer<'q> = SqliteArgumentsBuffer;
type Statement = SqliteStatement;

View File

@ -74,7 +74,7 @@ extern crate sqlx_core;
use std::sync::atomic::AtomicBool;
pub use arguments::{SqliteArgumentValue, SqliteArguments};
pub use arguments::{SqliteArgumentValue, SqliteArguments, SqliteArgumentsBuffer};
pub use column::SqliteColumn;
#[cfg(feature = "deserialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "deserialize")))]
@ -147,7 +147,7 @@ impl<'c, T: Executor<'c, Database = Sqlite>> SqliteExecutor<'c> for T {}
pub type SqliteTransaction<'c> = sqlx_core::transaction::Transaction<'c, Sqlite>;
// NOTE: required due to the lack of lazy normalization
impl_into_arguments_for_arguments!(SqliteArguments<'q>);
impl_into_arguments_for_arguments!(SqliteArguments);
impl_column_index_for_row!(SqliteRow);
impl_column_index_for_statement!(SqliteStatement);
impl_acquire!(Sqlite, SqliteConnection);

View File

@ -45,7 +45,7 @@ impl Statement for SqliteStatement {
&self.columns
}
impl_statement_query!(SqliteArguments<'_>);
impl_statement_query!(SqliteArguments);
}
impl ColumnIndex<SqliteStatement> for &'_ str {
@ -57,20 +57,3 @@ impl ColumnIndex<SqliteStatement> for &'_ str {
.copied()
}
}
// #[cfg(feature = "any")]
// impl<'q> From<SqliteStatement<'q>> for crate::any::AnyStatement<'q> {
// #[inline]
// fn from(statement: SqliteStatement<'q>) -> Self {
// crate::any::AnyStatement::<'q> {
// columns: statement
// .columns
// .iter()
// .map(|col| col.clone().into())
// .collect(),
// column_names: statement.column_names,
// parameters: Some(Either::Right(statement.parameters)),
// sql: statement.sql,
// }
// }
// }

View File

@ -1,3 +1,4 @@
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
@ -15,11 +16,8 @@ impl Type<Sqlite> for bool {
}
}
impl<'q> Encode<'q, Sqlite> for bool {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for bool {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int((*self).into()));
Ok(IsNull::No)

View File

@ -2,6 +2,7 @@ use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
@ -19,12 +20,9 @@ impl Type<Sqlite> for [u8] {
}
}
impl<'q> Encode<'q, Sqlite> for &'q [u8] {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Borrowed(self)));
impl Encode<'_, Sqlite> for &'_ [u8] {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self.to_vec())));
Ok(IsNull::No)
}
@ -37,19 +35,14 @@ impl<'r> Decode<'r, Sqlite> for &'r [u8] {
}
impl Encode<'_, Sqlite> for Box<[u8]> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self.into_vec())));
fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self.into_vec())));
Ok(IsNull::No)
}
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(
self.clone().into_vec(),
)));
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self.clone().into_vec())));
Ok(IsNull::No)
}
@ -65,18 +58,15 @@ impl Type<Sqlite> for Vec<u8> {
}
}
impl<'q> Encode<'q, Sqlite> for Vec<u8> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self)));
impl Encode<'_, Sqlite> for Vec<u8> {
fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self)));
Ok(IsNull::No)
}
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self.clone())));
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self.clone())));
Ok(IsNull::No)
}
@ -88,37 +78,28 @@ impl<'r> Decode<'r, Sqlite> for Vec<u8> {
}
}
impl<'q> Encode<'q, Sqlite> for Cow<'q, [u8]> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(self));
impl Encode<'_, Sqlite> for Cow<'_, [u8]> {
fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self.into())));
Ok(IsNull::No)
}
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(self.clone()));
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(self.to_vec())));
Ok(IsNull::No)
}
}
impl<'q> Encode<'q, Sqlite> for Arc<[u8]> {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for Arc<[u8]> {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
<Vec<u8> as Encode<'_, Sqlite>>::encode(self.to_vec(), args)
}
}
impl<'q> Encode<'q, Sqlite> for Rc<[u8]> {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for Rc<[u8]> {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
<Vec<u8> as Encode<'_, Sqlite>>::encode(self.to_vec(), args)
}
}

View File

@ -7,7 +7,7 @@ use crate::{
error::BoxDynError,
type_info::DataType,
types::Type,
Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef,
Sqlite, SqliteArgumentsBuffer, SqliteTypeInfo, SqliteValueRef,
};
use chrono::FixedOffset;
use chrono::{
@ -65,25 +65,25 @@ impl<Tz: TimeZone> Encode<'_, Sqlite> for DateTime<Tz>
where
Tz::Offset: Display,
{
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.to_rfc3339_opts(SecondsFormat::AutoSi, false), buf)
}
}
impl Encode<'_, Sqlite> for NaiveDateTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format("%F %T%.f").to_string(), buf)
}
}
impl Encode<'_, Sqlite> for NaiveDate {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format("%F").to_string(), buf)
}
}
impl Encode<'_, Sqlite> for NaiveTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format("%T%.f").to_string(), buf)
}
}

View File

@ -1,3 +1,4 @@
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
@ -11,11 +12,8 @@ impl Type<Sqlite> for f32 {
}
}
impl<'q> Encode<'q, Sqlite> for f32 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for f32 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Double((*self).into()));
Ok(IsNull::No)
@ -36,11 +34,8 @@ impl Type<Sqlite> for f64 {
}
}
impl<'q> Encode<'q, Sqlite> for f64 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for f64 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Double(*self));
Ok(IsNull::No)

View File

@ -1,3 +1,4 @@
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
@ -15,11 +16,8 @@ impl Type<Sqlite> for i8 {
}
}
impl<'q> Encode<'q, Sqlite> for i8 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for i8 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
Ok(IsNull::No)
@ -46,11 +44,8 @@ impl Type<Sqlite> for i16 {
}
}
impl<'q> Encode<'q, Sqlite> for i16 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for i16 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
Ok(IsNull::No)
@ -73,11 +68,8 @@ impl Type<Sqlite> for i32 {
}
}
impl<'q> Encode<'q, Sqlite> for i32 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for i32 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self));
Ok(IsNull::No)
@ -100,11 +92,8 @@ impl Type<Sqlite> for i64 {
}
}
impl<'q> Encode<'q, Sqlite> for i64 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for i64 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int64(*self));
Ok(IsNull::No)

View File

@ -1,10 +1,11 @@
use serde::{Deserialize, Serialize};
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::types::{Json, Type};
use crate::{type_info::DataType, Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef};
use crate::{type_info::DataType, Sqlite, SqliteTypeInfo, SqliteValueRef};
impl<T> Type<Sqlite> for Json<T> {
fn type_info() -> SqliteTypeInfo {
@ -20,7 +21,7 @@ impl<T> Encode<'_, Sqlite> for Json<T>
where
T: Serialize,
{
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.encode_to_string()?, buf)
}
}

View File

@ -1,13 +1,14 @@
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::type_info::DataType;
use crate::types::Type;
use crate::{Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef};
use sqlx_core::database::Database;
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
impl Type<Sqlite> for str {
fn type_info() -> SqliteTypeInfo {
@ -15,12 +16,9 @@ impl Type<Sqlite> for str {
}
}
impl<'q> Encode<'q, Sqlite> for &'q str {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Borrowed(*self)));
impl Encode<'_, Sqlite> for &'_ str {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.to_string())));
Ok(IsNull::No)
}
@ -33,19 +31,14 @@ impl<'r> Decode<'r, Sqlite> for &'r str {
}
impl Encode<'_, Sqlite> for Box<str> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.into_string())));
fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::TextSlice(Arc::from(self)));
Ok(IsNull::No)
}
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'_>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(
self.clone().into_string(),
)));
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.to_string())));
Ok(IsNull::No)
}
@ -57,18 +50,15 @@ impl Type<Sqlite> for String {
}
}
impl<'q> Encode<'q, Sqlite> for String {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self)));
impl Encode<'_, Sqlite> for String {
fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self)));
Ok(IsNull::No)
}
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.clone())));
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.clone())));
Ok(IsNull::No)
}
@ -80,37 +70,40 @@ impl<'r> Decode<'r, Sqlite> for String {
}
}
impl<'q> Encode<'q, Sqlite> for Cow<'q, str> {
fn encode(self, args: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(self));
impl Encode<'_, Sqlite> for Cow<'_, str> {
fn encode(self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.into())));
Ok(IsNull::No)
}
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(self.clone()));
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.to_string())));
Ok(IsNull::No)
}
}
impl<'q> Encode<'q, Sqlite> for Arc<str> {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for Arc<str> {
fn encode(
self,
args: &mut <Sqlite as Database>::ArgumentBuffer<'_>,
) -> Result<IsNull, BoxDynError>
where
Self: Sized,
{
args.push(SqliteArgumentValue::TextSlice(self));
Ok(IsNull::No)
}
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
<String as Encode<'_, Sqlite>>::encode(self.to_string(), args)
}
}
impl<'q> Encode<'q, Sqlite> for Rc<str> {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for Rc<str> {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
<String as Encode<'_, Sqlite>>::encode(self.to_string(), args)
}
}

View File

@ -1,4 +1,5 @@
use crate::{Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef};
use crate::arguments::SqliteArgumentsBuffer;
use crate::{Sqlite, SqliteTypeInfo, SqliteValueRef};
use sqlx_core::decode::Decode;
use sqlx_core::encode::{Encode, IsNull};
use sqlx_core::error::BoxDynError;
@ -16,11 +17,11 @@ impl<T> Type<Sqlite> for Text<T> {
}
}
impl<'q, T> Encode<'q, Sqlite> for Text<T>
impl<T> Encode<'_, Sqlite> for Text<T>
where
T: Display,
{
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'q>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.0.to_string(), buf)
}
}

View File

@ -1,3 +1,4 @@
use crate::arguments::SqliteArgumentsBuffer;
use crate::value::ValueRef;
use crate::{
decode::Decode,
@ -5,7 +6,7 @@ use crate::{
error::BoxDynError,
type_info::DataType,
types::Type,
Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef,
Sqlite, SqliteTypeInfo, SqliteValueRef,
};
use time::format_description::{well_known::Rfc3339, BorrowedFormatItem};
use time::macros::format_description as fd;
@ -55,27 +56,27 @@ impl Type<Sqlite> for Time {
}
impl Encode<'_, Sqlite> for OffsetDateTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
Encode::<Sqlite>::encode(self.format(&Rfc3339)?, buf)
}
}
impl Encode<'_, Sqlite> for PrimitiveDateTime {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
let format = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
Encode::<Sqlite>::encode(self.format(&format)?, buf)
}
}
impl Encode<'_, Sqlite> for Date {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
let format = fd!("[year]-[month]-[day]");
Encode::<Sqlite>::encode(self.format(&format)?, buf)
}
}
impl Encode<'_, Sqlite> for Time {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
fn encode_by_ref(&self, buf: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
let format = fd!("[hour]:[minute]:[second].[subsecond]");
Encode::<Sqlite>::encode(self.format(&format)?, buf)
}

View File

@ -1,3 +1,4 @@
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
@ -15,11 +16,8 @@ impl Type<Sqlite> for u8 {
}
}
impl<'q> Encode<'q, Sqlite> for u8 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for u8 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
Ok(IsNull::No)
@ -46,11 +44,8 @@ impl Type<Sqlite> for u16 {
}
}
impl<'q> Encode<'q, Sqlite> for u16 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for u16 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int(*self as i32));
Ok(IsNull::No)
@ -73,11 +68,8 @@ impl Type<Sqlite> for u32 {
}
}
impl<'q> Encode<'q, Sqlite> for u32 {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
impl Encode<'_, Sqlite> for u32 {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Int64(*self as i64));
Ok(IsNull::No)

View File

@ -1,10 +1,11 @@
use crate::arguments::SqliteArgumentsBuffer;
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::type_info::DataType;
use crate::types::Type;
use crate::{Sqlite, SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef};
use std::borrow::Cow;
use std::sync::Arc;
use uuid::{
fmt::{Hyphenated, Simple},
Uuid,
@ -20,12 +21,9 @@ impl Type<Sqlite> for Uuid {
}
}
impl<'q> Encode<'q, Sqlite> for Uuid {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Cow::Owned(
impl Encode<'_, Sqlite> for Uuid {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Blob(Arc::new(
self.as_bytes().to_vec(),
)));
@ -46,12 +44,9 @@ impl Type<Sqlite> for Hyphenated {
}
}
impl<'q> Encode<'q, Sqlite> for Hyphenated {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string())));
impl Encode<'_, Sqlite> for Hyphenated {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.to_string())));
Ok(IsNull::No)
}
@ -72,12 +67,9 @@ impl Type<Sqlite> for Simple {
}
}
impl<'q> Encode<'q, Sqlite> for Simple {
fn encode_by_ref(
&self,
args: &mut Vec<SqliteArgumentValue<'q>>,
) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Cow::Owned(self.to_string())));
impl Encode<'_, Sqlite> for Simple {
fn encode_by_ref(&self, args: &mut SqliteArgumentsBuffer) -> Result<IsNull, BoxDynError> {
args.push(SqliteArgumentValue::Text(Arc::new(self.to_string())));
Ok(IsNull::No)
}

View File

@ -175,6 +175,31 @@ async fn test_query_scalar() -> anyhow::Result<()> {
Ok(())
}
#[sqlx_macros::test]
async fn query_by_string() -> anyhow::Result<()> {
let mut conn = new::<Sqlite>().await?;
let string = "Hello, world!".to_string();
let ref tuple = ("Hello, world!".to_string(),);
let result = sqlx::query!(
"SELECT 'Hello, world!' as string where 'Hello, world!' in (?, ?, ?, ?, ?, ?, ?)",
string, // make sure we don't actually take ownership here
&string[..],
Some(&string),
Some(&string[..]),
Option::<String>::None,
string.clone(),
tuple.0 // make sure we're not trying to move out of a field expression
)
.fetch_one(&mut conn)
.await?;
assert_eq!(result.string, string);
Ok(())
}
#[sqlx_macros::test]
async fn macro_select_from_view() -> anyhow::Result<()> {
let mut conn = new::<Sqlite>().await?;