mirror of
https://github.com/fluencelabs/llamadb
synced 2025-05-01 09:42:17 +00:00
Implement NULL columns
This commit is contained in:
parent
0f7f2e20a9
commit
710fe31858
@ -136,7 +136,7 @@ pub struct CreateTableColumnConstraint {
|
|||||||
pub constraint: CreateTableColumnConstraintType
|
pub constraint: CreateTableColumnConstraintType
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum CreateTableColumnConstraintType {
|
pub enum CreateTableColumnConstraintType {
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Unique,
|
Unique,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use columnvalueops::ColumnValueOps;
|
use columnvalueops::{ColumnValueOps, ColumnValueOpsExt};
|
||||||
use databaseinfo::{DatabaseInfo, TableInfo, ColumnInfo};
|
use databaseinfo::{DatabaseInfo, TableInfo, ColumnInfo};
|
||||||
use databasestorage::{Group, DatabaseStorage};
|
use databasestorage::{Group, DatabaseStorage};
|
||||||
use identifier::Identifier;
|
use identifier::Identifier;
|
||||||
@ -84,6 +84,17 @@ impl<'a> Group for ScanGroup<'a> {
|
|||||||
let mut key_offset = 8;
|
let mut key_offset = 8;
|
||||||
|
|
||||||
let v: Vec<Variant> = columns.iter().map(|column| {
|
let v: Vec<Variant> = columns.iter().map(|column| {
|
||||||
|
let is_null = if column.nullable {
|
||||||
|
let flag = raw_key[key_offset];
|
||||||
|
key_offset += 1;
|
||||||
|
flag != 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_null {
|
||||||
|
ColumnValueOpsExt::null()
|
||||||
|
} else {
|
||||||
let size = match column.dbtype.get_fixed_length() {
|
let size = match column.dbtype.get_fixed_length() {
|
||||||
Some(l) => l as usize,
|
Some(l) => l as usize,
|
||||||
None => {
|
None => {
|
||||||
@ -99,6 +110,7 @@ impl<'a> Group for ScanGroup<'a> {
|
|||||||
let value = ColumnValueOps::from_bytes(column.dbtype, bytes.into_cow()).unwrap();
|
let value = ColumnValueOps::from_bytes(column.dbtype, bytes.into_cow()).unwrap();
|
||||||
key_offset += size;
|
key_offset += size;
|
||||||
value
|
value
|
||||||
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
v.into_cow()
|
v.into_cow()
|
||||||
@ -160,10 +172,15 @@ impl TempDb {
|
|||||||
|
|
||||||
let dbtype = try!(DbType::from_identifier(&type_name, type_array_size).ok_or(format!("{} is not a valid column type", type_name)));
|
let dbtype = try!(DbType::from_identifier(&type_name, type_array_size).ok_or(format!("{} is not a valid column type", type_name)));
|
||||||
|
|
||||||
|
let nullable = column.constraints.iter().any(|c| {
|
||||||
|
c.constraint == ast::CreateTableColumnConstraintType::Nullable
|
||||||
|
});
|
||||||
|
|
||||||
Ok(table::Column {
|
Ok(table::Column {
|
||||||
offset: i as u32,
|
offset: i as u32,
|
||||||
name: name,
|
name: name,
|
||||||
dbtype: dbtype
|
dbtype: dbtype,
|
||||||
|
nullable: nullable
|
||||||
})
|
})
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
@ -186,8 +203,8 @@ impl TempDb {
|
|||||||
|
|
||||||
let mut table = try!(self.get_table_mut(stmt.table));
|
let mut table = try!(self.get_table_mut(stmt.table));
|
||||||
|
|
||||||
let column_types: Vec<DbType> = table.get_columns().iter().map(|c| {
|
let column_types: Vec<(DbType, bool)> = table.get_columns().iter().map(|c| {
|
||||||
c.dbtype
|
(c.dbtype, c.nullable)
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let ast_index_to_column_index: Vec<u32> = match stmt.into_columns {
|
let ast_index_to_column_index: Vec<u32> = match stmt.into_columns {
|
||||||
@ -220,7 +237,7 @@ impl TempDb {
|
|||||||
return Err(format!("INSERT value contains wrong amount of columns"));
|
return Err(format!("INSERT value contains wrong amount of columns"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let iter = column_types.iter().enumerate().map(|(i, &dbtype)| {
|
let iter = column_types.iter().enumerate().map(|(i, &(dbtype, nullable))| {
|
||||||
let ast_index = column_index_to_ast_index.get(&i);
|
let ast_index = column_index_to_ast_index.get(&i);
|
||||||
|
|
||||||
match ast_index {
|
match ast_index {
|
||||||
@ -228,12 +245,13 @@ impl TempDb {
|
|||||||
// TODO - allocate buffer outside of loop
|
// TODO - allocate buffer outside of loop
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let expr = &row[*ast_index];
|
let expr = &row[*ast_index];
|
||||||
ast_expression_to_data(expr, dbtype, &mut buf);
|
let is_null = ast_expression_to_data(expr, dbtype, nullable, &mut buf);
|
||||||
Cow::Owned(buf)
|
(Cow::Owned(buf), is_null)
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
// use default value for column type
|
// use default value for column type
|
||||||
dbtype.get_default()
|
let is_null = if nullable { Some(true) } else { None };
|
||||||
|
(dbtype.get_default(), is_null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -313,11 +331,14 @@ impl TempDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ast_expression_to_data(expr: &ast::Expression, column_type: DbType, buf: &mut Vec<u8>) {
|
fn ast_expression_to_data(expr: &ast::Expression, column_type: DbType, nullable: bool, buf: &mut Vec<u8>) -> Option<bool> {
|
||||||
use sqlsyntax::ast::Expression::*;
|
use sqlsyntax::ast::Expression::*;
|
||||||
use std::borrow::IntoCow;
|
use std::borrow::IntoCow;
|
||||||
|
|
||||||
let value: Variant = match expr {
|
let value: Variant = match expr {
|
||||||
|
&Null => {
|
||||||
|
ColumnValueOpsExt::null()
|
||||||
|
},
|
||||||
&StringLiteral(ref s) => {
|
&StringLiteral(ref s) => {
|
||||||
let r: &str = &s;
|
let r: &str = &s;
|
||||||
ColumnValueOps::from_string_literal(r.into_cow()).unwrap()
|
ColumnValueOps::from_string_literal(r.into_cow()).unwrap()
|
||||||
@ -329,6 +350,17 @@ fn ast_expression_to_data(expr: &ast::Expression, column_type: DbType, buf: &mut
|
|||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match (value.is_null(), nullable) {
|
||||||
|
(true, true) => Some(true),
|
||||||
|
(true, false) => {
|
||||||
|
// TODO: return error
|
||||||
|
unimplemented!()
|
||||||
|
},
|
||||||
|
(false, nullable) => {
|
||||||
let bytes = value.to_bytes(column_type).unwrap();
|
let bytes = value.to_bytes(column_type).unwrap();
|
||||||
buf.push_all(&bytes);
|
buf.push_all(&bytes);
|
||||||
|
|
||||||
|
if nullable { Some(false) } else { None }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ pub struct Column {
|
|||||||
pub offset: u32,
|
pub offset: u32,
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub dbtype: DbType,
|
pub dbtype: DbType,
|
||||||
|
pub nullable: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableInfo for Table {
|
impl TableInfo for Table {
|
||||||
@ -63,7 +64,7 @@ impl TableInfo for Table {
|
|||||||
impl Table {
|
impl Table {
|
||||||
/// rowid is automatically added, and is not included as a specified column
|
/// rowid is automatically added, and is not included as a specified column
|
||||||
pub fn insert_row<'a, I>(&mut self, column_data: I) -> Result<(), UpdateError>
|
pub fn insert_row<'a, I>(&mut self, column_data: I) -> Result<(), UpdateError>
|
||||||
where I: ExactSizeIterator, I: Iterator<Item = Cow<'a, [u8]>>
|
where I: ExactSizeIterator, I: Iterator<Item = (Cow<'a, [u8]>, Option<bool>)>
|
||||||
{
|
{
|
||||||
// TODO - remove Cow in favor of yielding either `u8` slices or `u8` iterators
|
// TODO - remove Cow in favor of yielding either `u8` slices or `u8` iterators
|
||||||
|
|
||||||
@ -80,13 +81,29 @@ impl Table {
|
|||||||
|
|
||||||
trace!("columns: {:?}", self.columns);
|
trace!("columns: {:?}", self.columns);
|
||||||
|
|
||||||
for (column, data_cow) in self.columns.iter().zip(column_data) {
|
for (column, (data_cow, is_null)) in self.columns.iter().zip(column_data) {
|
||||||
let data: &[u8] = &*data_cow;
|
let data: &[u8] = &*data_cow;
|
||||||
|
|
||||||
trace!("column data for {}: {:?}", column.name, data);
|
trace!("column data for {}: {:?}", column.name, data);
|
||||||
|
|
||||||
let len = data.len() as u64;
|
let len = data.len() as u64;
|
||||||
|
|
||||||
|
let append_data = match is_null {
|
||||||
|
Some(true) => {
|
||||||
|
assert_eq!(len, 0);
|
||||||
|
key.push(1);
|
||||||
|
|
||||||
|
false
|
||||||
|
},
|
||||||
|
Some(false) => {
|
||||||
|
key.push(0);
|
||||||
|
|
||||||
|
true
|
||||||
|
},
|
||||||
|
None => true
|
||||||
|
};
|
||||||
|
|
||||||
|
if append_data {
|
||||||
if column.dbtype.is_valid_length(len) {
|
if column.dbtype.is_valid_length(len) {
|
||||||
if column.dbtype.is_variable_length() {
|
if column.dbtype.is_variable_length() {
|
||||||
let mut buf = [0; 8];
|
let mut buf = [0; 8];
|
||||||
@ -94,6 +111,14 @@ impl Table {
|
|||||||
lengths.push_all(&buf);
|
lengths.push_all(&buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(column.nullable, is_null.is_some());
|
||||||
|
|
||||||
|
if let Some(is_null) = is_null {
|
||||||
|
if is_null {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
key.push_all(data);
|
key.push_all(data);
|
||||||
} else {
|
} else {
|
||||||
return Err(UpdateError::ValidationError {
|
return Err(UpdateError::ValidationError {
|
||||||
@ -101,6 +126,7 @@ impl Table {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
key.extend(lengths);
|
key.extend(lengths);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user