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

View File

@ -30,7 +30,7 @@ futures-util = { version = "^0.3" }
log = { version = "^0.4", optional = true }
rust_decimal = { version = "^1", 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"] }
serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1", optional = true }

View File

@ -19,9 +19,11 @@ pub enum DatabaseConnection {
/// Create a MYSQL database connection and pool
#[cfg(feature = "sqlx-mysql")]
SqlxMySqlPoolConnection {
/// A SQLx MySQL pool
/// The SQLx MySQL pool
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,
},
/// Create a PostgreSQL database connection and pool
@ -226,7 +228,7 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection {
fn support_returning(&self) -> bool {
match self {
#[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection { .. } => false,
DatabaseConnection::SqlxMySqlPoolConnection { support_returning, .. } => *support_returning,
#[cfg(feature = "sqlx-postgres")]
DatabaseConnection::SqlxPostgresPoolConnection(_) => true,
#[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 {
/// Check if the URI is the same as the specified database backend.
/// Returns true if they match.

View File

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

View File

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

View File

@ -1,8 +1,10 @@
use crate::{
error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel, Iterable,
PrimaryKeyTrait, SelectModel, SelectorRaw, Statement, TryFromU64,
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, Insert, IntoActiveModel,
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};
/// Defines a structure to perform INSERT operations in an ActiveModel
@ -40,11 +42,10 @@ where
// so that self is dropped before entering await
let mut query = self.query;
if db.support_returning() && <A::Entity as EntityTrait>::PrimaryKey::iter().count() > 0 {
query.returning(Returning::Columns(
<A::Entity as EntityTrait>::PrimaryKey::iter()
.map(|c| c.into_column_ref())
.collect(),
));
let mut returning = Query::select();
returning
.columns(<A::Entity as EntityTrait>::PrimaryKey::iter().map(|c| c.into_column_ref()));
query.returning(returning);
}
Inserter::<A>::new(self.primary_key, query).exec(db)
}
@ -147,11 +148,17 @@ where
let db_backend = db.get_database_backend();
let found = match db.support_returning() {
true => {
insert_statement.returning(Returning::Columns(
<A::Entity as EntityTrait>::Column::iter()
.map(|c| c.into_column_ref())
.collect(),
));
let mut returning = Query::select();
returning.exprs(<A::Entity as EntityTrait>::Column::iter().map(|c| {
let col = Expr::col(c);
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(
db_backend.build(&insert_statement),
)

View File

@ -1,8 +1,8 @@
use crate::{
error::*, ActiveModelTrait, ConnectionTrait, EntityTrait, IntoActiveModel, Iterable,
SelectModel, SelectorRaw, Statement, UpdateMany, UpdateOne,
error::*, ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, IntoActiveModel,
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;
/// Defines an update operation
@ -92,11 +92,17 @@ where
{
match db.support_returning() {
true => {
query.returning(Returning::Columns(
<A::Entity as EntityTrait>::Column::iter()
.map(|c| c.into_column_ref())
.collect(),
));
let mut returning = Query::select();
returning.exprs(<A::Entity as EntityTrait>::Column::iter().map(|c| {
let col = Expr::col(c);
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 found: Option<<A::Entity as EntityTrait>::Model> =
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(())
}