mirror of
https://github.com/diesel-rs/diesel.git
synced 2024-10-04 01:28:13 +03:00
re-implement SqlLiteral::Bind
update changelog
This commit is contained in:
parent
118bbeba9d
commit
261b3834f2
@ -8,6 +8,10 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
|
||||
|
||||
### Added
|
||||
|
||||
* Added `SqlLiteral::bind()`.
|
||||
This is intended to be used for binding values to small SQL fragments.
|
||||
Use `sql_query` if you are writing full queries.
|
||||
|
||||
* Added support for `INSERT INTO table (...) SELECT ...` queries. Tables, select
|
||||
select statements, and boxed select statements can now be used just like any
|
||||
other `Insertable` value.
|
||||
|
@ -7,56 +7,187 @@ use query_dsl::RunQueryDsl;
|
||||
use result::QueryResult;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
|
||||
/// Returned by the [`sql()`] function.
|
||||
///
|
||||
/// [`sql()`]: ../dsl/fn.sql.html
|
||||
pub struct SqlLiteral<ST> {
|
||||
pub struct SqlLiteral<ST, T = ()> {
|
||||
sql: String,
|
||||
inner: T,
|
||||
_marker: PhantomData<ST>,
|
||||
}
|
||||
|
||||
impl<ST> SqlLiteral<ST> {
|
||||
impl<ST, T> SqlLiteral<ST, T> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(sql: String) -> Self {
|
||||
pub fn new(sql: String, inner: T) -> Self {
|
||||
SqlLiteral {
|
||||
sql: sql,
|
||||
inner: inner,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Bind a value for use with this SQL query.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should be used with care, as Diesel cannot validate that
|
||||
/// the value is of the right type nor can it validate that you have passed
|
||||
/// the correct number of parameters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # include!("../doctest_setup.rs");
|
||||
/// #
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # use self::users::dsl::*;
|
||||
/// # use diesel::dsl::sql;
|
||||
/// # use diesel::sql_types::{Integer, Text};
|
||||
/// # let connection = establish_connection();
|
||||
/// let seans_id = users
|
||||
/// .select(id)
|
||||
/// .filter(sql("name = ").bind::<Text, _>("Sean"))
|
||||
/// .get_result(&connection);
|
||||
/// assert_eq!(Ok(1), seans_id);
|
||||
///
|
||||
/// let tess_id = sql::<Integer>("SELECT id FROM users WHERE name = ")
|
||||
/// .bind::<Text, _>("Tess")
|
||||
/// .get_result(&connection);
|
||||
/// assert_eq!(Ok(2), tess_id);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ### Multiple Bind Params
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # include!("../doctest_setup.rs");
|
||||
///
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # use self::users::dsl::*;
|
||||
/// # use diesel::dsl::sql;
|
||||
/// # use diesel::sql_types::{Integer, Text};
|
||||
/// # let connection = establish_connection();
|
||||
/// # diesel::insert_into(users).values(name.eq("Ryan"))
|
||||
/// # .execute(&connection).unwrap();
|
||||
/// let query = users
|
||||
/// .select(name)
|
||||
/// .filter(
|
||||
/// sql("id > ")
|
||||
/// .bind::<Integer,_>(1)
|
||||
/// .sql(" AND name <> ")
|
||||
/// .bind::<Text, _>("Ryan")
|
||||
/// )
|
||||
/// .get_results(&connection);
|
||||
/// let expected = vec!["Tess".to_string()];
|
||||
/// assert_eq!(Ok(expected), query);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bind<BindST, U>(self, bind_value: U) -> UncheckedBind<Self, U::Expression>
|
||||
where
|
||||
U: AsExpression<BindST>,
|
||||
{
|
||||
UncheckedBind::new(self, bind_value.as_expression())
|
||||
}
|
||||
|
||||
/// Use literal SQL in the query builder
|
||||
///
|
||||
/// This function is intended for use when you need a small bit of raw SQL in
|
||||
/// your query. If you want to write the entire query using raw SQL, use
|
||||
/// [`sql_query`](../fn.sql_query.html) instead.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should be used with care, as Diesel cannot validate that
|
||||
/// the value is of the right type nor can it validate that you have passed
|
||||
/// the correct number of parameters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # include!("../doctest_setup.rs");
|
||||
///
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # use self::users::dsl::*;
|
||||
/// # use diesel::dsl::sql;
|
||||
/// # use diesel::sql_types::{Integer, Text};
|
||||
/// # let connection = establish_connection();
|
||||
/// # diesel::insert_into(users).values(name.eq("Ryan"))
|
||||
/// # .execute(&connection).unwrap();
|
||||
/// let query = users
|
||||
/// .select(name)
|
||||
/// .filter(
|
||||
/// sql("id > 1")
|
||||
/// .sql(" AND name <> 'Ryan'")
|
||||
/// )
|
||||
/// .get_results(&connection);
|
||||
/// let expected = vec!["Tess".to_string()];
|
||||
/// assert_eq!(Ok(expected), query);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sql(self, sql: &str) -> SqlLiteral<ST, Self> {
|
||||
SqlLiteral::new(sql.into(), self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ST> Expression for SqlLiteral<ST> {
|
||||
impl<ST, T> Expression for SqlLiteral<ST, T> {
|
||||
type SqlType = ST;
|
||||
}
|
||||
|
||||
impl<ST, DB> QueryFragment<DB> for SqlLiteral<ST>
|
||||
impl<ST, T, DB> QueryFragment<DB> for SqlLiteral<ST, T>
|
||||
where
|
||||
DB: Backend,
|
||||
T: QueryFragment<DB>,
|
||||
{
|
||||
fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
|
||||
out.unsafe_to_cache_prepared();
|
||||
self.inner.walk_ast(out.reborrow())?;
|
||||
out.push_sql(&self.sql);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<ST> QueryId for SqlLiteral<ST> {
|
||||
impl<ST, T> QueryId for SqlLiteral<ST, T> {
|
||||
type QueryId = ();
|
||||
|
||||
const HAS_STATIC_QUERY_ID: bool = false;
|
||||
}
|
||||
|
||||
impl<ST> Query for SqlLiteral<ST> {
|
||||
impl<ST, T> Query for SqlLiteral<ST, T> {
|
||||
type SqlType = ST;
|
||||
}
|
||||
|
||||
impl<ST, Conn> RunQueryDsl<Conn> for SqlLiteral<ST> {}
|
||||
impl<ST, T, Conn> RunQueryDsl<Conn> for SqlLiteral<ST, T> {}
|
||||
|
||||
impl<QS, ST> SelectableExpression<QS> for SqlLiteral<ST> {}
|
||||
impl<QS, ST, T> SelectableExpression<QS> for SqlLiteral<ST, T> {}
|
||||
|
||||
impl<QS, ST> AppearsOnTable<QS> for SqlLiteral<ST> {}
|
||||
impl<QS, ST, T> AppearsOnTable<QS> for SqlLiteral<ST, T> {}
|
||||
|
||||
impl<ST> NonAggregate for SqlLiteral<ST> {}
|
||||
impl<ST, T> NonAggregate for SqlLiteral<ST, T> {}
|
||||
|
||||
/// Use literal SQL in the query builder
|
||||
///
|
||||
@ -94,5 +225,119 @@ impl<ST> NonAggregate for SqlLiteral<ST> {}
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sql<ST>(sql: &str) -> SqlLiteral<ST> {
|
||||
SqlLiteral::new(sql.into())
|
||||
SqlLiteral::new(sql.into(), ())
|
||||
}
|
||||
|
||||
#[derive(QueryId, Debug, Clone, Copy)]
|
||||
#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."]
|
||||
/// Returned by the [`bind()`] method when binding a value to a fragment of SQL.
|
||||
///
|
||||
/// [`bind()`]: ./struct.SqlLiteral.html#method.bind
|
||||
pub struct UncheckedBind<Query, Value> {
|
||||
query: Query,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
impl<Query, Value> UncheckedBind<Query, Value>
|
||||
where
|
||||
Query: Expression,
|
||||
{
|
||||
pub(crate) fn new(query: Query, value: Value) -> Self {
|
||||
UncheckedBind { query, value }
|
||||
}
|
||||
|
||||
/// Use literal SQL in the query builder
|
||||
///
|
||||
/// This function is intended for use when you need a small bit of raw SQL in
|
||||
/// your query. If you want to write the entire query using raw SQL, use
|
||||
/// [`sql_query`](../fn.sql_query.html) instead.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should be used with care, as Diesel cannot validate that
|
||||
/// the value is of the right type nor can it validate that you have passed
|
||||
/// the correct number of parameters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # include!("../doctest_setup.rs");
|
||||
///
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # use self::users::dsl::*;
|
||||
/// # use diesel::dsl::sql;
|
||||
/// # use diesel::sql_types::{Integer, Text};
|
||||
/// # let connection = establish_connection();
|
||||
/// # diesel::insert_into(users).values(name.eq("Ryan"))
|
||||
/// # .execute(&connection).unwrap();
|
||||
/// let query = users
|
||||
/// .select(name)
|
||||
/// .filter(
|
||||
/// sql("id > ")
|
||||
/// .bind::<Integer,_>(1)
|
||||
/// .sql(" AND name <> 'Ryan'")
|
||||
/// )
|
||||
/// .get_results(&connection);
|
||||
/// let expected = vec!["Tess".to_string()];
|
||||
/// assert_eq!(Ok(expected), query);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sql(self, sql: &str) -> SqlLiteral<Query::SqlType, Self> {
|
||||
SqlLiteral::new(sql.into(), self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Query, Value> Expression for UncheckedBind<Query, Value>
|
||||
where
|
||||
Query: Expression,
|
||||
{
|
||||
type SqlType = Query::SqlType;
|
||||
}
|
||||
|
||||
impl<Query, Value, DB> QueryFragment<DB> for UncheckedBind<Query, Value>
|
||||
where
|
||||
DB: Backend,
|
||||
Query: QueryFragment<DB>,
|
||||
Value: QueryFragment<DB>,
|
||||
{
|
||||
fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
|
||||
self.query.walk_ast(out.reborrow())?;
|
||||
self.value.walk_ast(out.reborrow())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Q, Value> Query for UncheckedBind<Q, Value>
|
||||
where
|
||||
Q: Query,
|
||||
{
|
||||
type SqlType = Q::SqlType;
|
||||
}
|
||||
|
||||
impl<Query, Value> NonAggregate for UncheckedBind<Query, Value>
|
||||
where
|
||||
Self: Expression,
|
||||
{
|
||||
}
|
||||
|
||||
impl<QS, Query, Value> SelectableExpression<QS> for UncheckedBind<Query, Value>
|
||||
where
|
||||
Self: AppearsOnTable<QS>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<QS, Query, Value> AppearsOnTable<QS> for UncheckedBind<Query, Value>
|
||||
where
|
||||
Self: Expression,
|
||||
{
|
||||
}
|
||||
|
||||
impl<Query, Value, Conn> RunQueryDsl<Conn> for UncheckedBind<Query, Value> {}
|
||||
|
Loading…
Reference in New Issue
Block a user