S
) for additional informationtable! {
students {
id -> Integer,
name -> Text,
}
}
#[derive(Queryable)]
struct Student {
id: i32,
name: String,
}
let students: Vec<Student> =
students::table
.filter(students::id.eq(42))
.load::<Student>(&conn)?;
diesel::insert_into(students::table)
.values((
students::id.eq(43),
students::name.eq("weiznich"),
)).execute(&conn)?;
General design assumptions for diesel:
diesel_cli
(A tool to manage migrations/your schema)table!
marcoQueryDsl
traita_table::table.select((
a_table::column_a, // just select a column
a_table::column_b + a_table::column_b, // an expression
"abc".into_sql::<Text>() // or a constant
))
table!
macroa_table::table
.filter(a_table::column_a.eq(foo)
.and(a_table::column_b.like(baz)))
.or_filter(a_table::column_c.is_null())
.filter
twice appends the new where clause with an AND
to the old one*ExpressionMethods
traits for methods useful to construct inner expressionsASC
Queryable
)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 columnsdiesel::insert_into(a_table::table)
.values((
a_table::column_a.eq(value),
a_table::column_b.eq(other_value)
)).execute(&conn);
INSERT INTO
statement.diesel::update(a_table::table.filter(a_table::id.eq(1)))
.set((
a_table::column_a.eq(some_value),
a_table::column_b.eq(a_table::column_b + 5.into_sql::<Integer>())
)).execute(&conn);
UPDATE
statementDROP FROM a_table
statement#[derive(QueryableByName)]
#[table_name = "students"]
struct Student {
id: i32,
name: String
}
diesel::sql_query("SELECT id, name FROM students WHERE name = $1")
.bind::<Text, _>("weiznich")
.load(&conn);
#[derive(Identifiable, Queryable)]
#[table_name = "teachers"]
struct Teacher {
id: i32,
name: String,
}
#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(Teacher, foreign_key = "supervisor")]
#[table_name = "students"]
struct Student {
id: i32,
name: String,
supervisor: i32
}
let teachers = teachers::table.load::<Teacher>(&conn)?;
let students = Student::belonging_to(&teachers)
.load::<Student>(&conn)?
.grouped_by(&teachers);
let teachers_with_students = teachers.into_iter()
.zip(students)
.collect::<Vec<(Teacher, Vec<Student>)>>();
error[E0277]: the trait bound `teachers::columns::name: diesel::SelectableExpression<students::table>` is not satisfied
--> src/main.rs:30:21
|
30 | students::table.select(teachers::name).load::<Student>(&conn);
| ^^^^^^ the trait `diesel::SelectableExpression<students::table>` is not implemented for `teachers::columns::name`
|
= help: the following implementations were found:
#[derive(Queryable)]
struct Student {
id: i32,
name: String
}
let s: Vec<Student> = students::table
.select(students::id)
.load(conn);
error[E0277]: the trait bound `(std::string::String, i32): diesel::Queryable<diesel::sql_types::Integer, diesel::pg::Pg>` is not satisfied
--> src/main.rs:34:64
|
34 | let s: Vec<Student> = students::table.select(students::id).load(&conn)?;
| ^^^^ the trait `diesel::Queryable<diesel::sql_types::Integer, diesel::pg::Pg>` is not implemented for `(std::string::String, i32)`
error[E0277]: the trait bound `*const str: diesel::deserialize::FromSql<diesel::sql_types::Integer, diesel::pg::Pg>` is not satisfied
--> src/main.rs:34:63
|
34 | let s: Vec<String> = students::table.select(students::id).load(&conn)?;
| ^^^^ the trait `diesel::deserialize::FromSql<diesel::sql_types::Integer, diesel::pg::Pg>` is not implemented for `*const str`
= help: the following implementations were found:
<*const [u8] as diesel::deserialize::FromSql<diesel::sql_types::Binary, DB>>
<*const str as diesel::deserialize::FromSql<diesel::sql_types::Text, DB>>
= note: required because of the requirements on the impl of `diesel::deserialize::FromSql<diesel::sql_types::Integer, diesel::pg::Pg>` for `std::string::String`
GROUP BY
clauses and mixing aggregating and non aggregating expressionsSELECT
statements at compile time:
users
posts
comments
Bootstrap a simple REST API for the given database schema:
diesel_cli
impl FromSql<ColorType, Pg> for Color {
fn from_sql(bytes: Option<&[u8]>) -> Result<Self> {
match bytes {
Some(b"red") => Ok(Color::Red),
Some(b"green") => Ok(Color::Green),
Some(b"blue") => Ok(Color::Blue),
_ => Err("Unrecognized enum variant".into())
}
}
}
impl ToSql<ColorType, Pg> Color {
fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> Result<IsNull> {
match *self {
Color::Red => out.write_all(b"red")?;,
Color::Green => out.write_all(b"green")?;
Color::Blue => out.write_all(b"blue")?;
}
Ok(IsNull::No)
}
}
RawValue
says which how data are represented at protocol levelstruct Eq<L, R>{
left: L,
right: R,
}
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(())
}
}
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;
}
TypeId
of the composite type, use that as static prepared statement cache key
let mut query = students::table.select(students::name);
if let Some(id) = filter_by_id {
query = query.filter(students::id.eq(id));
}
let mut query = students::table.select(students::name)
.into_boxed();
if let Some(id) = filter_by_id {
query = query.filter(students::id.eq(id));
}
.into_boxed
creates a boxed select statement that erases all generic arguments beside of the table name and target backend for the given select statementpost
table indicating the state of the post.
Draft
, Published
, Deleted
http://localhost/posts?name="foo"&order="id"
post
table
http://localhost/posts/page/$page_number?post_count=$postcount
to return the number of total pages and the posts of the requested page.{
Teachers {
teacherName: name
students {
name
}
}
}
#[derive(GraphQLObject)]
struct Student {
id: i32,
name: String,
}
struct Query;
juniper::graphql_object!(Query: Context |&self| {
field apiVersion() -> &str {
"1.0"
}
field student(&executor, id: i32) -> FieldResult<Student> {
let context = executor.context();
context.load_student_with_id(id)
}
});
#[derive(GraphQLObject, Queryable)]
struct Student {
id: i32,
name: String,
}
#[derive(Queryable)]
struct Teacher {
id: i32,
name: String,
}
juniper::graphql_object!(Teacher: Context |&self| {
field id() -> i32 {
self.id
}
field name() -> &str {
&self.name
}
field students(&executor) -> FieldResult<Vec<Student>> {
let conn = executor.context().pool.get_connection()?;
let students = students::table
.filter(students::supervisor.eq(self.id))
.load::<Student>(&conn)?;
Ok(students)
}
})
#[derive(Identifiable, WundergraphEntity)]
#[table_name = "students"]
struct Student {
id: i32,
name: String,
supervisor: HasOne<i32, Teacher>,
}
#[derive(Identifiable, WundergraphEntity)]
#[table_name = "teachers"]
struct Teacher {
id: i32,
name: String,
students: HasMany<Student, students::supervisor>,
}
wundergraph::query_object! {
Query {
Student,
Teacher
}
}
WundergraphEntity
#[derive(WundergraphEntity, Identifiable)]
#[table_name = "student"]
#[primary_key(id)]
/// GraphQL type description
struct Student {
/// GraphQL field description
id: i32,
#[wundergraph(graphql_name = "name")]
#[column_name = "name"]
name: String,
supervisor: HasOne<i32, Teacher>,
papers: HasMany<Paper, papers::student>,
}
query_object!
mutation CreateStudent {
createStudent(name: "weiznich", supervisor: 42) {
id
name
}
}
Insertable
are automatically usable as insert mutationAsChangeset
are automatically usable as update mutationwundergraph::query_builder::mutations
mutation_object!
WundergraphContext
as juniper context typeConnection
traittrait QueryModifier<L, DB> {
fn modify_query(
&self,
select: &LookAheadSelection<WundergraphScalarValue>,
query: BoxedQuery<L, DB, Self>,
) -> Result<BoxedQuery<L, DB, Self>>;
}
table!
macro callNULL
instead of removing the fieldAssociations
approach involving a fixed number of queries insteadBoxedSelectStatement
helps there, but writing generic code also requiredpub trait LoadingHandler<DB, Ctx> : HasTable + Sized
where DB: Backend
{
type Columns;
type FieldList;
type PrimaryKeyIndex;
type Filter;
const FIELD_NAMES: &[&str];
const TYPE_NAME: &str;
}
pub trait WundergraphBelongsTo<Other, DB, Ctx, FK>: LoadingHandler<DB, Ctx> {
type Key;
fn resolve(
glob_args: &[LookAheadArgument<WundergraphScalarValue>],
look_ahead: &[LookAheadSelection<WundergraphScalarValue>],
selection: Option<&[Selection<WundergraphScalarValue>],
keys: &[Option<Self::Key>],
executor: Executor<Ctx, WundergraphScalarValue>,
) -> Result<
HashMap<
Option<Self::Key>,
Vec<Value<WundergraphScalarValue>>
>>;
}
LoadingHandler
trait ExtractTableFields {
type Out;
}
impl<T1, T2> ExtractTableFields for (T1, T2)
where T1: WundergraphValue, (T2,): ExtractTableFields
<(T2,) as ExtractTableFields>::Out: AppendToTuple<T1>
{
type Out = <<(T2,) as ExtractTableFields>::Out
as AppendToTuple<T1>>::Out;
}
impl<C, FK, T2> ExtractTableFields for (HasMany<C, FK>, T2)
where (T2,): ExtractTableFields
{
type Out = <(T2,) as ExtractTableFields>::Out;
}
SELECT * FROM post_at_version(5);