Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions src/db/table/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct ColumnConstraint {
pub constraint_type: String,
}

#[derive(Debug, PartialEq, PartialOrd)]
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum Value {
Integer(i64),
Real(f64),
Expand All @@ -32,6 +32,7 @@ pub enum Value {
Null
}


impl Value {
pub fn get_type(&self) -> DataType {
match self {
Expand All @@ -42,16 +43,6 @@ impl Value {
Value::Null => DataType::Null,
}
}

pub fn clone(&self) -> Value {
match self {
Value::Integer(value) => Value::Integer(*value),
Value::Real(value) => Value::Real(*value),
Value::Text(value) => Value::Text(value.clone()),
Value::Blob(value) => Value::Blob(value.clone()),
Value::Null => Value::Null,
}
}
}

#[derive(Debug)]
Expand Down
123 changes: 123 additions & 0 deletions src/db/table/select/limit_clause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::cmp::min;

use crate::cli::ast::LimitClause;
use crate::db::table::Value;


pub fn get_limited_rows<'a>(rows: Vec<Vec<Value>>, limit_clause: &LimitClause) -> Result<Vec<Vec<Value>>, String> {
let mut index = 0;
if let Some(offset) = &limit_clause.offset && let Value::Integer(offset) = offset {
index = *offset as usize;
}
if index >= rows.len() {
return Ok(vec![]);
}

let limit = match limit_clause.limit {
Value::Integer(limit) => {
if limit < 0 {
rows.len()
} else {
min((limit as usize)+index, rows.len())
}
},
_ => return Err("Limit must be an integer".to_string()), // The parser should have already validated this
};

let mut limited_rows: Vec<Vec<Value>> = vec![];
for i in index..limit {
limited_rows.push(rows[i].clone());
}
return Ok(limited_rows);
}


#[cfg(test)]
mod tests {
use super::*;

fn default_rows() -> Vec<Vec<Value>> {
vec![
vec![Value::Integer(1)],
vec![Value::Integer(2)],
vec![Value::Integer(3)],
vec![Value::Integer(4)],
vec![Value::Integer(5)],
vec![Value::Integer(6)],
vec![Value::Integer(7)],
vec![Value::Integer(8)],
vec![Value::Integer(9)],
vec![Value::Integer(10)],
]
}

fn generate_limit_clause(limit: i64, offset: Option<i64>) -> LimitClause {
LimitClause {
limit: Value::Integer(limit as i64),
offset: offset.map(|offset| Value::Integer(offset as i64)),
}
}

#[test]
fn no_offset_and_limit_is_equal_to_rows_length() {
let limit_clause = generate_limit_clause(10, None);
let result = get_limited_rows(default_rows(), &limit_clause);
assert!(result.is_ok());
assert_eq!(default_rows(), result.unwrap());
}

#[test]
fn no_offset_and_limit_is_greater_than_rows_length() {
let limit_clause = generate_limit_clause(15, None);
let result = get_limited_rows(default_rows(), &limit_clause);
assert!(result.is_ok());
assert_eq!(default_rows(), result.unwrap());
}

#[test]
fn no_offset_and_limit_is_less_than_rows_length() {
let limit_clause = generate_limit_clause(5, None);
let result = get_limited_rows(default_rows(), &limit_clause);
assert!(result.is_ok());
let expected = vec![
vec![Value::Integer(1)],
vec![Value::Integer(2)],
vec![Value::Integer(3)],
vec![Value::Integer(4)],
vec![Value::Integer(5)],
];
assert_eq!(expected, result.unwrap());
}

#[test]
fn no_offset_and_negative_limit_returns_all_rows() {
let limit_clause = generate_limit_clause(-1, None);
let result = get_limited_rows(default_rows(), &limit_clause);
assert!(result.is_ok());
assert_eq!(default_rows(), result.unwrap());
}

#[test]
fn offset_and_limit_is_generated_correctly() {
let limit_clause = generate_limit_clause(5, Some(1));
let result = get_limited_rows(default_rows(), &limit_clause);
assert!(result.is_ok());
let expected = vec![
vec![Value::Integer(2)],
vec![Value::Integer(3)],
vec![Value::Integer(4)],
vec![Value::Integer(5)],
vec![Value::Integer(6)],
];
assert_eq!(expected, result.unwrap());
}

#[test]
fn offset_is_greater_than_rows_length_returns_empty_rows() {
let limit_clause = generate_limit_clause(5, Some(10));
let result = get_limited_rows(default_rows(), &limit_clause);
assert!(result.is_ok());
let expected: Vec<Vec<Value>> = vec![];
assert_eq!(expected, result.unwrap());
}
}
38 changes: 36 additions & 2 deletions src/db/table/select/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
pub mod where_clause;
pub mod limit_clause;
use crate::db::table::{Table, Value};
use crate::cli::ast::SelectStatement;
use crate::cli::ast::SelectStatementColumns;
use crate::db::table::common::validate_and_clone_row;


pub fn select(table: &Table, statement: SelectStatement) -> Result<Vec<Vec<Value>>, String> {
let mut rows = get_initial_rows(table, &statement)?;
// Implement order by
if let Some(limit_clause) = &statement.limit_clause {
rows = limit_clause::get_limited_rows(rows, limit_clause)?;
}

return Ok(rows);
}

pub fn get_initial_rows(table: &Table, statement: &SelectStatement) -> Result<Vec<Vec<Value>>, String> {
let mut rows: Vec<Vec<Value>> = vec![];
if let Some(where_clause) = statement.where_clause {
if let Some(where_clause) = &statement.where_clause {
for row in table.rows.iter() {
if where_clause::matches_where_clause(table, &row, &where_clause) {
rows.push(get_columns_from_row(table, &row, &statement.columns)?);
Expand All @@ -18,9 +29,10 @@ pub fn select(table: &Table, statement: SelectStatement) -> Result<Vec<Vec<Value
rows.push(get_columns_from_row(table, &row, &statement.columns)?);
}
}
return Ok(rows);
Ok(rows)
}


pub fn get_columns_from_row(table: &Table, row: &Vec<Value>, selected_columns: &SelectStatementColumns) -> Result<Vec<Value>, String> {
let mut row_values: Vec<Value> = vec![];
if *selected_columns == SelectStatementColumns::All {
Expand All @@ -43,6 +55,7 @@ mod tests {
use crate::cli::ast::SelectStatementColumns;
use crate::cli::ast::Operator;
use crate::cli::ast::WhereClause;
use crate::cli::ast::LimitClause;

fn default_table() -> Table {
Table {
Expand Down Expand Up @@ -147,4 +160,25 @@ mod tests {
];
assert_eq!(expected, result.unwrap());
}

#[test]
fn select_with_limit_clause_is_generated_correctly() {
let table = default_table();
let statement = SelectStatement {
table_name: "users".to_string(),
columns: SelectStatementColumns::All,
where_clause: None,
order_by_clause: None,
limit_clause: Some(LimitClause {
limit: Value::Integer(1),
offset: Some(Value::Integer(1)),
}),
};
let result = select(&table, statement);
assert!(result.is_ok());
let expected = vec![
vec![Value::Integer(2), Value::Text("Jane".to_string()), Value::Integer(30), Value::Real(2000.0)],
];
assert_eq!(expected, result.unwrap());
}
}
Loading