#![cfg(any(feature = "postgres-sync", feature = "tokio-postgres"))]
use drizzle::core::expr::*;
use drizzle::postgres::prelude::*;
#[PostgresTable]
pub struct FkParent {
#[column(primary)]
pub id: i32,
pub name: String,
}
#[PostgresTable]
pub struct FkSetDefault {
#[column(serial, primary)]
pub id: i32,
#[column(REFERENCES = FkParent::id, ON_DELETE = SET_DEFAULT, DEFAULT = 0)]
pub parent_id: i32,
pub value: String,
}
#[derive(PostgresSchema)]
pub struct FkSetDefaultSchema {
pub fk_parent: FkParent,
pub fk_set_default: FkSetDefault,
}
#[PostgresTable]
pub struct FkActionsFirst {
#[column(serial, primary)]
pub id: i32,
#[column(ON_DELETE = CASCADE, DEFERRABLE, REFERENCES = FkParent::id)]
pub parent_id: i32,
}
#[test]
fn referential_options_may_precede_references() {
let sql = FkActionsFirst::create_table_sql();
assert!(sql.contains("ON DELETE CASCADE"), "{sql}");
assert!(sql.contains("DEFERRABLE"), "{sql}");
}
#[test]
fn test_on_delete_set_default_sql() {
let sql = FkSetDefault::create_table_sql();
assert!(
sql.contains("ON DELETE SET DEFAULT"),
"Should contain ON DELETE SET DEFAULT. Got: {}",
sql
);
}
#[drizzle::test]
fn test_set_default_sets_default_value(db: &mut TestDb<FkSetDefaultSchema>) {
let FkSetDefaultSchema {
fk_parent,
fk_set_default,
} = schema;
db.insert(fk_parent)
.values([InsertFkParent::new(0, "DefaultParent")])
.execute();
db.insert(fk_parent)
.values([InsertFkParent::new(1, "Parent1")])
.execute();
db.insert(fk_set_default)
.values([InsertFkSetDefault::new("Child1").with_parent_id(1)])
.execute();
let children: Vec<SelectFkSetDefault> = db.select(()).from(fk_set_default).all();
assert_eq!(children.len(), 1);
assert_eq!(children[0].parent_id, 1);
db.delete(fk_parent).r#where(eq(fk_parent.id, 1)).execute();
let children: Vec<SelectFkSetDefault> = db.select(()).from(fk_set_default).all();
assert_eq!(children.len(), 1, "Child should still exist");
assert_eq!(
children[0].parent_id, 0,
"Parent ID should be default (0) after SET DEFAULT"
);
}
#[PostgresTable]
pub struct FkTenant {
#[column(primary)]
pub id: i32,
}
#[PostgresTable]
pub struct FkTenantUser {
#[column(primary)]
pub tenant_id: i32,
#[column(primary)]
pub id: i32,
}
#[PostgresTable(FOREIGN_KEY(columns(tenant_id, user_id), references(FkTenantUser, tenant_id, id)))]
pub struct FkTenantDocument {
#[column(primary)]
pub id: i32,
#[column(references = FkTenant::id)]
pub tenant_id: i32,
pub user_id: i32,
}
#[derive(PostgresSchema)]
pub struct FkTenantSchema {
pub tenant: FkTenant,
pub tenant_user: FkTenantUser,
pub tenant_document: FkTenantDocument,
}
#[test]
fn foreign_keys_sharing_a_first_column_get_distinct_names() {
let sql = FkTenantDocument::create_table_sql();
assert!(
sql.contains(
"CONSTRAINT \"fk_tenant_document_tenant_id_fkey\" FOREIGN KEY (\"tenant_id\")"
),
"{sql}"
);
assert!(
sql.contains(
"CONSTRAINT \"fk_tenant_document_tenant_id_user_id_fkey\" FOREIGN KEY (\"tenant_id\", \"user_id\")"
),
"{sql}"
);
}
#[drizzle::test]
fn foreign_keys_sharing_a_first_column_are_both_enforced(db: &mut TestDb<FkTenantSchema>) {
let FkTenantSchema {
tenant,
tenant_user,
tenant_document,
} = schema;
db.insert(tenant).values([InsertFkTenant::new(1)]).execute();
db.insert(tenant_user)
.values([InsertFkTenantUser::new(1, 10)])
.execute();
db.insert(tenant_document)
.values([InsertFkTenantDocument::new(1, 1, 10)])
.execute();
let missing_user = result!(
db.insert(tenant_document)
.values([InsertFkTenantDocument::new(2, 1, 11)])
.execute()
);
assert!(missing_user.is_err(), "the composite key must be enforced");
}