use sea_orm_migration::schema::{integer_null, string};
use sea_orm_migration::sea_query::{ColumnDef, IntoIden};
pub fn sqlite_rowid_alias<T: IntoIden>(name: T) -> ColumnDef {
integer_null(name).primary_key().take()
}
pub fn sqlite_case_insensitive_string<T: IntoIden>(name: T) -> ColumnDef {
string(name).extra("COLLATE NOCASE").take()
}
#[cfg(test)]
mod tests {
use sea_orm_migration::async_trait::async_trait;
use sea_orm_migration::sea_orm::QueryFilter;
use sea_orm_migration::sea_orm::{
ActiveModelBehavior, ActiveModelTrait, ActiveValue::NotSet, ActiveValue::Set, ColumnTrait,
ConnectOptions, Database, DatabaseConnection, DbErr, DeriveEntityModel,
DeriveMigrationName, DerivePrimaryKey, EntityTrait, EnumIter, PrimaryKeyTrait, RelationDef,
RelationTrait,
};
use sea_orm_migration::sea_query::{self, Iden, Table};
use sea_orm_migration::{MigrationTrait, MigratorTrait, SchemaManager};
use crate::model::id::{Id, SeaOrmRepr};
use super::{sqlite_case_insensitive_string, sqlite_rowid_alias};
async fn new_memory_db() -> DatabaseConnection {
let options = ConnectOptions::new("sqlite:/tmp/db?mode=memory");
Database::connect(options).await.expect("Database::connect")
}
#[tokio::test]
async fn test_sqlite_rowid_alias() {
struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(Migration)]
}
}
#[derive(Iden)]
pub(super) enum TestTable {
Table,
Id,
}
#[derive(DeriveMigrationName)]
struct Migration;
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(TestTable::Table)
.col(sqlite_rowid_alias(TestTable::Id))
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(Debug, Clone, DeriveEntityModel)]
#[sea_orm(table_name = "test_table")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
id: Id<Model>,
}
#[derive(Debug, EnumIter)]
pub enum Relation {}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!("No RelationDef")
}
}
impl ActiveModelBehavior for ActiveModel {}
let db = new_memory_db().await;
Migrator::up(&db, None).await.expect("up");
for i in 1..=3 {
let model = ActiveModel { id: NotSet };
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
for i in 3..=3 {
let model = ActiveModel {
id: Set(Id::from_raw(i)),
};
model.delete(&db).await.expect("delete");
let model = ActiveModel { id: NotSet };
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
for i in 100..=103 {
let model = ActiveModel {
id: Set(Id::from_raw(i)),
};
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
for i in 104..=104 {
let model = ActiveModel { id: NotSet };
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
for i in SeaOrmRepr::MAX..=SeaOrmRepr::MAX {
let model = ActiveModel {
id: Set(Id::from_raw(i)),
};
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
for _ in 0..3 {
let model = ActiveModel { id: NotSet };
let model = model.insert(&db).await.expect("insert");
assert!(model.id.into_raw() > 0);
}
for i in 0..=0 {
let model = ActiveModel {
id: Set(Id::from_raw(i)),
};
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
for i in SeaOrmRepr::MIN..=(SeaOrmRepr::MIN + 3) {
let model = ActiveModel {
id: Set(Id::from_raw(i)),
};
let model = model.insert(&db).await.expect("insert");
assert_eq!(model.id.into_raw(), i);
}
}
#[tokio::test]
async fn test_sqlite_case_insensitive_string() {
struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(Migration)]
}
}
#[derive(Iden)]
pub(super) enum TestTable {
Table,
Id,
CiStr,
}
#[derive(DeriveMigrationName)]
struct Migration;
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(TestTable::Table)
.col(sqlite_rowid_alias(TestTable::Id))
.col(sqlite_case_insensitive_string(TestTable::CiStr))
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(Debug, Clone, DeriveEntityModel)]
#[sea_orm(table_name = "test_table")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
id: i64,
ci_str: String,
}
#[derive(Debug, EnumIter)]
pub enum Relation {}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
panic!("No RelationDef")
}
}
impl ActiveModelBehavior for ActiveModel {}
let db = new_memory_db().await;
Migrator::up(&db, None).await.expect("up");
let i = 50;
{
let model = ActiveModel {
id: Set(i),
ci_str: Set("abcd".to_owned()),
};
model.insert(&db).await.expect("insert");
}
{
let model = Entity::find()
.filter(Column::CiStr.contains("ABCD"))
.one(&db)
.await
.expect("find by case insensitive string")
.expect("find by case insensitive string");
assert_eq!(model.id, i);
assert_eq!(model.ci_str, "abcd");
}
}
}