Merge branch 'master' into ss/actix-example

This commit is contained in:
Sam Samai 2021-09-19 21:43:10 +10:00
commit 54edd87706
31 changed files with 280 additions and 184 deletions

View File

@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## 0.2.2 - 2021-09-18
- [[#105]] Compact entity format
- [[#132]] Add ActiveModel `insert` & `update`
- [[#129]] Add `set` method to `UpdateMany`
- [[#118]] Initial lock support
- [[#167]] Add `FromQueryResult::find_by_statement`
[#105]: https://github.com/SeaQL/sea-orm/issues/105
[#132]: https://github.com/SeaQL/sea-orm/issues/132
[#129]: https://github.com/SeaQL/sea-orm/issues/129
[#118]: https://github.com/SeaQL/sea-orm/issues/118
[#167]: https://github.com/SeaQL/sea-orm/issues/167
## 0.2.1 - 2021-09-04 ## 0.2.1 - 2021-09-04
- Update dependencies - Update dependencies

View File

@ -3,7 +3,7 @@ members = [".", "sea-orm-macros", "sea-orm-codegen"]
[package] [package]
name = "sea-orm" name = "sea-orm"
version = "0.2.1" version = "0.2.2"
authors = ["Chris Tsang <tyt2y7@gmail.com>"] authors = ["Chris Tsang <tyt2y7@gmail.com>"]
edition = "2018" edition = "2018"
description = "🐚 An async & dynamic ORM for Rust" description = "🐚 An async & dynamic ORM for Rust"
@ -29,8 +29,8 @@ futures = { version = "^0.3" }
futures-util = { version = "^0.3" } futures-util = { version = "^0.3" }
log = { version = "^0.4", optional = true } log = { version = "^0.4", optional = true }
rust_decimal = { version = "^1", optional = true } rust_decimal = { version = "^1", optional = true }
sea-orm-macros = { version = "^0.2", path = "sea-orm-macros", optional = true } sea-orm-macros = { version = "^0.2.2", path = "sea-orm-macros", optional = true }
sea-query = { version = "^0.16.1", features = ["thread-safe"] } sea-query = { version = "^0.16.3", features = ["thread-safe"] }
sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] } sea-strum = { version = "^0.21", features = ["derive", "sea-orm"] }
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1", optional = true } serde_json = { version = "^1", optional = true }

View File

@ -23,11 +23,6 @@ rocket_dyn_templates = { git = "https://github.com/SergioBenitez/Rocket.git", fe
sea-orm = { path = "../../", version = "^0.2", features = ["macros"], default-features = false } sea-orm = { path = "../../", version = "^0.2", features = ["macros"], default-features = false }
serde_json = { version = "^1" } serde_json = { version = "^1" }
[dependencies.sqlx]
version = "^0.5"
default-features = false
features = ["macros", "offline", "migrate"]
[features] [features]
default = ["sqlx-postgres"] default = ["sqlx-postgres"]
sqlx-mysql = ["sea-orm/sqlx-mysql", "rocket_db_pools/sqlx_mysql"] sqlx-mysql = ["sea-orm/sqlx-mysql", "rocket_db_pools/sqlx_mysql"]

View File

@ -10,7 +10,7 @@ use rocket::{Build, Request, Rocket};
use rocket_db_pools::{sqlx, Connection, Database}; use rocket_db_pools::{sqlx, Connection, Database};
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{context, Template};
use sea_orm::entity::*; use sea_orm::{entity::*, query::*};
mod pool; mod pool;
use pool::RocketDbPool; use pool::RocketDbPool;
@ -81,7 +81,9 @@ async fn list(
) -> Template { ) -> Template {
let page = page.unwrap_or(0); let page = page.unwrap_or(0);
let posts_per_page = posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE); let posts_per_page = posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE);
let paginator = Post::find().paginate(&conn, posts_per_page); let paginator = Post::find()
.order_by_asc(post::Column::Id)
.paginate(&conn, posts_per_page);
let num_pages = paginator.num_pages().await.ok().unwrap(); let num_pages = paginator.num_pages().await.ok().unwrap();
let posts = paginator let posts = paginator

View File

@ -1,65 +1,17 @@
use rocket::serde::{Deserialize, Serialize}; use rocket::serde::{Deserialize, Serialize};
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, FromForm)]
#[serde(crate = "rocket::serde")]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"posts"
}
}
#[derive(
Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Deserialize, Serialize, FromForm,
)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
#[sea_orm(table_name = "posts")]
pub struct Model { pub struct Model {
#[serde(skip_deserializing, skip_serializing_if = "Option::is_none")] #[sea_orm(primary_key)]
pub id: Option<i32>, pub id: i32,
pub title: String, pub title: String,
pub text: String, pub text: String,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Column {
Id,
Title,
Text,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {} pub enum Relation {}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Title => ColumnType::String(None).def(),
Self::Text => ColumnType::String(None).def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!()
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -5,6 +5,12 @@
<div class="ten columns"> <div class="ten columns">
<form action="/{{ post.id }}" method="post"> <form action="/{{ post.id }}" method="post">
<div class="twelve columns"> <div class="twelve columns">
<input
type="hidden"
name="id"
id="id"
value="0"
/>
<input <input
type="text" type="text"
placeholder="title" placeholder="title"

View File

@ -3,6 +3,12 @@
<h4>New Post</h4> <h4>New Post</h4>
<form action="/" method="post"> <form action="/" method="post">
<div class="twelve columns"> <div class="twelve columns">
<input
type="hidden"
name="id"
id="id"
value="0"
/>
<input <input
type="text" type="text"
placeholder="enter title" placeholder="enter title"

View File

@ -8,7 +8,7 @@ edition = "2018"
publish = false publish = false
[dependencies] [dependencies]
sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-tokio-native-tls" ] } sea-orm = { path = "../../", features = [ "sqlx-all", "runtime-tokio-native-tls", "debug-print" ] }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
env_logger = { version = "^0.9" } env_logger = { version = "^0.9" }
log = { version = "^0.4" } log = { version = "^0.4" }

View File

@ -1,57 +1,14 @@
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
pub struct Entity; #[sea_orm(table_name = "cake")]
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model { pub struct Model {
#[sea_orm(primary_key)]
pub id: i32, pub id: i32,
pub name: String, pub name: String,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {} pub enum Relation {}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
unreachable!()
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -3,7 +3,7 @@
[package] [package]
name = "sea-orm-cli" name = "sea-orm-cli"
version = "0.2.0" version = "0.2.2"
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ] authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
edition = "2018" edition = "2018"
description = "Command line utility for SeaORM" description = "Command line utility for SeaORM"
@ -21,8 +21,8 @@ path = "src/main.rs"
clap = { version = "^2.33.3" } clap = { version = "^2.33.3" }
dotenv = { version = "^0.15" } dotenv = { version = "^0.15" }
async-std = { version = "^1.9", features = [ "attributes" ] } async-std = { version = "^1.9", features = [ "attributes" ] }
sea-orm-codegen = { version = "^0.2.0", path = "../sea-orm-codegen" } sea-orm-codegen = { version = "^0.2.2", path = "../sea-orm-codegen" }
sea-schema = { version = "^0.2.8", git = "https://github.com/SeaQL/sea-schema.git", default-features = false, features = [ sea-schema = { version = "^0.2.8", default-features = false, features = [
"debug-print", "debug-print",
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "sea-orm-codegen" name = "sea-orm-codegen"
version = "0.2.0" version = "0.2.2"
authors = ["Billy Chan <ccw.billy.123@gmail.com>"] authors = ["Billy Chan <ccw.billy.123@gmail.com>"]
edition = "2018" edition = "2018"
description = "Code Generator for SeaORM" description = "Code Generator for SeaORM"

View File

@ -38,6 +38,7 @@ impl Column {
ColumnType::Double(_) => "f64", ColumnType::Double(_) => "f64",
ColumnType::Json | ColumnType::JsonBinary => "Json", ColumnType::Json | ColumnType::JsonBinary => "Json",
ColumnType::DateTime(_) | ColumnType::Timestamp(_) => "DateTime", ColumnType::DateTime(_) | ColumnType::Timestamp(_) => "DateTime",
ColumnType::TimestampWithTimeZone(_) => "DateTimeWithTimeZone",
ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal", ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal",
ColumnType::Uuid => "Uuid", ColumnType::Uuid => "Uuid",
ColumnType::Binary(_) => "Vec<u8>", ColumnType::Binary(_) => "Vec<u8>",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "sea-orm-macros" name = "sea-orm-macros"
version = "0.2.0" version = "0.2.2"
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ] authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
edition = "2018" edition = "2018"
description = "Derive macros for SeaORM" description = "Derive macros for SeaORM"

View File

@ -165,19 +165,22 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
"String" | "&str" => quote! { String(None) }, "String" | "&str" => quote! { String(None) },
"u8" | "i8" => quote! { TinyInteger }, "u8" | "i8" => quote! { TinyInteger },
"u16" | "i16" => quote! { SmallInteger }, "u16" | "i16" => quote! { SmallInteger },
"u32" | "u64" | "i32" | "i64" => quote! { Integer }, "u32" | "i32" => quote! { Integer },
"u128" | "i128" => quote! { BigInteger }, "u64" | "i64" => quote! { BigInteger },
"f32" => quote! { Float }, "f32" => quote! { Float },
"f64" => quote! { Double }, "f64" => quote! { Double },
"bool" => quote! { Boolean }, "bool" => quote! { Boolean },
"NaiveDate" => quote! { Date }, "NaiveDate" => quote! { Date },
"NaiveTime" => quote! { Time }, "NaiveTime" => quote! { Time },
"DateTime" | "NaiveDateTime" | "DateTimeWithTimeZone" => { "DateTime" | "NaiveDateTime" => {
quote! { DateTime } quote! { DateTime }
} }
"DateTimeWithTimeZone" => {
quote! { TimestampWithTimeZone }
}
"Uuid" => quote! { Uuid }, "Uuid" => quote! { Uuid },
"Json" => quote! { Json }, "Json" => quote! { Json },
"Decimal" => quote! { Decimal }, "Decimal" => quote! { Decimal(None) },
"Vec<u8>" => quote! { Binary }, "Vec<u8>" => quote! { Binary },
_ => { _ => {
return Err(Error::new( return Err(Error::new(

View File

@ -1,6 +1,7 @@
use crate::{error::*, ExecResult, QueryResult, Statement, StatementBuilder}; use crate::{error::*, ExecResult, QueryResult, Statement, StatementBuilder};
use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder}; use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, QueryBuilder, SqliteQueryBuilder};
#[cfg_attr(not(feature = "mock"), derive(Clone))]
pub enum DatabaseConnection { pub enum DatabaseConnection {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection), SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection),
@ -143,3 +144,15 @@ impl DbBackend {
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::DatabaseConnection;
#[test]
fn assert_database_connection_traits() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<DatabaseConnection>();
}
}

View File

@ -13,7 +13,7 @@ use super::sqlx_common::*;
#[derive(Debug)] #[derive(Debug)]
pub struct SqlxMySqlConnector; pub struct SqlxMySqlConnector;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct SqlxMySqlPoolConnection { pub struct SqlxMySqlPoolConnection {
pool: MySqlPool, pool: MySqlPool,
} }

View File

@ -13,7 +13,7 @@ use super::sqlx_common::*;
#[derive(Debug)] #[derive(Debug)]
pub struct SqlxPostgresConnector; pub struct SqlxPostgresConnector;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct SqlxPostgresPoolConnection { pub struct SqlxPostgresPoolConnection {
pool: PgPool, pool: PgPool,
} }

View File

@ -13,7 +13,7 @@ use super::sqlx_common::*;
#[derive(Debug)] #[derive(Debug)]
pub struct SqlxSqliteConnector; pub struct SqlxSqliteConnector;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct SqlxSqlitePoolConnection { pub struct SqlxSqlitePoolConnection {
pool: SqlitePool, pool: SqlitePool,
} }

View File

@ -541,3 +541,58 @@ pub trait EntityTrait: EntityName {
Delete::many(Self::default()) Delete::many(Self::default())
} }
} }
#[cfg(test)]
mod tests {
#[test]
#[cfg(feature = "macros")]
fn entity_model_1() {
use crate::entity::*;
mod hello {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "hello")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
assert_eq!(hello::Entity.table_name(), "hello");
assert_eq!(hello::Entity.schema_name(), None);
}
#[test]
#[cfg(feature = "macros")]
fn entity_model_2() {
use crate::entity::*;
mod hello {
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "hello", schema_name = "world")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
assert_eq!(hello::Entity.table_name(), "hello");
assert_eq!(hello::Entity.schema_name(), Some("world"));
}
}

View File

@ -24,6 +24,7 @@ pub enum ColumnType {
Decimal(Option<(u32, u32)>), Decimal(Option<(u32, u32)>),
DateTime, DateTime,
Timestamp, Timestamp,
TimestampWithTimeZone,
Time, Time,
Date, Date,
Binary, Binary,
@ -278,6 +279,7 @@ impl From<ColumnType> for sea_query::ColumnType {
ColumnType::Decimal(s) => sea_query::ColumnType::Decimal(s), ColumnType::Decimal(s) => sea_query::ColumnType::Decimal(s),
ColumnType::DateTime => sea_query::ColumnType::DateTime(None), ColumnType::DateTime => sea_query::ColumnType::DateTime(None),
ColumnType::Timestamp => sea_query::ColumnType::Timestamp(None), ColumnType::Timestamp => sea_query::ColumnType::Timestamp(None),
ColumnType::TimestampWithTimeZone => sea_query::ColumnType::TimestampWithTimeZone(None),
ColumnType::Time => sea_query::ColumnType::Time(None), ColumnType::Time => sea_query::ColumnType::Time(None),
ColumnType::Date => sea_query::ColumnType::Date, ColumnType::Date => sea_query::ColumnType::Date,
ColumnType::Binary => sea_query::ColumnType::Binary(None), ColumnType::Binary => sea_query::ColumnType::Binary(None),
@ -309,6 +311,7 @@ impl From<sea_query::ColumnType> for ColumnType {
sea_query::ColumnType::Decimal(s) => Self::Decimal(s), sea_query::ColumnType::Decimal(s) => Self::Decimal(s),
sea_query::ColumnType::DateTime(_) => Self::DateTime, sea_query::ColumnType::DateTime(_) => Self::DateTime,
sea_query::ColumnType::Timestamp(_) => Self::Timestamp, sea_query::ColumnType::Timestamp(_) => Self::Timestamp,
sea_query::ColumnType::TimestampWithTimeZone(_) => Self::TimestampWithTimeZone,
sea_query::ColumnType::Time(_) => Self::Time, sea_query::ColumnType::Time(_) => Self::Time,
sea_query::ColumnType::Date => Self::Date, sea_query::ColumnType::Date => Self::Date,
sea_query::ColumnType::Binary(_) => Self::Binary, sea_query::ColumnType::Binary(_) => Self::Binary,

View File

@ -314,29 +314,50 @@ where
} }
} }
impl<T> TryGetableMany for (T, T) impl<A, B> TryGetableMany for (A, B)
where where
T: TryGetable, A: TryGetable,
B: TryGetable,
{ {
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> { fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
try_get_many_with_slice_len_of(2, cols)?; try_get_many_with_slice_len_of(2, cols)?;
Ok(( Ok((
T::try_get(res, pre, &cols[0])?, A::try_get(res, pre, &cols[0])?,
T::try_get(res, pre, &cols[1])?, B::try_get(res, pre, &cols[1])?,
)) ))
} }
} }
impl<T> TryGetableMany for (T, T, T) impl<A, B, C> TryGetableMany for (A, B, C)
where where
T: TryGetable, A: TryGetable,
B: TryGetable,
C: TryGetable,
{ {
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> { fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
try_get_many_with_slice_len_of(3, cols)?; try_get_many_with_slice_len_of(3, cols)?;
Ok(( Ok((
T::try_get(res, pre, &cols[0])?, A::try_get(res, pre, &cols[0])?,
T::try_get(res, pre, &cols[1])?, B::try_get(res, pre, &cols[1])?,
T::try_get(res, pre, &cols[2])?, C::try_get(res, pre, &cols[2])?,
))
}
}
impl<A, B, C, D> TryGetableMany for (A, B, C, D)
where
A: TryGetable,
B: TryGetable,
C: TryGetable,
D: TryGetable,
{
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError> {
try_get_many_with_slice_len_of(4, cols)?;
Ok((
A::try_get(res, pre, &cols[0])?,
B::try_get(res, pre, &cols[1])?,
C::try_get(res, pre, &cols[2])?,
D::try_get(res, pre, &cols[3])?,
)) ))
} }
} }
@ -370,15 +391,27 @@ macro_rules! try_from_u64_err {
} }
} }
}; };
}
macro_rules! try_from_u64_tuple { ( $($gen_type: ident),* ) => {
( $type: ty ) => { impl<$( $gen_type, )*> TryFromU64 for ($( $gen_type, )*)
try_from_u64_err!(($type, $type)); where
try_from_u64_err!(($type, $type, $type)); $( $gen_type: TryFromU64, )*
{
fn try_from_u64(_: u64) -> Result<Self, DbErr> {
Err(DbErr::Exec(format!(
"{} cannot be converted from u64",
stringify!(($($gen_type,)*))
)))
}
}
}; };
} }
// impl TryFromU64 for tuples with generic types
try_from_u64_err!(A, B);
try_from_u64_err!(A, B, C);
try_from_u64_err!(A, B, C, D);
macro_rules! try_from_u64_numeric { macro_rules! try_from_u64_numeric {
( $type: ty ) => { ( $type: ty ) => {
impl TryFromU64 for $type { impl TryFromU64 for $type {
@ -393,7 +426,6 @@ macro_rules! try_from_u64_numeric {
}) })
} }
} }
try_from_u64_tuple!($type);
}; };
} }
@ -413,19 +445,10 @@ macro_rules! try_from_u64_string {
Ok(n.to_string()) Ok(n.to_string())
} }
} }
try_from_u64_tuple!($type);
}; };
} }
try_from_u64_string!(String); try_from_u64_string!(String);
macro_rules! try_from_u64_dummy {
( $type: ty ) => {
try_from_u64_err!($type);
try_from_u64_err!(($type, $type));
try_from_u64_err!(($type, $type, $type));
};
}
#[cfg(feature = "with-uuid")] #[cfg(feature = "with-uuid")]
try_from_u64_dummy!(uuid::Uuid); try_from_u64_err!(uuid::Uuid);

