Skip to content
Snippets Groups Projects
Commit ee45da42 authored by Denis Smirnov's avatar Denis Smirnov
Browse files

feat: implement relation ir

1. Implement columns (with types) and tables as a part of IR.
2. Use serde deserialization trait to build tables from YAML files.
   It would be also helpful when writing tests for arbitrary plan
   transformations.
parent a817ec34
No related branches found
No related tags found
1 merge request!1414sbroad import
...@@ -11,8 +11,12 @@ decimal = "2.1.0" ...@@ -11,8 +11,12 @@ decimal = "2.1.0"
tarantool = "0.4.2" tarantool = "0.4.2"
sqlparser = "0.11.0" sqlparser = "0.11.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
fasthash = "0.4.0" fasthash = "0.4.0"
yaml-rust = "0.4.1" yaml-rust = "0.4.1"
[dev-dependencies]
pretty_assertions = "1.0.0"
[lib] [lib]
crate-type = ["cdylib"] crate-type = ["cdylib"]
use serde::Serialize; use serde::Serialize;
use std::fmt; use std::fmt;
const BUCKET_ID_ERROR: &str = "field doesn't contains sharding key value";
const DUPLICATE_COLUMN_ERROR: &str = "duplicate column";
const INVALID_SHARDING_KEY_ERROR: &str = "invalid sharding key";
const SERIALIZATION_ERROR: &str = "serialization";
const SIMPLE_QUERY_ERROR: &str = "query doesn't simple"; const SIMPLE_QUERY_ERROR: &str = "query doesn't simple";
const SIMPLE_UNION_QUERY_ERROR: &str = "query doesn't simple union"; const SIMPLE_UNION_QUERY_ERROR: &str = "query doesn't simple union";
const QUERY_NOT_IMPLEMENTED: &str = "query wasn't s implemented"; const QUERY_NOT_IMPLEMENTED: &str = "query wasn't s implemented";
const BUCKET_ID_ERROR: &str = "field doesn't contains sharding key value"; const VALUE_OUT_OF_RANGE_ERROR: &str = "value out of range";
#[derive(Debug, Clone, PartialEq, Serialize)] #[derive(Debug, Clone, PartialEq, Serialize)]
pub enum QueryPlannerError { pub enum QueryPlannerError {
BucketIdError,
DuplicateColumn,
InvalidShardingKey,
Serialization,
SimpleQueryError, SimpleQueryError,
SimpleUnionQueryError, SimpleUnionQueryError,
QueryNotImplemented, QueryNotImplemented,
BucketIdError, ValueOutOfRange,
} }
impl fmt::Display for QueryPlannerError { impl fmt::Display for QueryPlannerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let p = match self { let p = match self {
QueryPlannerError::BucketIdError => BUCKET_ID_ERROR,
QueryPlannerError::DuplicateColumn => DUPLICATE_COLUMN_ERROR,
QueryPlannerError::InvalidShardingKey => INVALID_SHARDING_KEY_ERROR,
QueryPlannerError::Serialization => SERIALIZATION_ERROR,
QueryPlannerError::SimpleQueryError => SIMPLE_QUERY_ERROR, QueryPlannerError::SimpleQueryError => SIMPLE_QUERY_ERROR,
QueryPlannerError::SimpleUnionQueryError => SIMPLE_UNION_QUERY_ERROR, QueryPlannerError::SimpleUnionQueryError => SIMPLE_UNION_QUERY_ERROR,
QueryPlannerError::QueryNotImplemented => QUERY_NOT_IMPLEMENTED, QueryPlannerError::QueryNotImplemented => QUERY_NOT_IMPLEMENTED,
QueryPlannerError::BucketIdError => BUCKET_ID_ERROR, QueryPlannerError::ValueOutOfRange => VALUE_OUT_OF_RANGE_ERROR,
}; };
write!(f, "{}", p) write!(f, "{}", p)
} }
......
pub mod relation;
pub mod value; pub mod value;
use crate::errors::QueryPlannerError;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub enum Type {
Boolean,
Number,
String,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Column {
name: String,
type_name: Type,
}
#[allow(dead_code)]
impl Column {
fn new(n: &str, t: Type) -> Self {
Column {
name: n.into(),
type_name: t,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TableShard {
name: String,
columns: Vec<Column>,
sharding_key: Vec<usize>,
}
#[allow(dead_code)]
impl TableShard {
fn new(n: &str, c: Vec<Column>, k: &[&str]) -> Result<Self, QueryPlannerError> {
let mut pos_map: HashMap<&str, usize> = HashMap::new();
let cols = &c;
let no_duplicates = cols
.iter()
.enumerate()
.all(|(pos, col)| matches!(pos_map.insert(&col.name, pos), None));
if !no_duplicates {
return Err(QueryPlannerError::DuplicateColumn);
}
let keys = &k;
let res_positions: Result<Vec<_>, _> = keys
.iter()
.map(|name| match pos_map.get(*name) {
Some(pos) => Ok(*pos),
None => Err(QueryPlannerError::InvalidShardingKey),
})
.collect();
let positions = res_positions?;
Ok(TableShard {
name: n.into(),
columns: c,
sharding_key: positions,
})
}
fn from_yaml(s: &str) -> Result<Self, QueryPlannerError> {
let ts: TableShard = match serde_yaml::from_str(s) {
Ok(t) => t,
Err(_) => return Err(QueryPlannerError::Serialization),
};
let mut uniq_cols: HashSet<&str> = HashSet::new();
let cols = &ts.columns;
let no_duplicates = cols.iter().all(|col| uniq_cols.insert(&col.name));
if !no_duplicates {
return Err(QueryPlannerError::DuplicateColumn);
}
let keys = &ts.sharding_key;
let in_range = keys.iter().all(|pos| *pos < cols.len());
if !in_range {
return Err(QueryPlannerError::ValueOutOfRange);
}
Ok(ts)
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::{assert_eq, assert_ne};
use std::fs;
use std::path::Path;
#[test]
fn column() {
let a = Column {
name: String::from("a"),
type_name: Type::Boolean,
};
assert_eq!(a, Column::new("a", Type::Boolean));
assert_ne!(a, Column::new("a", Type::String));
assert_ne!(a, Column::new("b", Type::Boolean));
}
#[test]
fn table() {
let t = TableShard::new(
"t",
vec![
Column::new("a", Type::Boolean),
Column::new("b", Type::Number),
Column::new("c", Type::String),
Column::new("d", Type::String),
],
&["b", "a"],
)
.unwrap();
assert_eq!(2, t.sharding_key.len());
assert_eq!(0, t.sharding_key[1]);
assert_eq!(1, t.sharding_key[0]);
}
#[test]
fn table_duplicate_columns() {
assert_eq!(
TableShard::new(
"t",
vec![
Column::new("a", Type::Boolean),
Column::new("b", Type::Number),
Column::new("c", Type::String),
Column::new("a", Type::String),
],
&["b", "a"],
)
.unwrap_err(),
QueryPlannerError::DuplicateColumn
);
}
#[test]
fn table_wrong_sharding_key() {
assert_eq!(
TableShard::new(
"t",
vec![
Column::new("a", Type::Boolean),
Column::new("b", Type::Number),
Column::new("c", Type::String),
Column::new("d", Type::String),
],
&["a", "e"],
)
.unwrap_err(),
QueryPlannerError::InvalidShardingKey
);
}
#[test]
fn table_serialized() {
let t = TableShard::new(
"t",
vec![
Column::new("a", Type::Boolean),
Column::new("b", Type::Number),
Column::new("c", Type::String),
Column::new("d", Type::String),
],
&["a", "d"],
)
.unwrap();
let path = Path::new("")
.join("tests")
.join("artifactory")
.join("ir")
.join("relation")
.join("table_serialized.yaml");
let s = fs::read_to_string(path).unwrap();
let t_yaml = TableShard::from_yaml(&s).unwrap();
assert_eq!(t, t_yaml);
}
#[test]
fn table_serialized_duplicate_columns() {
let path = Path::new("")
.join("tests")
.join("artifactory")
.join("ir")
.join("relation")
.join("table_serialized_duplicate_columns.yaml");
let s = fs::read_to_string(path).unwrap();
assert_eq!(
TableShard::from_yaml(&s).unwrap_err(),
QueryPlannerError::DuplicateColumn
);
}
#[test]
fn table_serialized_out_of_range_sharding_key() {
let path = Path::new("")
.join("tests")
.join("artifactory")
.join("ir")
.join("relation")
.join("table_serialized_out_of_range_sharding_key.yaml");
let s = fs::read_to_string(path).unwrap();
assert_eq!(
TableShard::from_yaml(&s).unwrap_err(),
QueryPlannerError::ValueOutOfRange
);
}
#[test]
fn table_serialized_no_sharding_key() {
let path = Path::new("")
.join("tests")
.join("artifactory")
.join("ir")
.join("relation")
.join("table_serialized_no_sharding_key.yaml");
let s = fs::read_to_string(path).unwrap();
let t = TableShard::from_yaml(&s);
assert_eq!(t.unwrap_err(), QueryPlannerError::Serialization);
}
#[test]
fn table_serialized_no_columns() {
let path = Path::new("")
.join("tests")
.join("artifactory")
.join("ir")
.join("relation")
.join("table_serialized_no_columns.yaml");
let s = fs::read_to_string(path).unwrap();
assert_eq!(
TableShard::from_yaml(&s).unwrap_err(),
QueryPlannerError::Serialization
);
}
}
columns:
- name: a
type_name: Boolean
- name: b
type_name: Number
- name: c
type_name: String
- name: d
type_name: String
name: t
sharding_key:
- 0
- 3
columns:
- name: a
type_name: Boolean
- name: b
type_name: String
- name: a
type_name: String
name: t
sharding_key:
- 0
columns: ~
name: t
sharding_key:
- 0
columns:
- name: a
type_name: Boolean
name: t
sharding_key: ~
columns:
- name: a
type_name: Boolean
name: t
sharding_key:
- 10
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment