Skip to content
This repository was archived by the owner on Jul 7, 2021. It is now read-only.

Add mysql support #41

Merged
merged 18 commits into from
Jan 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ required-features = ["sqlite3"]
default = []
diesel-filled = ["tempdir", "diesel"]
sqlite3 = []
mysql = []
pg = []

# Enables unstable (in-development) features,
Expand All @@ -40,4 +41,4 @@ diesel = { version = ">= 1.2, < 2.0", default_features = false, optional = true

# We need to explicitly toggle features here because
# `all-features` causes weird transient bugs
features = ["diesel-filled", "sqlite3", "pg", "unstable"]
features = ["diesel-filled", "mysql", "sqlite3", "pg", "unstable"]
4 changes: 4 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
//!
//! It also re-exports the generators for existing databases
//! so they can be used more conveniently.
#[cfg(feature = "mysql")]
mod mysql;
#[cfg(feature = "mysql")]
pub use self::mysql::MySql;

#[cfg(feature = "pg")]
mod pg;
Expand Down
110 changes: 110 additions & 0 deletions src/backend/mysql.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! MySQL implementation of a generator
//!
//! This module generates strings that are specific to MySQL
//! databases. They should be thoroughly tested via unit testing

use super::SqlGenerator;
use types::{BaseType, Type};

/// MySQL generator backend
pub struct MySql;
impl SqlGenerator for MySql {
fn create_table(name: &str) -> String {
format!("CREATE TABLE {}", name)
}

fn create_table_if_not_exists(name: &str) -> String {
format!("CREATE TABLE {} IF NOT EXISTS", name)
}

fn drop_table(name: &str) -> String {
format!("DROP TABLE {}", name)
}

fn drop_table_if_exists(name: &str) -> String {
format!("DROP TABLE {} IF EXISTS", name)
}

fn rename_table(old: &str, new: &str) -> String {
format!("RENAME TABLE \"{}\" TO \"{}\"", old, new)
}

fn alter_table(name: &str) -> String {
format!("ALTER TABLE \"{}\"", name)
}

fn add_column(ex: bool, name: &str, tt: &Type) -> String {
let bt: BaseType = tt.get_inner();
use self::BaseType::*;

#[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */
format!(
"{}{}{}",
match bt {
Text => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Varchar(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Primary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Integer => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Float => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Double => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
UUID => unimplemented!(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

According to the MySQL 8.0 docs there are supported UUID fields. This is definitely something that barrel should reflect.

Unrelated: this also makes me wonder about how to document the vast amounts of implementation specific features across multiple versions as well 😅

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will add support for UUID for sure. As far as documentation goes, that is a great question, to which, I don't have a great answer. I can look around to see if there are others who've done it well and if not I'm willing to help in that cause.

Json => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Boolean => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Date => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Binary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Foreign(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Custom(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt)),
Array(it) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(Array(Box::new(*it))))
},
match (&tt.default).as_ref() {
Some(ref m) => format!(" DEFAULT '{}'", m),
_ => format!(""),
},
match tt.nullable {
true => "",
false => " NOT NULL",
}
)
}

fn drop_column(name: &str) -> String {
format!("DROP COLUMN \"{}\"", name)
}

fn rename_column(old: &str, new: &str) -> String {
format!("CHANGE COLUMN \"{}\" \"{}\"", old, new)
}
}

impl MySql {
fn prefix(ex: bool) -> String {
match ex {
true => format!("ADD COLUMN "),
false => format!(""),
}
}

fn print_type(t: BaseType) -> String {
use self::BaseType::*;
match t {
Text => format!("TEXT"),
Varchar(l) => match l {
0 => format!("VARCHAR"), // For "0" remove the limit
_ => format!("VARCHAR({})", l),
},
/* "NOT NULL" is added here because normally primary keys are implicitly not-null */
Primary => format!("INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY"),
Integer => format!("INTEGER"),
Float => format!("FLOAT"),
Double => format!("DOUBLE"),
UUID => format!("CHAR(36)"),
Boolean => format!("BOOLEAN"),
Date => format!("DATE"),
Json => format!("JSON"),
Binary => format!("BYTEA"),
Foreign(t) => format!("INTEGER REFERENCES {}", t),
Custom(t) => format!("{}", t),
Array(meh) => format!("{}[]", MySql::print_type(*meh)),
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
//! Also, if there is missing or invalid documentation for this crate, PR's are
//! always welcome 💚

#[cfg(not(any(feature = "sqlite", feature = "pg")))]
#[cfg(not(any(feature = "sqlite3", feature = "pg", feature = "mysql" )))]
compile_error!("`barrel` cannot be built without a database backend speccified via cargo `--features`");

// TODO: Make this "diesel" block prettier
Expand Down
4 changes: 4 additions & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
// We can always trust these tests 👍
mod common;

#[cfg(feature = "mysql")]
mod mysql;

#[cfg(feature = "pg")]
mod pg;

#[cfg(feature = "sqlite3")]
mod sqlite3;

74 changes: 74 additions & 0 deletions src/tests/mysql/add_column.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! All add_column combinations for pgsql
#![allow(unused_imports)]

use backend::{MySql, SqlGenerator};
use types;

#[test]
fn text() {
let sql = MySql::add_column(true, "Text", &types::text());
assert_eq!(String::from("ADD COLUMN \"Text\" TEXT NOT NULL"), sql);
}

#[test]
fn varchar() {
let sql = MySql::add_column(true, "Varchar", &types::varchar(255));
assert_eq!(
String::from("ADD COLUMN \"Varchar\" VARCHAR(255) NOT NULL"),
sql
);
}

#[test]
fn integer() {
let sql = MySql::add_column(true, "Integer", &types::integer());
assert_eq!(String::from("ADD COLUMN \"Integer\" INTEGER NOT NULL"), sql);
}

#[test]
fn float() {
let sql = MySql::add_column(true, "Float", &types::float());
assert_eq!(String::from("ADD COLUMN \"Float\" FLOAT NOT NULL"), sql);
}

#[test]
fn double() {
let sql = MySql::add_column(true, "Double", &types::double());
assert_eq!(String::from("ADD COLUMN \"Double\" DOUBLE PRECISION NOT NULL"), sql);
}

#[test]
fn boolean() {
let sql = MySql::add_column(true, "Boolean", &types::boolean());
assert_eq!(String::from("ADD COLUMN \"Boolean\" BOOLEAN NOT NULL"), sql);
}

#[test]
fn binary() {
let sql = MySql::add_column(true, "Binary", &types::binary());
assert_eq!(String::from("ADD COLUMN \"Binary\" BYTEA NOT NULL"), sql);
}

#[test]
fn date() {
let sql = MySql::add_column(true, "Date", &types::date());
assert_eq!(String::from("ADD COLUMN \"Date\" DATE NOT NULL"), sql);
}

#[test]
fn foreign() {
let sql = MySql::add_column(true, "Foreign", &types::foreign("posts"));
assert_eq!(
String::from("ADD COLUMN \"Foreign\" INTEGER REFERENCES posts NOT NULL"),
sql
);
}

#[test]
fn uuid() {
let sql = MySql::add_column(true, "MyUUID", &types::UUID);
assert_eq!(
String::from("ADD COLUMN Foreign CHAR(36)"),
sql
);
}
22 changes: 22 additions & 0 deletions src/tests/mysql/create_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Some unit tests that create create tables
#![allow(unused_imports)]

use backend::{SqlGenerator, MySql};
use {types, Migration, Table};

#[test]
fn create_multiple_tables() {
let mut m = Migration::new();
m.create_table("artist", |t| {
t.add_column("name", types::text().nullable(true));
t.add_column("description", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
m.create_table("album", |t| {
t.add_column("name", types::text().nullable(true));
t.add_column("pic", types::text().nullable(true));
t.add_column("mbid", types::text().nullable(true));
});
assert_eq!(m.make::<MySql>(), String::from("CREATE TABLE artist (id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, name TEXT, description TEXT, pic TEXT, mbid TEXT);CREATE TABLE album (id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, name TEXT, pic TEXT, mbid TEXT);"));
}
4 changes: 4 additions & 0 deletions src/tests/mysql/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! A few simple tests for the sqlite3 string backend

mod create_table;
mod simple;
47 changes: 47 additions & 0 deletions src/tests/mysql/simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Other simple table/ column migrations

#![allow(unused_imports)]

use backend::{SqlGenerator, MySql};

#[test]
fn create_table() {
let sql = MySql::create_table("table_to_create");
assert_eq!(String::from("CREATE TABLE table_to_create"), sql);
}

#[test]
fn create_table_if_not_exists() {
let sql = MySql::create_table_if_not_exists("table_to_create");
assert_eq!(
String::from("CREATE TABLE table_to_create IF NOT EXISTS"),
sql
);
}

#[test]
fn drop_table() {
let sql = MySql::drop_table("table_to_drop");
assert_eq!(String::from("DROP TABLE table_to_drop"), sql);
}

#[test]
fn drop_table_if_exists() {
let sql = MySql::drop_table_if_exists("table_to_drop");
assert_eq!(String::from("DROP TABLE table_to_drop IF EXISTS"), sql);
}

#[test]
fn rename_table() {
let sql = MySql::rename_table("old_table", "new_table");
assert_eq!(
String::from("RENAME TABLE \"old_table\" TO \"new_table\""),
sql
);
}

#[test]
fn alter_table() {
let sql = MySql::alter_table("table_to_alter");
assert_eq!(String::from("ALTER TABLE \"table_to_alter\""), sql);
}