From 7bfe4a6259d5e1337ffdedb3e83d6ac35e59dc1f Mon Sep 17 00:00:00 2001 From: Lewin Probst Date: Fri, 19 Mar 2021 23:40:52 +0100 Subject: [PATCH 1/6] Added ReferentialAction enum to support ON UPDATE and ON DELETE statements, updated Foreign type. --- src/backend/mysql.rs | 28 ++++++++++++++++++-------- src/backend/pg.rs | 28 ++++++++++++++++++-------- src/backend/sqlite3.rs | 16 ++++++++++++--- src/types/builders.rs | 27 +++++++++++++++++++++---- src/types/impls.rs | 45 +++++++++++++++++++++++++++++++++++++++++- src/types/mod.rs | 2 +- 6 files changed, 121 insertions(+), 25 deletions(-) diff --git a/src/backend/mysql.rs b/src/backend/mysql.rs index 6db7474..12c32a9 100644 --- a/src/backend/mysql.rs +++ b/src/backend/mysql.rs @@ -6,7 +6,7 @@ use super::SqlGenerator; use crate::{ functions::AutogenFunction, - types::{BaseType, Type, WrappedDefault}, + types::{BaseType, ReferentialAction, Type, WrappedDefault}, }; /// A simple macro that will generate a schema prefix if it exists @@ -70,7 +70,7 @@ impl SqlGenerator for MySql { 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)), + Foreign(_, _, _, _, _) => format!("{}{} INTEGER, FOREIGN KEY ({}) {}", MySql::prefix(ex), name, 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!("Indices are handled via custom builder"), @@ -222,12 +222,24 @@ impl MySql { DateTime => format!("DATETIME"), Json => format!("JSON"), Binary => format!("BYTEA"), - Foreign(s, t, refs) => format!( - "INTEGER REFERENCES {}{}({})", - prefix!(s), - t, - refs.0.join(",") - ), + Foreign(s, t, refs, on_update, on_delete) => { + let d = match on_delete { + ReferentialAction::Unset => String::from(""), + _ => format!(" {}", on_delete.on_delete()), + }; + let u = match on_update { + ReferentialAction::Unset => String::from(""), + _ => format!(" {}", on_update.on_update()), + }; + format!( + "REFERENCES {}{}({}){}{}", + prefix!(s), + t, + refs.0.join(","), + u, + d + ) + } Custom(t) => format!("{}", t), Array(meh) => format!("{}[]", MySql::print_type(*meh, schema)), Index(_) => unreachable!(), diff --git a/src/backend/pg.rs b/src/backend/pg.rs index d309bda..ebb1bfa 100644 --- a/src/backend/pg.rs +++ b/src/backend/pg.rs @@ -6,7 +6,7 @@ use super::SqlGenerator; use crate::{ functions::AutogenFunction, - types::{BaseType, Type, WrappedDefault}, + types::{BaseType, ReferentialAction, Type, WrappedDefault}, }; /// A simple macro that will generate a schema prefix if it exists @@ -72,7 +72,7 @@ impl SqlGenerator for Pg { 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)), + Foreign(_, _, _, _, _) => format!("{}\"{}\" INTEGER, FOREIGN KEY ({}) {}", Pg::prefix(ex), name, 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"), @@ -219,12 +219,24 @@ impl Pg { DateTime => format!("TIMESTAMP"), Json => format!("JSON"), Binary => format!("BYTEA"), - Foreign(s, t, refs) => format!( - "INTEGER REFERENCES {}\"{}\"({})", - prefix!(s.or(schema.map(|s| s.into()))), - t, - refs.0.join(",") - ), + Foreign(s, t, refs, on_update, on_delete) => { + let d = match on_delete { + ReferentialAction::Unset => String::from(""), + _ => format!(" {}", on_delete.on_delete()), + }; + let u = match on_update { + ReferentialAction::Unset => String::from(""), + _ => format!(" {}", on_update.on_update()), + }; + format!( + "REFERENCES {}\"{}\"({}){}{}", + prefix!(s.or(schema.map(|s| s.into()))), + t, + refs.0.join(","), + u, + d + ) + } Custom(t) => format!("{}", t), Array(meh) => format!("{}[]", Pg::print_type(*meh, schema)), Index(_) => unimplemented!("Indices are handled via custom builder"), diff --git a/src/backend/sqlite3.rs b/src/backend/sqlite3.rs index 178dbc8..2e6b786 100644 --- a/src/backend/sqlite3.rs +++ b/src/backend/sqlite3.rs @@ -3,7 +3,7 @@ use super::SqlGenerator; use crate::{ functions::AutogenFunction, - types::{BaseType, Type, WrappedDefault}, + types::{BaseType, ReferentialAction, Type, WrappedDefault}, }; /// A simple macro that will generate a schema prefix if it exists @@ -68,7 +68,7 @@ impl SqlGenerator for Sqlite { Time => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), DateTime => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), Binary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Foreign(_, _, _) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Foreign(_, _, _, _, _) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))), Index(_) => unreachable!("Indices are handled via custom builder"), @@ -215,7 +215,17 @@ impl Sqlite { DateTime => format!("DATETIME"), Json => panic!("Json is not supported by Sqlite3"), Binary => format!("BINARY"), - Foreign(_, t, refs) => format!("INTEGER REFERENCES {}({})", t, refs.0.join(",")), + Foreign(_, t, refs, on_update, on_delete) => { + let d = match on_delete { + ReferentialAction::Unset => String::from(""), + _ => format!(" {}", on_delete.on_delete()), + }; + let u = match on_update { + ReferentialAction::Unset => String::from(""), + _ => format!(" {}", on_update.on_update()), + }; + format!("INTEGER REFERENCES {}({}){}{}", t, refs.0.join(","), u, d) + } Custom(t) => format!("{}", t), Array(meh) => format!("{}[]", Sqlite::print_type(*meh)), Index(_) => unimplemented!(), diff --git a/src/types/builders.rs b/src/types/builders.rs index 6a18192..37fbc89 100644 --- a/src/types/builders.rs +++ b/src/types/builders.rs @@ -1,7 +1,7 @@ //! Builder API's module use super::impls::Constraint; -use super::impls::{BaseType, WrapVec}; +use super::impls::{BaseType, ReferentialAction, WrapVec}; use crate::types::Type; /// A standard primary numeric key type @@ -82,18 +82,35 @@ pub fn binary<'inner>() -> Type { } /// Create a column that points to some foreign table -pub fn foreign(table: S, keys: I) -> Type +pub fn foreign( + table: S, + keys: I, + on_update: ReferentialAction, + on_delete: ReferentialAction, +) -> Type where S: Into, I: Into>, { - Type::new(BaseType::Foreign(None, table.into(), keys.into())) + Type::new(BaseType::Foreign( + None, + table.into(), + keys.into(), + on_update, + on_delete, + )) } /// Like `foreign(...)` but letting you provide an external schema /// /// This function is important when making cross-schema references -pub fn foreign_schema(schema: S, table: S, keys: I) -> Type +pub fn foreign_schema( + schema: S, + table: S, + keys: I, + on_update: ReferentialAction, + on_delete: ReferentialAction, +) -> Type where S: Into, I: Into>, @@ -102,6 +119,8 @@ where Some(schema.into()), table.into(), keys.into(), + on_update, + on_delete, )) } diff --git a/src/types/impls.rs b/src/types/impls.rs index 114a381..7eb80d0 100644 --- a/src/types/impls.rs +++ b/src/types/impls.rs @@ -55,7 +55,13 @@ pub enum BaseType { /// Binary, /// Foreign key to other table - Foreign(Option, String, WrapVec), + Foreign( + Option, + String, + WrapVec, + ReferentialAction, + ReferentialAction, + ), /// I have no idea what you are – but I *like* it Custom(&'static str), /// Any of the above, but **many** of them @@ -168,6 +174,43 @@ impl Type { } } +/// Describes the referential_action for ON DELETE and ON UPDATE statements. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ReferentialAction { + Unset, + NoAction, + Restrict, + SetNull, + SetDefault, + Cascade, +} + +impl ReferentialAction { + /// Returns the ON DELETE statement + pub fn on_delete(&self) -> String { + format!("ON DELETE {}", self) + } + + /// Returns the ON UPDATE statement + pub fn on_update(&self) -> String { + format!("ON UPDATE {}", self) + } +} + +impl std::fmt::Display for ReferentialAction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Unset => panic!("The Unset variant cannot be displayed!"), + Self::NoAction => "NO ACTION", + Self::Restrict => "RESTRICT", + Self::SetNull => "SET NULL", + Self::SetDefault => "SET DEFAULT", + Self::Cascade => "CASCADE", + }; + write!(f, "{}", s) + } +} + impl<'a> From<&'a str> for WrapVec { fn from(s: &'a str) -> Self { WrapVec(vec![s.into()]) diff --git a/src/types/mod.rs b/src/types/mod.rs index 0be7684..2d03a73 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -6,4 +6,4 @@ mod impls; pub use self::builders::*; pub use self::defaults::{null, WrappedDefault}; -pub use self::impls::{BaseType, Type, WrapVec}; +pub use self::impls::{BaseType, ReferentialAction, Type, WrapVec}; From ff3597202d4f46dab91c427e1c65c5684e490b10 Mon Sep 17 00:00:00 2001 From: Lewin Probst Date: Mon, 22 Mar 2021 18:17:39 +0100 Subject: [PATCH 2/6] Fixed NOT NULL constraint in foreign keys when adding a column. --- src/backend/mysql.rs | 112 ++++++++++++++++++++--------------- src/backend/pg.rs | 118 +++++++++++++++++++++---------------- src/backend/sqlite3.rs | 131 ++++++++++++++++++++++++----------------- 3 files changed, 209 insertions(+), 152 deletions(-) diff --git a/src/backend/mysql.rs b/src/backend/mysql.rs index 12c32a9..37cd349 100644 --- a/src/backend/mysql.rs +++ b/src/backend/mysql.rs @@ -48,62 +48,78 @@ impl SqlGenerator for MySql { fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String { let bt: BaseType = tt.get_inner(); + let btc = bt.clone(); use self::BaseType::*; let name = format!("`{}`", name); - + let nullable_definition = match tt.nullable { + true => "", + false => " NOT NULL", + }; + let unique_definition = match tt.unique { + true => " UNIQUE", + false => "", + }; + let primary_definition = match tt.primary { + true => " PRIMARY KEY", + false => "", + }; #[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, 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)), + let base_type_definition = match bt { + Text => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Varchar(_) => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Char(_) => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Primary => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Integer => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Serial => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Float => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Double => format!("{}{} {}", Self::prefix(ex), name, Self::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!("{}{} INTEGER, FOREIGN KEY ({}) {}", MySql::prefix(ex), name, 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)), + Json => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Boolean => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Date => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Time => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + DateTime => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Binary => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Foreign(_, _, _, _, _) => format!("{}{} INTEGER{}, FOREIGN KEY ({}) {}", Self::prefix(ex), name, nullable_definition, name, Self::print_type(bt, schema)), + Custom(_) => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Array(it) => format!("{}{} {}", Self::prefix(ex), name, Self::print_type(Array(Box::new(*it)), schema)), 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) => 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), + }; + let default_definition = match (&tt.default).as_ref() { + Some(ref m) => match m { + WrappedDefault::Function(ref fun) => match fun { + AutogenFunction::CurrentTimestamp => format!(" DEFAULT CURRENT_TIMESTAMP"), }, - _ => format!(""), - }, - match tt.nullable { - true => "", - false => " NOT NULL", - }, - match tt.unique { - true => " UNIQUE", - false => "", + 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 btc { + Foreign(_, _, _, _, _) => { + format!( + "{}{}{}{}", + base_type_definition, primary_definition, default_definition, unique_definition, + ) + } + _ => { + format!( + "{}{}{}{}{}", + base_type_definition, + primary_definition, + default_definition, + nullable_definition, + unique_definition, + ) + } + } } fn drop_column(name: &str) -> String { diff --git a/src/backend/pg.rs b/src/backend/pg.rs index ebb1bfa..0b2bd72 100644 --- a/src/backend/pg.rs +++ b/src/backend/pg.rs @@ -51,60 +51,76 @@ impl SqlGenerator for Pg { fn add_column(ex: bool, schema: Option<&str>, name: &str, tt: &Type) -> String { let bt: BaseType = tt.get_inner(); + let btc = bt.clone(); use self::BaseType::*; - - #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */ - format!( - "{}{}{}{}{}", - 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!("{}\"{}\" INTEGER, FOREIGN KEY ({}) {}", Pg::prefix(ex), name, 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"), - Constraint(_, _) => unreachable!("Constraints are handled via custom builder"), - }, - match tt.primary { - true => " PRIMARY KEY", - false => "", - }, - match (&tt.default).as_ref() { - 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::Custom(ref val) => format!(" DEFAULT '{}'", val), - _ => format!(" DEFAULT {}", m) + let primary_definition = match tt.primary { + true => " PRIMARY KEY", + false => "", + }; + let default_definition = match (&tt.default).as_ref() { + Some(ref m) => match m { + WrappedDefault::Function(ref fun) => match fun { + AutogenFunction::CurrentTimestamp => format!(" DEFAULT CURRENT_TIMESTAMP"), }, - _ => format!(""), + 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::Custom(ref val) => format!(" DEFAULT '{}'", val), + _ => format!(" DEFAULT {}", m), }, - match tt.nullable { - true => "", - false => " NOT NULL", - }, - match tt.unique { - true => " UNIQUE", - false => "", - }, - ) + _ => format!(""), + }; + let nullable_definition = match tt.nullable { + true => "", + false => " NOT NULL", + }; + let unique_definition = match tt.unique { + true => " UNIQUE", + false => "", + }; + #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */ + let base_type_definition = match bt { + Text => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Varchar(_) => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Char(_) => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Primary => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Integer => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Serial => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Float => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Double => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + UUID => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Json => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Boolean => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Date => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Time => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + DateTime => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Binary => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Foreign(_, _, _, _, _) => format!("{}\"{}\" INTEGER{}, FOREIGN KEY ({}) {}", Self::prefix(ex), name, nullable_definition, name, Self::print_type(bt, schema)), + Custom(_) => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(bt, schema)), + Array(it) => format!("{}\"{}\" {}", Self::prefix(ex), name, Self::print_type(Array(Box::new(*it)), schema)), + Index(_) => unreachable!("Indices are handled via custom builder"), + Constraint(_, _) => unreachable!("Constraints are handled via custom builder"), + }; + + match btc { + Foreign(_, _, _, _, _) => { + format!( + "{}{}{}{}", + base_type_definition, primary_definition, default_definition, unique_definition, + ) + } + _ => { + format!( + "{}{}{}{}{}", + base_type_definition, + primary_definition, + default_definition, + nullable_definition, + unique_definition, + ) + } + } } fn drop_column(name: &str) -> String { diff --git a/src/backend/sqlite3.rs b/src/backend/sqlite3.rs index 2e6b786..0c67956 100644 --- a/src/backend/sqlite3.rs +++ b/src/backend/sqlite3.rs @@ -46,62 +46,87 @@ impl SqlGenerator for Sqlite { fn add_column(ex: bool, _: Option<&str>, name: &str, tt: &Type) -> String { let bt: BaseType = tt.get_inner(); + let btc = bt.clone(); use self::BaseType::*; - - #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */ - format!( - // SQL base - default - nullable - unique - "{}{}{}{}{}", - match bt { - Text => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Varchar(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Char(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Primary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Integer => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Serial => panic!("SQLite has no serials for non-primary key columns"), - Float => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Double => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - UUID => panic!("`UUID` not supported by Sqlite3. Use `Text` instead!"), - Json => panic!("`Json` not supported by Sqlite3. Use `Text` instead!"), - Boolean => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Date => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Time => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - DateTime => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Binary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Foreign(_, _, _, _, _) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))), - 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) => 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) + let primary_definition = match tt.primary { + true => " PRIMARY KEY", + false => "", + }; + let default_definition = match (&tt.default).as_ref() { + Some(ref m) => match m { + WrappedDefault::Function(ref fun) => match fun { + AutogenFunction::CurrentTimestamp => format!(" DEFAULT CURRENT_TIMESTAMP"), }, - _ => format!(""), + 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), }, - match tt.nullable { - true => "", - false => " NOT NULL", - }, - match tt.unique { - true => " UNIQUE", - false => "", + _ => format!(""), + }; + let nullable_definition = match tt.nullable { + true => "", + false => " NOT NULL", + }; + let unique_definition = match tt.unique { + true => " UNIQUE", + false => "", + }; + #[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */ + let base_type_definition = match bt { + Text => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Varchar(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Char(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Primary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Integer => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Serial => panic!("SQLite has no serials for non-primary key columns"), + Float => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Double => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + UUID => panic!("`UUID` not supported by Sqlite3. Use `Text` instead!"), + Json => panic!("`Json` not supported by Sqlite3. Use `Text` instead!"), + Boolean => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Date => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Time => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + DateTime => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Binary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Foreign(_, _, _, _, _) => format!("{}\"{}\" INTEGER{}, REFERENCES {}", Sqlite::prefix(ex), name, nullable_definition, Sqlite::print_type(bt)), + Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), + Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))), + Index(_) => unreachable!("Indices are handled via custom builder"), + Constraint(_, _) => unreachable!("Constraints are handled via custom builder"), + }; + + match btc { + Foreign(_, _, _, _, _) => { + format!( + "{}{}{}{}", + base_type_definition, primary_definition, default_definition, unique_definition, + ) } - ) + _ => { + format!( + "{}{}{}{}{}", + base_type_definition, + primary_definition, + default_definition, + nullable_definition, + unique_definition, + ) + } + } + //#[cfg_attr(rustfmt, rustfmt_skip)] /* This shouldn't be formatted. It's too long */ + //format!( + // // SQL base - default - nullable - unique + // "{}{}{}{}{}", + // base_type_definition, + // primary_definition, + // default_definition, + // nullable_definition, + // unique_definition + //) } /// Create a multi-column index @@ -224,7 +249,7 @@ impl Sqlite { ReferentialAction::Unset => String::from(""), _ => format!(" {}", on_update.on_update()), }; - format!("INTEGER REFERENCES {}({}){}{}", t, refs.0.join(","), u, d) + format!("{}({}){}{}", t, refs.0.join(","), u, d) } Custom(t) => format!("{}", t), Array(meh) => format!("{}[]", Sqlite::print_type(*meh)), From 9d27dc0841a71abce948a0a74039d0aef68dd38f Mon Sep 17 00:00:00 2001 From: Lewin Probst Date: Thu, 25 Mar 2021 18:49:28 +0100 Subject: [PATCH 3/6] Bugfix, Foreign key syntax in sqlite backend. --- src/backend/sqlite3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/sqlite3.rs b/src/backend/sqlite3.rs index 0c67956..7b2c988 100644 --- a/src/backend/sqlite3.rs +++ b/src/backend/sqlite3.rs @@ -92,7 +92,7 @@ impl SqlGenerator for Sqlite { Time => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), DateTime => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), Binary => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), - Foreign(_, _, _, _, _) => format!("{}\"{}\" INTEGER{}, REFERENCES {}", Sqlite::prefix(ex), name, nullable_definition, Sqlite::print_type(bt)), + Foreign(_, _, _, _, _) => format!("{}\"{}\" INTEGER{} REFERENCES {}", Sqlite::prefix(ex), name, nullable_definition, Sqlite::print_type(bt)), Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))), Index(_) => unreachable!("Indices are handled via custom builder"), From 741ecf75f2623af9a5088950552c835adc6806c2 Mon Sep 17 00:00:00 2001 From: Lewin Probst Date: Sat, 10 Apr 2021 13:41:53 +0200 Subject: [PATCH 4/6] Indention fix. --- src/backend/sqlite3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/sqlite3.rs b/src/backend/sqlite3.rs index 7b2c988..6b175be 100644 --- a/src/backend/sqlite3.rs +++ b/src/backend/sqlite3.rs @@ -96,7 +96,7 @@ impl SqlGenerator for Sqlite { Custom(_) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(bt)), Array(it) => format!("{}\"{}\" {}", Sqlite::prefix(ex), name, Sqlite::print_type(Array(Box::new(*it)))), Index(_) => unreachable!("Indices are handled via custom builder"), - Constraint(_, _) => unreachable!("Constraints are handled via custom builder"), + Constraint(_, _) => unreachable!("Constraints are handled via custom builder"), }; match btc { From 5986ee07e3fb17f72993835ce73ec150c83d2b76 Mon Sep 17 00:00:00 2001 From: Lewin Probst Date: Sat, 10 Apr 2021 13:42:34 +0200 Subject: [PATCH 5/6] Adjusted sqlite_strings example to the new API. --- examples/sqlite_strings.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/sqlite_strings.rs b/examples/sqlite_strings.rs index cac5e50..08f04bf 100644 --- a/examples/sqlite_strings.rs +++ b/examples/sqlite_strings.rs @@ -8,7 +8,15 @@ fn main() { t.add_column("name", types::varchar(255).default("Anonymous")); // Default name is "Anonymous" t.add_column("description", types::text().nullable(true)); // Can be null t.add_column("age", types::integer()); - t.add_column("posts", types::foreign("posts", "id")); + t.add_column( + "posts", + types::foreign( + "posts", + "id", + types::ReferentialAction::Unset, + types::ReferentialAction::Unset, + ), + ); t.add_column("created_at", types::date()); t.add_column("owns_plushy_sharks", types::boolean()); }); From 8f1cec9ec4bebed763c0b32f94fa177e2a94562a Mon Sep 17 00:00:00 2001 From: Lewin Probst Date: Sun, 11 Apr 2021 10:14:48 +0200 Subject: [PATCH 6/6] Fixed warning about no_run attribute. --- src/types/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/impls.rs b/src/types/impls.rs index 7eb80d0..d5cc614 100644 --- a/src/types/impls.rs +++ b/src/types/impls.rs @@ -98,7 +98,7 @@ pub enum BaseType { /// /// ## Examples /// -/// ```rust,norun +/// ```rust,no_run /// extern crate barrel; /// use barrel::types::*; ///