Skip to content

Commit 2856496

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 2856496

19 files changed

+655
-99
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: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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+
//! FIXME: Representation needs to know how to "rewrite" child group IDs to whatever a fingerprint
5+
//! will need.
6+
//!
7+
//! TODO figure out if each relation should be in a different submodule.
8+
//! TODO This entire file is a WIP.
9+
10+
use crate::entities::*;
11+
use serde::{Deserialize, Serialize};
12+
13+
#[derive(Clone, Debug)]
14+
pub enum LogicalExpression {
15+
Scan(Scan),
16+
Filter(Filter),
17+
Join(Join),
18+
}
19+
20+
#[derive(Serialize, Deserialize, Clone, Debug)]
21+
pub struct Scan {
22+
table_schema: String,
23+
}
24+
25+
#[derive(Serialize, Deserialize, Clone, Debug)]
26+
pub struct Filter {
27+
child: i32,
28+
expression: String,
29+
}
30+
31+
#[derive(Serialize, Deserialize, Clone, Debug)]
32+
pub struct Join {
33+
left: i32,
34+
right: i32,
35+
expression: String,
36+
}
37+
38+
/// TODO Use a macro instead.
39+
impl From<logical_expression::Model> for LogicalExpression {
40+
fn from(value: logical_expression::Model) -> Self {
41+
match value.kind {
42+
0 => Self::Scan(
43+
serde_json::from_value(value.data)
44+
.expect("unable to deserialize data into a logical `Scan`"),
45+
),
46+
1 => Self::Filter(
47+
serde_json::from_value(value.data)
48+
.expect("Unable to deserialize data into a logical `Filter`"),
49+
),
50+
2 => Self::Join(
51+
serde_json::from_value(value.data)
52+
.expect("Unable to deserialize data into a logical `Join`"),
53+
),
54+
_ => panic!(),
55+
}
56+
}
57+
}
58+
59+
/// TODO Use a macro instead.
60+
impl From<LogicalExpression> for logical_expression::Model {
61+
fn from(value: LogicalExpression) -> logical_expression::Model {
62+
fn create_logical_expression(
63+
kind: i16,
64+
data: serde_json::Value,
65+
) -> logical_expression::Model {
66+
logical_expression::Model {
67+
id: -1,
68+
group_id: -1,
69+
kind,
70+
data,
71+
}
72+
}
73+
74+
match value {
75+
LogicalExpression::Scan(scan) => create_logical_expression(
76+
0,
77+
serde_json::to_value(scan).expect("unable to serialize logical `Scan`"),
78+
),
79+
LogicalExpression::Filter(filter) => create_logical_expression(
80+
1,
81+
serde_json::to_value(filter).expect("unable to serialize logical `Filter`"),
82+
),
83+
LogicalExpression::Join(join) => create_logical_expression(
84+
2,
85+
serde_json::to_value(join).expect("unable to serialize logical `Join`"),
86+
),
87+
}
88+
}
89+
}
90+
91+
#[cfg(test)]
92+
pub use build::*;
93+
94+
#[cfg(test)]
95+
mod build {
96+
use super::*;
97+
use crate::expression::Expression;
98+
99+
pub fn scan(table_schema: String) -> Expression {
100+
Expression::Logical(LogicalExpression::Scan(Scan { table_schema }))
101+
}
102+
103+
pub fn filter(child_group: i32, expression: String) -> Expression {
104+
Expression::Logical(LogicalExpression::Filter(Filter {
105+
child: child_group,
106+
expression,
107+
}))
108+
}
109+
110+
pub fn join(left_group: i32, right_group: i32, expression: String) -> Expression {
111+
Expression::Logical(LogicalExpression::Join(Join {
112+
left: left_group,
113+
right: right_group,
114+
expression,
115+
}))
116+
}
117+
}

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: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
//! TODO This entire file is a WIP.
7+
8+
use crate::entities::*;
9+
use serde::{Deserialize, Serialize};
10+
11+
#[derive(Clone, Debug)]
12+
pub enum PhysicalExpression {
13+
TableScan(TableScan),
14+
Filter(PhysicalFilter),
15+
HashJoin(HashJoin),
16+
}
17+
18+
#[derive(Serialize, Deserialize, Clone, Debug)]
19+
pub struct TableScan {
20+
table_schema: String,
21+
}
22+
23+
#[derive(Serialize, Deserialize, Clone, Debug)]
24+
pub struct PhysicalFilter {
25+
child: i32,
26+
expression: String,
27+
}
28+
29+
#[derive(Serialize, Deserialize, Clone, Debug)]
30+
pub struct HashJoin {
31+
left: i32,
32+
right: i32,
33+
expression: String,
34+
}
35+
36+
/// TODO Use a macro instead.
37+
impl From<physical_expression::Model> for PhysicalExpression {
38+
fn from(value: physical_expression::Model) -> Self {
39+
match value.kind {
40+
0 => Self::TableScan(
41+
serde_json::from_value(value.data)
42+
.expect("unable to deserialize data into a physical `TableScan`"),
43+
),
44+
1 => Self::Filter(
45+
serde_json::from_value(value.data)
46+
.expect("Unable to deserialize data into a physical `Filter`"),
47+
),
48+
2 => Self::HashJoin(
49+
serde_json::from_value(value.data)
50+
.expect("Unable to deserialize data into a physical `HashJoin`"),
51+
),
52+
_ => panic!(),
53+
}
54+
}
55+
}
56+
57+
/// TODO Use a macro instead.
58+
impl From<PhysicalExpression> for physical_expression::Model {
59+
fn from(value: PhysicalExpression) -> physical_expression::Model {
60+
fn create_physical_expression(
61+
kind: i16,
62+
data: serde_json::Value,
63+
) -> physical_expression::Model {
64+
physical_expression::Model {
65+
id: -1,
66+
group_id: -1,
67+
kind,
68+
data,
69+
}
70+
}
71+
72+
match value {
73+
PhysicalExpression::TableScan(scan) => create_physical_expression(
74+
0,
75+
serde_json::to_value(scan).expect("unable to serialize physical `TableScan`"),
76+
),
77+
PhysicalExpression::Filter(filter) => create_physical_expression(
78+
1,
79+
serde_json::to_value(filter).expect("unable to serialize physical `Filter`"),
80+
),
81+
PhysicalExpression::HashJoin(join) => create_physical_expression(
82+
2,
83+
serde_json::to_value(join).expect("unable to serialize physical `HashJoin`"),
84+
),
85+
}
86+
}
87+
}
88+
89+
#[cfg(test)]
90+
pub use build::*;
91+
92+
#[cfg(test)]
93+
mod build {
94+
use super::*;
95+
use crate::expression::Expression;
96+
97+
pub fn table_scan(table_schema: String) -> Expression {
98+
Expression::Physical(PhysicalExpression::TableScan(TableScan { table_schema }))
99+
}
100+
101+
pub fn filter(child_group: i32, expression: String) -> Expression {
102+
Expression::Physical(PhysicalExpression::Filter(PhysicalFilter {
103+
child: child_group,
104+
expression,
105+
}))
106+
}
107+
108+
pub fn hash_join(left_group: i32, right_group: i32, expression: String) -> Expression {
109+
Expression::Physical(PhysicalExpression::HashJoin(HashJoin {
110+
left: left_group,
111+
right: right_group,
112+
expression,
113+
}))
114+
}
115+
}

0 commit comments

Comments
 (0)