Skip to content

add partly Mysql support #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
baf05ae
Add mysql feature flags
Apr 22, 2020
702ddad
Generate an impl for LoadingHandler for diesel::mysql::Mysql
Apr 22, 2020
778fecb
Generate an impl for WundergraphBelongsTo for diesel::mysql::Mysql
Apr 22, 2020
6f76152
Generate an impl for BuildFilterHelper for diesel::mysql::Mysql
Apr 22, 2020
61641d2
Implement ApplyOffset for diesel::mysql::Mysq
May 2, 2020
0da2814
wundergraph_example supports mysql
May 2, 2020
0ebcb0d
ApplyOffset for diesel::mysql::Mysql sets mandatory limit parameter
May 4, 2020
52759f9
[UNFINISHED DRAFT] Implement HandleInsert and HandleBatchInsert for d…
May 5, 2020
5f9478d
Fix wundergraph_example to compile with the mysql backend
weiznich May 12, 2020
a92f2c5
Add mysql feature flags
Apr 22, 2020
e8e1a41
Generate an impl for LoadingHandler for diesel::mysql::Mysql
Apr 22, 2020
add2c6d
Generate an impl for WundergraphBelongsTo for diesel::mysql::Mysql
Apr 22, 2020
0e82a5c
Generate an impl for BuildFilterHelper for diesel::mysql::Mysql
Apr 22, 2020
f781c32
Implement ApplyOffset for diesel::mysql::Mysq
May 2, 2020
0bd0c52
Update wundergraph/src/query_builder/selection/offset.rs
p-alik May 16, 2020
bafc006
rustfmt adds an empty like for sake of CI
May 16, 2020
463dafc
avoid warnings: trait objects without an explicit dyn are deprecated
May 17, 2020
858d82f
wundergraph_example supports MySql
Jun 15, 2020
4ed4ca7
wundergraph_cli supports MySql
Jun 15, 2020
494511e
Merge branch 'issue-3' of git://github.com/weiznich/wundergraph into …
Jun 15, 2020
b1e714b
Merge branch 'weiznich-issue-3' into issue-3
Jun 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 107 additions & 3 deletions wundergraph_cli/src/print_schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,45 @@ pub fn print<W: Write>(
)?;
writeln!(out, "use wundergraph::scalar::WundergraphScalarValue;")?;
writeln!(out, "use wundergraph::WundergraphEntity;")?;
if cfg!(feature = "mysql") {
writeln!(out, "use diesel::dsl::SqlTypeOf;")?;
writeln!(out, "use diesel::mysql::Mysql;")?;
writeln!(out, "use diesel::query_dsl::methods::FilterDsl;")?;
writeln!(out, "use diesel::sql_types::{{Bigint, HasSqlType}};")?;
writeln!(out, "use diesel::{{")?;
writeln!(out, " no_arg_sql_function, AppearsOnTable, Connection, EqAll, Identifiable, Insertable, RunQueryDsl,")?;
writeln!(out, "}};")?;
writeln!(
out,
"use juniper::{{ExecutionResult, Executor, Selection, Value}};"
)?;
writeln!(out, "use std::convert::TryFrom;")?;
writeln!(
out,
"use wundergraph::query_builder::mutations::{{HandleBatchInsert, HandleInsert}};"
)?;
writeln!(
out,
"use wundergraph::query_builder::selection::fields::WundergraphFieldList;"
)?;
writeln!(
out,
"use wundergraph::query_builder::selection::filter::BuildFilter;"
)?;
writeln!(
out,
"use wundergraph::query_builder::selection::order::BuildOrder;"
)?;
writeln!(
out,
"use wundergraph::query_builder::selection::select::BuildSelect;"
)?;
writeln!(out, "use wundergraph::query_builder::selection::{{LoadingHandler, QueryModifier, SqlTypeOfPlaceholder}};")?;
writeln!(out, "use wundergraph::WundergraphContext;")?;
writeln!(out)?;
writeln!(out, "diesel::no_arg_sql_function!(LAST_INSERT_ID, Bigint);")?;
}

