diff --git a/optd-mvp/src/entities/cascades_group.rs b/optd-mvp/src/entities/cascades_group.rs new file mode 100644 index 0000000..9c2ba83 --- /dev/null +++ b/optd-mvp/src/entities/cascades_group.rs @@ -0,0 +1,76 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cascades_group")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub winner: Option, + pub cost: Option, + pub is_optimized: bool, + pub parent_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "Entity", + from = "Column::ParentId", + to = "Column::Id", + on_update = "Cascade", + on_delete = "SetNull" + )] + SelfRef, + #[sea_orm(has_many = "super::logical_children::Entity")] + LogicalChildren, + #[sea_orm(has_many = "super::logical_expression::Entity")] + LogicalExpression, + #[sea_orm(has_many = "super::physical_children::Entity")] + PhysicalChildren, + #[sea_orm( + belongs_to = "super::physical_expression::Entity", + from = "Column::Winner", + to = "super::physical_expression::Column::Id", + on_update = "Cascade", + on_delete = "SetNull" + )] + PhysicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PhysicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::logical_children::Relation::LogicalExpression.def() + } + fn via() -> Option { + Some(super::logical_children::Relation::CascadesGroup.def().rev()) + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::physical_children::Relation::PhysicalExpression.def() + } + fn via() -> Option { + Some( + super::physical_children::Relation::CascadesGroup + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/logical_children.rs b/optd-mvp/src/entities/logical_children.rs new file mode 100644 index 0000000..120641f --- /dev/null +++ b/optd-mvp/src/entities/logical_children.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "logical_children")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub logical_expression_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub group_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm( + belongs_to = "super::logical_expression::Entity", + from = "Column::GroupId", + to = "super::logical_expression::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + LogicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CascadesGroup.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalExpression.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/logical_expression.rs b/optd-mvp/src/entities/logical_expression.rs new file mode 100644 index 0000000..1e85d1d --- /dev/null +++ b/optd-mvp/src/entities/logical_expression.rs @@ -0,0 +1,49 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "logical_expression")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub group_id: i32, + pub fingerprint: i64, + pub kind: i16, + pub data: Json, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm(has_many = "super::logical_children::Entity")] + LogicalChildren, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::logical_children::Relation::CascadesGroup.def() + } + fn via() -> Option { + Some( + super::logical_children::Relation::LogicalExpression + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/mod.rs b/optd-mvp/src/entities/mod.rs new file mode 100644 index 0000000..701abe4 --- /dev/null +++ b/optd-mvp/src/entities/mod.rs @@ -0,0 +1,9 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +pub mod prelude; + +pub mod cascades_group; +pub mod logical_children; +pub mod logical_expression; +pub mod physical_children; +pub mod physical_expression; diff --git a/optd-mvp/src/entities/physical_children.rs b/optd-mvp/src/entities/physical_children.rs new file mode 100644 index 0000000..d8f9db0 --- /dev/null +++ b/optd-mvp/src/entities/physical_children.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "physical_children")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub physical_expression_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub group_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm( + belongs_to = "super::physical_expression::Entity", + from = "Column::PhysicalExpressionId", + to = "super::physical_expression::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + PhysicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CascadesGroup.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PhysicalExpression.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/physical_expression.rs b/optd-mvp/src/entities/physical_expression.rs new file mode 100644 index 0000000..2d9a2ae --- /dev/null +++ b/optd-mvp/src/entities/physical_expression.rs @@ -0,0 +1,49 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "physical_expression")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub group_id: i32, + pub fingerprint: i64, + pub kind: i16, + pub data: Json, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm(has_many = "super::physical_children::Entity")] + PhysicalChildren, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PhysicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::physical_children::Relation::CascadesGroup.def() + } + fn via() -> Option { + Some( + super::physical_children::Relation::PhysicalExpression + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/prelude.rs b/optd-mvp/src/entities/prelude.rs new file mode 100644 index 0000000..0b8c910 --- /dev/null +++ b/optd-mvp/src/entities/prelude.rs @@ -0,0 +1,9 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +#![allow(unused_imports)] + +pub use super::cascades_group::Entity as CascadesGroup; +pub use super::logical_children::Entity as LogicalChildren; +pub use super::logical_expression::Entity as LogicalExpression; +pub use super::physical_children::Entity as PhysicalChildren; +pub use super::physical_expression::Entity as PhysicalExpression; diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs b/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs new file mode 100644 index 0000000..3a0e7d0 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs @@ -0,0 +1,123 @@ +//! An entity representing a group / equivalence class in the Cascades framework. +//! +//! Quoted from the Microsoft article _Extensible query optimizers in practice_: +//! +//! > In the memo, each class of equivalent expressions is called an equivalent class or a group, +//! > and all equivalent expressions within the class are called group expressions or simply +//! > expressions. +//! +//! A Cascades group is defined as a class of equivalent logical or physical expressions. The +//! Cascades framework uses these groups as a way of storing the best query sub-plans for use in the +//! dynamic programming search algorithm. +//! +//! For example, a Cascades group could be the set of expressions containing the logical expressions +//! `Join(A, B)` and `Join(B, A)`, as well as the physical expressions `HashJoin(A, B)` and +//! `NestedLoopJoin(B, A)`. +//! +//! # Columns +//! +//! Each group is assigned a monotonically-increasing (unique) ID. This ID will be important since +//! there are many foreign key references from other tables to `cascades_group`. +//! +//! We additionally store a `latest_winner` foreign key reference to a physical expression. See +//! the [section](#best-physical-plan-winner) below for more details. +//! +//! Finally, we store an `is_optimized` flag that is used for quickly determining the state of +//! optimization for this group during the dynamic programming search. +//! +//! # Entity Relationships +//! +//! ### Child Expressions (Logical and Physical) +//! +//! To retrieve all of a `cascades_group`'s equivalent expressions, you must query the +//! [`logical_expression`] or the [`physical_expression`] entities via their foreign keys to +//! `cascades_group`. The relationship between [`logical_expression`] and `cascades_group` is +//! many-to-one, and the exact same many-to-one relationship is held for [`physical_expression`] to +//! `cascades_group`. +//! +//! ### Parent Expressions (Logical and Physical) +//! +//! Additionally, each logical or physical expression can have any number of `cascades_group`s as +//! children, and a group can be a child of any expression. Thus, `cascades_group` additionally has +//! a many-to-many relationship with [`logical_expression`] and [`physical_expression`] via the +//! [`logical_children`] and [`physical_children`] entities. +//! +//! To reiterate, `cascades_group` has **both** a one-to-many **and** a many-to-many relationship +//! with both [`logical_expression`] and [`physical_expression`]. This is due to groups being both +//! parents and children of expressions. +//! +//! ### Best Physical Plan (Winner) +//! +//! The `cascades_group` entity also stores a `latest_winner` _nullable_ foreign key reference to +//! a physical expression. This represents the most recent best query plan we have computed. The +//! reason it is nullable is because we may not have come up with any best query plan yet. +//! +//! ### Logical Properties +//! +//! FIXME: Add a logical properties table. +//! +//! Lastly, each `cascades_group` record will have a set of logical properties store in the +//! `logical_property` entity, where there is an many-to-one relationship from +//! `logical_property` to `cascades_group`. Note that we do not store physical properties directly +//! on the `cascades_group`, but rather we store them for each [`physical_expression`] record. +//! +//! [`logical_expression`]: super::logical_expression +//! [`physical_expression`]: super::physical_expression +//! [`logical_children`]: super::logical_children +//! [`physical_children`]: super::physical_children +//! `logical_property`: super::logical_property + +use crate::migrator::memo::physical_expression::PhysicalExpression; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum CascadesGroup { + Table, + Id, + Winner, + Cost, + IsOptimized, + ParentId, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(CascadesGroup::Table) + .if_not_exists() + .col(pk_auto(CascadesGroup::Id)) + .col(integer_null(CascadesGroup::Winner)) + .col(big_unsigned_null(CascadesGroup::Cost)) + .foreign_key( + ForeignKey::create() + .from(CascadesGroup::Table, CascadesGroup::Winner) + .to(PhysicalExpression::Table, PhysicalExpression::Id) + .on_delete(ForeignKeyAction::SetNull) + .on_update(ForeignKeyAction::Cascade), + ) + .col(boolean(CascadesGroup::IsOptimized)) + .col(integer_null(CascadesGroup::ParentId)) + .foreign_key( + ForeignKey::create() + .from(CascadesGroup::Table, CascadesGroup::ParentId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::SetNull) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(CascadesGroup::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs new file mode 100644 index 0000000..d0835f4 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs @@ -0,0 +1,65 @@ +//! An entity representing the [`cascades_group`] children of every [`logical_expression`]. +//! +//! Formally, this entity is a junction which allows us to represent a many-to-many relationship +//! between [`logical_expression`] and [`cascades_group`]. Expressions can have any number of child +//! groups, and every group can be a child of many different expressions, hence the many-to-many +//! relationship. +//! +//! See [`cascades_group`] for more details. +//! +//! [`cascades_group`]: super::cascades_group +//! [`logical_expression`]: super::logical_expression + +use crate::migrator::memo::{cascades_group::CascadesGroup, logical_expression::LogicalExpression}; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum LogicalChildren { + Table, + LogicalExpressionId, + GroupId, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(LogicalChildren::Table) + .if_not_exists() + .col(integer(LogicalChildren::LogicalExpressionId)) + .col(integer(LogicalChildren::GroupId)) + .primary_key( + Index::create() + .col(LogicalChildren::LogicalExpressionId) + .col(LogicalChildren::GroupId), + ) + .foreign_key( + ForeignKey::create() + .from(LogicalChildren::Table, LogicalChildren::GroupId) + .to(LogicalExpression::Table, LogicalExpression::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .from(LogicalChildren::Table, LogicalChildren::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(LogicalChildren::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs new file mode 100644 index 0000000..3682032 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs @@ -0,0 +1,84 @@ +//! An entity representing a logical plan expression in the Cascades framework. +//! +//! Quoted from the Microsoft article _Extensible query optimizers in practice_: +//! +//! > A logical expression is defined as a tree of logical operators, and corresponds to a +//! > relational algebraic expression. +//! +//! In the Cascades query optimization framework, the memo table stores equivalence classes of +//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both +//! `logical_expression`s and [`physical_expression`]s. +//! +//! Optimization starts by "exploring" equivalent logical expressions within a group. For example, +//! the logical expressions `Join(A, B)` and `Join(B, A)` are contained in the same group. The +//! logical expressions are defined as a `Join` operator with the groups representing a scan of +//! table `A` and a scan of table `B` as its children. +//! +//! # Columns +//! +//! Each `logical_expression` has a unique primary key ID, but it holds little importance other than +//! helping distinguish between two different expressions. +//! +//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint +//! value that can be used to efficiently check equality between two potentially equivalent logical +//! expressions (hash-consing). See ???FIXME??? for more information on expression fingerprints. +//! +//! Finally, since there are many different types of operators, we store a variant tag and a data +//! column as JSON to represent the semi-structured data fields of logical operators. +//! +//! # Entity Relationships +//! +//! The only relationship that `logical_expression` has is to [`cascades_group`]. It has **both** a +//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more +//! details about this in the module-level documentation for [`cascades_group`]. +//! +//! [`cascades_group`]: super::cascades_group +//! [`physical_expression`]: super::physical_expression + +use crate::migrator::memo::cascades_group::CascadesGroup; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum LogicalExpression { + Table, + Id, + GroupId, + Fingerprint, + Kind, + Data, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(LogicalExpression::Table) + .if_not_exists() + .col(pk_auto(LogicalExpression::Id)) + .col(integer(LogicalExpression::GroupId)) + .foreign_key( + ForeignKey::create() + .from(LogicalExpression::Table, LogicalExpression::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(big_unsigned(LogicalExpression::Fingerprint)) + .col(small_integer(LogicalExpression::Kind)) + .col(json(LogicalExpression::Data)) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(LogicalExpression::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs new file mode 100644 index 0000000..3983f0c --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs @@ -0,0 +1,70 @@ +//! An entity representing the [`cascades_group`] children of every [`physical_expression`]. +//! +//! Formally, this entity is a junction which allows us to represent a many-to-many relationship +//! between [`physical_expression`] and [`cascades_group`]. Expressions can have any number of child +//! groups, and every group can be a child of many different expressions, hence the many-to-many +//! relationship. +//! +//! See [`cascades_group`] for more details. +//! +//! [`cascades_group`]: super::cascades_group +//! [`physical_expression`]: super::physical_expression + +use crate::migrator::memo::{ + cascades_group::CascadesGroup, physical_expression::PhysicalExpression, +}; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum PhysicalChildren { + Table, + PhysicalExpressionId, + GroupId, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(PhysicalChildren::Table) + .if_not_exists() + .col(integer(PhysicalChildren::PhysicalExpressionId)) + .col(integer(PhysicalChildren::GroupId)) + .primary_key( + Index::create() + .col(PhysicalChildren::PhysicalExpressionId) + .col(PhysicalChildren::GroupId), + ) + .foreign_key( + ForeignKey::create() + .from( + PhysicalChildren::Table, + PhysicalChildren::PhysicalExpressionId, + ) + .to(PhysicalExpression::Table, PhysicalExpression::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .from(PhysicalChildren::Table, PhysicalChildren::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(PhysicalChildren::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs new file mode 100644 index 0000000..7653112 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs @@ -0,0 +1,85 @@ +//! An entity representing a logical plan expression in the Cascades framework. +//! +//! Quoted from the Microsoft article _Extensible query optimizers in practice_: +//! +//! > A physical expression is a tree of physical operators, which is also referred to as the +//! > _physical plan_ or simply _plan_. +//! +//! In the Cascades query optimization framework, the memo table stores equivalence classes of +//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both +//! [`logical_expression`]s and `physical_expression`s. +//! +//! Optimization starts by exploring equivalent logical expressions within a group, and then it +//! proceeds to implement / optimize those logical operators into physical operators. For example, +//! the logical expression `Join(A, B)` could be implemented into a `HashJoin(A, B)` or a +//! `NestedLoopJoin(A, B)`, and both of these new physical expressions would be contained in the +//! same group. +//! +//! # Columns +//! +//! Each `physical_expression` has a unique primary key ID, and other tables will store a foreign +//! key reference to a specific `physical_expression`s. +//! +//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint +//! value that can be used to efficiently check equality between two potentially equivalent physical +//! expressions (hash-consing). See ???FIXME??? for more information on expression fingerprints. +//! +//! Finally, since there are many different types of operators, we store a variant tag and a data +//! column as JSON to represent the semi-structured data fields of logical operators. +//! +//! # Entity Relationships +//! +//! The only relationship that `physical_expression` has is to [`cascades_group`]. It has **both** a +//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more +//! details about this in the module-level documentation for [`cascades_group`]. +//! +//! [`cascades_group`]: super::cascades_group +//! [`logical_expression`]: super::logical_expression + +use crate::migrator::memo::cascades_group::CascadesGroup; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum PhysicalExpression { + Table, + Id, + GroupId, + Fingerprint, + Kind, + Data, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(PhysicalExpression::Table) + .if_not_exists() + .col(pk_auto(PhysicalExpression::Id)) + .col(integer(PhysicalExpression::GroupId)) + .foreign_key( + ForeignKey::create() + .from(PhysicalExpression::Table, PhysicalExpression::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(big_unsigned(PhysicalExpression::Fingerprint)) + .col(small_integer(PhysicalExpression::Kind)) + .col(json(PhysicalExpression::Data)) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(PhysicalExpression::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/mod.rs b/optd-mvp/src/migrator/memo/mod.rs new file mode 100644 index 0000000..8ed9390 --- /dev/null +++ b/optd-mvp/src/migrator/memo/mod.rs @@ -0,0 +1,14 @@ +//! Entities related to the memo table used for dynamic programming in the Cascades query +//! optimization framework. + +pub(crate) mod m20241127_000001_cascades_group; +pub(crate) mod m20241127_000001_logical_children; +pub(crate) mod m20241127_000001_logical_expression; +pub(crate) mod m20241127_000001_physical_children; +pub(crate) mod m20241127_000001_physical_expression; + +pub(crate) use m20241127_000001_cascades_group as cascades_group; +pub(crate) use m20241127_000001_logical_children as logical_children; +pub(crate) use m20241127_000001_logical_expression as logical_expression; +pub(crate) use m20241127_000001_physical_children as physical_children; +pub(crate) use m20241127_000001_physical_expression as physical_expression; diff --git a/optd-mvp/src/migrator/mod.rs b/optd-mvp/src/migrator/mod.rs new file mode 100644 index 0000000..179c406 --- /dev/null +++ b/optd-mvp/src/migrator/mod.rs @@ -0,0 +1,18 @@ +use sea_orm_migration::prelude::*; + +mod memo; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(memo::cascades_group::Migration), + Box::new(memo::logical_expression::Migration), + Box::new(memo::logical_children::Migration), + Box::new(memo::physical_expression::Migration), + Box::new(memo::physical_children::Migration), + ] + } +}