View File

@ -116,7 +116,7 @@ where
); );
} }
stmt.table(entity).if_not_exists().take() stmt.table(entity).take()
} }
#[cfg(test)] #[cfg(test)]
@ -130,7 +130,6 @@ mod tests {
Schema::create_table_from_entity(CakeFillingPrice).to_string(MysqlQueryBuilder), Schema::create_table_from_entity(CakeFillingPrice).to_string(MysqlQueryBuilder),
Table::create() Table::create()
.table(CakeFillingPrice) .table(CakeFillingPrice)
.if_not_exists()
.col( .col(
ColumnDef::new(cake_filling_price::Column::CakeId) ColumnDef::new(cake_filling_price::Column::CakeId)
.integer() .integer()

View File

@ -2,22 +2,21 @@ pub mod common;
pub use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn}; pub use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn};
// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlite,runtime-async-std-native-tls --test basic // cargo test --features sqlx-sqlite,runtime-async-std-native-tls --test basic
#[sea_orm_macros::test] #[sea_orm_macros::test]
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
async fn main() { async fn main() -> Result<(), DbErr> {
use std::env; let base_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite::memory:".to_owned());
let base_url = env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite::memory:".to_owned());
let db: DbConn = Database::connect(&base_url).await.unwrap(); let db: DbConn = Database::connect(&base_url).await?;
setup_schema(&db).await?;
crud_cake(&db).await?;
setup_schema(&db).await; Ok(())
crud_cake(&db).await.unwrap();
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
async fn setup_schema(db: &DbConn) { async fn setup_schema(db: &DbConn) -> Result<(), DbErr> {
use sea_query::*; use sea_query::*;
let stmt = sea_query::Table::create() let stmt = sea_query::Table::create()
@ -33,8 +32,10 @@ async fn setup_schema(db: &DbConn) {
.to_owned(); .to_owned();
let builder = db.get_database_backend(); let builder = db.get_database_backend();
let result = db.execute(builder.build(&stmt)).await; let result = db.execute(builder.build(&stmt)).await?;
println!("Create table cake: {:?}", result); println!("Create table cake: {:?}", result);
Ok(())
} }
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]

View File

@ -1,3 +1,5 @@
# Schema for SeaORM test suite # Schema for SeaORM test suite
![Entity Relation Diagram](bakery_chain_erd.png)
ERD generated by DataGrip. ERD generated by DataGrip.

View File

@ -0,0 +1,15 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "applog")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub json: Json,
pub created_at: DateTimeWithTimeZone,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -1,5 +1,4 @@
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "metadata")] #[sea_orm(table_name = "metadata")]
@ -11,13 +10,7 @@ pub struct Model {
pub bytes: Vec<u8>, pub bytes: Vec<u8>,
} }
#[derive(Copy, Clone, Debug, EnumIter)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {} pub enum Relation {}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
unreachable!()
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -1,3 +1,4 @@
pub mod applog;
pub mod baker; pub mod baker;
pub mod bakery; pub mod bakery;
pub mod cake; pub mod cake;
@ -7,6 +8,7 @@ pub mod lineitem;
pub mod metadata; pub mod metadata;
pub mod order; pub mod order;
pub use super::applog::Entity as Applog;
pub use super::baker::Entity as Baker; pub use super::baker::Entity as Baker;
pub use super::bakery::Entity as Bakery; pub use super::bakery::Entity as Bakery;
pub use super::cake::Entity as Cake; pub use super::cake::Entity as Cake;