writeln!(out)?;
writeln!(out, "{}", definitions)?;
writeln!(out)?;
Expand Down Expand Up @@ -85,6 +124,9 @@ mod tests {
#[cfg(feature = "postgres")]
const BACKEND: &str = "postgres";

#[cfg(feature = "mysql")]
const BACKEND: &str = "mysql";

#[cfg(feature = "sqlite")]
const BACKEND: &str = "sqlite";

Expand Down Expand Up @@ -125,6 +167,36 @@ mod tests {
);"#,
];

#[cfg(feature = "mysql")]
const MIGRATION: &[&str] = &[
r#"DROP TABLE IF EXISTS comments;"#,
r#"DROP TABLE IF EXISTS posts;"#,
r#"DROP TABLE IF EXISTS users;"#,
r#"CREATE TABLE users(
id INTEGER NOT NULL AUTO_INCREMENT,
name TEXT NOT NULL,
PRIMARY KEY (`id`)
);"#,
r#"CREATE TABLE posts(
id INTEGER NOT NULL AUTO_INCREMENT,
author INTEGER DEFAULT NULL,
title TEXT NOT NULL,
datetime TIMESTAMP NULL DEFAULT NULL,
content TEXT,
PRIMARY KEY (`id`),
FOREIGN KEY (`author`) REFERENCES `users` (`id`)
);"#,
r#"CREATE TABLE comments(
id INTEGER NOT NULL AUTO_INCREMENT,
post INTEGER DEFAULT NULL,
commenter INTEGER DEFAULT NULL,
content TEXT NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`post`) REFERENCES `posts` (`id`),
FOREIGN KEY (`commenter`) REFERENCES `users` (`id`)
);"#,
];

fn setup_simple_schema(conn: &InferConnection) {
use diesel::prelude::*;
use diesel::sql_query;
Expand All @@ -141,6 +213,12 @@ mod tests {
sql_query(*m).execute(conn).unwrap();
}
}
#[cfg(feature = "mysql")]
InferConnection::Mysql(conn) => {
for m in MIGRATION {
sql_query(*m).execute(conn).unwrap();
}
}
}
}

