Skip to content

Commit d3f36fd

Browse files
author
Alexei Pastuchov
committed
wundergraph_cli supports MySql
1 parent 858d82f commit d3f36fd

File tree

3 files changed

+705
-2
lines changed

3 files changed

+705
-2
lines changed

wundergraph_cli/src/print_schema/mod.rs

+108-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,45 @@ pub fn print<W: Write>(
3939
)?;
4040
writeln!(out, "use wundergraph::scalar::WundergraphScalarValue;")?;
4141
writeln!(out, "use wundergraph::WundergraphEntity;")?;
42+
if cfg!(feature = "mysql") {
43+
writeln!(out, "use diesel::dsl::SqlTypeOf;")?;
44+
writeln!(out, "use diesel::mysql::Mysql;")?;
45+
writeln!(out, "use diesel::query_dsl::methods::FilterDsl;")?;
46+
writeln!(out, "use diesel::sql_types::{{Bigint, HasSqlType}};")?;
47+
writeln!(out, "use diesel::{{")?;
48+
writeln!(out, " no_arg_sql_function, AppearsOnTable, Connection, EqAll, Identifiable, Insertable, RunQueryDsl,")?;
49+
writeln!(out, "}};")?;
50+
writeln!(
51+
out,
52+
"use juniper::{{ExecutionResult, Executor, Selection, Value}};"
53+
)?;
54+
writeln!(out, "use std::convert::TryFrom;")?;
55+
writeln!(
56+
out,
57+
"use wundergraph::query_builder::mutations::{{HandleBatchInsert, HandleInsert}};"
58+
)?;
59+
writeln!(
60+
out,
61+
"use wundergraph::query_builder::selection::fields::WundergraphFieldList;"
62+
)?;
63+
writeln!(
64+
out,
65+
"use wundergraph::query_builder::selection::filter::BuildFilter;"
66+
)?;
67+
writeln!(
68+
out,
69+
"use wundergraph::query_builder::selection::order::BuildOrder;"
70+
)?;
71+
writeln!(
72+
out,
73+
"use wundergraph::query_builder::selection::select::BuildSelect;"
74+
)?;
75+
writeln!(out, "use wundergraph::query_builder::selection::{{LoadingHandler, QueryModifier, SqlTypeOfPlaceholder}};")?;
76+
writeln!(out, "use wundergraph::WundergraphContext;")?;
77+
writeln!(out)?;
78+
writeln!(out, "diesel::no_arg_sql_function!(LAST_INSERT_ID, Bigint);")?;
79+
}
80+
4281
writeln!(out)?;
4382
writeln!(out, "{}", definitions)?;
4483
writeln!(out)?;
@@ -85,6 +124,9 @@ mod tests {
85124
#[cfg(feature = "postgres")]
86125
const BACKEND: &str = "postgres";
87126

127+
#[cfg(feature = "mysql")]
128+
const BACKEND: &str = "mysql";
129+
88130
#[cfg(feature = "sqlite")]
89131
const BACKEND: &str = "sqlite";
90132

@@ -125,6 +167,36 @@ mod tests {
125167
);"#,
126168
];
127169

170+
#[cfg(feature = "mysql")]
171+
const MIGRATION: &[&str] = &[
172+
r#"DROP TABLE IF EXISTS comments;"#,
173+
r#"DROP TABLE IF EXISTS posts;"#,
174+
r#"DROP TABLE IF EXISTS users;"#,
175+
r#"CREATE TABLE users(
176+
id INTEGER NOT NULL AUTO_INCREMENT,
177+
name TEXT NOT NULL,
178+
PRIMARY KEY (`id`)
179+
);"#,
180+
r#"CREATE TABLE posts(
181+
id INTEGER NOT NULL AUTO_INCREMENT,
182+
author INTEGER DEFAULT NULL,
183+
title TEXT NOT NULL,
184+
datetime TIMESTAMP NULL DEFAULT NULL,
185+
content TEXT,
186+
PRIMARY KEY (`id`),
187+
FOREIGN KEY (`author`) REFERENCES `users` (`id`)
188+
);"#,
189+
r#"CREATE TABLE comments(
190+
id INTEGER NOT NULL AUTO_INCREMENT,
191+
post INTEGER DEFAULT NULL,
192+
commenter INTEGER DEFAULT NULL,
193+
content TEXT NOT NULL,
194+
PRIMARY KEY (`id`),
195+
FOREIGN KEY (`post`) REFERENCES `posts` (`id`),
196+
FOREIGN KEY (`commenter`) REFERENCES `users` (`id`)
197+
);"#,
198+
];
199+
128200
fn setup_simple_schema(conn: &InferConnection) {
129201
use diesel::prelude::*;
130202
use diesel::sql_query;
@@ -141,6 +213,12 @@ mod tests {
141213
sql_query(*m).execute(conn).unwrap();
142214
}
143215
}
216+
#[cfg(feature = "mysql")]
217+
InferConnection::Mysql(conn) => {
218+
for m in MIGRATION {
219+
sql_query(*m).execute(conn).unwrap();
220+
}
221+
}
144222
}
145223
}
146224