View File

@ -53,6 +53,7 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection {
schema::create_cakes_bakers_table(&db).await.unwrap(); schema::create_cakes_bakers_table(&db).await.unwrap();
schema::create_lineitem_table(&db).await.unwrap(); schema::create_lineitem_table(&db).await.unwrap();
schema::create_metadata_table(&db).await.unwrap(); schema::create_metadata_table(&db).await.unwrap();
schema::create_log_table(&db).await.unwrap();
db db
} }

View File

@ -1,18 +1,30 @@
pub use super::super::bakery_chain::*; pub use super::super::bakery_chain::*;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use sea_orm::{error::*, sea_query, DbConn, EntityTrait, ExecResult, Schema}; use sea_orm::{error::*, sea_query, DbBackend, DbConn, EntityTrait, ExecResult, Schema};
use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, Table, TableCreateStatement}; use sea_query::{
Alias, ColumnDef, ForeignKey, ForeignKeyAction, Index, Table, TableCreateStatement,
};
async fn create_table<E>( async fn create_table<E>(
db: &DbConn, db: &DbConn,
stmt: &TableCreateStatement, create: &TableCreateStatement,
entity: E, entity: E,
) -> Result<ExecResult, DbErr> ) -> Result<ExecResult, DbErr>
where where
E: EntityTrait, E: EntityTrait,
{ {
let builder = db.get_database_backend(); let builder = db.get_database_backend();
let stmt = builder.build(stmt); if builder != DbBackend::Sqlite {
let stmt = builder.build(
Table::drop()
.table(Alias::new(create.get_table_name().unwrap().as_ref()))
.if_exists()
.cascade(),
);
db.execute(stmt).await?;
}
let stmt = builder.build(create);
assert_eq!( assert_eq!(
builder.build(&Schema::create_table_from_entity(entity)), builder.build(&Schema::create_table_from_entity(entity)),
stmt stmt
@ -23,7 +35,6 @@ where
pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(bakery::Entity) .table(bakery::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(bakery::Column::Id) ColumnDef::new(bakery::Column::Id)
.integer() .integer()
@ -45,7 +56,6 @@ pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(baker::Entity) .table(baker::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(baker::Column::Id) ColumnDef::new(baker::Column::Id)
.integer() .integer()
@ -76,7 +86,6 @@ pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(customer::Entity) .table(customer::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(customer::Column::Id) ColumnDef::new(customer::Column::Id)
.integer() .integer()
@ -94,7 +103,6 @@ pub async fn create_customer_table(db: &DbConn) -> Result<ExecResult, DbErr> {
pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(order::Entity) .table(order::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(order::Column::Id) ColumnDef::new(order::Column::Id)
.integer() .integer()
@ -142,7 +150,6 @@ pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(lineitem::Entity) .table(lineitem::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(lineitem::Column::Id) ColumnDef::new(lineitem::Column::Id)
.integer() .integer()
@ -194,7 +201,6 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(cakes_bakers::Entity) .table(cakes_bakers::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(cakes_bakers::Column::CakeId) ColumnDef::new(cakes_bakers::Column::CakeId)
.integer() .integer()
@ -235,7 +241,6 @@ pub async fn create_cakes_bakers_table(db: &DbConn) -> Result<ExecResult, DbErr>
pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = Table::create() let stmt = Table::create()
.table(cake::Entity) .table(cake::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(cake::Column::Id) ColumnDef::new(cake::Column::Id)
.integer() .integer()
@ -272,7 +277,6 @@ pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
pub async fn create_metadata_table(db: &DbConn) -> Result<ExecResult, DbErr> { pub async fn create_metadata_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create() let stmt = sea_query::Table::create()
.table(metadata::Entity) .table(metadata::Entity)
.if_not_exists()
.col( .col(
ColumnDef::new(metadata::Column::Uuid) ColumnDef::new(metadata::Column::Uuid)
.uuid() .uuid()
@ -286,3 +290,24 @@ pub async fn create_metadata_table(db: &DbConn) -> Result<ExecResult, DbErr> {
create_table(db, &stmt, Metadata).await create_table(db, &stmt, Metadata).await
} }
pub async fn create_log_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(applog::Entity)
.col(
ColumnDef::new(applog::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(applog::Column::Json).json().not_null())
.col(
ColumnDef::new(applog::Column::CreatedAt)
.timestamp_with_time_zone()
.not_null(),
)
.to_owned();
create_table(db, &stmt, Applog).await
}

31
tests/timestamp_tests.rs Normal file
View File

@ -0,0 +1,31 @@
pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext};
use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel};
#[sea_orm_macros::test]
#[cfg(feature = "sqlx-postgres")]
async fn main() -> Result<(), DbErr> {
let ctx = TestContext::new("bakery_chain_schema_timestamp_tests").await;
create_applog(&ctx.db).await?;
ctx.delete().await;
Ok(())
}
pub async fn create_applog(db: &DatabaseConnection) -> Result<(), DbErr> {
let log = applog::Model {
id: 1,
json: Json::String("HI".to_owned()),
created_at: "2021-09-17T17:50:20+08:00".parse().unwrap(),
};
let res = Applog::insert(log.clone().into_active_model())
.exec(db)
.await?;
assert_eq!(log.id.clone(), res.last_insert_id);
assert_eq!(Applog::find().one(db).await?, Some(log.clone()));
Ok(())
}

View File

@ -2,7 +2,6 @@ pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext}; pub use common::{bakery_chain::*, setup::*, TestContext};
use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel}; use sea_orm::{entity::prelude::*, DatabaseConnection, IntoActiveModel};
use uuid::Uuid;
#[sea_orm_macros::test] #[sea_orm_macros::test]
#[cfg(any( #[cfg(any(
@ -11,10 +10,8 @@ use uuid::Uuid;
feature = "sqlx-postgres" feature = "sqlx-postgres"
))] ))]
async fn main() -> Result<(), DbErr> { async fn main() -> Result<(), DbErr> {
let ctx = TestContext::new("bakery_chain_schema_primary_key_tests").await; let ctx = TestContext::new("bakery_chain_schema_uuid_tests").await;
create_metadata(&ctx.db).await?; create_metadata(&ctx.db).await?;
ctx.delete().await; ctx.delete().await;
Ok(()) Ok(())