Expand All @@ -153,7 +231,7 @@ mod tests {

#[cfg(feature = "postgres")]
print(&conn, Some("infer_test"), &mut out).unwrap();
#[cfg(feature = "sqlite")]
#[cfg(any(feature = "mysql", feature = "sqlite"))]
print(&conn, None, &mut out).unwrap();

let s = String::from_utf8(out).unwrap();
Expand Down Expand Up @@ -187,7 +265,7 @@ mod tests {
let mut api_file = File::create(api).unwrap();
#[cfg(feature = "postgres")]
print(&conn, Some("infer_test"), &mut api_file).unwrap();
#[cfg(feature = "sqlite")]
#[cfg(any(feature = "mysql", feature = "sqlite"))]
print(&conn, None, &mut api_file).unwrap();

let main = tmp_dir
Expand Down Expand Up @@ -224,6 +302,17 @@ mod tests {
)
.unwrap();

#[cfg(feature = "mysql")]
write!(
main_file,
include_str!("template_main.rs"),
conn = "MysqlConnection",
db_url = std::env::var("DATABASE_URL").unwrap(),
migrations = migrations,
listen_url = listen_url
)
.unwrap();

let cargo_toml = tmp_dir.path().join("wundergraph_roundtrip_test/Cargo.toml");
let mut cargo_toml_file = std::fs::OpenOptions::new()
.write(true)
Expand Down Expand Up @@ -269,6 +358,21 @@ mod tests {
)
.unwrap();
}
#[cfg(feature = "mysql")]
{
writeln!(
cargo_toml_file,
r#"diesel = {{version = "1.4", features = ["mysql", "chrono"]}}"#
)
.unwrap();

writeln!(
cargo_toml_file,
"wundergraph = {{path = \"{}\", features = [\"mysql\", \"chrono\"] }}",
wundergraph_dir
)
.unwrap();
}
writeln!(cargo_toml_file, r#"juniper = "0.14""#).unwrap();
writeln!(cargo_toml_file, r#"failure = "0.1""#).unwrap();
writeln!(cargo_toml_file, r#"actix-web = "1""#).unwrap();
Expand Down Expand Up @@ -316,7 +420,7 @@ mod tests {
println!("Started server");

let client = reqwest::Client::new();
std::thread::sleep(std::time::Duration::from_secs(1));
std::thread::sleep(std::time::Duration::from_secs(5));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 secs was used to run mysql tests locally. But since ci postgres test failed for same reason, I increased the sleep duration globally.


let query = "{\"query\": \"{ Users { id name } } \"}";
let mutation = r#"{"query":"mutation CreateUser {\n CreateUser(NewUser: {name: \"Max\"}) {\n id\n name\n }\n}","variables":null,"operationName":"CreateUser"}"#;
Expand Down
194 changes: 194 additions & 0 deletions wundergraph_cli/src/print_schema/print_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,200 @@ impl<'a> Display for GraphqlInsertable<'a> {
}
}
writeln!(f, "}}")?;
if cfg!(feature = "mysql") && self.table.primary_key.iter().len() == 1 {
let mut out = PadAdapter::new(f);
writeln!(out)?;
// FIXME ensure type of id is appropriate for i32
let id = self.table.primary_key.iter().next().unwrap();
let table_name = &self.table.name.name;

writeln!(
out,
"impl<L, Ctx> HandleInsert<L, New{}, Mysql, Ctx> for {}::table",
fix_table_name(&self.table.name.name),
&table_name
)?;
writeln!(out, "where")?;
writeln!(
out,
" L: LoadingHandler<Mysql, Ctx, Table = {}::table> + 'static,",
&table_name
)?;
writeln!(out, " L::FieldList: WundergraphFieldList<Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
writeln!(
out,
" <L::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<{}::table>,",
&table_name
)?;
writeln!(
out,
" L::Columns: BuildOrder<{}::table, Mysql>",
&table_name
)?;
writeln!(out, " + BuildSelect<")?;
writeln!(out, " {}::table,", &table_name)?;
writeln!(out, " Mysql,")?;
writeln!(out, " SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
writeln!(out, " >,")?;
writeln!(out, " &'static L: Identifiable,")?;
writeln!(
out,
" Ctx: WundergraphContext + QueryModifier<L, Mysql>,"
)?;
writeln!(out, " Ctx::Connection: Connection<Backend = Mysql>,")?;
writeln!(
out,
" <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<{}::id>>",
&table_name
)?;
writeln!(out, " + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>>,", table_name)?;
writeln!(out, "{{")?;
writeln!(out, " fn handle_insert(")?;
writeln!(
out,
" selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,"
)?;
writeln!(
out,
" executor: &Executor<'_, Ctx, WundergraphScalarValue>,"
)?;
writeln!(
out,
" insertable: New{},",
fix_table_name(&self.table.name.name)
)?;
writeln!(out, " ) -> ExecutionResult<WundergraphScalarValue> {{")?;
writeln!(out, " let ctx = executor.context();")?;
writeln!(out, " let conn = ctx.get_connection();")?;
writeln!(out, " let look_ahead = executor.look_ahead();")?;
writeln!(
out,
" insertable.insert_into({}::table).execute(conn).unwrap();",
&table_name
)?;
writeln!(
out,
" let last_insert_id: i64 = diesel::select(LAST_INSERT_ID).first(conn)?;"
)?;
writeln!(
out,
" let last_insert_id = i32::try_from(last_insert_id)?;"
)?;
writeln!(out, " let q = L::build_query(&[], &look_ahead)?;")?;
writeln!(
out,
" let q = FilterDsl::filter(q, {}::{}.eq_all(last_insert_id));",
&table_name, &id
)?;
writeln!(
out,
" let items = L::load(&look_ahead, selection, executor, q)?;"
)?;
writeln!(
out,
" Ok(items.into_iter().next().unwrap_or(Value::Null))"
)?;
writeln!(out, " }}")?;
writeln!(out, "}}")?;
writeln!(out)?;

writeln!(
out,
"impl<L, Ctx> HandleBatchInsert<L, New{}, Mysql, Ctx> for {}::table",
fix_table_name(&self.table.name.name),
&table_name
)?;
writeln!(out, "where")?;
writeln!(
out,
" L: LoadingHandler<Mysql, Ctx, Table = {}::table> + 'static,",
&table_name
)?;
writeln!(out, " L::FieldList: WundergraphFieldList<Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
writeln!(
out,
" <L::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<{}::table>,",
&table_name
)?;
writeln!(
out,
" L::Columns: BuildOrder<{}::table, Mysql>",
&table_name
)?;
writeln!(out, " + BuildSelect<")?;
writeln!(out, " {}::table,", &table_name)?;
writeln!(out, " Mysql,")?;
writeln!(out, " SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
writeln!(out, " >,")?;
writeln!(out, " &'static L: Identifiable,")?;
writeln!(
out,
" Ctx: WundergraphContext + QueryModifier<L, Mysql>,"
)?;
writeln!(out, " Ctx::Connection: Connection<Backend = Mysql>,")?;
writeln!(
out,
" <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<{}::id>>",
&table_name
)?;
writeln!(out, " + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>>,", table_name)?;
writeln!(out, "{{")?;
writeln!(out, " fn handle_batch_insert(")?;
writeln!(
out,
" selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,"
)?;
writeln!(
out,
" executor: &Executor<'_, Ctx, WundergraphScalarValue>,"
)?;
writeln!(
out,
" batch: Vec<New{}>,",
fix_table_name(&self.table.name.name)
)?;
writeln!(out, " ) -> ExecutionResult<WundergraphScalarValue> {{")?;
writeln!(out, " let ctx = executor.context();")?;
writeln!(out, " let conn = ctx.get_connection();")?;
writeln!(out, " let look_ahead = executor.look_ahead();")?;
writeln!(out, " let single_insert = |insertable: New{}| -> ExecutionResult<WundergraphScalarValue> {{", fix_table_name(&self.table.name.name))?;
writeln!(
out,
" insertable.insert_into({}::table).execute(conn).unwrap();",
&table_name
)?;
writeln!(out, " let last_insert_id: i64 = diesel::select(LAST_INSERT_ID).first(conn)?;")?;
writeln!(
out,
" let last_insert_id = i32::try_from(last_insert_id)?;"
)?;
writeln!(
out,
" let q = L::build_query(&[], &look_ahead)?;"
)?;
writeln!(
out,
" let q = FilterDsl::filter(q, {}::{}.eq_all(last_insert_id));",
&table_name, &id
)?;
writeln!(
out,
" let items = L::load(&look_ahead, selection, executor, q)?;"
)?;
writeln!(
out,
" Ok(items.into_iter().next().unwrap_or(Value::Null))"
)?;
writeln!(out, " }};")?;
writeln!(out, " let r = batch")?;
writeln!(out, " .into_iter()")?;
writeln!(out, " .map(|i| single_insert(i))")?;
writeln!(out, " .collect::<Result<Vec<_>, _>>()?;")?;
writeln!(out, " Ok(Value::List(r))")?;
writeln!(out, " }}")?;
writeln!(out, "}}")?;
writeln!(out)?;
}
Ok(())
}
}
Expand Down
Loading