Merge remote-tracking branch 'origin/main' into optimize-operation
This commit is contained in:
commit
900608b2f4
17 changed files with 814 additions and 198 deletions
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
bimap = "0.6.3"
|
||||
thiserror = "1.0.50"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,33 @@
|
|||
use std::num::{ParseFloatError, ParseIntError};
|
||||
use std::str::Utf8Error;
|
||||
use thiserror::Error;
|
||||
use crate::internals::row::ColumnPosition;
|
||||
use crate::schema::{ColumnName, TableName};
|
||||
use crate::type_system::{DbType, Uuid, Value};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("column position {1} of table {0} does not exist")]
|
||||
ColumnPositionDoesNotExist(TableName, ColumnPosition),
|
||||
#[error("column {1} of table {0} has unexpected type {2:?} and value {3:?}")]
|
||||
ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, Value),
|
||||
#[error("table {0} already contains row with id {1}")]
|
||||
AttemptingToInsertAlreadyPresentId(TableName, Uuid),
|
||||
#[error("table {0} cannot be indexed on column {1}")]
|
||||
AttemptToIndexNonIndexableColumn(TableName, ColumnName),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TypeConversionError {
|
||||
#[error("failed to decode bytes to string")]
|
||||
TextDecodeFailed(#[from] Utf8Error),
|
||||
#[error("failed to parse float from text")]
|
||||
NumberDecodeFailed(#[from] ParseFloatError),
|
||||
#[error("failed to parse int from text")]
|
||||
IntDecodeFailed(#[from] ParseIntError),
|
||||
#[error("unknown type with oid {oid} and size {size}")]
|
||||
UnknownType {
|
||||
oid: i32,
|
||||
size: i16
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::type_system::Value;
|
|||
use crate::operation::InsertionValues;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::slice::SliceIndex;
|
||||
use crate::restricted_row::RestrictedRow;
|
||||
|
||||
pub type ColumnPosition = usize;
|
||||
|
||||
|
|
@ -63,14 +64,15 @@ impl Row {
|
|||
self.0.get(column_position)
|
||||
}
|
||||
|
||||
pub fn restrict_columns(&self, columns: &Vec<ColumnPosition>) -> Row {
|
||||
pub fn restrict_columns(&self, columns: &Vec<ColumnPosition>) -> RestrictedRow {
|
||||
// If the index from `columns` is non-existant in `row`, it will just ignore it.
|
||||
let mut subrow: Row = Row::new();
|
||||
let mut subrow: Vec<(ColumnPosition, Value)> = vec![];
|
||||
for column_position in columns {
|
||||
if let Some(value) = self.get(*column_position) {
|
||||
subrow.0.push(value.clone())
|
||||
subrow.push((*column_position, value.clone()));
|
||||
}
|
||||
}
|
||||
subrow
|
||||
|
||||
subrow.into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};
|
|||
use crate::error::Error;
|
||||
use crate::internals::column_index::ColumnIndex;
|
||||
use crate::internals::row::{ColumnPosition, Row};
|
||||
use crate::restricted_row::RestrictedRow;
|
||||
use crate::schema::{ColumnName, TableSchema, TableName};
|
||||
use crate::result::DbResult;
|
||||
use crate::type_system::{IndexableValue, Uuid, Value};
|
||||
|
|
@ -67,7 +68,7 @@ impl Table {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn select_all_rows<'a>(&'a self, selected_column_positions: Vec<ColumnPosition>) -> impl Iterator<Item=Row> + 'a {
|
||||
pub fn select_all_rows<'a>(&'a self, selected_column_positions: Vec<ColumnPosition>) -> impl Iterator<Item=RestrictedRow> + 'a {
|
||||
self.rows
|
||||
.values()
|
||||
.map(move |row| row.restrict_columns(&selected_column_positions))
|
||||
|
|
@ -78,7 +79,7 @@ impl Table {
|
|||
selected_column_positions: Vec<ColumnPosition>,
|
||||
column_position: ColumnPosition,
|
||||
value: Value,
|
||||
) -> DbResult<impl Iterator<Item=Row> + 'a> {
|
||||
) -> DbResult<impl Iterator<Item=RestrictedRow> + 'a> {
|
||||
let restrict_columns_of_row = move |row: Row| row.restrict_columns(&selected_column_positions);
|
||||
match value {
|
||||
Value::Indexable(value) => match self.fetch_ids_from_index(column_position, &value)? {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use crate::internals::row::Row;
|
||||
use crate::internals::row::ColumnPosition;
|
||||
use crate::schema::{TableName, TableSchema};
|
||||
use crate::internals::table::Table;
|
||||
use crate::operation::{Operation, Condition};
|
||||
use crate::result::DbResult;
|
||||
use bimap::BiMap;
|
||||
use crate::restricted_row::RestrictedRow;
|
||||
|
||||
// Use `TablePosition` as index
|
||||
pub type Tables = Vec<Table>;
|
||||
|
|
@ -18,7 +19,7 @@ pub struct State {
|
|||
|
||||
// #[derive(Debug)]
|
||||
pub enum Response<'a> {
|
||||
Selected(&'a TableSchema, Box<dyn Iterator<Item=Row> + 'a + Send>),
|
||||
Selected(&'a TableSchema, Box<dyn Iterator<Item=RestrictedRow> + 'a + Send>),
|
||||
Inserted,
|
||||
Deleted(usize), // how many were deleted
|
||||
TableCreated,
|
||||
|
|
@ -89,8 +90,8 @@ impl State {
|
|||
|
||||
let selected_rows = match maybe_condition {
|
||||
None => {
|
||||
let x = table.select_all_rows(column_selection);
|
||||
Box::new(x) as Box<dyn Iterator<Item=Row> + 'a + Send>
|
||||
let rows = table.select_all_rows(column_selection);
|
||||
Box::new(rows) as Box<dyn Iterator<Item=RestrictedRow> + 'a + Send>
|
||||
},
|
||||
|
||||
Some(Condition::Eq(eq_column, value)) => {
|
||||
|
|
@ -100,7 +101,7 @@ impl State {
|
|||
eq_column,
|
||||
value,
|
||||
)?;
|
||||
Box::new(x) as Box<dyn Iterator<Item=Row> + 'a + Send>
|
||||
Box::new(x) as Box<dyn Iterator<Item=RestrictedRow> + 'a + Send>
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -246,9 +247,9 @@ mod tests {
|
|||
let row = &rows[0];
|
||||
|
||||
assert!(row.len() == 3);
|
||||
assert!(row[0] == id);
|
||||
assert!(row[1] == name);
|
||||
assert!(row[2] == age);
|
||||
assert!(row[0].1 == id);
|
||||
assert!(row[1].1 == name);
|
||||
assert!(row[2].1 == age);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -305,23 +306,24 @@ mod tests {
|
|||
let response: Response = state.interpret(Select(users_position, users_schema.all_selection(), None)).unwrap();
|
||||
|
||||
assert!(matches!(response, Response::Selected(_, _)));
|
||||
let Response::Selected(_schema, rows) = response else {
|
||||
let Response::Selected(_, rows) = response else {
|
||||
panic!()
|
||||
};
|
||||
let rows: Vec<_> = rows.collect();
|
||||
|
||||
let rows: Vec<_> = rows.collect();
|
||||
assert!(rows.len() == 2);
|
||||
let row0 = &rows[0];
|
||||
let row1 = &rows[1];
|
||||
|
||||
assert!(row0.len() == 3);
|
||||
assert!(row0[0] == id0);
|
||||
assert!(row0[1] == name0);
|
||||
assert!(row0[2] == age0);
|
||||
assert!(row0[0].1 == id0);
|
||||
assert!(row0[1].1 == name0);
|
||||
assert!(row0[2].1 == age0);
|
||||
|
||||
assert!(row1.len() == 3);
|
||||
assert!(row1[0] == id1);
|
||||
assert!(row1[1] == name1);
|
||||
assert!(row1[2] == age1);
|
||||
assert!(row1[0].1 == id1);
|
||||
assert!(row1[1].1 == name1);
|
||||
assert!(row1[2].1 == age1);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -333,7 +335,7 @@ mod tests {
|
|||
))
|
||||
.unwrap();
|
||||
assert!(matches!(response, Response::Selected(_, _)));
|
||||
let Response::Selected(_schema, rows) = response else {
|
||||
let Response::Selected(_, rows) = response else {
|
||||
panic!()
|
||||
};
|
||||
let rows: Vec<_> = rows.collect();
|
||||
|
|
@ -341,9 +343,9 @@ mod tests {
|
|||
let row0 = &rows[0];
|
||||
|
||||
assert!(row0.len() == 3);
|
||||
assert!(row0[0] == id0);
|
||||
assert!(row0[1] == name0);
|
||||
assert!(row0[2] == age0);
|
||||
assert!(row0[0].1 == id0);
|
||||
assert!(row0[1].1 == name0);
|
||||
assert!(row0[2].1 == age0);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -355,7 +357,7 @@ mod tests {
|
|||
))
|
||||
.unwrap();
|
||||
assert!(matches!(response, Response::Selected(_, _)));
|
||||
let Response::Selected(_schema, rows) = response else {
|
||||
let Response::Selected(_, rows) = response else {
|
||||
panic!()
|
||||
};
|
||||
let rows: Vec<_> = rows.collect();
|
||||
|
|
@ -363,8 +365,8 @@ mod tests {
|
|||
let row0 = &rows[0];
|
||||
|
||||
assert!(row0.len() == 2);
|
||||
assert!(row0[0] == name0);
|
||||
assert!(row0[1] == id0);
|
||||
assert!(row0[0].1 == name0);
|
||||
assert!(row0[1].1 == id0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -430,7 +432,7 @@ mod tests {
|
|||
let response: Response = state.interpret(Select(users_position, users_schema.all_selection(), None)).unwrap();
|
||||
|
||||
assert!(matches!(response, Response::Selected(_, _)));
|
||||
let Response::Selected(_schema, rows) = response else {
|
||||
let Response::Selected(_, rows) = response else {
|
||||
panic!()
|
||||
};
|
||||
let rows: Vec<_> = rows.collect();
|
||||
|
|
@ -438,9 +440,9 @@ mod tests {
|
|||
let row = &rows[0];
|
||||
|
||||
assert!(row.len() == 3);
|
||||
assert!(row[0] == id1);
|
||||
assert!(row[1] == name1);
|
||||
assert!(row[2] == age1);
|
||||
assert!(row[0].1 == id1);
|
||||
assert!(row[1].1 == name1);
|
||||
assert!(row[2].1 == age1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -516,7 +518,6 @@ mod tests {
|
|||
|
||||
pub fn example() {
|
||||
use crate::type_system::{IndexableValue, Value, DbType};
|
||||
use crate::internals::row::ColumnPosition;
|
||||
use Condition::*;
|
||||
use IndexableValue::*;
|
||||
use Operation::*;
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ pub mod type_system;
|
|||
mod error;
|
||||
mod internals;
|
||||
mod result;
|
||||
pub mod restricted_row;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::internals::row::ColumnPosition;
|
|||
use crate::interpreter::TablePosition;
|
||||
|
||||
// Validated operation. Constructed by validation crate.
|
||||
#[derive(Debug)]
|
||||
pub enum Operation {
|
||||
Select(TablePosition, ColumnSelection, Option<Condition>),
|
||||
Insert(TablePosition, InsertionValues),
|
||||
|
|
@ -16,6 +17,7 @@ pub type InsertionValues = Vec<Value>;
|
|||
|
||||
pub type ColumnSelection = Vec<ColumnPosition>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Condition {
|
||||
Eq(ColumnPosition, Value),
|
||||
}
|
||||
|
|
|
|||
35
minisql/src/restricted_row.rs
Normal file
35
minisql/src/restricted_row.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use std::ops::Index;
|
||||
use std::slice::SliceIndex;
|
||||
use crate::internals::row::ColumnPosition;
|
||||
use crate::type_system::Value;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RestrictedRow(Vec<(ColumnPosition, Value)>);
|
||||
|
||||
impl<Idx> Index<Idx> for RestrictedRow
|
||||
where
|
||||
Idx: SliceIndex<[(ColumnPosition, Value)]>,
|
||||
{
|
||||
type Output = Idx::Output;
|
||||
|
||||
fn index(&self, index: Idx) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(ColumnPosition, Value)>> for RestrictedRow {
|
||||
fn from(v: Vec<(ColumnPosition, Value)>) -> Self {
|
||||
RestrictedRow(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl RestrictedRow {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item=&(ColumnPosition, Value)> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
use crate::error::TypeConversionError;
|
||||
|
||||
// ==============Types================
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DbType {
|
||||
|
|
@ -39,4 +41,156 @@ impl Value {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_oid(&self) -> i32 {
|
||||
match self {
|
||||
Self::Number(_) => 701,
|
||||
Self::Indexable(val) => match val {
|
||||
IndexableValue::String(_) => 25,
|
||||
IndexableValue::Int(_) => 23,
|
||||
IndexableValue::Uuid(_) => 2950,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_size(&self) -> i16 {
|
||||
match self {
|
||||
Self::Number(_) => 8,
|
||||
Self::Indexable(val) => match val {
|
||||
IndexableValue::String(_) => -2, // null terminated string
|
||||
IndexableValue::Int(_) => 8,
|
||||
IndexableValue::Uuid(_) => 16,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_text_bytes(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Number(n) => format!("{n}").into_bytes(),
|
||||
Self::Indexable(i) => match i {
|
||||
IndexableValue::String(s) => format!("{s}\0").into_bytes(),
|
||||
IndexableValue::Int(i) => format!("{i}").into_bytes(),
|
||||
IndexableValue::Uuid(u) => format!("{u}").into_bytes(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_text_bytes(bytes: &[u8], type_oid: i32, type_size: i16) -> Result<Value, TypeConversionError> {
|
||||
match (type_oid, type_size) {
|
||||
(701, 8) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let n = s.parse::<f64>()?;
|
||||
Ok(Value::Number(n))
|
||||
}
|
||||
(25, -2) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let s = &s[..s.len() - 1]; // remove null terminator
|
||||
Ok(Value::Indexable(IndexableValue::String(s.to_string())))
|
||||
}
|
||||
(23, 8) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let n = s.parse::<u64>()?;
|
||||
Ok(Value::Indexable(IndexableValue::Int(n)))
|
||||
}
|
||||
(2950, 16) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let n = s.parse::<Uuid>()?;
|
||||
Ok(Value::Indexable(IndexableValue::Uuid(n)))
|
||||
}
|
||||
(oid, size) => Err(TypeConversionError::UnknownType { oid, size }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::error::TypeConversionError::UnknownType;
|
||||
use super::{Value, IndexableValue};
|
||||
|
||||
#[test]
|
||||
fn test_encode_number() {
|
||||
let value = Value::Number(123.456);
|
||||
let oid = value.type_oid();
|
||||
let size = value.type_size();
|
||||
|
||||
let bytes = value.as_text_bytes();
|
||||
let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap();
|
||||
|
||||
assert_eq!(value, from_bytes);
|
||||
|
||||
assert_eq!(oid, 701);
|
||||
assert_eq!(size, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_string() {
|
||||
let value = Value::Indexable(IndexableValue::String("hello".to_string()));
|
||||
let oid = value.type_oid();
|
||||
let size = value.type_size();
|
||||
|
||||
let bytes = value.as_text_bytes();
|
||||
let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap();
|
||||
|
||||
assert_eq!(value, from_bytes);
|
||||
|
||||
assert_eq!(oid, 25);
|
||||
assert_eq!(size, -2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_string_utf8() {
|
||||
let value = Value::Indexable(IndexableValue::String("#速度与激情9 早上好中国 现在我有冰激淋 我很喜欢冰激淋 但是《速度与激情9》比冰激淋 🍧🍦🍨".to_string()));
|
||||
let oid = value.type_oid();
|
||||
let size = value.type_size();
|
||||
|
||||
let bytes = value.as_text_bytes();
|
||||
let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap();
|
||||
|
||||
assert_eq!(value, from_bytes);
|
||||
|
||||
assert_eq!(oid, 25);
|
||||
assert_eq!(size, -2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_int() {
|
||||
let value = Value::Indexable(IndexableValue::Int(123));
|
||||
let oid = value.type_oid();
|
||||
let size = value.type_size();
|
||||
|
||||
let bytes = value.as_text_bytes();
|
||||
let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap();
|
||||
|
||||
assert_eq!(value, from_bytes);
|
||||
|
||||
assert_eq!(oid, 23);
|
||||
assert_eq!(size, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_uuid() {
|
||||
let value = Value::Indexable(IndexableValue::Uuid(123));
|
||||
let oid = value.type_oid();
|
||||
let size = value.type_size();
|
||||
|
||||
let bytes = value.as_text_bytes();
|
||||
let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap();
|
||||
|
||||
assert_eq!(value, from_bytes);
|
||||
|
||||
assert_eq!(oid, 2950);
|
||||
assert_eq!(size, 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mismatched_size() {
|
||||
let value = Value::Indexable(IndexableValue::Uuid(123));
|
||||
let oid = value.type_oid();
|
||||
let size = 8;
|
||||
|
||||
let bytes = value.as_text_bytes();
|
||||
let from_bytes = Value::from_text_bytes(&bytes, oid, size);
|
||||
|
||||
assert!(matches!(from_bytes, Err(UnknownType { oid: 2950, size: 8 })))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue