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
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum CreateTableColumnConstraintType {
PrimaryKey,
Unique,

View File

@ -6,7 +6,7 @@
use std::borrow::Cow;
use std::collections::BTreeSet;
use columnvalueops::ColumnValueOps;
use columnvalueops::{ColumnValueOps, ColumnValueOpsExt};
use databaseinfo::{DatabaseInfo, TableInfo, ColumnInfo};
use databasestorage::{Group, DatabaseStorage};
use identifier::Identifier;
@ -84,6 +84,17 @@ impl<'a> Group for ScanGroup<'a> {
let mut key_offset = 8;
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() {
Some(l) => l as usize,
None => {
@ -99,6 +110,7 @@ impl<'a> Group for ScanGroup<'a> {
let value = ColumnValueOps::from_bytes(column.dbtype, bytes.into_cow()).unwrap();
key_offset += size;
value
}
}).collect();
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 nullable = column.constraints.iter().any(|c| {
c.constraint == ast::CreateTableColumnConstraintType::Nullable
});
Ok(table::Column {
offset: i as u32,
name: name,
dbtype: dbtype
dbtype: dbtype,
nullable: nullable
})
}).collect();
@ -186,8 +203,8 @@ impl TempDb {
let mut table = try!(self.get_table_mut(stmt.table));
let column_types: Vec<DbType> = table.get_columns().iter().map(|c| {
c.dbtype
let column_types: Vec<(DbType, bool)> = table.get_columns().iter().map(|c| {
(c.dbtype, c.nullable)
}).collect();
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"));
}
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);
match ast_index {
@ -228,12 +245,13 @@ impl TempDb {
// TODO - allocate buffer outside of loop
let mut buf = Vec::new();
let expr = &row[*ast_index];
ast_expression_to_data(expr, dbtype, &mut buf);
Cow::Owned(buf)
let is_null = ast_expression_to_data(expr, dbtype, nullable, &mut buf);
(Cow::Owned(buf), is_null)
},
None => {
// 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 std::borrow::IntoCow;
let value: Variant = match expr {
&Null => {
ColumnValueOpsExt::null()
},
&StringLiteral(ref s) => {
let r: &str = &s;
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!()
};
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();
buf.push_all(&bytes);
if nullable { Some(false) } else { None }
}
}
}

View File

@ -36,6 +36,7 @@ pub struct Column {
pub offset: u32,
pub name: Identifier,
pub dbtype: DbType,
pub nullable: bool
}
impl TableInfo for Table {
@ -63,7 +64,7 @@ impl TableInfo for Table {
impl Table {
/// 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>
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
@ -80,13 +81,29 @@ impl Table {
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;
trace!("column data for {}: {:?}", column.name, data);
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_variable_length() {
let mut buf = [0; 8];
@ -94,6 +111,14 @@ impl Table {
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);
} else {
return Err(UpdateError::ValidationError {
@ -101,6 +126,7 @@ impl Table {
});
}
}
}
key.extend(lengths);