This commit is contained in:
Billy Chan 2021-11-08 19:03:06 +08:00
parent 80c0d69733
commit 30f43b64c6
No known key found for this signature in database
GPG Key ID: A2D690CAC7DF3CC7
8 changed files with 122 additions and 34 deletions

View File

@ -350,6 +350,7 @@ jobs:
env: env:
DATABASE_URL: "sqlite::memory:" DATABASE_URL: "sqlite::memory:"
strategy: strategy:
fail-fast: false
matrix: matrix:
runtime: [async-std, actix, tokio] runtime: [async-std, actix, tokio]
tls: [native-tls, rustls] tls: [native-tls, rustls]
@ -392,6 +393,7 @@ jobs:
env: env:
DATABASE_URL: "mysql://root:@localhost" DATABASE_URL: "mysql://root:@localhost"
strategy: strategy:
fail-fast: false
matrix: matrix:
version: [8.0, 5.7] version: [8.0, 5.7]
runtime: [async-std, actix, tokio] runtime: [async-std, actix, tokio]
@ -452,8 +454,9 @@ jobs:
env: env:
DATABASE_URL: "mysql://root:@localhost" DATABASE_URL: "mysql://root:@localhost"
strategy: strategy:
fail-fast: false
matrix: matrix:
version: [10.6] version: [10.7, 10.6, 10.5, 10.0, 5.5]
runtime: [async-std, actix, tokio] runtime: [async-std, actix, tokio]
tls: [native-tls] tls: [native-tls]
services: services:
@ -512,6 +515,7 @@ jobs:
env: env:
DATABASE_URL: "postgres://root:root@localhost" DATABASE_URL: "postgres://root:root@localhost"
strategy: strategy:
fail-fast: false
matrix: matrix:
version: [13.3, 12.7, 11.12, 10.17, 9.6.22] version: [13.3, 12.7, 11.12, 10.17, 9.6.22]
runtime: [tokio] runtime: [tokio]

View File

