mirror of
https://github.com/diesel-rs/diesel.git
synced 2024-10-04 01:28:13 +03:00
Merge branch 'master' into jk_cli-list-migrations
This commit is contained in:
commit
1aed718dcf
@ -43,7 +43,7 @@ matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
include:
|
||||
- rust: nightly-2017-03-20
|
||||
- rust: nightly-2017-04-19
|
||||
env: CLIPPY=YESPLEASE
|
||||
script:
|
||||
- (cd diesel && cargo rustc --no-default-features --features "lint unstable chrono serde_json uuid sqlite postgres mysql" -- -Zno-trans)
|
||||
|
@ -6,10 +6,14 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
|
||||
|
||||
## Unreleased
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
* Added the `migration list` command to Diesel CLI for listing all available migrations and marking those that have been applied.
|
||||
|
||||
* Added support for adding two nullable columns.
|
||||
|
||||
|
||||
## [0.12.0] - 2016-03-16
|
||||
|
||||
### Added
|
||||
|
@ -14,12 +14,12 @@ categories = ["database"]
|
||||
[dependencies]
|
||||
byteorder = "1.0"
|
||||
chrono = { version = "0.3", optional = true }
|
||||
clippy = { optional = true, version = "=0.0.121" }
|
||||
libsqlite3-sys = { version = ">=0.7.1, <0.8.0", optional = true }
|
||||
clippy = { optional = true, version = "=0.0.125" }
|
||||
libsqlite3-sys = { version = ">=0.8.0, <0.9.0", optional = true, features = ["min_sqlite_version_3_7_16"] }
|
||||
mysqlclient-sys = { version = ">=0.1.0, <0.3.0", optional = true }
|
||||
pq-sys = { version = ">=0.3.0, <0.5.0", optional = true }
|
||||
quickcheck = { version = "0.3.1", optional = true }
|
||||
serde_json = { version = ">=0.8.0, <0.10.0", optional = true }
|
||||
serde_json = { version = ">=0.8.0, <2.0", optional = true }
|
||||
time = { version = "0.1", optional = true }
|
||||
url = { version = "1.4.0", optional = true }
|
||||
uuid = { version = ">=0.2.0, <0.5.0", optional = true, features = ["use_std"] }
|
||||
@ -27,7 +27,7 @@ uuid = { version = ">=0.2.0, <0.5.0", optional = true, features = ["use_std"] }
|
||||
[dev-dependencies]
|
||||
cfg-if = "0.1.0"
|
||||
diesel_codegen = "0.12.0"
|
||||
dotenv = "0.8.0"
|
||||
dotenv = ">=0.8, <0.10"
|
||||
quickcheck = "0.3.1"
|
||||
tempdir = "^0.3.4"
|
||||
|
||||
|
@ -43,6 +43,10 @@
|
||||
//!
|
||||
//! [Identifiable]: trait.Identifiable.html
|
||||
//!
|
||||
//! If the name of your foreign key doesn't follow the convention `tablename_id`, you can specify a
|
||||
//! custom one to `#[has_many]` and `#[belongs_to]` by adding a `foreign_key` argument to the
|
||||
//! attribute like so `#[has_many, foreign_key="mykey"]`.
|
||||
//!
|
||||
//! `#[has_many]` actually has no behavior on its own. It only enables joining between the two
|
||||
//! tables. If you are only writing `children.inner_join(parents)` or
|
||||
//! `Child::belonging_to(&parents)`, you only need to define the `#[belongs_to]` side.
|
||||
|
@ -36,10 +36,8 @@ pub trait Connection: SimpleConnection + Sized + Send {
|
||||
/// a transaction is already occurring, savepoints will be used to emulate a nested
|
||||
/// transaction.
|
||||
///
|
||||
/// If the function returns an `Ok`, that value will be returned. If the
|
||||
/// function returns an `Err`,
|
||||
/// [`TransactionError::UserReturnedError`](../result/enum.TransactionError.html#variant.UserReturnedError)
|
||||
/// will be returned wrapping that value.
|
||||
/// The error returned from the function must implement
|
||||
/// `From<diesel::result::Error>`.
|
||||
fn transaction<T, E, F>(&self, f: F) -> Result<T, E> where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
E: From<Error>,
|
||||
|
@ -14,6 +14,7 @@ cfg_if! {
|
||||
let connection = diesel::pg::PgConnection::establish(&connection_url).unwrap();
|
||||
connection.begin_test_transaction().unwrap();
|
||||
connection.execute("DROP TABLE IF EXISTS users").unwrap();
|
||||
connection.execute("DROP TABLE IF EXISTS animals").unwrap();
|
||||
|
||||
connection
|
||||
}
|
||||
@ -28,6 +29,16 @@ cfg_if! {
|
||||
)").unwrap();
|
||||
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap();
|
||||
|
||||
connection.execute("CREATE TABLE animals (
|
||||
id SERIAL PRIMARY KEY,
|
||||
species VARCHAR NOT NULL,
|
||||
legs INTEGER NOT NULL,
|
||||
name VARCHAR
|
||||
)").unwrap();
|
||||
connection.execute("INSERT INTO animals (species, legs, name) VALUES
|
||||
('dog', 4, 'Jack'),
|
||||
('spider', 8, null)").unwrap();
|
||||
|
||||
connection
|
||||
}
|
||||
} else if #[cfg(feature = "sqlite")] {
|
||||
@ -48,6 +59,16 @@ cfg_if! {
|
||||
)").unwrap();
|
||||
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap();
|
||||
|
||||
connection.execute("CREATE TABLE animals (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
species VARCHAR NOT NULL,
|
||||
legs INTEGER NOT NULL,
|
||||
name VARCHAR
|
||||
)").unwrap();
|
||||
connection.execute("INSERT INTO animals (species, legs, name) VALUES
|
||||
('dog', 4, 'Jack'),
|
||||
('spider', 8, null)").unwrap();
|
||||
|
||||
connection
|
||||
}
|
||||
} else if #[cfg(feature = "mysql")] {
|
||||
@ -58,6 +79,7 @@ cfg_if! {
|
||||
let connection_url = database_url_from_env("MYSQL_UNIT_TEST_DATABASE_URL");
|
||||
let connection = diesel::mysql::MysqlConnection::establish(&connection_url).unwrap();
|
||||
connection.execute("DROP TABLE IF EXISTS users").unwrap();
|
||||
connection.execute("DROP TABLE IF EXISTS animals").unwrap();
|
||||
|
||||
connection
|
||||
}
|
||||
@ -71,8 +93,18 @@ cfg_if! {
|
||||
name TEXT NOT NULL
|
||||
) CHARACTER SET utf8mb4").unwrap();
|
||||
connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap();
|
||||
connection.begin_test_transaction().unwrap();
|
||||
|
||||
connection.execute("CREATE TABLE animals (
|
||||
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||
species TEXT NOT NULL,
|
||||
legs INTEGER NOT NULL,
|
||||
name TEXT
|
||||
) CHARACTER SET utf8mb4").unwrap();
|
||||
connection.execute("INSERT INTO animals (species, legs, name) VALUES
|
||||
('dog', 4, 'Jack'),
|
||||
('spider', 8, null)").unwrap();
|
||||
|
||||
connection.begin_test_transaction().unwrap();
|
||||
connection
|
||||
}
|
||||
} else {
|
||||
@ -111,3 +143,39 @@ impl_Insertable! {
|
||||
name: String,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
animals {
|
||||
id -> Integer,
|
||||
species -> VarChar,
|
||||
legs -> Integer,
|
||||
name -> Nullable<VarChar>,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct NewAnimal {
|
||||
species: String,
|
||||
legs: i32,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
impl NewAnimal {
|
||||
pub fn new(species: &str, legs: i32, name: Option<&str>) -> Self {
|
||||
NewAnimal {
|
||||
species: species.into(),
|
||||
legs: legs,
|
||||
name: name.map(|n| n.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_Insertable! {
|
||||
(animals)
|
||||
struct NewAnimal {
|
||||
species: String,
|
||||
legs: i32,
|
||||
name: Option<String>,
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,27 @@ use types::BigInt;
|
||||
/// As with most bare functions, this is not exported by default. You can import
|
||||
/// it specifically as `diesel::expression::count`, or glob import
|
||||
/// `diesel::expression::dsl::*`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # include!("src/doctest_setup.rs");
|
||||
/// # use diesel::expression::dsl::*;
|
||||
/// #
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # use self::animals::dsl::*;
|
||||
/// # let connection = establish_connection();
|
||||
/// assert_eq!(Ok(1), animals.select(count(name)).first(&connection));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn count<T: Expression>(t: T) -> Count<T> {
|
||||
Count {
|
||||
target: t,
|
||||
@ -25,7 +46,7 @@ pub fn count<T: Expression>(t: T) -> Count<T> {
|
||||
/// it specifically as `diesel::expression::count_star`, or glob import
|
||||
/// `diesel::expression::dsl::*`
|
||||
///
|
||||
/// # Example
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
|
@ -56,8 +56,53 @@ macro_rules! fold_function {
|
||||
|
||||
fold_function!(sum, Sum, "SUM",
|
||||
"Represents a SQL `SUM` function. This function can only take types which are
|
||||
Foldable.");
|
||||
Foldable.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
# #[macro_use] extern crate diesel;
|
||||
# include!(\"src/doctest_setup.rs\");
|
||||
# use diesel::expression::dsl::*;
|
||||
#
|
||||
# table! {
|
||||
# users {
|
||||
# id -> Integer,
|
||||
# name -> VarChar,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn main() {
|
||||
# use self::animals::dsl::*;
|
||||
# let connection = establish_connection();
|
||||
assert_eq!(Ok(12i64), animals.select(sum(legs)).first(&connection));
|
||||
# }
|
||||
");
|
||||
|
||||
fold_function!(avg, Avg, "AVG",
|
||||
"Represents a SQL `AVG` function. This function can only take types which are
|
||||
Foldable.");
|
||||
Foldable.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
# #[macro_use] extern crate diesel;
|
||||
# include!(\"src/doctest_setup.rs\");
|
||||
# use diesel::expression::dsl::*;
|
||||
#
|
||||
# table! {
|
||||
# users {
|
||||
# id -> Integer,
|
||||
# name -> VarChar,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn main() {
|
||||
# use self::animals::dsl::*;
|
||||
# let connection = establish_connection();
|
||||
// assert_eq!(Ok(6f64), animals.select(avg(legs)).first(&connection));
|
||||
// TODO: There doesn't currently seem to be a way to use avg with integers, since
|
||||
// they return a `Numeric` which doesn't have a corresponding Rust type.
|
||||
# }
|
||||
```
|
||||
");
|
||||
|
@ -55,8 +55,50 @@ macro_rules! ord_function {
|
||||
|
||||
ord_function!(max, Max, "MAX",
|
||||
"Represents a SQL `MAX` function. This function can only take types which are
|
||||
ordered.");
|
||||
ordered.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
# #[macro_use] extern crate diesel;
|
||||
# include!(\"src/doctest_setup.rs\");
|
||||
# use diesel::expression::dsl::*;
|
||||
#
|
||||
# table! {
|
||||
# users {
|
||||
# id -> Integer,
|
||||
# name -> VarChar,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn main() {
|
||||
# use self::animals::dsl::*;
|
||||
# let connection = establish_connection();
|
||||
assert_eq!(Ok(Some(8)), animals.select(max(legs)).first(&connection));
|
||||
# }
|
||||
");
|
||||
|
||||
ord_function!(min, Min, "MIN",
|
||||
"Represents a SQL `MIN` function. This function can only take types which are
|
||||
ordered.");
|
||||
ordered.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
# #[macro_use] extern crate diesel;
|
||||
# include!(\"src/doctest_setup.rs\");
|
||||
# use diesel::expression::dsl::*;
|
||||
#
|
||||
# table! {
|
||||
# users {
|
||||
# id -> Integer,
|
||||
# name -> VarChar,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn main() {
|
||||
# use self::animals::dsl::*;
|
||||
# let connection = establish_connection();
|
||||
assert_eq!(Ok(Some(4)), animals.select(min(legs)).first(&connection));
|
||||
# }
|
||||
");
|
||||
|
@ -39,7 +39,28 @@ operator_allowed!(now, Add, add);
|
||||
operator_allowed!(now, Sub, sub);
|
||||
sql_function!(date, date_t, (x: Timestamp) -> Date,
|
||||
"Represents the SQL `DATE` function. The argument should be a Timestamp
|
||||
expression, and the return value will be an expression of type Date");
|
||||
expression, and the return value will be an expression of type Date.
|
||||
|
||||
# Examples
|
||||
|
||||
```ignore
|
||||
# #[macro_use] extern crate diesel;
|
||||
# extern crate chrono;
|
||||
# include!(\"src/doctest_setup.rs\");
|
||||
# use diesel::expression::dsl::*;
|
||||
#
|
||||
# table! {
|
||||
# users {
|
||||
# id -> Integer,
|
||||
# name -> VarChar,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn main() {
|
||||
# let connection = establish_connection();
|
||||
let today: chrono::NaiveDate = diesel::select(date(now)).first(&connection).unwrap();
|
||||
# }
|
||||
");
|
||||
|
||||
#[cfg(feature="postgres")]
|
||||
use expression::AsExpression;
|
||||
|
@ -10,6 +10,10 @@ use types::HasSqlType;
|
||||
/// Available for when you truly cannot represent something using the expression
|
||||
/// DSL. You will need to provide the type of the expression, in addition to the
|
||||
/// SQL. The compiler will be unable to verify the correctness of this type.
|
||||
///
|
||||
/// To get a SQL literal, use the [`sql()`] function.
|
||||
///
|
||||
/// [`sql()`]: fn.sql.html
|
||||
pub struct SqlLiteral<ST> {
|
||||
sql: String,
|
||||
_marker: PhantomData<ST>,
|
||||
@ -60,6 +64,54 @@ impl<QS, ST> AppearsOnTable<QS> for SqlLiteral<ST> {
|
||||
impl<ST> NonAggregate for SqlLiteral<ST> {
|
||||
}
|
||||
|
||||
/// Use literal SQL in the query builder
|
||||
///
|
||||
/// Available for when you truly cannot represent something using the expression
|
||||
/// DSL. You will need to provide the SQL type of the expression, in addition to
|
||||
/// the SQL.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The compiler will be unable to verify the correctness of the annotated type.
|
||||
/// If you give the wrong type, it'll either crash at runtime when deserializing
|
||||
/// the query result or produce invalid values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # #[macro_use] extern crate diesel_codegen;
|
||||
/// use diesel::expression::sql;
|
||||
/// use diesel::types::{Bool, Integer, Text};
|
||||
/// # include!("src/doctest_setup.rs");
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// #[derive(PartialEq, Debug, Queryable)]
|
||||
/// struct User {
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {
|
||||
/// # let connection = establish_connection();
|
||||
/// #
|
||||
/// let setup = sql::<Bool>("INSERT INTO users(name) VALUES('Ruby')");
|
||||
/// setup.execute(&connection).expect("Can't insert in users");
|
||||
///
|
||||
/// let query = sql::<(Integer, Text)>("SELECT id, name FROM users WHERE name='Ruby';");
|
||||
/// let users = query.load::<User>(&connection).expect("Can't query users");
|
||||
/// assert_eq!(users, vec![User{id: 3, name: "Ruby".to_owned()}]);
|
||||
///
|
||||
/// let query = users::table.filter(sql::<Bool>("name='Ruby'")); // Same query as above
|
||||
/// let users = query.load::<User>(&connection).expect("Can't query users");
|
||||
/// assert_eq!(users, vec![User{id: 3, name: "Ruby".to_owned()}]);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sql<ST>(sql: &str) -> SqlLiteral<ST> {
|
||||
SqlLiteral::new(sql.into())
|
||||
}
|
||||
|
@ -72,8 +72,41 @@ macro_rules! infer_table_from_schema {
|
||||
///
|
||||
/// This macro can only be used in combination with the `diesel_codegen` or
|
||||
/// `diesel_codegen_syntex` crates. It will not work on its own.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # #[macro_use] extern crate diesel_codegen;
|
||||
/// # include!("src/doctest_setup.rs");
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # #[cfg(feature = "postgres")]
|
||||
/// # embed_migrations!("../migrations/postgresql");
|
||||
/// # #[cfg(all(feature = "mysql", not(feature = "postgres")))]
|
||||
/// # embed_migrations!("../migrations/mysql");
|
||||
/// # #[cfg(all(feature = "sqlite", not(any(feature = "postgres", feature = "mysql"))))]
|
||||
/// embed_migrations!("../migrations/sqlite");
|
||||
///
|
||||
/// fn main() {
|
||||
/// let connection = establish_connection();
|
||||
///
|
||||
/// // This will run the necessary migrations.
|
||||
/// embedded_migrations::run(&connection);
|
||||
///
|
||||
/// // By default the output is thrown out. If you want to redirect it to stdout, you
|
||||
/// // should call embedded_migrations::run_with_output.
|
||||
/// embedded_migrations::run_with_output(&connection, &mut std::io::stdout());
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! embed_migrations {
|
||||
() => {
|
||||
#[allow(dead_code)]
|
||||
mod embedded_migrations {
|
||||
#[derive(EmbedMigrations)]
|
||||
struct _Dummy;
|
||||
@ -81,6 +114,7 @@ macro_rules! embed_migrations {
|
||||
};
|
||||
|
||||
($migrations_path: expr) => {
|
||||
#[allow(dead_code)]
|
||||
mod embedded_migrations {
|
||||
#[derive(EmbedMigrations)]
|
||||
#[embed_migrations_options(migrations_path=$migrations_path)]
|
||||
|
@ -12,6 +12,35 @@ use query_source::QuerySource;
|
||||
/// columns.
|
||||
///
|
||||
/// This is automatically implemented for the various query builder types.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate diesel;
|
||||
/// # include!("src/doctest_setup.rs");
|
||||
/// #
|
||||
/// # table! {
|
||||
/// # users {
|
||||
/// # id -> Integer,
|
||||
/// # name -> VarChar,
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use self::users::dsl::{users, id, name};
|
||||
///
|
||||
/// let connection = establish_connection();
|
||||
/// # connection.execute("DELETE FROM users").unwrap();
|
||||
/// connection.execute("INSERT INTO users (name) VALUES ('Saul'), ('Steve'), ('Stan')").unwrap();
|
||||
/// // load all users' names, ordered by their name descending
|
||||
/// let ordered_names: Vec<String> = users.select(name).order(name.desc()).load(&connection).unwrap();
|
||||
/// assert_eq!(vec![String::from("Steve"), String::from("Stan"), String::from("Saul")], ordered_names);
|
||||
///
|
||||
/// connection.execute("INSERT INTO users (name) VALUES ('Stan')").unwrap();
|
||||
/// let ordered_name_id_pairs = users.select((name, id)).order((name.asc(), id.desc())).load(&connection).unwrap();
|
||||
/// assert_eq!(vec![(String::from("Saul"), 3), (String::from("Stan"), 6), (String::from("Stan"), 5), (String::from("Steve"), 4)], ordered_name_id_pairs);
|
||||
/// # }
|
||||
/// ```
|
||||
pub trait OrderDsl<Expr: Expression>: AsQuery {
|
||||
type Output: AsQuery<SqlType=Self::SqlType>;
|
||||
|
||||
|
@ -85,7 +85,7 @@ impl<Left, Right, T> SelectableExpression<Join<Left, Right, LeftOuter>>
|
||||
|
||||
/// Indicates that two tables can be used together in a JOIN clause.
|
||||
/// Implementations of this trait will be generated for you automatically by
|
||||
/// the [association annotations](FIXME: Add link) from codegen.
|
||||
/// the [association annotations](../associations/index.html) from codegen.
|
||||
pub trait JoinTo<T: Table, JoinType>: Table {
|
||||
#[doc(hidden)]
|
||||
type JoinClause;
|
||||
|
@ -30,20 +30,40 @@ macro_rules! numeric_type {
|
||||
type Output = super::$tpe;
|
||||
}
|
||||
|
||||
impl Add for super::Nullable<super::$tpe> {
|
||||
type Rhs = super::Nullable<super::$tpe>;
|
||||
type Output = super::Nullable<super::$tpe>;
|
||||
}
|
||||
|
||||
impl Sub for super::$tpe {
|
||||
type Rhs = super::$tpe;
|
||||
type Output = super::$tpe;
|
||||
}
|
||||
|
||||
impl Sub for super::Nullable<super::$tpe> {
|
||||
type Rhs = super::Nullable<super::$tpe>;
|
||||
type Output = super::Nullable<super::$tpe>;
|
||||
}
|
||||
|
||||
impl Mul for super::$tpe {
|
||||
type Rhs = super::$tpe;
|
||||
type Output = super::$tpe;
|
||||
}
|
||||
|
||||
impl Mul for super::Nullable<super::$tpe> {
|
||||
type Rhs = super::Nullable<super::$tpe>;
|
||||
type Output = super::Nullable<super::$tpe>;
|
||||
}
|
||||
|
||||
impl Div for super::$tpe {
|
||||
type Rhs = super::$tpe;
|
||||
type Output = super::$tpe;
|
||||
}
|
||||
|
||||
impl Div for super::Nullable<super::$tpe> {
|
||||
type Rhs = super::Nullable<super::$tpe>;
|
||||
type Output = super::Nullable<super::$tpe>;
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ name = "diesel"
|
||||
chrono = "0.3"
|
||||
clap = "2.20.3"
|
||||
diesel = { version = "0.12.0", default-features = false }
|
||||
dotenv = "0.8.0"
|
||||
dotenv = ">=0.8, <0.10"
|
||||
diesel_infer_schema = "0.12.0"
|
||||
clippy = { optional = true, version = "=0.0.121" }
|
||||
clippy = { optional = true, version = "=0.0.125" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3.4"
|
||||
|
@ -13,10 +13,10 @@ keywords = ["orm", "database", "postgres", "sql", "codegen"]
|
||||
[dependencies]
|
||||
syn = { version = "0.11.4", features = ["aster"] }
|
||||
quote = "0.3.12"
|
||||
dotenv = { version = "0.8.0", optional = true }
|
||||
dotenv = { version = ">=0.8, <0.10", optional = true }
|
||||
diesel = { version = "0.12.0", default-features = false }
|
||||
diesel_infer_schema = { version = "0.12.0", default-features = false, optional = true }
|
||||
clippy = { optional = true, version = "=0.0.121" }
|
||||
clippy = { optional = true, version = "=0.0.125" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3.4"
|
||||
|
@ -13,10 +13,10 @@ keywords = ["orm", "database", "postgres", "postgresql", "sql"]
|
||||
diesel = { version = "0.12.0", default-features = false }
|
||||
syn = "0.11.4"
|
||||
quote = "0.3.1"
|
||||
clippy = { optional = true, version = "=0.0.121" }
|
||||
clippy = { optional = true, version = "=0.0.125" }
|
||||
|
||||
[dev-dependencies]
|
||||
dotenv = "0.8.0"
|
||||
dotenv = ">=0.8, <0.10"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -7,17 +7,17 @@ build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
diesel = { path = "../diesel", default-features = false }
|
||||
dotenv = "0.8.0"
|
||||
dotenv = ">=0.8, <0.10"
|
||||
|
||||
[dependencies]
|
||||
assert_matches = "1.0.1"
|
||||
chrono = { version = "0.3" }
|
||||
diesel = { path = "../diesel", default-features = false, features = ["quickcheck", "chrono", "uuid", "serde_json"] }
|
||||
diesel_codegen = { path = "../diesel_codegen" }
|
||||
dotenv = "0.8.0"
|
||||
dotenv = ">=0.8, <0.10"
|
||||
quickcheck = { version = "0.3.1", features = ["unstable"] }
|
||||
uuid = { version = ">=0.2.0, <0.5.0" }
|
||||
serde_json = "0.9"
|
||||
serde_json = { version=">=0.9, <2.0" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -92,6 +92,77 @@ fn dividing_column() {
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adding_nullables() {
|
||||
use schema::nullable_table::dsl::*;
|
||||
let connection = connection_with_nullable_table_data();
|
||||
|
||||
let expected_data = vec![None, None, Some(2), Some(3), Some(2)];
|
||||
let data = nullable_table.select(value + Some(1)).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data: Vec<Option<i32>> = vec![None; 5];
|
||||
let data = nullable_table.select(value + None as Option<i32>).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data = vec![None, None, Some(2), Some(4), Some(2)];
|
||||
let data = nullable_table.select(value + value).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_substracting_nullables() {
|
||||
use schema::nullable_table::dsl::*;
|
||||
let connection = connection_with_nullable_table_data();
|
||||
|
||||
let expected_data = vec![None, None, Some(0), Some(1), Some(0)];
|
||||
let data = nullable_table.select(value - Some(1)).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data: Vec<Option<i32>> = vec![None; 5];
|
||||
let data = nullable_table.select(value - None as Option<i32>).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data = vec![None, None, Some(0), Some(0), Some(0)];
|
||||
let data = nullable_table.select(value - value).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplying_nullables() {
|
||||
use schema::nullable_table::dsl::*;
|
||||
let connection = connection_with_nullable_table_data();
|
||||
|
||||
let expected_data = vec![None, None, Some(3), Some(6), Some(3)];
|
||||
let data = nullable_table.select(value * Some(3)).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data: Vec<Option<i32>> = vec![None; 5];
|
||||
let data = nullable_table.select(value * None as Option<i32>).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data = vec![None, None, Some(1), Some(4), Some(1)];
|
||||
let data = nullable_table.select(value * value).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dividing_nullables() {
|
||||
use schema::nullable_table::dsl::*;
|
||||
let connection = connection_with_nullable_table_data();
|
||||
|
||||
let expected_data = vec![None, None, Some(0), Some(1), Some(0)];
|
||||
let data = nullable_table.select(value / Some(2)).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data: Vec<Option<i32>> = vec![None; 5];
|
||||
let data = nullable_table.select(value / None as Option<i32>).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
|
||||
let expected_data = vec![None, None, Some(1), Some(1), Some(1)];
|
||||
let data = nullable_table.select(value / value).load(&connection);
|
||||
assert_eq!(Ok(expected_data), data);
|
||||
}
|
||||
#[test]
|
||||
fn mix_and_match_all_numeric_ops() {
|
||||
use schema::users::dsl::*;
|
||||
|
@ -148,6 +148,15 @@ impl FkTest {
|
||||
}
|
||||
}
|
||||
|
||||
numeric_expr!(nullable_table::value);
|
||||
|
||||
#[derive(Queryable, Insertable)]
|
||||
#[table_name="nullable_table"]
|
||||
pub struct NullableColumn {
|
||||
id: i32,
|
||||
value: Option<i32>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
pub type TestConnection = ::diesel::pg::PgConnection;
|
||||
#[cfg(feature = "sqlite")]
|
||||
@ -203,6 +212,22 @@ pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection {
|
||||
connection
|
||||
}
|
||||
|
||||
pub fn connection_with_nullable_table_data() -> TestConnection {
|
||||
let connection = connection();
|
||||
|
||||
let test_data = vec![
|
||||
NullableColumn { id: 1, value: None },
|
||||
NullableColumn { id: 2, value: None },
|
||||
NullableColumn { id: 3, value: Some(1) },
|
||||
NullableColumn { id: 4, value: Some(2) },
|
||||
NullableColumn { id: 5, value: Some(1) },
|
||||
];
|
||||
insert(&test_data).into(nullable_table::table)
|
||||
.execute(&connection).unwrap();
|
||||
|
||||
connection
|
||||
}
|
||||
|
||||
fn ensure_primary_key_seq_greater_than(x: i64, connection: &TestConnection) {
|
||||
if cfg!(feature = "postgres") {
|
||||
for _ in 0..x {
|
||||
|
@ -0,0 +1 @@
|
||||
DROP TABLE nullable_table;
|
@ -0,0 +1,4 @@
|
||||
CREATE TABLE nullable_table (
|
||||
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||
value INTEGER
|
||||
) CHARACTER SET utf8mb4;
|
@ -0,0 +1 @@
|
||||
DROP TABLE nullable_table;
|
@ -0,0 +1,4 @@
|
||||
CREATE TABLE nullable_table (
|
||||
id SERIAL PRIMARY KEY,
|
||||
value INTEGER
|
||||
);
|
@ -0,0 +1 @@
|
||||
DROP TABLE nullable_table;
|
@ -0,0 +1,4 @@
|
||||
CREATE TABLE nullable_table (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value INTEGER
|
||||
);
|
Loading…
Reference in New Issue
Block a user