Skip to content

Commit

Permalink
Merge branch 'INSERT_fixes' of https://github.com/KyGost/gluesql into…
Browse files Browse the repository at this point in the history
… INSERT_fixes
  • Loading branch information
KyGost committed Apr 3, 2021
2 parents 328858d + bd401a7 commit 4f1b1da
Show file tree
Hide file tree
Showing 21 changed files with 566 additions and 31 deletions.
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ keywords = ["sql-database", "sql", "functional", "no-mut-in-the-middle", "webass
all-features = true

[features]
default = ["sled-storage", "alter-table"]
default = ["sled-storage", "alter-table", "expanded-api", "auto-increment"]

# ALTER TABLE is optional.
# You can include whether ALTER TABLE support or not for your custom database implementation.
alter-table = []
auto-increment = []

expanded-api = ["serde_json"]

# Who wants to make a custom storage engine,
# default storage engine sled-storage is not required.
Expand All @@ -37,6 +40,11 @@ pin-project = "1.0.1"
serde = { version = "1.0.117", features = ["derive"] }
sqlparser = { version = "0.8.0", features = ["serde"] }
thiserror = "1.0.21"
fstrings = "0.2.3"

# OPTIONAL DEPENDENCIES
# dependencies for sled-storage
serde_json = { version = "1.0.0", optional = true }

# dependencies for sled-storage
bincode = { version = "1.3.1", optional = true }
Expand Down
21 changes: 21 additions & 0 deletions src/data/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use {
thiserror::Error,
};

#[cfg(feature = "auto-increment")]
use sqlparser::ast::Value as Literal;

#[derive(Error, Serialize, Debug, PartialEq)]
pub enum RowError {
#[error("lack of required column: {0}")]
Expand Down Expand Up @@ -131,6 +134,24 @@ macro_rules! bulk_build_rows {
columns.iter().position(|target| target.value == name)
};

let expr = i.map(|i| values.get(i));

#[cfg(feature = "auto-increment")]
if matches!(expr, None | Some(Some(Expr::Value(Literal::Null))))
&& column_def.is_auto_incremented()
{
return Ok(Value::Null);
}

let default = column_def.get_default();
let expr = match (expr, default) {
(Some(expr), _) => {
expr.ok_or_else(|| RowError::LackOfRequiredValue(name.clone()))
}
(None, Some(expr)) => Ok(expr),
(None, _) => Err(RowError::LackOfRequiredColumn(name.clone())),
}?;

let nullable = column_def.is_nullable();

let default = column_def.get_default();
Expand Down
49 changes: 47 additions & 2 deletions src/data/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ use {
sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, Expr},
};

#[cfg(feature = "auto-increment")]
use sqlparser::{
dialect::keywords::Keyword,
tokenizer::{Token, Word},
};

#[derive(Clone, Serialize, Deserialize)]
pub struct Schema {
pub table_name: String,
Expand All @@ -12,6 +18,9 @@ pub struct Schema {
pub trait ColumnDefExt {
fn is_nullable(&self) -> bool;

#[cfg(feature = "auto-increment")]
fn is_auto_incremented(&self) -> bool;

fn get_default(&self) -> Option<&Expr>;
}

Expand All @@ -22,13 +31,49 @@ impl ColumnDefExt for ColumnDef {
.any(|ColumnOptionDef { option, .. }| option == &ColumnOption::Null)
}

#[cfg(feature = "auto-increment")]
fn is_auto_incremented(&self) -> bool {
self.options
.iter()
.any(|ColumnOptionDef { option, .. }| option.is_auto_increment())
}

fn get_default(&self) -> Option<&Expr> {
self.options
.iter()
.filter_map(|ColumnOptionDef { option, .. }| match option {
.find_map(|ColumnOptionDef { option, .. }| match option {
ColumnOption::Default(expr) => Some(expr),
_ => None,
})
.next()
}
}

pub trait ColumnOptionExt {
fn is_auto_increment(&self) -> bool;
}

#[cfg(not(feature = "auto-increment"))]
impl ColumnOptionExt for ColumnOption {
fn is_auto_increment(&self) -> bool {
false
}
}

#[cfg(feature = "auto-increment")]
impl ColumnOptionExt for ColumnOption {
fn is_auto_increment(&self) -> bool {
matches!(self,
ColumnOption::DialectSpecific(tokens)
if matches!(
tokens[..],
[
Token::Word(Word {
keyword: Keyword::AUTO_INCREMENT,
..
}),
..
]
)
)
}
}
3 changes: 3 additions & 0 deletions src/executor/alter/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ pub enum AlterError {

#[error("column '{0}' of data type '{1}' is unsupported for unique constraint")]
UnsupportedDataTypeForUniqueColumn(String, String),

#[error("column '{0}' of data type '{1}' is unsupported for auto increment constraint, only INTEGER is allowed")]
UnsupportedDataTypeForAutoIncrementColumn(String, String),
}
17 changes: 15 additions & 2 deletions src/executor/alter/validate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
super::AlterError,
crate::result::Result,
crate::{data::schema::ColumnOptionExt, result::Result},
sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType},
};

Expand Down Expand Up @@ -28,7 +28,7 @@ pub fn validate(column_def: &ColumnDef) -> Result<()> {
| ColumnOption::NotNull
| ColumnOption::Default(_)
| ColumnOption::Unique { .. }
)
) && !option.is_auto_increment()
}) {
return Err(AlterError::UnsupportedColumnOption(option.to_string()).into());
}
Expand All @@ -46,5 +46,18 @@ pub fn validate(column_def: &ColumnDef) -> Result<()> {
.into());
}

