Implement NULL columns

This commit is contained in:
Dan Spencer 2015-04-14 03:05:32 -06:00
parent 0f7f2e20a9
commit 710fe31858
3 changed files with 95 additions and 37 deletions

View File

@ -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,

View File

@ -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 }
}
}
} }

View File

@ -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);