Allow for creation of indexes for PostgeSQL and SQLite (#593)

* Allow for creation of indexes for PostgeSQL and SQLite

PostgreSQL and SQLite do not allow creation of general indexes within a `CREATE TABLE` statement, so a method is required to generate `CREATE INDEX` statements for these.

`create_table_from_entity` avoids creating invalid statements for non-MySQL backends,
forcing uses to explicitly run `create_index_from_entity`.  Ideally creating indexes would be removed from `create_table_from_entity` entirely, but this would introduce a breaking change for MySQL use.

* Remove index creation from create_table_from_entity

Use `create_index_from_entity` for all index creation for consistency across all backends.  This is a backwards incompatible change, affecting those using MySQL backend when creating the schema only.

* Revert change to join_8 test after migration to new indexes entity
This commit is contained in:
Nick Burrett 2022-03-20 16:45:08 +00:00 committed by GitHub
parent fe1f763feb
commit a09790ef81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 175 additions and 15 deletions

View File

@ -5,6 +5,7 @@ use crate::{
use sea_query::{ use sea_query::{
extension::postgres::{Type, TypeCreateStatement}, extension::postgres::{Type, TypeCreateStatement},
Alias, ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement, Alias, ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement,
IndexCreateStatement,
}; };
impl Schema { impl Schema {
@ -24,13 +25,22 @@ impl Schema {
create_enum_from_entity(entity, self.backend) create_enum_from_entity(entity, self.backend)
} }
/// Creates a table from an Entity. See [TableCreateStatement] for more details /// Creates a table from an Entity. See [TableCreateStatement] for more details.
pub fn create_table_from_entity<E>(&self, entity: E) -> TableCreateStatement pub fn create_table_from_entity<E>(&self, entity: E) -> TableCreateStatement
where where
E: EntityTrait, E: EntityTrait,
{ {
create_table_from_entity(entity, self.backend) create_table_from_entity(entity, self.backend)
} }
/// Creates the indexes from an Entity, returning an empty Vec if there are none
/// to create. See [IndexCreateStatement] for more details
pub fn create_index_from_entity<E>(&self, entity: E) -> Vec<IndexCreateStatement>
where
E: EntityTrait,
{
create_index_from_entity(entity, self.backend)
}
} }
pub(crate) fn create_enum_from_active_enum<A>(backend: DbBackend) -> TypeCreateStatement pub(crate) fn create_enum_from_active_enum<A>(backend: DbBackend) -> TypeCreateStatement
@ -77,6 +87,31 @@ where
vec vec
} }
pub(crate) fn create_index_from_entity<E>(entity: E, _backend: DbBackend) -> Vec<IndexCreateStatement>
where
E: EntityTrait,
{
let mut vec = Vec::new();
for column in E::Column::iter() {
let column_def = column.def();
if !column_def.indexed {
continue;
}
let stmt = Index::create()
.name(&format!(
"idx-{}-{}",
entity.to_string(),
column.to_string()
))
.table(entity)
.col(column)
.to_owned();
vec.push(stmt)
}
vec
}
pub(crate) fn create_table_from_entity<E>(entity: E, backend: DbBackend) -> TableCreateStatement pub(crate) fn create_table_from_entity<E>(entity: E, backend: DbBackend) -> TableCreateStatement
where where
E: EntityTrait, E: EntityTrait,
@ -116,18 +151,6 @@ where
} }
} }
} }
if orm_column_def.indexed {
stmt.index(
Index::create()
.name(&format!(
"idx-{}-{}",
entity.to_string(),
column.to_string()
))
.table(entity)
.col(column),
);
}
stmt.col(&mut column_def); stmt.col(&mut column_def);
} }
@ -206,12 +229,12 @@ mod tests {
let schema = Schema::new(builder); let schema = Schema::new(builder);
assert_eq!( assert_eq!(
builder.build(&schema.create_table_from_entity(CakeFillingPrice)), builder.build(&schema.create_table_from_entity(CakeFillingPrice)),
builder.build(&get_stmt().table(CakeFillingPrice.table_ref()).to_owned()) builder.build(&get_cake_filling_price_stmt().table(CakeFillingPrice.table_ref()).to_owned())
); );
} }
} }
fn get_stmt() -> TableCreateStatement { fn get_cake_filling_price_stmt() -> TableCreateStatement {
Table::create() Table::create()
.col( .col(
ColumnDef::new(cake_filling_price::Column::CakeId) ColumnDef::new(cake_filling_price::Column::CakeId)
@ -247,4 +270,73 @@ mod tests {
) )
.to_owned() .to_owned()
} }
#[test]
fn test_create_index_from_entity_table_ref() {
for builder in [DbBackend::MySql, DbBackend::Postgres, DbBackend::Sqlite] {
let schema = Schema::new(builder);
assert_eq!(
builder.build(&schema.create_table_from_entity(indexes::Entity)),
builder.build(&get_indexes_stmt().table(indexes::Entity.table_ref()).to_owned())
);
let stmts = schema.create_index_from_entity(indexes::Entity);
assert_eq!(stmts.len(), 2);
let idx: IndexCreateStatement = Index::create()
.name("idx-indexes-index1_attr")
.table(indexes::Entity)
.col(indexes::Column::Index1Attr)
.to_owned();
assert_eq!(
builder.build(&stmts[0]),
builder.build(&idx)
);
let idx: IndexCreateStatement = Index::create()
.name("idx-indexes-index2_attr")
.table(indexes::Entity)
.col(indexes::Column::Index2Attr)
.to_owned();
assert_eq!(
builder.build(&stmts[1]),
builder.build(&idx)
);
}
}
fn get_indexes_stmt() -> TableCreateStatement {
Table::create()
.col(
ColumnDef::new(indexes::Column::IndexesId)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(
ColumnDef::new(indexes::Column::UniqueAttr)
.integer()
.not_null()
.unique_key(),
)
.col(
ColumnDef::new(indexes::Column::Index1Attr)
.integer()
.not_null(),
)
.col(
ColumnDef::new(indexes::Column::Index2Attr)
.integer()
.not_null()
.unique_key(),
)
.to_owned()
}
} }

67
src/tests_cfg/indexes.rs Normal file
View File

@ -0,0 +1,67 @@
//! An entity definition for testing table index creation.
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn schema_name(&self) -> Option<&str> {
Some("public")
}
fn table_name(&self) -> &str {
"indexes"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub indexes_id: i32,
pub unique_attr: i32,
pub index1_attr: i32,
pub index2_attr: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
IndexesId,
UniqueAttr,
Index1Attr,
Index2Attr,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
IndexesId,
}
impl PrimaryKeyTrait for PrimaryKey {
type ValueType = i32;
fn auto_increment() -> bool {
true
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation { }
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::IndexesId => ColumnType::Integer.def(),
Self::UniqueAttr => ColumnType::Integer.def().unique(),
Self::Index1Attr => ColumnType::Integer.def().indexed(),
Self::Index2Attr => ColumnType::Integer.def().indexed().unique(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef { panic!() }
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -9,6 +9,7 @@ pub mod filling;
pub mod fruit; pub mod fruit;
pub mod rust_keyword; pub mod rust_keyword;
pub mod vendor; pub mod vendor;
pub mod indexes;
pub use cake::Entity as Cake; pub use cake::Entity as Cake;
pub use cake_expanded::Entity as CakeExpanded; pub use cake_expanded::Entity as CakeExpanded;