@@ -153,7 +231,7 @@ mod tests {
153231

154232
#[cfg(feature = "postgres")]
155233
print(&conn, Some("infer_test"), &mut out).unwrap();
156-
#[cfg(feature = "sqlite")]
234+
#[cfg(any(feature = "mysql", feature = "sqlite"))]
157235
print(&conn, None, &mut out).unwrap();
158236

159237
let s = String::from_utf8(out).unwrap();
@@ -187,7 +265,7 @@ mod tests {
187265
let mut api_file = File::create(api).unwrap();
188266
#[cfg(feature = "postgres")]
189267
print(&conn, Some("infer_test"), &mut api_file).unwrap();
190-
#[cfg(feature = "sqlite")]
268+
#[cfg(any(feature = "mysql", feature = "sqlite"))]
191269
print(&conn, None, &mut api_file).unwrap();
192270

193271
let main = tmp_dir
@@ -224,6 +302,17 @@ mod tests {
224302
)
225303
.unwrap();
226304

305+
#[cfg(feature = "mysql")]
306+
write!(
307+
main_file,
308+
include_str!("template_main.rs"),
309+
conn = "MysqlConnection",
310+
db_url = std::env::var("DATABASE_URL").unwrap(),
311+
migrations = migrations,
312+
listen_url = listen_url
313+
)
314+
.unwrap();
315+
227316
let cargo_toml = tmp_dir.path().join("wundergraph_roundtrip_test/Cargo.toml");
228317
let mut cargo_toml_file = std::fs::OpenOptions::new()
229318
.write(true)
@@ -269,6 +358,21 @@ mod tests {
269358
)
270359
.unwrap();
271360
}
361+
#[cfg(feature = "mysql")]
362+
{
363+
writeln!(
364+
cargo_toml_file,
365+
r#"diesel = {{version = "1.4", features = ["mysql", "chrono"]}}"#
366+
)
367+
.unwrap();
368+
369+
writeln!(
370+
cargo_toml_file,
371+
"wundergraph = {{path = \"{}\", features = [\"mysql\", \"chrono\"] }}",
372+
wundergraph_dir
373+
)
374+
.unwrap();
375+
}
272376
writeln!(cargo_toml_file, r#"juniper = "0.14""#).unwrap();
273377
writeln!(cargo_toml_file, r#"failure = "0.1""#).unwrap();
274378
writeln!(cargo_toml_file, r#"actix-web = "1""#).unwrap();
@@ -317,6 +421,8 @@ mod tests {
317421

318422
let client = reqwest::Client::new();
319423
std::thread::sleep(std::time::Duration::from_secs(1));
424+
#[cfg(feature = "mysql")]
425+
std::thread::sleep(std::time::Duration::from_secs(4));
320426

321427
let query = "{\"query\": \"{ Users { id name } } \"}";
322428
let mutation = r#"{"query":"mutation CreateUser {\n CreateUser(NewUser: {name: \"Max\"}) {\n id\n name\n }\n}","variables":null,"operationName":"CreateUser"}"#;

wundergraph_cli/src/print_schema/print_helper.rs

+194
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,200 @@ impl<'a> Display for GraphqlInsertable<'a> {
494494
}
495495
}
496496
writeln!(f, "}}")?;
497+
if cfg!(feature = "mysql") && self.table.primary_key.iter().len() == 1 {
498+
let mut out = PadAdapter::new(f);
499+
writeln!(out)?;
500+
// FIXME ensure type of id is appropriate for i32
501+
let id = self.table.primary_key.iter().next().unwrap();
502+
let table_name = &self.table.name.name;
503+
504+
writeln!(
505+
out,
506+
"impl<L, Ctx> HandleInsert<L, New{}, Mysql, Ctx> for {}::table",
507+
fix_table_name(&self.table.name.name),
508+
&table_name
509+
)?;
510+
writeln!(out, "where")?;
511+
writeln!(
512+
out,
513+
" L: LoadingHandler<Mysql, Ctx, Table = {}::table> + 'static,",
514+
&table_name
515+
)?;
516+
writeln!(out, " L::FieldList: WundergraphFieldList<Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
517+
writeln!(
518+
out,
519+
" <L::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<{}::table>,",
520+
&table_name
521+
)?;
522+
writeln!(
523+
out,
524+
" L::Columns: BuildOrder<{}::table, Mysql>",
525+
&table_name
526+
)?;
527+
writeln!(out, " + BuildSelect<")?;
528+
writeln!(out, " {}::table,", &table_name)?;
529+
writeln!(out, " Mysql,")?;
530+
writeln!(out, " SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
531+
writeln!(out, " >,")?;
532+
writeln!(out, " &'static L: Identifiable,")?;
533+
writeln!(
534+
out,
535+
" Ctx: WundergraphContext + QueryModifier<L, Mysql>,"
536+
)?;
537+
writeln!(out, " Ctx::Connection: Connection<Backend = Mysql>,")?;
538+
writeln!(
539+
out,
540+
" <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<{}::id>>",
541+
&table_name
542+
)?;
543+
writeln!(out, " + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>>,", table_name)?;
544+
writeln!(out, "{{")?;
545+
writeln!(out, " fn handle_insert(")?;
546+
writeln!(
547+
out,
548+
" selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,"
549+
)?;
550+
writeln!(
551+
out,
552+
" executor: &Executor<'_, Ctx, WundergraphScalarValue>,"
553+
)?;
554+
writeln!(
555+
out,
556+
" insertable: New{},",
557+
fix_table_name(&self.table.name.name)
558+
)?;
559+
writeln!(out, " ) -> ExecutionResult<WundergraphScalarValue> {{")?;
560+
writeln!(out, " let ctx = executor.context();")?;
561+
writeln!(out, " let conn = ctx.get_connection();")?;
562+
writeln!(out, " let look_ahead = executor.look_ahead();")?;
563+
writeln!(
564+
out,
565+
" insertable.insert_into({}::table).execute(conn).unwrap();",
566+
&table_name
567+
)?;
568+
writeln!(
569+
out,
570+
" let last_insert_id: i64 = diesel::select(LAST_INSERT_ID).first(conn)?;"
571+
)?;
572+
writeln!(
573+
out,
574+
" let last_insert_id = i32::try_from(last_insert_id)?;"
575+
)?;
576+
writeln!(out, " let q = L::build_query(&[], &look_ahead)?;")?;
577+
writeln!(
578+
out,
579+
" let q = FilterDsl::filter(q, {}::{}.eq_all(last_insert_id));",
580+
&table_name, &id
581+
)?;
582+
writeln!(
583+
out,
584+
" let items = L::load(&look_ahead, selection, executor, q)?;"
585+
)?;
586+
writeln!(
587+
out,
588+
" Ok(items.into_iter().next().unwrap_or(Value::Null))"
589+
)?;
590+
writeln!(out, " }}")?;
591+
writeln!(out, "}}")?;
592+
writeln!(out)?;
593+
594+
writeln!(
595+
out,
596+
"impl<L, Ctx> HandleBatchInsert<L, New{}, Mysql, Ctx> for {}::table",
597+
fix_table_name(&self.table.name.name),
598+
&table_name
599+
)?;
600+
writeln!(out, "where")?;
601+
writeln!(
602+
out,
603+
" L: LoadingHandler<Mysql, Ctx, Table = {}::table> + 'static,",
604+
&table_name
605+
)?;
606+
writeln!(out, " L::FieldList: WundergraphFieldList<Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
607+
writeln!(
608+
out,
609+
" <L::Filter as BuildFilter<Mysql>>::Ret: AppearsOnTable<{}::table>,",
610+
&table_name
611+
)?;
612+
writeln!(
613+
out,
614+
" L::Columns: BuildOrder<{}::table, Mysql>",
615+
&table_name
616+
)?;
617+
writeln!(out, " + BuildSelect<")?;
618+
writeln!(out, " {}::table,", &table_name)?;
619+
writeln!(out, " Mysql,")?;
620+
writeln!(out, " SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>,", &table_name)?;
621+
writeln!(out, " >,")?;
622+
writeln!(out, " &'static L: Identifiable,")?;
623+
writeln!(
624+
out,
625+
" Ctx: WundergraphContext + QueryModifier<L, Mysql>,"
626+
)?;
627+
writeln!(out, " Ctx::Connection: Connection<Backend = Mysql>,")?;
628+
writeln!(
629+
out,
630+
" <Ctx::Connection as Connection>::Backend: HasSqlType<SqlTypeOf<{}::id>>",
631+
&table_name
632+
)?;
633+
writeln!(out, " + HasSqlType<SqlTypeOfPlaceholder<L::FieldList, Mysql, L::PrimaryKeyIndex, {}::table, Ctx>>,", table_name)?;
634+
writeln!(out, "{{")?;
635+
writeln!(out, " fn handle_batch_insert(")?;
636+
writeln!(
637+
out,
638+
" selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,"
639+
)?;
640+
writeln!(
641+
out,
642+
" executor: &Executor<'_, Ctx, WundergraphScalarValue>,"
643+
)?;
644+
writeln!(
645+
out,
646+
" batch: Vec<New{}>,",
647+
fix_table_name(&self.table.name.name)
648+
)?;
649+
writeln!(out, " ) -> ExecutionResult<WundergraphScalarValue> {{")?;
650+
writeln!(out, " let ctx = executor.context();")?;
651+
writeln!(out, " let conn = ctx.get_connection();")?;
652+
writeln!(out, " let look_ahead = executor.look_ahead();")?;
653+
writeln!(out, " let single_insert = |insertable: New{}| -> ExecutionResult<WundergraphScalarValue> {{", fix_table_name(&self.table.name.name))?;
654+
writeln!(
655+
out,
656+
" insertable.insert_into({}::table).execute(conn).unwrap();",
657+
&table_name
658+
)?;
659+
writeln!(out, " let last_insert_id: i64 = diesel::select(LAST_INSERT_ID).first(conn)?;")?;
660+
writeln!(
661+
out,
662+
" let last_insert_id = i32::try_from(last_insert_id)?;"
663+
)?;
664+
writeln!(
665+
out,
666+
" let q = L::build_query(&[], &look_ahead)?;"
667+
)?;
668+
writeln!(
669+
out,
670+
" let q = FilterDsl::filter(q, {}::{}.eq_all(last_insert_id));",
671+
&table_name, &id
672+
)?;
673+
writeln!(
674+
out,
675+
" let items = L::load(&look_ahead, selection, executor, q)?;"
676+
)?;
677+
writeln!(
678+
out,
679+
" Ok(items.into_iter().next().unwrap_or(Value::Null))"
680+
)?;
681+
writeln!(out, " }};")?;
682+
writeln!(out, " let r = batch")?;
683+
writeln!(out, " .into_iter()")?;
684+
writeln!(out, " .map(|i| single_insert(i))")?;
685+
writeln!(out, " .collect::<Result<Vec<_>, _>>()?;")?;
686+
writeln!(out, " Ok(Value::List(r))")?;
687+
writeln!(out, " }}")?;
688+
writeln!(out, "}}")?;
689+
writeln!(out)?;
690+
}
497691
Ok(())
498692
}
499693
}

0 commit comments

Comments
 (0)