Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert fixes #2

Merged
merged 12 commits into from
Apr 4, 2021
2 changes: 1 addition & 1 deletion src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub mod value;

pub use {
literal::{Literal, LiteralError},
row::{Row, RowError},
row::{bulk_build_rows_expr, bulk_build_rows_row, Row, RowError},
schema::Schema,
table::{get_name, Table, TableError},
value::{Value, ValueError},
Expand Down
214 changes: 156 additions & 58 deletions src/data/row.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use {
crate::{
data::{schema::ColumnDefExt, Value},
data::{schema::ColumnDefExt, value::TryFromLiteral, Value},
evaluate,
result::Result,
store::Store,
},
serde::{Deserialize, Serialize},
sqlparser::ast::{ColumnDef, Expr, Ident},
sqlparser::ast::{ColumnDef, DataType, Expr, Ident},
std::fmt::Debug,
thiserror::Error,
};
Expand All @@ -17,14 +19,14 @@ pub enum RowError {
#[error("lack of required column: {0}")]
LackOfRequiredColumn(String),

#[error("literals does not fit to columns")]
LackOfRequiredValue(String),

#[error("literals have more values than target columns")]
TooManyValues,
#[error("columns and values must match")]
WrongNumberOfValues,

#[error("conflict! row cannot be empty")]
ConflictOnEmptyRow,

#[error("unreachable")]
Unreachable,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
Expand All @@ -42,69 +44,165 @@ impl Row {
.ok_or_else(|| RowError::ConflictOnEmptyRow.into())
}

pub fn new(column_defs: &[ColumnDef], columns: &[Ident], values: &[Expr]) -> Result<Self> {
if values.len() > column_defs.len() {
return Err(RowError::TooManyValues.into());
}
pub async fn new<T: 'static + Debug>(
storage: &dyn Store<T>,
column_defs: &[ColumnDef],
columns: &[Ident],
expressions: &[Expr],
) -> Result<Self> {
Ok(bulk_build_rows_expr(
storage,
column_defs,
columns,
vec![expressions.to_vec()],
false,
false,
)
.await?
.into_iter()
.next()
.unwrap())
}
}

async fn evaluate_expression<T: 'static + Debug>(
storage: &dyn Store<T>,
expression: &Expr,
data_type: &DataType,
) -> Result<Value> {
Value::try_from_evaluated(
data_type,
evaluate(storage, None, None, expression, false).await?,
)
}

column_defs
.iter()
.enumerate()
.map(|(i, column_def)| {
// For macro TODO: find a better way
async fn process_value<T: 'static + Debug>(
_a: &dyn Store<T>,
value: Value,
_b: &DataType,
) -> Result<Value> {
Ok(value)
}

async fn process_expr<T: 'static + Debug>(
storage: &dyn Store<T>,
expression: Expr,
data_type: &DataType,
) -> Result<Value> {
evaluate_expression(storage, &expression, data_type).await
}

