mod column;
mod data_type;
mod definition;
mod foreign_key;
mod index;
mod sequence;
mod table;
mod table_definition;
mod table_name;
pub use column::{Column, ColumnOptions};
pub use data_type::DataType;
pub use definition::Definition;
pub use foreign_key::ForeignKey;
pub use index::{Index, IndexOptions};
pub use table::{PrimaryKey, Table, TableOptions, Timestamps};
pub use table_definition::TableDefinition;
pub use table_name::TableName;
#[derive(Copy, Clone, Debug)]
pub enum Dialect {
Sqlite,
}
pub trait SqlUp {
fn sqlite_up(&self) -> Option<String> {
None
}
fn sqlite(&self) -> Option<String> {
self.sql_up(Dialect::Sqlite)
}
fn sql_up(&self, dialect: Dialect) -> Option<String> {
match dialect {
Dialect::Sqlite => self.sqlite_up(),
}
}
}
pub trait SqlDown {
fn sqlite_down(&self) -> Option<String> {
None
}
fn sql_down(&self, dialect: Dialect) -> Option<String> {
match dialect {
Dialect::Sqlite => self.sqlite_down(),
}
}
}
#[cfg(test)]
macro_rules! create_tests {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input, expected) = $value;
assert_eq!(input.sql_up(Dialect::Sqlite).unwrap().as_str(), expected.trim());
}
)*
}
}
#[cfg(test)]
mod tests {
use super::*;
create_tests!(
test_table: (
Definition::table("users", vec![
TableDefinition::Column(Column::new("name")),
]), r#"
CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)
"#),
test_default_values_for_column: (Definition::column("users", DataType::String.column("name")), r#"
ALTER TABLE users ADD COLUMN name STRING
"#),
test_default_not_null_column: (Definition::column("users", Column::new("name").not_null().to_owned()), r#"
ALTER TABLE users ADD COLUMN name STRING NOT NULL
"#),
test_index: (Definition::index("users", Index::new(vec!["name".to_string()])), r#"
CREATE INDEX name ON users (name)
"#),
test_foreign_key: (Definition::foreign_key("users", ForeignKey::new("other")), r#"
ALTER TABLE users ADD FOREIGN KEY (other_id) REFERENCES other (id)
"#),
);
#[test]
fn integration_test() {
let definition = vec![
Definition::table_no_timestamps(
"changeloglock",
vec![
TableDefinition::column(
DataType::Bool
.column("locked")
.not_null()
.default(serde_value::Value::Bool(false))
.to_owned(),
),
TableDefinition::column(
DataType::Text.column("locked_by").not_null().to_owned(),
),
TableDefinition::column(
DataType::Datetime
.column("locked_at")
.not_null()
.default_raw("CURRENT_TIMESTAMP".to_string())
.to_owned(),
),
],
),
Definition::statement(
"INSERT INTO changeloglock (id, locked, locked_by) VALUES (1, false, '')",
),
Definition::table_no_timestamps(
"migrations",
vec![
TableDefinition::column(
DataType::Text.column("identifier").not_null().to_owned(),
),
TableDefinition::column(
DataType::Datetime
.column("applied_at")
.not_null()
.default_raw("CURRENT_TIMESTAMP".to_string())
.to_owned(),
),
],
),
];
assert_eq!(definition.iter().sql_up(Dialect::Sqlite).unwrap().as_str(), vec![
"CREATE TABLE IF NOT EXISTS changeloglock (id INTEGER PRIMARY KEY AUTOINCREMENT, locked BOOLEAN NOT NULL DEFAULT FALSE, locked_by TEXT NOT NULL, locked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);",
"INSERT INTO changeloglock (id, locked, locked_by) VALUES (1, false, '');",
"CREATE TABLE IF NOT EXISTS migrations (id INTEGER PRIMARY KEY AUTOINCREMENT, identifier TEXT NOT NULL, applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP)"
].join(" ").trim());
let connection = rusqlite::Connection::open_in_memory().unwrap();
connection.execute_batch(definition.iter().sqlite().unwrap().as_str()).unwrap();
let mut stmt = connection.prepare("SELECT name FROM sqlite_master WHERE type='table'").unwrap();
let tables = stmt.query_map([], |row| row.get(0)).unwrap().collect::<Result<Vec<String>, _>>().unwrap();
assert_eq!(tables, vec!["changeloglock", "sqlite_sequence", "migrations"]);
let mut stmt = connection.prepare("SELECT * FROM changeloglock").unwrap();
let rows = stmt.query_map([], |row| {
Ok((row.get(0)?, row.get(1)?, row.get(2)?))
}).unwrap().collect::<Result<Vec<(i64, bool, String)>, _>>().unwrap();
assert_eq!(rows, vec![(1, false, "".to_string())]);
}
}