@ -30,7 +30,7 @@ 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.3.1", path = "sea-orm-macros", optional = true } sea-orm-macros = { version = "^0.3.1", path = "sea-orm-macros", optional = true }
sea-query = { version = "^0.18.0", git = "https://github.com/marlon-sousa/sea-query.git", branch = "extended-returning-support", features = ["thread-safe"] } sea-query = { version = "^0.18.2", 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

@ -19,9 +19,11 @@ pub enum DatabaseConnection {
/// Create a MYSQL database connection and pool /// Create a MYSQL database connection and pool
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
SqlxMySqlPoolConnection { SqlxMySqlPoolConnection {
/// A SQLx MySQL pool /// The SQLx MySQL pool
conn: crate::SqlxMySqlPoolConnection, conn: crate::SqlxMySqlPoolConnection,
/// A flag indicating whether `RETURNING` syntax is supported /// The MySQL version
version: String,
/// The flag indicating whether `RETURNING` syntax is supported
support_returning: bool, support_returning: bool,
}, },
/// Create a PostgreSQL database connection and pool /// Create a PostgreSQL database connection and pool
@ -226,7 +228,7 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection {
fn support_returning(&self) -> bool { fn support_returning(&self) -> bool {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection { .. } => false, DatabaseConnection::SqlxMySqlPoolConnection { support_returning, .. } => *support_returning,
#[cfg(feature = "sqlx-postgres")] #[cfg(feature = "sqlx-postgres")]
DatabaseConnection::SqlxPostgresPoolConnection(_) => true, DatabaseConnection::SqlxPostgresPoolConnection(_) => true,
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
@ -264,6 +266,40 @@ impl DatabaseConnection {
} }
} }
impl DatabaseConnection {
/// Get database version
pub fn db_version(&self) -> String {
match self {
#[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection { version, .. } => version.to_string(),
// #[cfg(feature = "sqlx-postgres")]
// DatabaseConnection::SqlxPostgresPoolConnection(conn) => ,
// #[cfg(feature = "sqlx-sqlite")]
// DatabaseConnection::SqlxSqlitePoolConnection(conn) => ,
// #[cfg(feature = "mock")]
// DatabaseConnection::MockDatabaseConnection(conn) => ,
DatabaseConnection::Disconnected => panic!("Disconnected"),
_ => unimplemented!(),
}
}
/// Check if database supports `RETURNING`
pub fn db_support_returning(&self) -> bool {
match self {
#[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection { support_returning, .. } => *support_returning,
#[cfg(feature = "sqlx-postgres")]
DatabaseConnection::SqlxPostgresPoolConnection(_) => true,
// #[cfg(feature = "sqlx-sqlite")]
// DatabaseConnection::SqlxSqlitePoolConnection(conn) => ,
// #[cfg(feature = "mock")]
// DatabaseConnection::MockDatabaseConnection(conn) => ,
DatabaseConnection::Disconnected => panic!("Disconnected"),
_ => unimplemented!(),
}
}
}
impl DbBackend { impl DbBackend {
/// Check if the URI is the same as the specified database backend. /// Check if the URI is the same as the specified database backend.
/// Returns true if they match. /// Returns true if they match.

View File

@ -349,11 +349,7 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction {
} }
fn support_returning(&self) -> bool { fn support_returning(&self) -> bool {
match self.backend { panic!("FIXME: How?")
DbBackend::MySql => false,
DbBackend::Postgres => true,
DbBackend::Sqlite => false,
}
} }
} }

View File

@ -191,9 +191,9 @@ async fn into_db_connection(pool: MySqlPool) -> Result<DatabaseConnection, DbErr
r#"SHOW VARIABLES LIKE "version""#.to_owned(), r#"SHOW VARIABLES LIKE "version""#.to_owned(),
)) ))
.await?; .await?;
let support_returning = if let Some(query_result) = res { let (version, support_returning) = if let Some(query_result) = res {
let version: String = query_result.try_get("", "Value")?; let version: String = query_result.try_get("", "Value")?;
if !version.contains("MariaDB") { let support_returning = if !version.contains("MariaDB") {
// This is MySQL // This is MySQL
false false
} else { } else {
@ -213,12 +213,14 @@ async fn into_db_connection(pool: MySqlPool) -> Result<DatabaseConnection, DbErr
let ver_major = parse_captures!(1); let ver_major = parse_captures!(1);
let ver_minor = parse_captures!(2); let ver_minor = parse_captures!(2);
ver_major >= 10 && ver_minor >= 5 ver_major >= 10 && ver_minor >= 5
} };
(version, support_returning)
} else { } else {
return Err(DbErr::Conn("Fail to parse MySQL version".to_owned())); return Err(DbErr::Conn("Fail to parse MySQL version".to_owned()));
}; };
Ok(DatabaseConnection::SqlxMySqlPoolConnection { Ok(DatabaseConnection::SqlxMySqlPoolConnection {
conn, conn,
version,
support_returning, support_returning,
}) })
} }

View File