macro_rules! bulk_build_rows {
($type: ty, $name: ident, $processing_function: ident) => {
pub async fn $name<T: 'static + Debug>(
storage: &dyn Store<T>,
column_defs: &[ColumnDef],
columns: &[Ident],
rows: Vec<Vec<$type>>,
do_default: bool,
do_validate: bool,
) -> Result<Vec<Row>> {
// FAIL: No mut
let table_columns_count = column_defs.len();
let selection_columns_count = if columns.len() == 0 {
table_columns_count
} else {
columns.len()
};

if rows.iter().any(|row| row.len() != selection_columns_count) {
return Err(RowError::WrongNumberOfValues.into());
} // KG: I don't like this

let mut output_values: Vec<Vec<Value>> = Vec::new();
output_values.resize(rows.len(), Vec::new());
for row in &mut output_values {
row.resize(table_columns_count, Value::Null);
}
// This feels slow, how can we initialise better?
let column_defs_indexed = column_defs.into_iter().enumerate();
for (column_index, column_def) in column_defs_indexed {
let ColumnDef {
name, data_type, ..
} = column_def;
let name = name.to_string();

let i = match columns.len() {
0 => Some(i),
_ => columns.iter().position(|target| target.value == name),
let name = name.to_string();
let values_index = if columns.len() == 0 {
Some(column_index)
} else {
columns.iter().position(|target| target.value == name)
};

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

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

let default = column_def.get_default();
let expr = match (expr, default) {
(Some(expr), _) => {
expr.ok_or_else(|| RowError::LackOfRequiredValue(name.clone()))
let failure_value = if do_default && default.is_some() {
if let Some(default) = default {
FailureValue::Default(default) // We can't execute this here because some defaults might be functions and some functions might not always have the same result.
} else {
return Err(RowError::Unreachable.into());
}
(None, Some(expr)) => Ok(expr),
(None, _) => Err(RowError::LackOfRequiredColumn(name.clone())),
}?;

let nullable = column_def.is_nullable();

Value::from_expr(&data_type, nullable, expr)
})
.collect::<Result<_>>()
.map(Self)
}

pub fn validate(&self, column_defs: &[ColumnDef]) -> Result<()> {
let items = column_defs
.iter()
.enumerate()
.filter_map(|(index, column_def)| {
let value = self.get_value(index);

value.map(|v| (v, column_def))
});

for (value, column_def) in items {
let ColumnDef { data_type, .. } = column_def;
let nullable = column_def.is_nullable();
} else if nullable {
FailureValue::Null
} else {
FailureValue::Throw
};

value.validate_type(data_type)?;
value.validate_null(nullable)?;
for (row_index, row) in rows.iter().enumerate() {
output_values[row_index][column_index] = if let Some(index) = values_index {
let found = row[index].clone();
let value = $processing_function(storage, found, data_type).await?;

if do_validate {
value.validate_null(nullable)?;
value.validate_type(data_type)?;
}
value
} else {
match failure_value {
FailureValue::Throw => {
return Err(RowError::LackOfRequiredColumn(name.clone()).into());
}
FailureValue::Null => Value::Null,
FailureValue::Default(expression) => {
evaluate_expression(storage, expression, data_type).await?
}
}
}
}
}
Ok(output_values.into_iter().map(Row).collect())
}
};
}

Ok(())
}
bulk_build_rows!(Expr, bulk_build_rows_expr, process_expr);
bulk_build_rows!(Value, bulk_build_rows_value, process_value);

pub async fn bulk_build_rows_row<T: 'static + Debug>(
storage: &dyn Store<T>,
column_defs: &[ColumnDef],
columns: &[Ident],
rows: Vec<Row>,
do_default: bool,
do_validate: bool,
) -> Result<Vec<Row>> {
bulk_build_rows_value(
storage,
column_defs,
columns,
rows.into_iter().map(|row| row.0).collect(),
do_default,
do_validate,
)
.await
}

enum FailureValue<'a> {
Default(&'a Expr),
Null,
Throw,
}
19 changes: 19 additions & 0 deletions src/data/value/evaluated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use {
crate::{
data::Value,
executor::Evaluated,
result::{Error, Result},
},
std::convert::TryFrom,
};

impl TryFrom<Evaluated<'_>> for Value {
type Error = Error;

fn try_from(evaluated: Evaluated) -> Result<Self> {
match evaluated {
Evaluated::Literal(literal) => Value::try_from(literal),
Evaluated::Value(value) => Ok(value),
}
}
}
10 changes: 10 additions & 0 deletions src/data/value/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use {
crate::{
data::Literal,
result::{Error, Result},
Evaluated,
},
sqlparser::ast::DataType,
std::{cmp::Ordering, convert::TryFrom},
Expand Down Expand Up @@ -88,12 +89,21 @@ impl TryFrom<Literal<'_>> for Value {
}

pub trait TryFromLiteral {
fn try_from_evaluated(data_type: &DataType, evaluated: Evaluated<'_>) -> Result<Value>;

fn try_from_literal(data_type: &DataType, literal: &Literal<'_>) -> Result<Value>;

fn try_cast_from_literal(data_type: &DataType, literal: &Literal<'_>) -> Result<Value>;
}

impl TryFromLiteral for Value {
fn try_from_evaluated(data_type: &DataType, evaluated: Evaluated<'_>) -> Result<Value> {
match evaluated {
Evaluated::Literal(value) => Value::try_from_literal(data_type, &value),
Evaluated::Value(value) => Ok(value.into_owned()),
}
}

fn try_from_literal(data_type: &DataType, literal: &Literal<'_>) -> Result<Value> {
match (data_type, literal) {
(DataType::Boolean, Literal::Boolean(v)) => Ok(Value::Bool(*v)),
Expand Down
Loading