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
58 changes: 40 additions & 18 deletions src/executor/execute.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use {
crate::{parse_sql::Query, Glue, Result, Row, Value},
super::types::get_first_name,
crate::{parse_sql::Query, Glue, Result, Row},
serde::Serialize,
sqlparser::ast::{SetVariableValue, Statement},
std::convert::TryInto,
sqlparser::ast::{ObjectType, Statement},
thiserror::Error as ThisError,
};

Expand Down Expand Up @@ -57,6 +57,24 @@ impl Glue {
let Query(statement) = statement;

match statement {
Statement::CreateDatabase {
db_name,
if_not_exists,
location,
..
} => {
if !self.try_extend_from_path(
db_name.0[0].value.clone(),
location
.clone()
.ok_or(ExecuteError::InvalidDatabaseLocation)?,
)? && !if_not_exists
{
Err(ExecuteError::DatabaseExists(db_name.0[0].value.clone()).into())
} else {
Ok(Payload::Success)
}
}
//- Modification
//-- Tables
Statement::CreateTable {
Expand All @@ -73,10 +91,20 @@ impl Glue {
names,
if_exists,
..
} => self
.drop(object_type, names, *if_exists)
.await
.map(|_| Payload::DropTable),
} => match object_type {
ObjectType::Schema => {
// Schema for now // TODO: sqlparser-rs#454
if !self.reduce(&get_first_name(names)?) && !if_exists {
Err(ExecuteError::ObjectNotRecognised.into())
} else {
Ok(Payload::Success)
}
}
object_type => self
.drop(object_type, names, *if_exists)
.await
.map(|_| Payload::DropTable),
},
#[cfg(feature = "alter-table")]
Statement::AlterTable { name, operation } => self
.alter_table(name, operation)
Expand Down Expand Up @@ -128,20 +156,14 @@ impl Glue {
//- Context
Statement::SetVariable {
variable, value, ..
} => {
let first_value = value.get(0).unwrap(); // Why might one want anything else?
let value: Value = match first_value {
SetVariableValue::Ident(..) => unimplemented!(),
SetVariableValue::Literal(literal) => literal.try_into()?,
};
let name = variable.value.clone();
self.get_mut_context()?.set_variable(name, value);
Ok(Payload::Success)
}
} => self
.set_variable(variable, value)
.await
.map(|_| Payload::Success),

Statement::ExplainTable { table_name, .. } => self.explain(table_name).await,

Statement::CreateDatabase { .. } => unreachable!(), // Handled at Glue interface // TODO: Clean up somehow
Statement::Execute { name, parameters } => self.procedure(name, parameters).await,
_ => Err(ExecuteError::QueryNotSupported.into()),
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ mod alter_table;
mod execute;
mod fetch;
mod other;
mod procedure;
mod query;
mod recipe;
mod set_variable;
mod types;

pub use {
Expand Down
25 changes: 25 additions & 0 deletions src/executor/procedure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use {
crate::{ExecuteError, Glue, Payload, Result},
sqlparser::ast::{Expr, Ident, Value as AstValue},
};

impl Glue {
pub async fn procedure(&mut self, name: &Ident, parameters: &[Expr]) -> Result<Payload> {
return match name.value.as_str() {
"FILE" => {
if let Some(Ok(query)) = parameters.get(0).map(|path| {
if let Expr::Value(AstValue::SingleQuotedString(path)) = path {
std::fs::read_to_string(path).map_err(|_| ())
} else {
Err(())
}
}) {
self.execute(&query)
} else {
Err(ExecuteError::InvalidFileLocation.into())
}
}
_ => Err(ExecuteError::Unimplemented.into()),
};
}
}
21 changes: 21 additions & 0 deletions src/executor/set_variable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use {
crate::{ExecuteError, Glue, Result, Value},
sqlparser::ast::{Ident, SetVariableValue},
};

impl Glue {
pub async fn set_variable(
&mut self,
variable: &Ident,
value: &[SetVariableValue],
) -> Result<()> {
let first_value = value.get(0).ok_or(ExecuteError::MissingComponentsForSet)?;
let value: Value = match first_value {
SetVariableValue::Ident(..) => unimplemented!(),
SetVariableValue::Literal(literal) => literal.try_into()?,
};
let name = variable.value.clone();
self.get_mut_context()?.set_variable(name, value);
Ok(())
}
}
9 changes: 8 additions & 1 deletion src/executor/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
crate::{JoinError, Result, Value},
crate::{ExecuteError, JoinError, Result, Value},
serde::Serialize,
sqlparser::ast::{ObjectName as AstObjectName, TableFactor},
std::fmt::Debug,
Expand All @@ -18,6 +18,13 @@ pub struct ColumnInfo {
pub index: Option<String>,
}

pub(crate) fn get_first_name(names: &[AstObjectName]) -> Result<String> {
names
.get(0)
.and_then(|name| name.0.get(0).map(|name| name.value.clone()))
.ok_or(ExecuteError::ObjectNotRecognised.into())
}

#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct ComplexTableName {
pub database: Option<String>,
Expand Down
123 changes: 50 additions & 73 deletions src/glue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use {
},
futures::executor::block_on,
sqlparser::ast::{
Expr, Ident, ObjectName, ObjectType, Query as AstQuery, SetExpr, Statement,
Value as AstValue, Values,
Expr, Ident, ObjectName, Query as AstQuery, SetExpr, Statement, Value as AstValue, Values,
},
std::{collections::HashMap, fmt::Debug},
};
Expand Down Expand Up @@ -104,10 +103,10 @@ impl Glue {
/// .expect("Storage Creation Failed");
/// let mut other_glue = Glue::new(String::from("other"), other_storage);
///
/// glue.extend(vec![other_glue]);
/// glue.extend_many_glues(vec![other_glue]);
/// ```
///
pub fn extend(&mut self, glues: Vec<Glue>) {
pub fn extend_many_glues(&mut self, glues: Vec<Glue>) {
self.databases.extend(
glues
.into_iter()
Expand All @@ -119,6 +118,53 @@ impl Glue {
.databases,
)
}
pub fn extend_glue(&mut self, glue: Glue) {
self.databases.extend(glue.databases)
}

/// Extend using a ~~[Path]~~ [String] which represents a path
/// Guesses the type of database based on the extension
/// Returns [bool] of whether action was taken
pub fn try_extend_from_path(
&mut self,
database_name: String,
database_path: String,
) -> Result<bool> {
if self.databases.contains_key(&database_name) {
return Ok(false);
}
let connection = if database_path.ends_with('/') {
Connection::Sled(database_path)
} else if database_path.ends_with(".csv") {
Connection::CSV(database_path, CSVSettings::default())
} else if database_path.ends_with(".xlsx") {
Connection::Sheet(database_path)
} else {
return Err(ExecuteError::InvalidDatabaseLocation.into());
};
let database = connection.try_into()?;
Ok(self.extend(database_name, database))
}

/// Extend [Glue] by single database
/// Returns [bool] of whether action was taken
pub fn extend(&mut self, database_name: String, database: Storage) -> bool {
let database_present = self.databases.contains_key(&database_name);
if !database_present {
self.databases.insert(database_name, database);
}
!database_present
}

/// Opposite of [Glue::extend], removes database
/// Returns [bool] of whether action was taken
pub fn reduce(&mut self, database_name: &String) -> bool {
let database_present = self.databases.contains_key(database_name);
if database_present {
self.databases.remove(database_name);
}
database_present
}
}

/// Internal: Modify
Expand Down Expand Up @@ -165,75 +211,6 @@ impl Glue {
}
/// Will execute a pre-parsed query (see [Glue::pre_parse()] for more).
pub fn execute_parsed(&mut self, query: Query) -> Result<Payload> {
if let Query(Statement::CreateDatabase {
db_name,
if_not_exists,
location,
..
}) = query
{
let store_name = db_name.0[0].value.clone();
return if self.databases.iter().any(|(store, _)| store == &store_name) {
if if_not_exists {
Ok(Payload::Success)
} else {
Err(ExecuteError::DatabaseExists(store_name).into())
}
} else {
match location {
None => Err(ExecuteError::InvalidDatabaseLocation.into()), // TODO: Memory
Some(location) => {
let store = if location.ends_with('/') {
Connection::Sled(location).try_into()?
} else if location.ends_with(".csv") {
Connection::CSV(location, CSVSettings::default()).try_into()?
} else if location.ends_with(".xlsx") {
Connection::Sheet(location).try_into()?
} else {
return Err(ExecuteError::InvalidDatabaseLocation.into());
};
self.extend(vec![Glue::new(store_name, store)]);
Ok(Payload::Success)
}
}
};
} else if let Query(Statement::Execute { name, parameters }) = query {
return match name.value.as_str() {
"FILE" => {
if let Some(Ok(query)) = parameters.get(0).map(|path| {
if let Expr::Value(AstValue::SingleQuotedString(path)) = path {
std::fs::read_to_string(path).map_err(|_| ())
} else {
Err(())
}
}) {
self.execute(&query)
} else {
Err(ExecuteError::InvalidFileLocation.into())
}
}
_ => Err(ExecuteError::Unimplemented.into()),
};
} else if let Query(Statement::Drop {
object_type: ObjectType::Schema, // FOR NOW! // TODO: sqlparser-rs#454
if_exists,
names,
..
}) = query
{
let database_name = names
.get(0)
.and_then(|name| name.0.get(0).map(|name| name.value.clone()))
.ok_or(ExecuteError::ObjectNotRecognised)?;

if self.databases.contains_key(&database_name) {
self.databases.remove(&database_name);
} else if !if_exists {
return Err(ExecuteError::ObjectNotRecognised.into());
}
return Ok(Payload::Success);
}

block_on(self.execute_query(&query))
}
/// Provides a parsed query to execute later.
Expand Down
4 changes: 2 additions & 2 deletions src/storages/sheet_storage/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl Store for SheetStorage {
let rows = vec![vec![None; col_count as usize]; (row_count as usize) - 1];
let rows = sheet
.get_collection_to_hashmap()
.into_iter()
.iter()
.filter(|((row, _col), _)| row != &1)
.fold(rows, |mut rows, ((row_num, col_num), cell)| {
rows[(row_num - 2) as usize][(col_num - 1) as usize] = Some(cell.clone());
Expand All @@ -53,7 +53,7 @@ impl Store for SheetStorage {
cell.map(|cell| cell.get_value().to_string())
.unwrap_or_default(),
)
.cast_valuetype(&data_type)
.cast_valuetype(data_type)
.unwrap_or(Value::Null)
})
.collect()),
Expand Down