value exprs
This commit is contained in:
parent
824a5cdb43
commit
d298cb129a
|
@ -1,8 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 12/24/23
|
||||||
|
- Created common logic for parsing ValueExpressions
|
||||||
|
|
||||||
## 12/23/23
|
## 12/23/23
|
||||||
|
|
||||||
- Added LogicExpressions for creating and evaluating expressions
|
- Added LogicExpressions for creating and evaluating expressions
|
||||||
- Added LogicExpressions to SELECT and DELETE commands (i.e SELECT FROM WHERE and DELETE FROM WHERE)
|
- Added LogicExpressions to SELECT and DELETE commands (i.e SELECT FROM WHERE and DELETE FROM WHERE)
|
||||||
- Updated SELECT formatting to pad table columns
|
- Updated SELECT formatting to pad table columns
|
||||||
- General system stability improvements to enhance the user's experience.
|
- General system stability improvements to enhance the user's experience.
|
||||||
|
- Moved many functions over to squirrel_core library
|
||||||
|
|
|
@ -20,9 +20,9 @@ This is a SQL database written in Rust. It will be based off of (and hopefully b
|
||||||
|
|
||||||
[x] WHERE clause for SELECT and DELETE
|
[x] WHERE clause for SELECT and DELETE
|
||||||
|
|
||||||
[ ] Create squirrel-core library for shared code between client & server
|
[x] Create squirrel-core library for shared code between client & server
|
||||||
|
|
||||||
[ ] Update parser to use common logic to identify 'objects' (i.e function calls, column references, and variables)
|
[x] Update parser to use common logic to identify 'objects' (i.e function calls, column references, and variables)
|
||||||
|
|
||||||
[ ] Move parsing to client
|
[ ] Move parsing to client
|
||||||
|
|
||||||
|
|
7
squirrel-client/Cargo.lock
generated
7
squirrel-client/Cargo.lock
generated
|
@ -1,7 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "squirrel-client"
|
|
||||||
version = "0.1.0"
|
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "squirrel-client"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
|
@ -1,9 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "squirrel-server"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0.72"
|
|
23
squirrel_client/Cargo.lock
generated
Normal file
23
squirrel_client/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.76"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "squirrel_client"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"squirrel_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "squirrel_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
]
|
7
squirrel_client/Cargo.toml
Normal file
7
squirrel_client/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "squirrel_client"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
squirrel_core = { path = "../squirrel_core" }
|
16
squirrel_core/Cargo.lock
generated
Normal file
16
squirrel_core/Cargo.lock
generated
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.76"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "squirrel_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
]
|
11
squirrel_core/Cargo.toml
Normal file
11
squirrel_core/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "squirrel_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.72"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "squirrel_core"
|
||||||
|
path = "src/main.rs"
|
181
squirrel_core/src/main.rs
Normal file
181
squirrel_core/src/main.rs
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
pub mod parser;
|
||||||
|
pub mod table;
|
||||||
|
|
||||||
|
pub use crate::parser::command::Command;
|
||||||
|
use crate::parser::command::{CreateCommand, InsertCommand, SelectCommand, DeleteCommand, LogicExpression, InsertItem, DataValue, FunctionCall, ValueExpression};
|
||||||
|
pub use crate::table::datatypes::Datatype;
|
||||||
|
pub use crate::table::table_definition::{ColumnDefinition, TableDefinition};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::{BufRead, BufReader, Read, Write};
|
||||||
|
use std::net::{Shutdown, TcpListener, TcpStream};
|
||||||
|
use std::thread;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_expression() -> anyhow::Result<()> {
|
||||||
|
let tests = HashMap::from([
|
||||||
|
(
|
||||||
|
"TEST()",
|
||||||
|
Ok(ValueExpression::FunctionCall(FunctionCall { function_name: String::from("TEST"), parameters: vec![]}))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"TEST_TWO()",
|
||||||
|
Ok(ValueExpression::FunctionCall(FunctionCall { function_name: String::from("TEST_TWO"), parameters: vec![]}))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"\"Name\'",
|
||||||
|
Err(anyhow!("Error"))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
Ok(ValueExpression::ColumnName(String::from("id")))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"55",
|
||||||
|
Ok(ValueExpression::DataValue(DataValue::U8Value(55)))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"\"Name\"",
|
||||||
|
Ok(ValueExpression::DataValue(DataValue::StringValue(String::from("Name"))))
|
||||||
|
),
|
||||||
|
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (string, expected) in tests {
|
||||||
|
match expected {
|
||||||
|
Ok(expected_res) => {
|
||||||
|
assert_eq!(
|
||||||
|
parser::command::Command::value_expression_from_string(String::from(string)).unwrap(),
|
||||||
|
expected_res);
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
assert_eq!(
|
||||||
|
parser::command::Command::value_expression_from_string(String::from(string)).is_ok(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logical_expression() -> anyhow::Result<()> {
|
||||||
|
assert_eq!(Command::le_from_string(String::from("1 < 5")).unwrap().evaluate().unwrap(), true);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("1 > 5")).unwrap().evaluate().unwrap(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("1 <= 5")).unwrap().evaluate().unwrap(), true);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("1 >= 5")).unwrap().evaluate().unwrap(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("5 >= 5")).unwrap().evaluate().unwrap(), true);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("5 <= 5")).unwrap().evaluate().unwrap(), true);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("5 = 5")).unwrap().evaluate().unwrap(), true);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("5 AND 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("5 OR 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' = 'Test'")).unwrap().evaluate().unwrap(), true);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' = 'Text'")).unwrap().evaluate().unwrap(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' <= 'Test'")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' >= 'Test'")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' < 'Test'")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' > 'Test'")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' AND 'Test'")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' OR 'Test'")).unwrap().evaluate().is_ok(), false);
|
||||||
|
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' < 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' > 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' <= 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' >= 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' >= 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' <= 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' = 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' AND 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
assert_eq!(Command::le_from_string(String::from("'Test' OR 5")).unwrap().evaluate().is_ok(), false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_statement() -> anyhow::Result<()> {
|
||||||
|
let empty_statement = "";
|
||||||
|
let regular_statement = "INSERT INTO users (id, name) VALUES (1, \"Test\");";
|
||||||
|
let extra_ws_statement =
|
||||||
|
"INSERT INTO users (id, name) VALUES (1, \"Test\") ;";
|
||||||
|
let min_ws_statement = "INSERT INTO users(id, name) VALUES(1, \"Test\");";
|
||||||
|
let str_comma_statement = "INSERT INTO users(id, name) VALUES(1, \"Firstname, Lastname\");";
|
||||||
|
|
||||||
|
let expected_output = Command::Insert(InsertCommand {
|
||||||
|
table_name: "users".to_string(),
|
||||||
|
items: HashMap::from([
|
||||||
|
(
|
||||||
|
"id".to_string(),
|
||||||
|
InsertItem {
|
||||||
|
column_name: "id".to_string(),
|
||||||
|
column_value: "1".to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name".to_string(),
|
||||||
|
InsertItem {
|
||||||
|
column_name: "name".to_string(),
|
||||||
|
column_value: "Test".to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
let expected_output_comma = Command::Insert(InsertCommand {
|
||||||
|
table_name: "users".to_string(),
|
||||||
|
items: HashMap::from([
|
||||||
|
(
|
||||||
|
"id".to_string(),
|
||||||
|
InsertItem {
|
||||||
|
column_name: "id".to_string(),
|
||||||
|
column_value: "1".to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name".to_string(),
|
||||||
|
InsertItem {
|
||||||
|
column_name: "name".to_string(),
|
||||||
|
column_value: "Firstname, Lastname".to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Command::from_string(String::from(empty_statement)).is_ok(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Command::from_string(String::from(regular_statement))?,
|
||||||
|
expected_output
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Command::from_string(String::from(extra_ws_statement))?,
|
||||||
|
expected_output
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Command::from_string(String::from(min_ws_statement))?,
|
||||||
|
expected_output
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Command::from_string(String::from(str_comma_statement))?,
|
||||||
|
expected_output_comma
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::table::table_definition::ColumnDefinition;
|
use crate::table::table_definition::{TableDefinition, ColumnDefinition};
|
||||||
use crate::{Datatype, TableDefinition};
|
use crate::table::datatypes::{Datatype};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -55,26 +55,38 @@ enum LogicalOperator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub enum LogicValue {
|
pub enum DataValue {
|
||||||
StringValue(String),
|
StringValue(String),
|
||||||
U8Value(u8),
|
U8Value(u8),
|
||||||
BoolValue(bool),
|
BoolValue(bool),
|
||||||
ColumnName(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum LogicSide {
|
pub enum LogicSide {
|
||||||
// pub expression: LogicExpression,
|
// pub expression: LogicExpression,
|
||||||
Value(LogicValue)
|
Value(DataValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub struct LogicExpression {
|
pub struct LogicExpression {
|
||||||
pub left_hand: LogicValue,
|
pub left_hand: ValueExpression,
|
||||||
pub right_hand: LogicValue,
|
pub right_hand: ValueExpression,
|
||||||
pub operator: LogicalOperator
|
pub operator: LogicalOperator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
pub struct FunctionCall {
|
||||||
|
pub function_name: String,
|
||||||
|
pub parameters: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
pub enum ValueExpression {
|
||||||
|
FunctionCall(FunctionCall),
|
||||||
|
DataValue(DataValue),
|
||||||
|
ColumnName(String),
|
||||||
|
}
|
||||||
|
|
||||||
enum CreateParserState {
|
enum CreateParserState {
|
||||||
Object,
|
Object,
|
||||||
TableName,
|
TableName,
|
||||||
|
@ -114,6 +126,14 @@ enum InsertParserState {
|
||||||
Semicolon,
|
Semicolon,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ValueExpressionParserState {
|
||||||
|
NumericOrQuoteOrColumnOrFunction,
|
||||||
|
StringValue,
|
||||||
|
EndQuote,
|
||||||
|
FunctionOpenParenOrEnd,
|
||||||
|
FunctionCloseParen,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum LogicExpressionParserState {
|
enum LogicExpressionParserState {
|
||||||
NumberOrQuoteOrColname,
|
NumberOrQuoteOrColname,
|
||||||
|
@ -124,14 +144,26 @@ enum LogicExpressionParserState {
|
||||||
|
|
||||||
|
|
||||||
pub fn tokenizer(text: String) -> Vec<String> {
|
pub fn tokenizer(text: String) -> Vec<String> {
|
||||||
let parts = HashSet::from([' ', ',', ';', '(', ')', '\'']);
|
let parts = HashSet::from([' ', ',', ';', '(', ')', '\'', '\"']);
|
||||||
let mut tokens: Vec<String> = vec![];
|
let mut tokens: Vec<String> = vec![];
|
||||||
let mut cur_str = String::new();
|
let mut cur_str = String::new();
|
||||||
let mut in_quotes = false;
|
let mut in_quotes = false;
|
||||||
|
let mut cur_quote = '\0';
|
||||||
|
|
||||||
for cur_char in text.chars() {
|
for cur_char in text.chars() {
|
||||||
if cur_char == '\"' {
|
if !in_quotes && (cur_char == '\"' || cur_char == '\''){
|
||||||
in_quotes = !in_quotes;
|
tokens.push(cur_char.to_string());
|
||||||
|
in_quotes = true;
|
||||||
|
cur_quote = cur_char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if in_quotes && cur_char == cur_quote {
|
||||||
|
tokens.push(cur_str);
|
||||||
|
cur_str = String::new();
|
||||||
|
tokens.push(cur_char.to_string());
|
||||||
|
in_quotes = false;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !in_quotes && parts.contains(&cur_char) {
|
if !in_quotes && parts.contains(&cur_char) {
|
||||||
|
@ -146,21 +178,24 @@ pub fn tokenizer(text: String) -> Vec<String> {
|
||||||
cur_str.push(cur_char);
|
cur_str.push(cur_char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokens.push(cur_str);
|
|
||||||
|
if cur_str.len() > 0 {
|
||||||
|
tokens.push(cur_str);
|
||||||
|
}
|
||||||
|
|
||||||
tokens
|
tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogicValue {
|
impl DataValue {
|
||||||
pub fn from_string(string: String) -> ::anyhow::Result<LogicValue> {
|
pub fn from_string(string: String) -> ::anyhow::Result<DataValue> {
|
||||||
let test = string.parse::<u8>();
|
let test = string.parse::<u8>();
|
||||||
match test {
|
match test {
|
||||||
Ok(u8_val) => {
|
Ok(u8_val) => {
|
||||||
return Ok(LogicValue::U8Value(u8_val));
|
return Ok(DataValue::U8Value(u8_val));
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let res = string.trim_matches(char::from(0));
|
let res = string.trim_matches(char::from(0));
|
||||||
return Ok(LogicValue::StringValue(res.to_string()));
|
return Ok(DataValue::StringValue(res.to_string()));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,20 +203,29 @@ impl LogicValue {
|
||||||
|
|
||||||
impl LogicExpression {
|
impl LogicExpression {
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn is_valid(&self) -> bool {
|
||||||
return mem::discriminant(&self.left_hand) == mem::discriminant(&self.right_hand);
|
println!("{:?}", self.left_hand);
|
||||||
|
if !self.is_evaluatable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let ValueExpression::DataValue(left) = self.left_hand.clone() {
|
||||||
|
if let ValueExpression::DataValue(right) = self.right_hand.clone() {
|
||||||
|
return mem::discriminant(&left) == mem::discriminant(&right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_evaluatable(&self) -> bool {
|
pub fn is_evaluatable(&self) -> bool {
|
||||||
return mem::discriminant(&self.left_hand) != mem::discriminant(&LogicValue::ColumnName(String::from(""))) &&
|
return mem::discriminant(&self.left_hand) == mem::discriminant(&ValueExpression::DataValue(DataValue::U8Value(0))) &&
|
||||||
mem::discriminant(&self.right_hand) != mem::discriminant(&LogicValue::ColumnName(String::from("")));
|
mem::discriminant(&self.right_hand) == mem::discriminant(&ValueExpression::DataValue(DataValue::U8Value(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_values(&mut self, hmap: HashMap<String, LogicValue>) -> ::anyhow::Result<()> {
|
pub fn fill_values(&mut self, hmap: HashMap<String, ValueExpression>) -> ::anyhow::Result<()> {
|
||||||
for (name, value) in hmap {
|
for (name, value) in hmap {
|
||||||
if self.left_hand == LogicValue::ColumnName(name.clone()) {
|
if self.left_hand == ValueExpression::ColumnName(name.clone()) {
|
||||||
self.left_hand = value.clone();
|
self.left_hand = value.clone();
|
||||||
}
|
}
|
||||||
if self.right_hand == LogicValue::ColumnName(name.clone()) {
|
if self.right_hand == ValueExpression::ColumnName(name.clone()) {
|
||||||
self.right_hand = value.clone();
|
self.right_hand = value.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,18 +241,16 @@ impl LogicExpression {
|
||||||
}
|
}
|
||||||
println!("{:?}", self);
|
println!("{:?}", self);
|
||||||
match self.left_hand {
|
match self.left_hand {
|
||||||
LogicValue::StringValue(_) => {
|
ValueExpression::DataValue(DataValue::StringValue(_)) => {
|
||||||
return self.evaluate_string();
|
return self.evaluate_string();
|
||||||
}
|
}
|
||||||
LogicValue::BoolValue(_) => {
|
ValueExpression::DataValue(DataValue::BoolValue(_)) => {
|
||||||
return self.evaluate_bool();
|
return self.evaluate_bool();
|
||||||
}
|
}
|
||||||
LogicValue::U8Value(_) => {
|
ValueExpression::DataValue(DataValue::U8Value(_)) => {
|
||||||
return self.evaluate_u8();
|
return self.evaluate_u8();
|
||||||
}
|
}
|
||||||
LogicValue::ColumnName(_) => {
|
_ => {return Err(anyhow!("Cannot compare non-finalized value expressions"))}
|
||||||
return Err(anyhow!("Cannot compare names of 2 columns, only values"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,16 +271,16 @@ impl LogicExpression {
|
||||||
return Ok(self.left_hand == self.right_hand);
|
return Ok(self.left_hand == self.right_hand);
|
||||||
}
|
}
|
||||||
LogicalOperator::And => {
|
LogicalOperator::And => {
|
||||||
if let LogicValue::BoolValue(left) = self.left_hand {
|
if let ValueExpression::DataValue(DataValue::BoolValue(left)) = self.left_hand {
|
||||||
if let LogicValue::BoolValue(right) = self.right_hand {
|
if let ValueExpression::DataValue(DataValue::BoolValue(right)) = self.right_hand {
|
||||||
return Ok(left && right);
|
return Ok(left && right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(anyhow!("Mismatched datatypes"));
|
return Err(anyhow!("Mismatched datatypes"));
|
||||||
}
|
}
|
||||||
LogicalOperator::Or => {
|
LogicalOperator::Or => {
|
||||||
if let LogicValue::BoolValue(left) = self.left_hand {
|
if let ValueExpression::DataValue(DataValue::BoolValue(left)) = self.left_hand {
|
||||||
if let LogicValue::BoolValue(right) = self.right_hand {
|
if let ValueExpression::DataValue(DataValue::BoolValue(right)) = self.right_hand {
|
||||||
return Ok(left || right);
|
return Ok(left || right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,32 +298,32 @@ impl LogicExpression {
|
||||||
return Ok(self.left_hand == self.right_hand);
|
return Ok(self.left_hand == self.right_hand);
|
||||||
}
|
}
|
||||||
LogicalOperator::GreaterThan => {
|
LogicalOperator::GreaterThan => {
|
||||||
if let LogicValue::U8Value(left) = self.left_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(left)) = self.left_hand {
|
||||||
if let LogicValue::U8Value(right) = self.right_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(right)) = self.right_hand {
|
||||||
return Ok(left > right);
|
return Ok(left > right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(anyhow!("Mismatched datatypes"));
|
return Err(anyhow!("Mismatched datatypes"));
|
||||||
}
|
}
|
||||||
LogicalOperator::LessThan => {
|
LogicalOperator::LessThan => {
|
||||||
if let LogicValue::U8Value(left) = self.left_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(left)) = self.left_hand {
|
||||||
if let LogicValue::U8Value(right) = self.right_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(right)) = self.right_hand {
|
||||||
return Ok(left < right);
|
return Ok(left < right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(anyhow!("Mismatched datatypes"));
|
return Err(anyhow!("Mismatched datatypes"));
|
||||||
}
|
}
|
||||||
LogicalOperator::GreaterThanEqualTo => {
|
LogicalOperator::GreaterThanEqualTo => {
|
||||||
if let LogicValue::U8Value(left) = self.left_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(left)) = self.left_hand {
|
||||||
if let LogicValue::U8Value(right) = self.right_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(right)) = self.right_hand {
|
||||||
return Ok(left >= right);
|
return Ok(left >= right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(anyhow!("Mismatched datatypes"));
|
return Err(anyhow!("Mismatched datatypes"));
|
||||||
}
|
}
|
||||||
LogicalOperator::LessThanEqualTo => {
|
LogicalOperator::LessThanEqualTo => {
|
||||||
if let LogicValue::U8Value(left) = self.left_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(left)) = self.left_hand {
|
||||||
if let LogicValue::U8Value(right) = self.right_hand {
|
if let ValueExpression::DataValue(DataValue::U8Value(right)) = self.right_hand {
|
||||||
return Ok(left <= right);
|
return Ok(left <= right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,6 +337,70 @@ impl LogicExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
fn parse_value_expression(tokens: &mut Vec<String>) -> ::anyhow::Result<ValueExpression> {
|
||||||
|
let mut state: ValueExpressionParserState = ValueExpressionParserState::NumericOrQuoteOrColumnOrFunction;
|
||||||
|
let mut quote_type = "'";
|
||||||
|
let mut value_expr: Option<ValueExpression> = None;
|
||||||
|
let mut ref_name = String::from("");
|
||||||
|
|
||||||
|
while let Some(token) = &tokens.pop() {
|
||||||
|
match state {
|
||||||
|
ValueExpressionParserState::NumericOrQuoteOrColumnOrFunction => {
|
||||||
|
// String test
|
||||||
|
if token == "'" || token == "\"" {
|
||||||
|
quote_type = if token == "'" { "'" } else { "\"" };
|
||||||
|
state = ValueExpressionParserState::StringValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric Test
|
||||||
|
let test = token.parse::<u8>();
|
||||||
|
match test {
|
||||||
|
Ok(u8_val) => {
|
||||||
|
return Ok(ValueExpression::DataValue(DataValue::U8Value(u8_val)));
|
||||||
|
},
|
||||||
|
Err(_) => { },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function name / Column name test
|
||||||
|
ref_name = token.clone();
|
||||||
|
state = ValueExpressionParserState::FunctionOpenParenOrEnd;
|
||||||
|
}
|
||||||
|
ValueExpressionParserState::StringValue => {
|
||||||
|
value_expr = Some(ValueExpression::DataValue(DataValue::StringValue(token.to_string())));
|
||||||
|
state = ValueExpressionParserState::EndQuote;
|
||||||
|
}
|
||||||
|
ValueExpressionParserState::EndQuote => {
|
||||||
|
if token == quote_type {
|
||||||
|
return Ok(value_expr.unwrap());
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("Mismatched quotes at or near {}", token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueExpressionParserState::FunctionOpenParenOrEnd => {
|
||||||
|
if token == "(" {
|
||||||
|
state = ValueExpressionParserState::FunctionCloseParen;
|
||||||
|
} else {
|
||||||
|
tokens.push(token.to_string());
|
||||||
|
return Ok(ValueExpression::ColumnName(ref_name.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueExpressionParserState::FunctionCloseParen => {
|
||||||
|
if token == ")" {
|
||||||
|
return Ok(ValueExpression::FunctionCall(FunctionCall { function_name: ref_name, parameters: vec![] }));
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("Expected funciton closing parenthesis at or near {}", token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ref_name.len() != 0 {
|
||||||
|
return Ok(ValueExpression::ColumnName(ref_name.to_string()));
|
||||||
|
}
|
||||||
|
return Err(anyhow!("Unexpected end of statement"));
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_insert_command(tokens: &mut Vec<String>) -> ::anyhow::Result<Command> {
|
fn parse_insert_command(tokens: &mut Vec<String>) -> ::anyhow::Result<Command> {
|
||||||
let mut state: InsertParserState = InsertParserState::IntoKeyword;
|
let mut state: InsertParserState = InsertParserState::IntoKeyword;
|
||||||
|
|
||||||
|
@ -363,7 +469,23 @@ impl Command {
|
||||||
state = InsertParserState::Value;
|
state = InsertParserState::Value;
|
||||||
}
|
}
|
||||||
InsertParserState::Value => {
|
InsertParserState::Value => {
|
||||||
column_val = token.to_string();
|
tokens.push(token.to_string());
|
||||||
|
let expr = Self::parse_value_expression(tokens)?;
|
||||||
|
if let ValueExpression::DataValue(value) = expr {
|
||||||
|
match value {
|
||||||
|
DataValue::StringValue(val) => {
|
||||||
|
column_val = val.to_string()
|
||||||
|
},
|
||||||
|
DataValue::U8Value(val) => {
|
||||||
|
column_val = val.to_string();
|
||||||
|
}
|
||||||
|
DataValue::BoolValue(val) => {
|
||||||
|
column_val = if val { String::from("TRUE") } else { String::from("FALSE") };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("Expected data at or near {}", token));
|
||||||
|
}
|
||||||
state = InsertParserState::ValueEnd;
|
state = InsertParserState::ValueEnd;
|
||||||
}
|
}
|
||||||
InsertParserState::ValueEnd => {
|
InsertParserState::ValueEnd => {
|
||||||
|
@ -410,8 +532,8 @@ impl Command {
|
||||||
|
|
||||||
fn parse_logic_expression(tokens: &mut Vec<String>) -> ::anyhow::Result<LogicExpression> {
|
fn parse_logic_expression(tokens: &mut Vec<String>) -> ::anyhow::Result<LogicExpression> {
|
||||||
let mut state: LogicExpressionParserState = LogicExpressionParserState::NumberOrQuoteOrColname;
|
let mut state: LogicExpressionParserState = LogicExpressionParserState::NumberOrQuoteOrColname;
|
||||||
let mut left_hand: Option<LogicValue> = None;
|
let mut left_hand: Option<ValueExpression> = None;
|
||||||
let mut right_hand: Option<LogicValue> = None;
|
let mut right_hand: Option<ValueExpression> = None;
|
||||||
let mut operator: Option<LogicalOperator> = None;
|
let mut operator: Option<LogicalOperator> = None;
|
||||||
|
|
||||||
while let Some(token) = &tokens.pop() {
|
while let Some(token) = &tokens.pop() {
|
||||||
|
@ -424,19 +546,19 @@ impl Command {
|
||||||
match test {
|
match test {
|
||||||
Ok(u8_val) => {
|
Ok(u8_val) => {
|
||||||
if left_hand.is_none() {
|
if left_hand.is_none() {
|
||||||
left_hand = Some(LogicValue::U8Value(u8_val));
|
left_hand = Some(ValueExpression::DataValue(DataValue::U8Value(u8_val)));
|
||||||
state = LogicExpressionParserState::Operator;
|
state = LogicExpressionParserState::Operator;
|
||||||
} else {
|
} else {
|
||||||
right_hand = Some(LogicValue::U8Value(u8_val));
|
right_hand = Some(ValueExpression::DataValue(DataValue::U8Value(u8_val)));
|
||||||
return Ok(LogicExpression {left_hand: left_hand.unwrap(), right_hand: right_hand.unwrap(), operator: operator.unwrap()});
|
return Ok(LogicExpression {left_hand: left_hand.unwrap(), right_hand: right_hand.unwrap(), operator: operator.unwrap()});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
if left_hand.is_none() {
|
if left_hand.is_none() {
|
||||||
left_hand = Some(LogicValue::ColumnName(token.to_string()));
|
left_hand = Some(ValueExpression::ColumnName(token.to_string()));
|
||||||
state = LogicExpressionParserState::Operator;
|
state = LogicExpressionParserState::Operator;
|
||||||
} else {
|
} else {
|
||||||
right_hand = Some(LogicValue::ColumnName(token.to_string()));
|
right_hand = Some(ValueExpression::ColumnName(token.to_string()));
|
||||||
return Ok(LogicExpression {left_hand: left_hand.unwrap(), right_hand: right_hand.unwrap(), operator: operator.unwrap()});
|
return Ok(LogicExpression {left_hand: left_hand.unwrap(), right_hand: right_hand.unwrap(), operator: operator.unwrap()});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -445,11 +567,11 @@ impl Command {
|
||||||
|
|
||||||
}
|
}
|
||||||
LogicExpressionParserState::StringValue => {
|
LogicExpressionParserState::StringValue => {
|
||||||
let mut value: Option<LogicValue> = None;
|
let mut value: Option<ValueExpression> = None;
|
||||||
if token == "'" {
|
if token == "'" {
|
||||||
value = Some(LogicValue::StringValue("".to_string()));
|
value = Some(ValueExpression::DataValue(DataValue::StringValue("".to_string())));
|
||||||
} else {
|
} else {
|
||||||
value = Some(LogicValue::StringValue(token.to_string()));
|
value = Some(ValueExpression::DataValue(DataValue::StringValue(token.to_string())));
|
||||||
}
|
}
|
||||||
if left_hand.is_none() {
|
if left_hand.is_none() {
|
||||||
left_hand = value;
|
left_hand = value;
|
||||||
|
@ -701,4 +823,12 @@ impl Command {
|
||||||
tokens.reverse();
|
tokens.reverse();
|
||||||
return Ok(Self::parse_logic_expression(&mut tokens)?);
|
return Ok(Self::parse_logic_expression(&mut tokens)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn value_expression_from_string(command_str: String) -> ::anyhow::Result<ValueExpression> {
|
||||||
|
let mut tokens: Vec<String> = tokenizer(command_str.clone());
|
||||||
|
println!("{}", command_str);
|
||||||
|
println!("{:?}", tokens);
|
||||||
|
tokens.reverse();
|
||||||
|
return Ok(Self::parse_value_expression(&mut tokens)?);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -24,17 +24,7 @@ impl Datatype {
|
||||||
pub fn to_bytes(&self, data_val: String) -> ::anyhow::Result<Vec<u8>> {
|
pub fn to_bytes(&self, data_val: String) -> ::anyhow::Result<Vec<u8>> {
|
||||||
match self {
|
match self {
|
||||||
Datatype::CharacterVarying => {
|
Datatype::CharacterVarying => {
|
||||||
// Ensure string is formatted properly
|
|
||||||
if !data_val.starts_with('\"') || !data_val.ends_with('\"') {
|
|
||||||
return Err(::anyhow::anyhow!(
|
|
||||||
"ERROR: Unable to parse value for type CharacterVarying"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let mut str_bytes = data_val.as_bytes().to_vec();
|
let mut str_bytes = data_val.as_bytes().to_vec();
|
||||||
|
|
||||||
// Remove dquotes
|
|
||||||
str_bytes.remove(0);
|
|
||||||
str_bytes.remove(str_bytes.len() - 1);
|
|
||||||
Ok(str_bytes)
|
Ok(str_bytes)
|
||||||
}
|
}
|
||||||
Datatype::Integer => {
|
Datatype::Integer => {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Datatype;
|
use crate::table::datatypes::Datatype;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct ColumnDefinition {
|
pub struct ColumnDefinition {
|
2
squirrel_server/.gitignore
vendored
Normal file
2
squirrel_server/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/data
|
|
@ -9,8 +9,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
|
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "squirrel-server"
|
name = "squirrel_core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "squirrel_server"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"squirrel_core",
|
||||||
|
]
|
8
squirrel_server/Cargo.toml
Normal file
8
squirrel_server/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "squirrel_server"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.72"
|
||||||
|
squirrel_core = { path = "../squirrel_core" }
|
|
@ -6,13 +6,10 @@ use std::thread;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
mod parser;
|
pub use squirrel_core::parser::command::Command;
|
||||||
pub use parser::command::Command;
|
use squirrel_core::parser::command::{CreateCommand, InsertCommand, SelectCommand, DeleteCommand, LogicExpression, InsertItem, DataValue, ValueExpression};
|
||||||
|
pub use squirrel_core::table::datatypes::Datatype;
|
||||||
mod table;
|
pub use squirrel_core::table::table_definition::{ColumnDefinition, TableDefinition};
|
||||||
use parser::command::{CreateCommand, InsertCommand, SelectCommand, DeleteCommand, LogicExpression, InsertItem, LogicValue};
|
|
||||||
pub use table::datatypes::Datatype;
|
|
||||||
pub use table::table_definition::{ColumnDefinition, TableDefinition};
|
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 500;
|
const BUFFER_SIZE: usize = 500;
|
||||||
|
|
||||||
|
@ -101,7 +98,7 @@ fn handle_delete(command: DeleteCommand) -> ::anyhow::Result<String> {
|
||||||
.open(format!("./data/blobs/{}_new", command.table_name))?;
|
.open(format!("./data/blobs/{}_new", command.table_name))?;
|
||||||
|
|
||||||
while file.read_exact(buf.as_mut_slice()).is_ok() {
|
while file.read_exact(buf.as_mut_slice()).is_ok() {
|
||||||
let mut row_data: HashMap<String, LogicValue> = HashMap::new();
|
let mut row_data: HashMap<String, ValueExpression> = HashMap::new();
|
||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
if let Some(ref le) = command.logic_expression {
|
if let Some(ref le) = command.logic_expression {
|
||||||
let mut logic_expr = le.clone();
|
let mut logic_expr = le.clone();
|
||||||
|
@ -113,7 +110,7 @@ fn handle_delete(command: DeleteCommand) -> ::anyhow::Result<String> {
|
||||||
};
|
};
|
||||||
let str_val = col_def.data_type.from_bytes(&buf[idx..(idx + len)])?;
|
let str_val = col_def.data_type.from_bytes(&buf[idx..(idx + len)])?;
|
||||||
idx += len;
|
idx += len;
|
||||||
row_data.insert(col_def.name.clone(), LogicValue::from_string(str_val)?);
|
row_data.insert(col_def.name.clone(), ValueExpression::DataValue(DataValue::from_string(str_val)?));
|
||||||
}
|
}
|
||||||
idx = 0;
|
idx = 0;
|
||||||
logic_expr.fill_values(row_data);
|
logic_expr.fill_values(row_data);
|
||||||
|
@ -160,7 +157,7 @@ fn handle_select(command: SelectCommand) -> ::anyhow::Result<String> {
|
||||||
|
|
||||||
while file.read_exact(buf.as_mut_slice()).is_ok() {
|
while file.read_exact(buf.as_mut_slice()).is_ok() {
|
||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
let mut row_data: HashMap<String, LogicValue> = HashMap::new();
|
let mut row_data: HashMap<String, ValueExpression> = HashMap::new();
|
||||||
if let Some(ref le) = command.logic_expression {
|
if let Some(ref le) = command.logic_expression {
|
||||||
let mut logic_expr = le.clone();
|
let mut logic_expr = le.clone();
|
||||||
for col_def in &tabledef.column_defs {
|
for col_def in &tabledef.column_defs {
|
||||||
|
@ -171,7 +168,7 @@ fn handle_select(command: SelectCommand) -> ::anyhow::Result<String> {
|
||||||
};
|
};
|
||||||
let str_val = col_def.data_type.from_bytes(&buf[idx..(idx + len)])?;
|
let str_val = col_def.data_type.from_bytes(&buf[idx..(idx + len)])?;
|
||||||
idx += len;
|
idx += len;
|
||||||
row_data.insert(col_def.name.clone(), LogicValue::from_string(str_val)?);
|
row_data.insert(col_def.name.clone(), ValueExpression::DataValue(DataValue::from_string(str_val)?));
|
||||||
}
|
}
|
||||||
idx = 0;
|
idx = 0;
|
||||||
logic_expr.fill_values(row_data);
|
logic_expr.fill_values(row_data);
|
||||||
|
@ -315,117 +312,5 @@ fn main() -> std::io::Result<()> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn logical_expression() -> anyhow::Result<()> {
|
|
||||||
assert_eq!(Command::le_from_string(String::from("1 < 5")).unwrap().evaluate().unwrap(), true);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("1 > 5")).unwrap().evaluate().unwrap(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("1 <= 5")).unwrap().evaluate().unwrap(), true);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("1 >= 5")).unwrap().evaluate().unwrap(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("5 >= 5")).unwrap().evaluate().unwrap(), true);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("5 <= 5")).unwrap().evaluate().unwrap(), true);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("5 = 5")).unwrap().evaluate().unwrap(), true);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("5 AND 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("5 OR 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' = 'Test'")).unwrap().evaluate().unwrap(), true);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' = 'Text'")).unwrap().evaluate().unwrap(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' <= 'Test'")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' >= 'Test'")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' < 'Test'")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' > 'Test'")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' AND 'Test'")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' OR 'Test'")).unwrap().evaluate().is_ok(), false);
|
|
||||||
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' < 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' > 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' <= 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' >= 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' >= 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' <= 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' = 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' AND 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
assert_eq!(Command::le_from_string(String::from("'Test' OR 5")).unwrap().evaluate().is_ok(), false);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn insert_statement() -> anyhow::Result<()> {
|
|
||||||
let empty_statement = "";
|
|
||||||
let regular_statement = "INSERT INTO users (id, name) VALUES (1, \"Test\");";
|
|
||||||
let extra_ws_statement =
|
|
||||||
"INSERT INTO users (id, name) VALUES (1, \"Test\") ;";
|
|
||||||
let min_ws_statement = "INSERT INTO users(id, name) VALUES(1, \"Test\");";
|
|
||||||
let str_comma_statement = "INSERT INTO users(id, name) VALUES(1, \"Firstname, Lastname\");";
|
|
||||||
|
|
||||||
let expected_output = Command::Insert(InsertCommand {
|
|
||||||
table_name: "users".to_string(),
|
|
||||||
items: HashMap::from([
|
|
||||||
(
|
|
||||||
"id".to_string(),
|
|
||||||
InsertItem {
|
|
||||||
column_name: "id".to_string(),
|
|
||||||
column_value: "1".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"name".to_string(),
|
|
||||||
InsertItem {
|
|
||||||
column_name: "name".to_string(),
|
|
||||||
column_value: "\"Test\"".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
|
|
||||||
let expected_output_comma = Command::Insert(InsertCommand {
|
|
||||||
table_name: "users".to_string(),
|
|
||||||
items: HashMap::from([
|
|
||||||
(
|
|
||||||
"id".to_string(),
|
|
||||||
InsertItem {
|
|
||||||
column_name: "id".to_string(),
|
|
||||||
column_value: "1".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"name".to_string(),
|
|
||||||
InsertItem {
|
|
||||||
column_name: "name".to_string(),
|
|
||||||
column_value: "\"Firstname, Lastname\"".to_string(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Command::from_string(String::from(empty_statement)).is_ok(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Command::from_string(String::from(regular_statement))?,
|
|
||||||
expected_output
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Command::from_string(String::from(extra_ws_statement))?,
|
|
||||||
expected_output
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Command::from_string(String::from(min_ws_statement))?,
|
|
||||||
expected_output
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Command::from_string(String::from(str_comma_statement))?,
|
|
||||||
expected_output_comma
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
Loading…
Reference in a new issue