#[cfg(feature = "auto-increment")]
if !matches!(data_type, DataType::Int)
&& options
.iter()
.any(|ColumnOptionDef { option, .. }| option.is_auto_increment())
{
return Err(AlterError::UnsupportedDataTypeForAutoIncrementColumn(
name.to_string(),
data_type.to_string(),
)
.into());
}

Ok(())
}
54 changes: 54 additions & 0 deletions src/executor/column_options/auto_increment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#![cfg(feature = "auto-increment")]
use {
crate::{
data::{schema::ColumnDefExt, Row},
result::MutResult,
store::{AlterTable, AutoIncrement, Store, StoreMut},
Value,
},
sqlparser::ast::ColumnDef,
std::fmt::Debug,
};

pub async fn run<
T: 'static + Debug,
Storage: Store<T> + StoreMut<T> + AlterTable + AutoIncrement,
>(
storage: Storage,
rows: Vec<Row>,
column_defs: &[ColumnDef],
table_name: &str,
) -> MutResult<Storage, Vec<Row>> {
// FAIL: No-mut
let auto_increment_columns = column_defs
.iter()
.enumerate()
.filter(|(_, column_def)| column_def.is_auto_incremented())
.map(|(index, column_def)| {
(
index,
column_def.name.value.clone(),
rows.iter()
.filter(|row| matches!(row.get_value(index), Some(Value::Null)))
.count() as i64,
)
})
.collect();

let (storage, column_values) = storage
.generate_increment_values(table_name.to_string(), auto_increment_columns)
.await?;

let mut rows = rows;
let mut column_values = column_values;
for row in &mut rows {
for ((index, _name), value) in &mut column_values {
if matches!(row.0[*index], Value::Null) {
row.0[*index] = Value::I64(*value);

*value += 1;
}
}
}
Ok((storage, rows))
}
2 changes: 2 additions & 0 deletions src/executor/column_options/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(feature = "auto-increment")]
pub mod auto_increment;
31 changes: 26 additions & 5 deletions src/executor/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use {
data::{bulk_build_rows_expr, bulk_build_rows_row, get_name, Row, Schema},
parse_sql::Query,
result::{MutResult, Result},
store::{AlterTable, Store, StoreMut},
store::{AlterTable, AutoIncrement, Store, StoreMut},
},
futures::stream::TryStreamExt,
serde::Serialize,
Expand All @@ -22,6 +22,9 @@ use {
thiserror::Error as ThisError,
};

#[cfg(feature = "auto-increment")]
use super::column_options::auto_increment;

#[derive(ThisError, Serialize, Debug, PartialEq)]
pub enum ExecuteError {
#[error("query not supported")]
Expand Down Expand Up @@ -50,7 +53,7 @@ pub enum Payload {
AlterTable,
}

pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>(
pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable + AutoIncrement>(
storage: U,
query: &Query,
) -> MutResult<U, Payload> {
Expand All @@ -75,6 +78,17 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
};
}

macro_rules! try_into {
($self: expr, $expr: expr) => {
match $expr {
Err(e) => {
return Err(($self, e));
}
Ok(v) => v,
}
};
}

let Query(query) = query;

match query {
Expand Down Expand Up @@ -108,7 +122,8 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
source,
..
} => {
let (rows, table_name) = try_block!(storage, {
#[allow(unused_variables)]
let (rows, column_defs, column_validation, table_name) = try_block!(storage, {
let table_name = get_name(table_name)?;
let Schema { column_defs, .. } = storage
.fetch_schema(table_name)
Expand Down Expand Up @@ -151,9 +166,15 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
}
};

validate_unique(&storage, &table_name, column_validation, rows.iter()).await?;
Ok((rows, column_defs, column_validation, table_name))
});

#[cfg(feature = "auto-increment")]
let (storage, rows) =
auto_increment::run(storage, rows, &column_defs, table_name).await?;

Ok((rows, table_name))
try_into!(storage, {
validate_unique(&storage, &table_name, column_validation, rows.iter()).await
});

let num_rows = rows.len();
Expand Down
1 change: 1 addition & 0 deletions src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod aggregate;
mod alter;
mod blend;
mod column_options;
mod context;
mod evaluate;
mod execute;
Expand Down
Loading

0 comments on commit 4f1b1da

Please sign in to comment.