#![allow(unused_imports, dead_code)]
pub mod common;
use crate::common::TestContext;
use sea_orm::{
DatabaseBackend, DatabaseConnection, DbErr, Statement,
entity::*,
query::*,
sea_query::{Condition, Expr, Query, SelectStatement},
};
mod item_v1 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_item")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub name: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod product_v1 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_product")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod product_v2 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_product")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub sku: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod order_v1 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_order")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub ref_no: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod order_v2 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_order")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub ref_no: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod user_v1 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_user")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub email: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod user_v2 {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "sync_user")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(unique)]
pub email: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
mod custom_schema_entity {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(schema_name = "test_schema_2952", table_name = "sync_custom_schema")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
}
impl ActiveModelBehavior for ActiveModel {}
}
#[sea_orm_macros::test]
fn test_sync_unique_column_no_drop() -> Result<(), DbErr> {
let ctx = TestContext::new("test_sync_unique_column_no_drop");
let db = &ctx.db;
#[cfg(feature = "schema-sync")]
{
db.get_schema_builder().register(item_v1::Entity).sync(db)?;
db.get_schema_builder().register(item_v1::Entity).sync(db)?;
#[cfg(feature = "sqlx-postgres")]
assert!(
pg_index_exists(db, "sync_item", "sync_item_name_key")?,
"unique index on `sync_item.name` should still exist after repeated sync"
);
}
Ok(())
}
#[sea_orm_macros::test]
#[cfg(not(any(feature = "sqlx-sqlite", feature = "rusqlite")))]
fn test_sync_add_unique_column_no_drop() -> Result<(), DbErr> {
let ctx = TestContext::new("test_sync_add_unique_column_no_drop");
let db = &ctx.db;
#[cfg(feature = "schema-sync")]
{
db.get_schema_builder()
.register(product_v1::Entity)
.sync(db)?;
db.get_schema_builder()
.register(product_v2::Entity)
.sync(db)?;
db.get_schema_builder()
.register(product_v2::Entity)
.sync(db)?;
#[cfg(feature = "sqlx-postgres")]
assert!(
pg_index_exists(db, "sync_product", "sync_product_sku_key")?,
"unique index on `sync_product.sku` should still exist after repeated sync"
);
}
Ok(())
}
#[sea_orm_macros::test]
fn test_sync_make_existing_column_unique() -> Result<(), DbErr> {
let ctx = TestContext::new("test_sync_make_existing_column_unique");
let db = &ctx.db;
#[cfg(feature = "schema-sync")]
{
db.get_schema_builder().register(user_v1::Entity).sync(db)?;
db.get_schema_builder().register(user_v2::Entity).sync(db)?;
db.get_schema_builder().register(user_v2::Entity).sync(db)?;
#[cfg(feature = "sqlx-postgres")]
assert!(
pg_index_exists(db, "sync_user", "idx-sync_user-email")?,
"unique index on `sync_user.email` should be created when column is made unique"
);
}
Ok(())
}
#[sea_orm_macros::test]
#[cfg(feature = "sqlx-postgres")]
fn test_sync_drop_unique_constraint() -> Result<(), DbErr> {
let ctx = TestContext::new("test_sync_drop_unique_constraint");
let db = &ctx.db;
#[cfg(feature = "schema-sync")]
{
db.get_schema_builder()
.register(order_v1::Entity)
.sync(db)?;
assert!(
pg_index_exists(db, "sync_order", "sync_order_ref_no_key")?,
"unique constraint should exist after first sync"
);
db.get_schema_builder()
.register(order_v2::Entity)
.sync(db)?;
assert!(
!pg_index_exists(db, "sync_order", "sync_order_ref_no_key")?,
"unique constraint should be gone after second sync"
);
}
Ok(())
}
#[sea_orm_macros::test]
#[cfg(feature = "sqlx-postgres")]
fn test_sync_non_default_schema() -> Result<(), DbErr> {
let ctx = TestContext::new("test_sync_non_default_schema");
let db = &ctx.db;
#[cfg(feature = "schema-sync")]
{
db.execute_raw(Statement::from_string(
DatabaseBackend::Postgres,
"CREATE SCHEMA IF NOT EXISTS test_schema_2952".to_owned(),
))?;
db.get_schema_builder()
.register(custom_schema_entity::Entity)
.sync(db)?;
assert!(
pg_table_exists_in_schema(db, "test_schema_2952", "sync_custom_schema")?,
"table should exist in schema `test_schema_2952`"
);
assert!(
!pg_table_exists_in_schema(db, "public", "sync_custom_schema")?,
"table should NOT exist in schema `public`"
);
db.get_schema_builder()
.register(custom_schema_entity::Entity)
.sync(db)?;
}
Ok(())
}
#[cfg(feature = "sqlx-postgres")]
fn pg_table_exists_in_schema_query(schema: &str, table: &str) -> SelectStatement {
Query::select()
.expr(Expr::cust("COUNT(*) > 0"))
.from(("information_schema", "tables"))
.cond_where(
Condition::all()
.add(Expr::col("table_schema").eq(schema))
.add(Expr::col("table_name").eq(table)),
)
.to_owned()
}
#[cfg(feature = "sqlx-postgres")]
#[test]
fn pg_table_exists_in_schema_query_qualifies_information_schema() {
use sea_orm::sea_query::PostgresQueryBuilder;
assert_eq!(
pg_table_exists_in_schema_query("test_schema_2952", "sync_custom_schema")
.to_string(PostgresQueryBuilder),
r#"SELECT COUNT(*) > 0 FROM "information_schema"."tables" WHERE "table_schema" = 'test_schema_2952' AND "table_name" = 'sync_custom_schema'"#,
);
}
#[cfg(feature = "sqlx-postgres")]
fn pg_table_exists_in_schema(
db: &DatabaseConnection,
schema: &str,
table: &str,
) -> Result<bool, DbErr> {
db.query_one(&pg_table_exists_in_schema_query(schema, table))?
.unwrap()
.try_get_by_index(0)
.map_err(DbErr::from)
}
#[cfg(feature = "sqlx-postgres")]
fn pg_index_exists(db: &DatabaseConnection, table: &str, index: &str) -> Result<bool, DbErr> {
db.query_one(
Query::select()
.expr(Expr::cust("COUNT(*) > 0"))
.from("pg_indexes")
.cond_where(
Condition::all()
.add(Expr::cust("schemaname = CURRENT_SCHEMA()"))
.add(Expr::col("tablename").eq(table))
.add(Expr::col("indexname").eq(index)),
),
)?
.unwrap()
.try_get_by_index(0)
.map_err(DbErr::from)
}