Skip to content

Commit 24df49f

Browse files
committed
add expression representation and refactor memo
This commit adds the `src/expression` module which contains a very simple representation of Cascades expressions. The `Memo` trait interface and implemenation has also changed, where it now correctly detects exact match duplicates, and it does not track fingerprints for physical expressions (only logical). TODO: Add more tests. TODO: Figure out how to test in CI.
1 parent 0e54957 commit 24df49f

19 files changed

+646
-98
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ target/
1010

1111
# We will check in all code-generated entity files, as newer versions of `sea-orm-cli` might
1212
# conflict with previous versions.
13-
# **/entities
13+
# **/entities
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
2+
3+
use sea_orm::entity::prelude::*;
4+
5+
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
6+
#[sea_orm(table_name = "fingerprint")]
7+
pub struct Model {
8+
#[sea_orm(primary_key)]
9+
pub id: i32,
10+
pub logical_expression_id: i32,
11+
pub kind: i16,
12+
pub hash: i64,
13+
}
14+
15+
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
16+
pub enum Relation {
17+
#[sea_orm(
18+
belongs_to = "super::logical_expression::Entity",
19+
from = "Column::LogicalExpressionId",
20+
to = "super::logical_expression::Column::Id",
21+
on_update = "Cascade",
22+
on_delete = "Cascade"
23+
)]
24+
LogicalExpression,
25+
}
26+
27+
impl Related<super::logical_expression::Entity> for Entity {
28+
fn to() -> RelationDef {
29+
Relation::LogicalExpression.def()
30+
}
31+
}
32+
33+
impl ActiveModelBehavior for ActiveModel {}

optd-mvp/src/entities/logical_expression.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub struct Model {
88
#[sea_orm(primary_key)]
99
pub id: i32,
1010
pub group_id: i32,
11-
pub fingerprint: i64,
1211
pub kind: i16,
1312
pub data: Json,
1413
}
@@ -23,10 +22,18 @@ pub enum Relation {
2322
on_delete = "Cascade"
2423
)]
2524
CascadesGroup,
25+
#[sea_orm(has_many = "super::fingerprint::Entity")]
26+
Fingerprint,
2627
#[sea_orm(has_many = "super::logical_children::Entity")]
2728
LogicalChildren,
2829
}
2930

