Before Lunch: * Diesel Basics
After Lunch: * Testing with Diesel * Extending Diesel
diesel
cratediesel-cli
diesel-migration
diesel-full-text-search
diesel-dynamic-schema
diesel-async
diesel_cli
table!
Macrotable!
Macromod users {
pub struct table;
mod columns {
pub struct id;
pub struct name;
}
pub use columns::*;
pub mod dsl {
pub use super::table as users;
pub use super::columns::*;
}
}
users::id
users::table.select(users::id)
maps to
SELECT id FROM users
QueryDsl
traitRunQueryDsl
:
load::<U>
/get_results::<U>
:
Returns a list of U
get_result::<U>
: Returns the first U
ignores the restfirst::<U>
: Returns the first U
,
attaches a LIMIT 1 clause to the executed queryexecute
: Returns the number of affected columnQueryable
the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Integer): load_dsl::private::CompatibleType<User, Sqlite>` is not satisfied
--> src/main.rs:19:31
|
19 | users::table.load::<User>(conn);
| ---- ^^^^ the trait `load_dsl::private::CompatibleType<User, Sqlite>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Integer)`, which is required by `table: LoadQuery<'_, _, User>`
| |
| required by a bound introduced by this call
|
= note: this is a mismatch between what your query returns and what your type expects the query to return
= note: the fields in your struct need to match the fields returned by your query in count, order and type
= note: consider using `#[derive(Selectable)]` or #[derive(QueryableByName)] + `#[diesel(check_for_backend(Sqlite))]`
on your struct `User` and in your query `.select(User::as_select())` to get a better error message
-> Diesel provides a #[derive(Selectable)]
which
allows to generate a matching select clause from your struct
error[E0277]: cannot deserialize a value of the database type `diesel::sql_types::Integer` as `*const str`
--> src/main.rs:16:9
|
16 | id: String,
| ^^^^^^ the trait `FromSql<diesel::sql_types::Integer, Sqlite>` is not implemented for `*const str`, which is required by `String: FromSqlRow<diesel::sql_types::Integer, Sqlite>`
|
= note: double check your type mappings via the documentation of `diesel::sql_types::Integer`
= help: the trait `FromSql<diesel::sql_types::Text, Sqlite>` is implemented for `*const str`
= help: for that trait implementation, expected `diesel::sql_types::Text`, found `diesel::sql_types::Integer`
= note: required for `String` to implement `FromSql<diesel::sql_types::Integer, Sqlite>`
= note: required for `String` to implement `diesel::Queryable<diesel::sql_types::Integer, Sqlite>`
= note: required for `String` to implement `FromSqlRow<diesel::sql_types::Integer, Sqlite>`
users::table.select((
users::id, // select a column,
// select clause part based on selectable
UserWithName::as_select(),
users::id + users::id, // arbitary expressions
diesel::dsl::date(diesel::dsl::now), // SQL function calls
"abc".into_sql::<Text>((), // constants
))
users::table
.filter(users::id.eq(42))
.filter(users::name.like("%John%"))
.or_filter(users::name.is_null())
AND
expressionExpressionMethods
for expressionsASC
QueryDsl
methods for other clausesGROUP BY
is special, as it adds additional
requirements+-*/
) so that
they can be used to combine expressionsusers::table
.filter(users::my_boolean_column)
.filter(users::id.eq(42))
.filter(users::id.(users::age + users::age))
.filter(42.into_sql::<Integer>().eq(users::id))
.filter(users::id.eq(users::id + 5).eq(false))
.filter(users::id.eq_any(
posts::table
.filter(posts::name.eq("My fancy post"))
.select(posts::user_id)
))
diesel::insert_into(users::table)
.values((
users::name.eq("John"),
users::age.eq(42),
)).execute(&mut conn)?;
INSERT INTO table
statement#[derive(Insertable)]
#[diesel(table_name = users)]
struct NewUser {
name: String,
age: i32,
}
let values: Vec<NewUser> = /* … */;
diesel::insert_into(users::table)
.values(values)
.returning(User::as_returning())
.get_results(&mut conn)?;
diesel::update(users::table.find(42))
.set((
users::name.eq("Jane"),
users::age.eq(users::age + 5.into_sql::<Integer>())
)).execute(&mut conn)?;
UPDATE table
statementfind
is a shorthand for
.filter(users::id.eq(42))
#[derive(AsChangeset, Identifiable)]
#[diesel(table_name = users)]
struct UserChangeset {
id: i32,
name: String,
age: i32,
}
let value = UserChangeset { /*…*/ };
diesel::update(&value)
.set(&value)
.returning(users::id)
.get_result::<i32>(&mut conn)?;
Identifiable
DELETE FROM table
statement#[derive(QueryableByName)]
#[diesel(table_name = users)]
struct User {
id: i32,
name: String,
}
diesel::sql_query("SELECT id, name FROM users WHERE name = $1")
.bind::<Text, _>("Jane")
.load::<User>(&mut conn)?;
format!()
users::table.inner_join(posts::table)
.load::<(User, Post)>(&mut conn)?;
users::table
.inner_join(posts::table)
.group_by(users::id)
.select((User::as_select(), dsl::count(posts::id)))
.load::<(User, i64)>(&mut conn)?;
QueryDsl
let user = users::table.filter(users::name.eq("Jane"))
.get_result::<User>(&mut conn)?;
let following_user = Followers::belonging_to(&user)
.inner_join(users::table)
.select(User::as_select())
.load(&mut conn);
belonging_to
just constructs a query targeting the
followers
tableConnection::transaction
competition_overview.rs
)registration_list.rs
)registration.rs
)admin
module)RunQueryDsl
and Connection
Normal Diesel
use diesel::prelude::*;
let mut connection = PgConnection::establish(url)?;
let users = users::table.load::<User>(&mut conn)?;
Async Diesel
Use diesel:
Use Diesel-async:
lib.rs
and a
main.rs
filelib.rs
have a function that constructs your state
and your routerConnection::begin_test_transaction()
on creating a
new connection#[tokio::test]
async fn my_test() {
let (router, state) = race_timing::setup(test_config(true));
let resp = router.oneshot(
Request::get("/index.html")
.body(Body::empty())
.unwrap()
).await
.unwrap();
assert_eq!(res.status(), StatusCode::Ok);
// possibly interact with the database via state here
// to check that the database contains the relevant details
}
admin/users.rs
that also needs to be testedBackend
RawValue<'a>
describes how values are represented
at the protocol valuesdiesel::pg::Pg
,
diesel::sqlite::Sqlite
and
diesel::mysql::Mysql
QueryFragment
pub trait QueryFragment<DB: Backend, SP = NotSpecialized> {
fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>)
-> QueryResult<()>;
}
QueryFragment
QueryId
trait QueryId {
type QueryId: Any;
const HAS_STATIC_QUERY_ID: bool;
fn query_id() -> Option<TypeId> {/**/}
}
impl<L, R> QueryId for Eq<L, R>
where L: QueryId, R: QueryId
{
type QueryId = Self;
const HAS_STATIC_QUERY_ID: bool =
L::HAS_STATIC_QUERY_ID && R::HAS_STATIC_QUERY_ID;
}
Expression
SELECT
clausesFROM
clausesGROUP BY
clausesFromSql
A
is the SQL side typeSelf
is the rust side typeDB
FromSqlRow
if you implement this
traitToSql
pub trait ToSql<A, DB: Backend>: Debug {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>)
-> Result;
}
A
is the SQL side typeSelf
is the rust side typeDB
AsExpression
if you implement this
traitFromSql
and
ToSql
Connection
Backend
+
Connection
diesel::sql_types::Integer
i32
FromSql
+ ToSql
FromSqlRow
and
AsExpression
define_sql_function!()
to setup a new “bindings”
for a SQL functioninfix_operator!
/prefix_operator!
/postfix_operator!
to setup bindings for a SQL operatorQueryFragment
, Expression
and QueryId
for your typedefine_sql_function!()
define_sql_function! {
fn lower(x: Text) -> Text;
}
/// use it like
diesel::select(lower("TEST")).get_result::<String>(&mut conn)?;
/// for sqlite
lower_utils::register_impl(connection, |x: String| {
x.to_lowercase()
})?;
infix_operator!()
QueryFragment
QueryFragment
// also derive `QueryId` and `ValidGrouping` for the Eq struct
impl<L, R> QueryFragment<Pg> for Eq<L, R>
where
L: QueryFragment<Pg>,
R: QueryFragment<Pg>,
{
fn walk_ast(&self, mut pass: AstPass<DB>)
-> QueryResult<()> {
self.left.walk_ast(pass.reborrow())?;
pass.push_sql(" = ");
self.right.walk_ast(pass.reborrow())?;
Ok(())
}
}
QueryFragment
uuid::Uuid
schema.rs
fileID
typedef in the
database
modulediesel::MultiConnection
)upserts
or COPY TO
statements
Comments
s
on your keyboard