mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
fix(core): bind non-object-safe pieces of Type to Argument for later use
- allows for TypeDecode and TypeEncode to be simple Type + X aliases
This commit is contained in:
parent
a92925a045
commit
2934a18af4
@ -1,5 +1,7 @@
|
||||
use std::any;
|
||||
|
||||
use crate::database::HasOutput;
|
||||
use crate::{encode, Database, TypeEncode};
|
||||
use crate::{encode, Database, TypeEncode, TypeInfo};
|
||||
|
||||
/// A collection of arguments to be applied to a prepared statement.
|
||||
///
|
||||
@ -17,11 +19,33 @@ pub struct Arguments<'a, Db: Database> {
|
||||
pub struct Argument<'a, Db: Database> {
|
||||
unchecked: bool,
|
||||
|
||||
// preserved from `T::type_id()`
|
||||
type_id: Db::TypeId,
|
||||
|
||||
// preserved from `T::compatible`
|
||||
type_compatible: fn(&Db::TypeInfo) -> bool,
|
||||
|
||||
// preserved from `any::type_name::<T>`
|
||||
// used in error messages
|
||||
rust_type_name: &'static str,
|
||||
|
||||
// TODO: we might want to allow binding to Box<dyn TypeEncode<Db>>
|
||||
// this would allow an Owned storage of values
|
||||
value: &'a dyn TypeEncode<Db>,
|
||||
}
|
||||
|
||||
impl<'a, Db: Database> Argument<'a, Db> {
|
||||
fn new<T: 'a + TypeEncode<Db>>(value: &'a T, unchecked: bool) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unchecked,
|
||||
type_id: T::type_id(),
|
||||
type_compatible: T::compatible,
|
||||
rust_type_name: any::type_name::<T>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Db: Database> Default for Arguments<'_, Db> {
|
||||
fn default() -> Self {
|
||||
Self { named: Vec::new(), positional: Vec::new() }
|
||||
@ -43,7 +67,7 @@ impl<'a, Db: Database> Arguments<'a, Db> {
|
||||
/// and you attempt to bind a `&str` in Rust, an incompatible type error will be raised.
|
||||
///
|
||||
pub fn add<T: 'a + TypeEncode<Db>>(&mut self, value: &'a T) {
|
||||
self.positional.push(Argument { value, unchecked: false });
|
||||
self.positional.push(Argument::new(value, false));
|
||||
}
|
||||
|
||||
/// Add an unchecked value to the end of the arguments collection.
|
||||
@ -53,17 +77,17 @@ impl<'a, Db: Database> Arguments<'a, Db> {
|
||||
/// will not be hinted when preparing the statement.
|
||||
///
|
||||
pub fn add_unchecked<T: 'a + TypeEncode<Db>>(&mut self, value: &'a T) {
|
||||
self.positional.push(Argument { value, unchecked: true });
|
||||
self.positional.push(Argument::new(value, true));
|
||||
}
|
||||
|
||||
/// Add a named value to the argument collection.
|
||||
pub fn add_as<T: 'a + TypeEncode<Db>>(&mut self, name: &'a str, value: &'a T) {
|
||||
self.named.push((name, Argument { value, unchecked: false }));
|
||||
self.named.push((name, Argument::new(value, false)));
|
||||
}
|
||||
|
||||
/// Add an unchecked, named value to the arguments collection.
|
||||
pub fn add_unchecked_as<T: 'a + TypeEncode<Db>>(&mut self, name: &'a str, value: &'a T) {
|
||||
self.named.push((name, Argument { value, unchecked: true }));
|
||||
self.named.push((name, Argument::new(value, true)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,25 +144,14 @@ impl<'a, Db: Database> Arguments<'a, Db> {
|
||||
}
|
||||
|
||||
impl<'a, Db: Database> Argument<'a, Db> {
|
||||
/// Returns `true` if the argument is unchecked.
|
||||
#[must_use]
|
||||
pub fn unchecked(&self) -> bool {
|
||||
self.unchecked
|
||||
}
|
||||
|
||||
/// Returns the SQL type identifier of the argument.
|
||||
///
|
||||
/// When the statement is prepared, the database will often infer the type
|
||||
/// of the incoming argument. This method takes that (`ty`) along with the value of
|
||||
/// the argument to determine the actual type identifier that will be sent when
|
||||
/// the statement is executed.
|
||||
///
|
||||
#[must_use]
|
||||
pub fn type_id(&self, ty: &Db::TypeInfo) -> Db::TypeId {
|
||||
self.value.type_id(ty)
|
||||
pub fn type_id(&self) -> Db::TypeId {
|
||||
self.type_id
|
||||
}
|
||||
|
||||
/// Encode this argument into the output buffer, for use in executing the prepared statement.
|
||||
/// Encode this argument into the output buffer, for use in executing
|
||||
/// the prepared statement.
|
||||
///
|
||||
/// When the statement is prepared, the database will often infer the type
|
||||
/// of the incoming argument. This method takes that (`ty`) along with the value of
|
||||
@ -149,6 +162,13 @@ impl<'a, Db: Database> Argument<'a, Db> {
|
||||
ty: &Db::TypeInfo,
|
||||
out: &mut <Db as HasOutput<'x>>::Output,
|
||||
) -> encode::Result<()> {
|
||||
if !self.unchecked && !(self.type_compatible)(ty) {
|
||||
return Err(encode::Error::TypeNotCompatible {
|
||||
rust_type_name: self.rust_type_name,
|
||||
sql_type_name: ty.name(),
|
||||
});
|
||||
}
|
||||
|
||||
self.value.encode(ty, out)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
use crate::{Database, Decode, Encode, TypeInfo};
|
||||
|
||||
// NOTE: The interface here is not final. There are some special considerations
|
||||
// for MSSQL and Postgres (Arrays and Ranges) that need careful handling
|
||||
// to ensure we correctly cover them.
|
||||
use crate::database::{HasOutput, HasRawValue};
|
||||
use crate::{decode, encode, Database, Decode, Encode, RawValue, TypeInfo};
|
||||
|
||||
/// Indicates that a SQL type is supported for a database.
|
||||
pub trait Type<Db: Database> {
|
||||
@ -42,36 +39,12 @@ impl<Db: Database, T: Type<Db>> Type<Db> for &'_ T {
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub trait TypeEncode<Db: Database>: Type<Db> + Encode<Db> {
|
||||
/// Returns the canonical SQL type identifier for this Rust type.
|
||||
#[allow(unused_variables)]
|
||||
fn type_id(&self, ty: &Db::TypeInfo) -> Db::TypeId;
|
||||
pub trait TypeEncode<Db: Database>: Type<Db> + Encode<Db> {}
|
||||
|
||||
/// Determines if this Rust type is compatible with the specified SQL type.
|
||||
///
|
||||
/// To be compatible, the Rust type must support encoding _and_ decoding
|
||||
/// from the specified SQL type.
|
||||
///
|
||||
fn compatible(&self, ty: &Db::TypeInfo) -> bool {
|
||||
ty.id() == self.type_id(ty)
|
||||
}
|
||||
|
||||
/// Returns the Rust type name of this.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn __rust_type_name_of(&self) -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Db: Database, T: Type<Db> + Encode<Db>> TypeEncode<Db> for T {
|
||||
fn type_id(&self, _ty: &Db::TypeInfo) -> Db::TypeId {
|
||||
Self::type_id()
|
||||
}
|
||||
}
|
||||
impl<T: Type<Db> + Encode<Db>, Db: Database> TypeEncode<Db> for T {}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub trait TypeDecode<'r, Db: Database>: Type<Db> + Decode<'r, Db> {}
|
||||
pub trait TypeDecode<'r, Db: Database>: Sized + Type<Db> + Decode<'r, Db> {}
|
||||
|
||||
impl<'r, T: Type<Db> + Decode<'r, Db>, Db: Database> TypeDecode<'r, Db> for T {}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user