31+
impl Related<super::fingerprint::Entity> for Entity {
32+
fn to() -> RelationDef {
33+
Relation::Fingerprint.def()
34+
}
35+
}
36+
3037
impl Related<super::logical_children::Entity> for Entity {
3138
fn to() -> RelationDef {
3239
Relation::LogicalChildren.def()

optd-mvp/src/entities/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pub mod prelude;
44

55
pub mod cascades_group;
6+
pub mod fingerprint;
67
pub mod logical_children;
78
pub mod logical_expression;
89
pub mod physical_children;

optd-mvp/src/entities/physical_expression.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub struct Model {
88
#[sea_orm(primary_key)]
99
pub id: i32,
1010
pub group_id: i32,
11-
pub fingerprint: i64,
1211
pub kind: i16,
1312
pub data: Json,
1413
}

optd-mvp/src/entities/prelude.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
22
3-
#![allow(unused_imports)]
4-
53
pub use super::cascades_group::Entity as CascadesGroup;
4+
pub use super::fingerprint::Entity as Fingerprint;
65
pub use super::logical_children::Entity as LogicalChildren;
76
pub use super::logical_expression::Entity as LogicalExpression;
87
pub use super::physical_children::Entity as PhysicalChildren;
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//! Definition of logical expressions / relations in the Cascades query optimization framework.
2+
//!
3+
//! FIXME: All fields are placeholders, and group IDs are just represented as i32 for now.
4+
//!
5+
//! TODO figure out if each relation should be in a different submodule.
6+
7+
use crate::entities::*;
8+
use serde::{Deserialize, Serialize};
9+
10+
#[derive(Clone, Debug)]
11+
pub enum LogicalExpression {
12+
Scan(Scan),
13+
Filter(Filter),
14+
Join(Join),
15+
}
16+
17+
#[derive(Serialize, Deserialize, Clone, Debug)]
18+
pub struct Scan {
19+
table_schema: String,
20+
}
21+
22+
#[derive(Serialize, Deserialize, Clone, Debug)]
23+
pub struct Filter {
24+
child: i32,
25+
expression: String,
26+
}
27+
28+
#[derive(Serialize, Deserialize, Clone, Debug)]
29+
pub struct Join {
30+
left: i32,
31+
right: i32,
32+
expression: String,
33+
}
34+
35+
/// TODO Use a macro instead.
36+
impl From<logical_expression::Model> for LogicalExpression {
37+
fn from(value: logical_expression::Model) -> Self {
38+
match value.kind {
39+
0 => Self::Scan(
40+
serde_json::from_value(value.data)
41+
.expect("unable to deserialize data into a logical `Scan`"),
42+
),
43+
1 => Self::Filter(
44+
serde_json::from_value(value.data)
45+
.expect("Unable to deserialize data into a logical `Filter`"),
46+
),
47+
2 => Self::Join(
48+
serde_json::from_value(value.data)
49+
.expect("Unable to deserialize data into a logical `Join`"),
50+
),
51+
_ => panic!(),
52+
}
53+
}
54+
}
55+
56+
/// TODO Use a macro instead.
57+
impl From<LogicalExpression> for logical_expression::Model {
58+
fn from(value: LogicalExpression) -> logical_expression::Model {
59+
fn create_logical_expression(
60+
kind: i16,
61+
data: serde_json::Value,
62+
) -> logical_expression::Model {
63+
logical_expression::Model {
64+
id: -1,
65+
group_id: -1,
66+
kind,
67+
data,
68+
}
69+
}
70+
71+
match value {
72+
LogicalExpression::Scan(scan) => create_logical_expression(
73+
0,
74+
serde_json::to_value(scan).expect("unable to serialize logical `Scan`"),
75+
),
76+
LogicalExpression::Filter(filter) => create_logical_expression(
77+
1,
78+
serde_json::to_value(filter).expect("unable to serialize logical `Filter`"),
79+
),
80+
LogicalExpression::Join(join) => create_logical_expression(
81+
2,
82+
serde_json::to_value(join).expect("unable to serialize logical `Join`"),
83+
),
84+
}
85+
}
86+
}
87+
88+
#[cfg(test)]
89+
pub use build::*;
90+
91+
#[cfg(test)]
92+
mod build {
93+
use super::*;
94+
use crate::expression::Expression;
95+
96+
pub fn scan(table_schema: String) -> Expression {
97+
Expression::Logical(LogicalExpression::Scan(Scan { table_schema }))
98+
}
99+
100+
pub fn filter(child_group: i32, expression: String) -> Expression {
101+
Expression::Logical(LogicalExpression::Filter(Filter {
102+
child: child_group,
103+
expression,
104+
}))
105+
}
106+
107+
pub fn join(left_group: i32, right_group: i32, expression: String) -> Expression {
108+
Expression::Logical(LogicalExpression::Join(Join {
109+
left: left_group,
110+
right: right_group,
111+
expression,
112+
}))
113+
}
114+
}

optd-mvp/src/expression/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! In-memory representation of Cascades logical and physical expression / operators / relations.
2+
//!
3+
//! TODO more docs.
4+
5+
mod logical_expression;
6+
pub use logical_expression::*;
7+
8+
mod physical_expression;
9+
pub use physical_expression::*;
10+
11+
/// The representation of a Cascades expression.
12+
///
13+
/// TODO more docs.
14+
#[derive(Clone, Debug)]
15+
pub enum Expression {
16+
Logical(LogicalExpression),
17+
Physical(PhysicalExpression),
18+
}
19+
20+
/// Converts the database / JSON representation of a logical expression into an in-memory one.
21+
impl From<crate::entities::logical_expression::Model> for Expression {
22+
fn from(value: crate::entities::logical_expression::Model) -> Self {
23+
Self::Logical(value.into())
24+
}
25+
}
26+
27+
/// Converts the in-memory representation of a logical expression into the database / JSON version.
28+
///
29+
/// # Panics
30+
///
31+
/// This will panic if the [`Expression`] is [`Expression::Physical`].
32+
impl From<Expression> for crate::entities::logical_expression::Model {
33+
fn from(value: Expression) -> Self {
34+
let Expression::Logical(expr) = value else {
35+
panic!("Attempted to convert an in-memory physical expression into a logical database / JSON expression");
36+
};
37+
38+
expr.into()
39+
}
40+
}
41+
42+
/// Converts the database / JSON representation of a physical expression into an in-memory one.
43+
impl From<crate::entities::physical_expression::Model> for Expression {
44+
fn from(value: crate::entities::physical_expression::Model) -> Self {
45+
Self::Physical(value.into())
46+
}
47+
}
48+
49+
/// Converts the in-memory representation of a physical expression into the database / JSON version.
50+
///
51+
/// # Panics
52+
///
53+
/// This will panic if the [`Expression`] is [`Expression::Physical`].
54+
impl From<Expression> for crate::entities::physical_expression::Model {
55+
fn from(value: Expression) -> Self {
56+
let Expression::Physical(expr) = value else {
57+
panic!("Attempted to convert an in-memory logical expression into a physical database / JSON expression");
58+
};
59+
60+
expr.into()
61+
}
62+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//! Definition of physical expressions / operators in the Cascades query optimization framework.
2+
//!
3+
//! FIXME: All fields are placeholders, and group IDs are just represented as i32 for now.
4+
//!
5+
//! TODO figure out if each operator should be in a different submodule.
6+
7+
use crate::entities::*;
8+
use serde::{Deserialize, Serialize};
9+
10+
#[derive(Clone, Debug)]
11+
pub enum PhysicalExpression {
12+
TableScan(TableScan),
13+
Filter(PhysicalFilter),
14+
HashJoin(HashJoin),
15+
}
16+
17+
#[derive(Serialize, Deserialize, Clone, Debug)]
18+
pub struct TableScan {
19+
table_schema: String,
20+
}
21+
22+
#[derive(Serialize, Deserialize, Clone, Debug)]
23+
pub struct PhysicalFilter {
24+
child: i32,
25+
expression: String,
26+
}
27+
28+
#[derive(Serialize, Deserialize, Clone, Debug)]
29+
pub struct HashJoin {
30+
left: i32,
31+
right: i32,
32+
expression: String,
33+
}
34+
35+
/// TODO Use a macro instead.
36+
impl From<physical_expression::Model> for PhysicalExpression {
37+
fn from(value: physical_expression::Model) -> Self {
38+
match value.kind {
39+
0 => Self::TableScan(
40+
serde_json::from_value(value.data)
41+
.expect("unable to deserialize data into a physical `TableScan`"),
42+
),
43+
1 => Self::Filter(
44+
serde_json::from_value(value.data)
45+
.expect("Unable to deserialize data into a physical `Filter`"),
46+
),
47+
2 => Self::HashJoin(
48+
serde_json::from_value(value.data)
49+
.expect("Unable to deserialize data into a physical `HashJoin`"),
50+
),
51+
_ => panic!(),
52+
}
53+
}
54+
}
55+
56+
/// TODO Use a macro instead.
57+
impl From<PhysicalExpression> for physical_expression::Model {
58+
fn from(value: PhysicalExpression) -> physical_expression::Model {
59+
fn create_physical_expression(
60+
kind: i16,
61+
data: serde_json::Value,
62+
) -> physical_expression::Model {
63+
physical_expression::Model {
64+
id: -1,
65+
group_id: -1,
66+
kind,
67+
data,
68+
}
69+
}
70+
71+
match value {
72+
PhysicalExpression::TableScan(scan) => create_physical_expression(
73+
0,
74+
serde_json::to_value(scan).expect("unable to serialize physical `TableScan`"),
75+
),
76+
PhysicalExpression::Filter(filter) => create_physical_expression(
77+
1,
78+
serde_json::to_value(filter).expect("unable to serialize physical `Filter`"),
79+
),
80+
PhysicalExpression::HashJoin(join) => create_physical_expression(
81+
2,
82+
serde_json::to_value(join).expect("unable to serialize physical `HashJoin`"),
83+
),
84+
}
85+
}
86+
}
87+
88+
#[cfg(test)]
89+
pub use build::*;
90+
91+
#[cfg(test)]
92+
mod build {
93+
use super::*;
94+
use crate::expression::Expression;
95+
96+
pub fn table_scan(table_schema: String) -> Expression {
97+
Expression::Physical(PhysicalExpression::TableScan(TableScan { table_schema }))
98+
}
99+
100+
pub fn filter(child_group: i32, expression: String) -> Expression {
101+
Expression::Physical(PhysicalExpression::Filter(PhysicalFilter {
102+
child: child_group,
103+
expression,
104+
}))
105+
}
106+
107+
pub fn hash_join(left_group: i32, right_group: i32, expression: String) -> Expression {
108+
Expression::Physical(PhysicalExpression::HashJoin(HashJoin {
109+
left: left_group,
110+
right: right_group,
111+
expression,
112+
}))
113+
}
114+
}

0 commit comments

Comments
 (0)