Unverified Commit b89c10da authored by Katharina Fey's avatar Katharina Fey 馃彺 Committed by GitHub
Browse files

Merge branch 'master' into master

parents 504e8b7d 36b99053
......@@ -5,7 +5,7 @@ services: docker
sudo: required
cache: cargo
rust:
# - stable # Only build on nightly rust for now
- stable # Only build on nightly rust for now
- nightly
# This is required for coveralls
......
......@@ -34,6 +34,7 @@ default = []
diesel = ["tempfile", "diesel_rs"]
sqlite3 = []
mysql = []
mssql = []
pg = []
# Enables unstable (in-development) features,
......
github: spacekookie
![](assets/logo.svg)
[![](https://travis-ci.org/rust-db/barrel.svg?branch=master)](https://travis-ci.org/rust-db/barrel)
[![](https://ci.appveyor.com/api/projects/status/7e00r2e1xatxk3bj?svg=true)](https://ci.appveyor.com/project/spacekookie/barrel)
[![](https://coveralls.io/repos/github/rust-db/barrel/badge.svg?branch=master&service=github)](https://coveralls.io/github/rust-db/barrel?branch=master)
[![](https://docs.rs/barrel/badge.svg)](https://docs.rs/barrel/)
[![](https://img.shields.io/crates/v/barrel.svg)](https://crates.io/crates/barrel)
[![](https://img.shields.io/crates/d/barrel.svg)](https://crates.io/crates/barrel)
......@@ -18,6 +16,7 @@ database-specific builders.
This way you can focus on your Rust code, without having to worry
about SQL.
## Example
The following example will help you get started
......@@ -46,6 +45,7 @@ with `diesel`. A guide with some more information on how to get
started can be found
[here](https://github.com/spacekookie/barrel/blob/master/guides/diesel-setup.md)
### Migration guide
If you've been using `barrel` to write migrations for `diesel` before
......@@ -59,6 +59,7 @@ builder functions. The same type would now be `types::varchar(255)`
You can also directly created your own `Type` builders this way.
Check the docs for details!
## License
`barrel` is free software: you can redistribute it and/or modify it
......@@ -69,6 +70,7 @@ WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MIT
Public License for more details.
## Conduct
In the interest of fostering an open and welcoming environment, the
......
edition = "2018"
......@@ -19,6 +19,11 @@ mod sqlite3;
#[cfg(feature = "sqlite3")]
pub use self::sqlite3::Sqlite;
#[cfg(feature = "mssql")]
mod mssql;
#[cfg(feature = "mssql")]
pub use self::mssql::MsSql;
#[allow(unused_imports)]
use crate::{types::Type, Migration};
......@@ -31,6 +36,8 @@ pub enum SqlVariant {
Pg,
#[cfg(feature = "mysql")]
Mysql,
#[cfg(feature = "mssql")]
Mssql,
#[doc(hidden)]
__Empty,
}
......@@ -47,6 +54,9 @@ impl SqlVariant {
#[cfg(feature = "mysql")]
SqlVariant::Mysql => _migr.make::<MySql>(),
#[cfg(feature = "mssql")]
SqlVariant::Mssql => _migr.make::<MsSql>(),
_ => panic!("You need to select an Sql variant!"),
}
}
......@@ -84,6 +94,34 @@ pub trait SqlGenerator {
/// Create a multi-column index
fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String;
/// Create a constraint
fn create_constraint(name: &str, _type: &Type) -> String;
/// Create a multi-column index
fn create_partial_index(
table: &str,
schema: Option<&str>,
name: &str,
_type: &Type,
conditions: &str,
) -> String {
format!(
"{} WHERE {}",
Self::create_index(table, schema, name, _type),
conditions
)
}
/// Drop a multi-column index
fn drop_index(name: &str) -> String;
/// Add a foreign key
fn add_foreign_key(
columns: &[String],
table: &str,
relation_columns: &[String],
schema: Option<&str>,
) -> String;
fn add_primary_key(columns: &[String]) -> String;
}
//! Microsoft SQL Server implementation of a generator
//!
//! This module generates strings that are specific to SQL Server
//! databases. They should be thoroughly tested via unit testing
use super::SqlGenerator;
use crate::{
functions::AutogenFunction,
types::{BaseType, Type, WrappedDefault},
};
/// A simple macro that will generate a quoted schema prefix if it exists
macro_rules! quoted_prefix {
($schema:expr) => {
$schema
.map(|s| format!("[{}].", s))
.unwrap_or_else(|| String::new())
};
}
/// A simple macro that will generate a schema prefix if it exists
macro_rules! prefix {
($schema:expr) => {
$schema
.map(|s| format!("{}.", s))
.unwrap_or_else(|| String::new())
};
}
/// SQL Server generator backend
pub struct MsSql;
impl MsSql {
fn default(default: &WrappedDefault<'static>) -> String {
match default {
WrappedDefault::Function(ref fun) => match fun {
AutogenFunction::CurrentTimestamp => format!(" DEFAULT CURRENT_TIMESTAMP"),
},
WrappedDefault::Null => format!(" DEFAULT NULL"),
WrappedDefault::AnyText(ref val) => format!(" DEFAULT '{}'", val),
WrappedDefault::UUID(ref val) => format!(" DEFAULT '{}'", val),
WrappedDefault::Date(ref val) => format!(" DEFAULT '{:?}'", val),
WrappedDefault::Boolean(val) => format!(" DEFAULT {}", if *val { 1 } else { 0 }),
WrappedDefault::Custom(ref val) => format!(" DEFAULT '{}'", val),
_ => format!(" DEFAULT {}", default),
}
}
}
impl SqlGenerator for MsSql {
fn create_table(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE {}[{}]", quoted_prefix!(schema), name)
}
fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
let table = format!("{}[{}]", quoted_prefix!(schema), name);
match schema {
None => {
format!(
"IF NOT EXISTS (SELECT * FROM sys.tables WHERE name='{table}') CREATE TABLE {quoted}",
table = name,
quoted = table,
)
}
Some(schema) => {
format!(
"IF NOT EXISTS (SELECT * FROM sys.tables WHERE name='{table}' AND SCHEMA_NAME(schema_id) = '{schema}') CREATE TABLE {quoted}",
table = name,
quoted = table,
schema = schema,
)
}
}
}
fn drop_table(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE {}[{}]", quoted_prefix!(schema), name)
}
fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE IF EXISTS {}[{}]", quoted_prefix!(schema), name)
}
fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
let prefix = prefix!(schema);
format!(r#"EXEC sp_rename '{}{}', '{}'"#, prefix, old, new)
}
fn alter_table(name: &str, schema: Option<&str>) -> String {
format!("ALTER TABLE {}[{}]", quoted_prefix!(schema), name)
}
fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String {
let bt: BaseType = tt.get_inner();
let name_and_type = match bt {
BaseType::Array(_) => panic!("Arrays are not supported with SQL Server"),
BaseType::Index(_) => unreachable!("Indices are handled via custom builder"),
_ => format!(
"{}[{}] {}",
MsSql::prefix(ex),
name,
MsSql::print_type(bt, schema)
),
};
let primary_key_indicator = match tt.primary {
true => " PRIMARY KEY",
false => "",
};
let default_indicator = match (&tt.default).as_ref() {
Some(ref m) => Self::default(m),
_ => format!(""),
};
let nullable_indicator = match tt.nullable {
true => "",
false => " NOT NULL",
};
let unique_indicator = match tt.unique {
true => " UNIQUE",
false => "",
};
format!(
"{}{}{}{}{}",
// `ADD name VARCHAR(max)` or `name VARCHAR(max)`
name_and_type,
// `PRIMARY KEY` or nothing
primary_key_indicator,
// `DEFAULT 'foo'` or nothing
default_indicator,
// `NOT NULL` or nothing
nullable_indicator,
// `UNIQUE or nothing`
unique_indicator
)
}
fn drop_column(name: &str) -> String {
format!("DROP COLUMN [{}]", name)
}
fn rename_column(old: &str, new: &str) -> String {
format!(r#"EXEC sp_rename '{}', '{}'"#, old, new)
}
fn create_index(table: &str, schema: Option<&str>, name: &str, _type: &Type) -> String {
// FIXME: Implement PG specific index builder here
format!(
"CREATE {} INDEX [{}] ON {}[{}] ({})",
match _type.unique {
true => "UNIQUE",
false => "",
},
name,
prefix!(schema),
table,
match _type.inner {
BaseType::Index(ref cols) => cols
.iter()
.map(|col| format!("[{}]", col))
.collect::<Vec<_>>()
.join(", "),
_ => unreachable!(),
}
)
}
fn create_constraint(name: &str, _type: &Type) -> String {
let (r#type, columns) = match _type.inner {
BaseType::Constraint(ref r#type, ref columns) => (
r#type.clone(),
columns
.iter()
.map(|col| format!("[{}]", col))
.collect::<Vec<_>>(),
),
_ => unreachable!(),
};
format!("CONSTRAINT [{}] {} ({})", name, r#type, columns.join(", "),)
}
fn drop_index(name: &str) -> String {
format!("DROP INDEX [{}]", name)
}
fn add_foreign_key(
columns: &[String],
table: &str,
relation_columns: &[String],
schema: Option<&str>,
) -> String {
let columns: Vec<_> = columns.into_iter().map(|c| format!("[{}]", c)).collect();
let relation_columns: Vec<_> = relation_columns
.into_iter()
.map(|c| format!("[{}]", c))
.collect();
format!(
"FOREIGN KEY({}) REFERENCES {}[{}]({})",
columns.join(","),
prefix!(schema),
table,
relation_columns.join(","),
)
}
fn add_primary_key(columns: &[String]) -> String {
let columns: Vec<_> = columns.into_iter().map(|c| format!("[{}]", c)).collect();
format!("PRIMARY KEY ({})", columns.join(","))
}
}
impl MsSql {
fn prefix(ex: bool) -> String {
match ex {
true => format!("ADD "),
false => format!(""),
}
}
fn print_type(t: BaseType, schema: Option<&str>) -> String {
use self::BaseType::*;
match t {
Text => format!("TEXT"),
Varchar(l) => match l {
0 => format!("VARCHAR(MAX)"), // For "0" remove the limit
_ => format!("VARCHAR({})", l),
},
Char(l) => format!("CHAR({})", l),
/* "NOT NULL" is added here because normally primary keys are implicitly not-null */
Primary => format!("INT IDENTITY(1,1) PRIMARY KEY NOT NULL"),
Integer => format!("INT"),
Serial => format!("INT IDENTITY(1,1)"),
Float => format!("FLOAT(24)"),
Double => format!("FLOAT(53)"),
UUID => format!("UNIQUEIDENTIFIER"),
Boolean => format!("BIT"),
Date => format!("DATE"),
Time => format!("TIME"),
DateTime => format!("DATETIME2"),
Json => format!("JSON"),
Binary => format!("VARBINARY(MAX)"),
Foreign(s, t, refs) => format!(
"INT REFERENCES {}[{}]({})",
quoted_prefix!(s.or(schema.map(|s| s.into()))),
t,
refs.0
.iter()
.map(|r| format!("[{}]", r))
.collect::<Vec<_>>()
.join(",")
),
Custom(t) => format!("{}", t),
Array(meh) => format!("{}[]", MsSql::print_type(*meh, schema)),
Index(_) => unreachable!("Indices are handled via custom builder"),
Constraint(_, _) => unreachable!("Constraints are handled via custom builder"),
}
}
}
......@@ -4,7 +4,10 @@
//! databases. They should be thoroughly tested via unit testing
use super::SqlGenerator;
use crate::types::{BaseType, Type};
use crate::{
functions::AutogenFunction,
types::{BaseType, Type, WrappedDefault},
};
/// A simple macro that will generate a schema prefix if it exists
macro_rules! prefix {
......@@ -23,7 +26,7 @@ impl SqlGenerator for MySql {
}
fn create_table_if_not_exists(name: &str, schema: Option<&str>) -> String {
format!("CREATE TABLE {}`{}` IF NOT EXISTS", prefix!(schema), name)
format!("CREATE TABLE IF NOT EXISTS {}`{}`", prefix!(schema), name)
}
fn drop_table(name: &str, schema: Option<&str>) -> String {
......@@ -31,7 +34,7 @@ impl SqlGenerator for MySql {
}
fn drop_table_if_exists(name: &str, schema: Option<&str>) -> String {
format!("DROP TABLE {}`{}` IF EXISTS", prefix!(schema), name)
format!("DROP TABLE IF EXISTS {}`{}`", prefix!(schema), name)
}
fn rename_table(old: &str, new: &str, schema: Option<&str>) -> String {
......@@ -54,26 +57,42 @@ impl SqlGenerator for MySql {
match bt {
Text => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Varchar(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Char(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Primary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Integer => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Serial => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Float => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Double => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
UUID => unimplemented!(),
Json => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Boolean => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Date => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Time => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
DateTime => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Binary => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Foreign(_, _, _) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Custom(_) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(bt, schema)),
Array(it) => format!("{}{} {}", MySql::prefix(ex), name, MySql::print_type(Array(Box::new(*it)), schema)),
Index(_) => unreachable!(),
Index(_) => unreachable!("Indices are handled via custom builder"),
Constraint(_, _) => unreachable!("Constraints are handled via custom builder"),
},
match tt.primary {
true => " PRIMARY KEY",
false => "",
},
match (&tt.default).as_ref() {
Some(ref m) => format!(" DEFAULT '{}'", m),
Some(ref m) => match m {
WrappedDefault::Function(ref fun) => match fun {
AutogenFunction::CurrentTimestamp => format!(" DEFAULT CURRENT_TIMESTAMP")
},
WrappedDefault::Null => format!(" DEFAULT NULL"),
WrappedDefault::AnyText(ref val) => format!(" DEFAULT '{}'", val),
WrappedDefault::UUID(ref val) => format!(" DEFAULT '{}'", val),
WrappedDefault::Date(ref val) => format!(" DEFAULT '{:?}'", val),
WrappedDefault::Boolean(val) => format!(" DEFAULT {}", if *val { 1 } else { 0 }),
WrappedDefault::Custom(ref val) => format!(" DEFAULT '{}'", val),
_ => format!(" DEFAULT {}", m),
},
_ => format!(""),
},
match tt.nullable {
......@@ -117,9 +136,60 @@ impl SqlGenerator for MySql {
)
}
fn create_constraint(name: &str, _type: &Type) -> String {
let (r#type, columns) = match _type.inner {
BaseType::Constraint(ref r#type, ref columns) => (
r#type.clone(),
columns
.iter()
.map(|col| format!("`{}`", col))
.collect::<Vec<_>>(),
),
_ => unreachable!(),
};
format!("CONSTRAINT `{}` {} ({})", name, r#type, columns.join(", "),)
}
fn create_partial_index(
_table: &str,
_schema: Option<&str>,
_name: &str,
_type: &Type,
_conditions: &str,
) -> String {
panic!("Partial indices are not supported in MySQL")
}
fn drop_index(name: &str) -> String {
format!("DROP INDEX `{}`", name)
}
fn add_foreign_key(
columns: &[String],
table: &str,
relation_columns: &[String],
schema: Option<&str>,
) -> String {
let columns: Vec<_> = columns.into_iter().map(|c| format!("`{}`", c)).collect();
let relation_columns: Vec<_> = relation_columns
.into_iter()
.map(|c| format!("`{}`", c))
.collect();
format!(
"FOREIGN KEY ({}) REFERENCES {}`{}`({})",
columns.join(","),
prefix!(schema),
table,
relation_columns.join(","),
)
}
fn add_primary_key(columns: &[String]) -> String {
let columns: Vec<_> = columns.into_iter().map(|c| format!("`{}`", c)).collect();
format!("PRIMARY KEY ({})", columns.join(","))
}
}
impl MySql {
......@@ -138,14 +208,18 @@ impl MySql {
0 => format!("VARCHAR"), // For "0" remove the limit
_ => format!("VARCHAR({})", l),
},
Char(l) => format!("CHAR({})", 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"),
Serial => format!("INTEGER AUTO_INCREMENT"),
Float => format!("FLOAT"),
Double => format!("DOUBLE"),
UUID => format!("CHAR(36)"),
Boolean => format!("BOOLEAN"),
Date => format!("DATE"),
Time => format!("TIME"),
DateTime => format!("DATETIME"),
Json => format!("JSON"),
Binary => format!("BYTEA"),
Foreign(s, t, refs) => format!(
......@@ -157,6 +231,7 @@ impl MySql {
Custom(t) => format!("{}", t),
Array(meh) => format!("{}[]", MySql::print_type(*meh, schema)),
Index(_) => unreachable!(),
Constraint(_, _) => unreachable!(),
}
}
}
......@@ -4,7 +4,10 @@
//! databases. They should be thoroughly tested via unit testing
use super::SqlGenerator;
use crate::types::{BaseType, Type};
use crate::{
functions::AutogenFunction,
types::{BaseType, Type, WrappedDefault},
};
/// A simple macro that will generate a schema prefix if it exists
macro_rules! prefix {
......@@ -56,26 +59,41 @@ impl SqlGenerator for Pg {
match bt {
Text => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Varchar(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Char(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Primary => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Integer => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Serial => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Float => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Double => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
UUID => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Json => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Boolean => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Date => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Time => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
DateTime => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Binary => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Foreign(_, _, _) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Custom(_) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(bt, schema)),
Array(it) => format!("{}\"{}\" {}", Pg::prefix(ex), name, Pg::print_type(Array(Box::new(*it)), schema)),
Index(_) => unreachable!(), // Indices are handled via custom builder
Index(_) => unreachable!("Indices are handled via custom builder"),
Constraint(_, _) => unreachable!("Constraints are handled via custom builder"),
},
match tt.primary {
true => " PRIMARY KEY",
false => "",
},
match (&tt.default).as_ref() {
Some(ref m) => format!(" DEFAULT '{}'", m),
Some(ref m) => match m {
WrappedDefault::Function(ref fun) => match fun {
AutogenFunction::CurrentTimestamp => format!(" DEFAULT CURRENT_TIMESTAMP")
}
WrappedDefault::Null => format!(" DEFAULT NULL"),
WrappedDefault::AnyText(ref val) => format!(" DEFAULT '{}'", val),
WrappedDefault::UUID(ref val) => format!(" DEFAULT '{}'", val),
WrappedDefault::Date(ref val)