@ -1,8 +1,10 @@
use crate::{ use crate::{
error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel, Iterable, error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel,
PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64, Iterable, PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64,
};
use sea_query::{
Alias, Expr, FromValueTuple, Iden, InsertStatement, IntoColumnRef, Query, ValueTuple,
}; };
use sea_query::{FromValueTuple, Iden, InsertStatement, IntoColumnRef, Returning, ValueTuple};
use std::{future::Future, marker::PhantomData}; use std::{future::Future, marker::PhantomData};
/// Defines a structure to perform INSERT operations in an ActiveModel /// Defines a structure to perform INSERT operations in an ActiveModel
@ -40,11 +42,10 @@ where
// so that self is dropped before entering await // so that self is dropped before entering await
let mut query = self.query; let mut query = self.query;
if db.support_returning() && <A::Entity as EntityTrait>::PrimaryKey::iter().count() > 0 { if db.support_returning() && <A::Entity as EntityTrait>::PrimaryKey::iter().count() > 0 {
query.returning(Returning::Columns( let mut returning = Query::select();
<A::Entity as EntityTrait>::PrimaryKey::iter() returning
.map(|c| c.into_column_ref()) .columns(<A::Entity as EntityTrait>::PrimaryKey::iter().map(|c| c.into_column_ref()));
.collect(), query.returning(returning);
));
} }
Inserter::<A>::new(self.primary_key, query).exec(db) Inserter::<A>::new(self.primary_key, query).exec(db)
} }
@ -147,11 +148,17 @@ where
let db_backend = db.get_database_backend(); let db_backend = db.get_database_backend();
let found = match db.support_returning() { let found = match db.support_returning() {
true => { true => {
insert_statement.returning(Returning::Columns( let mut returning = Query::select();
<A::Entity as EntityTrait>::Column::iter() returning.exprs(<A::Entity as EntityTrait>::Column::iter().map(|c| {
.map(|c| c.into_column_ref()) let col = Expr::col(c);
.collect(), let col_def = ColumnTrait::def(&c);
)); let col_type = col_def.get_column_type();
match col_type.get_enum_name() {
Some(_) => col.as_enum(Alias::new("text")),
None => col.into(),
}
}));
insert_statement.returning(returning);
SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement( SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement(
db_backend.build(&insert_statement), db_backend.build(&insert_statement),
) )

View File

@ -1,8 +1,8 @@
use crate::{ use crate::{
error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, IntoActiveModel, Iterable, error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, IntoActiveModel,
SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne, Iterable, SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne,
}; };
use sea_query::{FromValueTuple, IntoColumnRef, Returning, UpdateStatement}; use sea_query::{Alias, Expr, FromValueTuple, Query, UpdateStatement};
use std::future::Future; use std::future::Future;
/// Defines an update operation /// Defines an update operation
@ -92,11 +92,17 @@ where
{ {
match db.support_returning() { match db.support_returning() {
true => { true => {
query.returning(Returning::Columns( let mut returning = Query::select();
<A::Entity as EntityTrait>::Column::iter() returning.exprs(<A::Entity as EntityTrait>::Column::iter().map(|c| {
.map(|c| c.into_column_ref()) let col = Expr::col(c);
.collect(), let col_def = c.def();
)); let col_type = col_def.get_column_type();
match col_type.get_enum_name() {
Some(_) => col.as_enum(Alias::new("text")),
None => col.into(),
}
}));
query.returning(returning);
let db_backend = db.get_database_backend(); let db_backend = db.get_database_backend();
let found: Option<<A::Entity as EntityTrait>::Model> = let found: Option<<A::Entity as EntityTrait>::Model> =
SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement( SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement(

37
tests/returning_tests.rs Normal file
View File

@ -0,0 +1,37 @@
pub mod common;
pub use common::{features::*, setup::*, TestContext};
use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection};
#[sea_orm_macros::test]
#[cfg(any(
feature = "sqlx-mysql",
feature = "sqlx-sqlite",
feature = "sqlx-postgres"
))]
async fn main() -> Result<(), DbErr> {
let ctx = TestContext::new("returning_tests").await;
let db = &ctx.db;
match db {
#[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection { .. } => {
let version = db.db_version();
match version.as_str() {
"5.7.26" => assert!(!db.db_support_returning()),
_ => unimplemented!("Version {} is not included", version),
};
},
#[cfg(feature = "sqlx-postgres")]
DatabaseConnection::SqlxPostgresPoolConnection(_) => {
assert!(db.db_support_returning());
},
#[cfg(feature = "sqlx-sqlite")]
DatabaseConnection::SqlxSqlitePoolConnection(_) => {},
_ => unreachable!(),
}
ctx.delete().await;
Ok(())
}