From 05d4ad063c3973c30ef0851c74ea150777343691 Mon Sep 17 00:00:00 2001 From: Linwei Zhang Date: Tue, 6 Feb 2024 23:12:48 +0800 Subject: [PATCH] Support null storage --- bustubx/src/common/scalar.rs | 1 + bustubx/src/expression/cast.rs | 47 +------ .../src/planner/logical_planner/bind_expr.rs | 5 +- .../logical_planner/plan_create_table.rs | 7 +- bustubx/src/storage/codec/mod.rs | 2 + bustubx/src/storage/codec/table_page.rs | 1 + bustubx/src/storage/table_page.rs | 120 +++++++----------- tests/sqllogictest/slt/insert.slt | 5 +- 8 files changed, 61 insertions(+), 127 deletions(-) create mode 100644 bustubx/src/storage/codec/table_page.rs diff --git a/bustubx/src/common/scalar.rs b/bustubx/src/common/scalar.rs index 192ab7c..82904c9 100644 --- a/bustubx/src/common/scalar.rs +++ b/bustubx/src/common/scalar.rs @@ -104,6 +104,7 @@ impl ScalarValue { ))), }, DataType::Int32 => match self { + ScalarValue::Int8(v) => Ok(ScalarValue::Int32(v.map(|v| v as i32))), ScalarValue::Int64(v) => Ok(ScalarValue::Int32(v.map(|v| v as i32))), _ => Err(BustubxError::NotSupport(format!( "Failed to cast {} to {} type", diff --git a/bustubx/src/expression/cast.rs b/bustubx/src/expression/cast.rs index fd9976a..8f996f1 100644 --- a/bustubx/src/expression/cast.rs +++ b/bustubx/src/expression/cast.rs @@ -23,52 +23,7 @@ impl ExprTrait for Cast { fn evaluate(&self, tuple: &Tuple) -> BustubxResult { let value = self.expr.evaluate(tuple)?; - match value { - ScalarValue::Boolean(v) => match self.data_type { - DataType::Boolean => Ok(value), - _ => Err(BustubxError::Internal(format!( - "Failed to cast {} as {}", - value, self.data_type - ))), - }, - ScalarValue::Int8(v) => match self.data_type { - DataType::Int8 => Ok(value), - _ => Err(BustubxError::Internal(format!( - "Failed to cast {} as {}", - value, self.data_type - ))), - }, - ScalarValue::Int16(v) => match self.data_type { - DataType::Int16 => Ok(value), - _ => Err(BustubxError::Internal(format!( - "Failed to cast {} as {}", - value, self.data_type - ))), - }, - ScalarValue::Int32(v) => match self.data_type { - DataType::Int32 => Ok(value), - _ => Err(BustubxError::Internal(format!( - "Failed to cast {} as {}", - value, self.data_type - ))), - }, - ScalarValue::Int64(v) => match self.data_type { - DataType::Int32 => Ok(v.map(|v| v as i32).into()), - DataType::Int64 => Ok(value), - _ => Err(BustubxError::Internal(format!( - "Failed to cast {} as {}", - value, self.data_type - ))), - }, - ScalarValue::UInt64(v) => match self.data_type { - DataType::Int32 => Ok(v.map(|v| v as i32).into()), - DataType::UInt64 => Ok(value), - _ => Err(BustubxError::Internal(format!( - "Failed to cast {} as {}", - value, self.data_type - ))), - }, - } + value.cast_to(&self.data_type) } fn to_column(&self, input_schema: &Schema) -> BustubxResult { diff --git a/bustubx/src/planner/logical_planner/bind_expr.rs b/bustubx/src/planner/logical_planner/bind_expr.rs index df3fdaf..1e7ce64 100644 --- a/bustubx/src/planner/logical_planner/bind_expr.rs +++ b/bustubx/src/planner/logical_planner/bind_expr.rs @@ -1,4 +1,4 @@ -use crate::common::TableReference; +use crate::common::{ScalarValue, TableReference}; use crate::expression::{BinaryExpr, ColumnExpr, Expr, Literal}; use crate::planner::LogicalPlanner; use crate::{BustubxError, BustubxResult}; @@ -65,6 +65,9 @@ impl LogicalPlanner<'_> { Ok(Expr::Literal(Literal { value: num.into() })) } sqlparser::ast::Value::Boolean(b) => Ok(Expr::Literal(Literal { value: (*b).into() })), + sqlparser::ast::Value::Null => Ok(Expr::Literal(Literal { + value: ScalarValue::Int8(None), + })), _ => Err(BustubxError::NotSupport(format!( "sqlparser value {} not supported", value diff --git a/bustubx/src/planner/logical_planner/plan_create_table.rs b/bustubx/src/planner/logical_planner/plan_create_table.rs index baa3038..9d46107 100644 --- a/bustubx/src/planner/logical_planner/plan_create_table.rs +++ b/bustubx/src/planner/logical_planner/plan_create_table.rs @@ -14,11 +14,16 @@ impl<'a> LogicalPlanner<'a> { let name = self.bind_table_name(name)?; let mut columns = vec![]; for col_def in column_defs { + let not_null: bool = col_def + .options + .iter() + .find(|opt| matches!(opt.option, sqlparser::ast::ColumnOption::NotNull)) + .is_some(); columns.push( Column::new( col_def.name.value.clone(), (&col_def.data_type).try_into()?, - false, + !not_null, ) .with_relation(Some(name.clone())), ) diff --git a/bustubx/src/storage/codec/mod.rs b/bustubx/src/storage/codec/mod.rs index cb014c6..a164796 100644 --- a/bustubx/src/storage/codec/mod.rs +++ b/bustubx/src/storage/codec/mod.rs @@ -1,9 +1,11 @@ mod common; mod scalar; +mod table_page; mod tuple; pub use common::CommonCodec; pub use scalar::ScalarValueCodec; +pub use table_page::TablePageCodec; pub use tuple::TupleCodec; // data + consumed offset diff --git a/bustubx/src/storage/codec/table_page.rs b/bustubx/src/storage/codec/table_page.rs new file mode 100644 index 0000000..155854d --- /dev/null +++ b/bustubx/src/storage/codec/table_page.rs @@ -0,0 +1 @@ +pub struct TablePageCodec; diff --git a/bustubx/src/storage/table_page.rs b/bustubx/src/storage/table_page.rs index 1014e7c..f8639b3 100644 --- a/bustubx/src/storage/table_page.rs +++ b/bustubx/src/storage/table_page.rs @@ -1,7 +1,7 @@ use crate::buffer::{PageId, BUSTUBX_PAGE_SIZE, INVALID_PAGE_ID}; use crate::catalog::SchemaRef; use crate::common::rid::Rid; -use crate::storage::codec::CommonCodec; +use crate::storage::codec::{CommonCodec, TupleCodec}; use super::tuple::{Tuple, TupleMeta}; @@ -60,13 +60,13 @@ impl TablePage { }; // Check if the current slot has enough space for the new tuple. Return None if not. - if slot_end_offset < tuple.to_bytes().len() as u16 { + if slot_end_offset < TupleCodec::encode(tuple).len() as u16 { return None; } // Calculate the insertion offset for the new tuple by subtracting its data length // from the ending offset of the current slot. - let tuple_offset = slot_end_offset - tuple.to_bytes().len() as u16; + let tuple_offset = slot_end_offset - TupleCodec::encode(tuple).len() as u16; // Calculate the minimum valid tuple insertion offset, including the table page header size, // the total size of each tuple info (existing tuple infos and newly added tuple info). @@ -86,8 +86,11 @@ impl TablePage { let tuple_id = self.num_tuples; // Store tuple information including offset, length, and metadata. - self.tuple_info - .push((tuple_offset, tuple.to_bytes().len() as u16, meta.clone())); + self.tuple_info.push(( + tuple_offset, + TupleCodec::encode(tuple).len() as u16, + meta.clone(), + )); // only check assert_eq!(tuple_id, self.tuple_info.len() as u16 - 1); @@ -98,8 +101,9 @@ impl TablePage { } // Copy the tuple's data into the appropriate position within the page's data buffer. - self.data[tuple_offset as usize..(tuple_offset + tuple.to_bytes().len() as u16) as usize] - .copy_from_slice(&tuple.to_bytes()); + self.data[tuple_offset as usize + ..(tuple_offset + TupleCodec::encode(tuple).len() as u16) as usize] + .copy_from_slice(&TupleCodec::encode(tuple)); return Some(tuple_id); } @@ -122,10 +126,11 @@ impl TablePage { } let (offset, size, meta) = self.tuple_info[tuple_id as usize]; - let tuple = Tuple::from_bytes( + let (tuple, _) = TupleCodec::decode( + &self.data[offset as usize..(offset + size) as usize], self.schema.clone(), - &self.data[offset as usize..(offset + size) as usize].to_vec(), - ); + ) + .unwrap(); return (meta, tuple); } @@ -256,52 +261,10 @@ impl TablePageV2 { mod tests { use crate::buffer::BUSTUBX_PAGE_SIZE; use crate::catalog::{Column, DataType, Schema}; - use crate::{common::rid::Rid, storage::Tuple}; + use crate::storage::codec::TupleCodec; + use crate::storage::Tuple; use std::sync::Arc; - #[test] - pub fn test_table_page_insert() { - let schema = Arc::new(Schema::new(vec![ - Column::new("a".to_string(), DataType::Int8, false), - Column::new("b".to_string(), DataType::Int16, false), - ])); - let mut table_page = super::TablePage::new(schema.clone(), 0); - let meta = super::TupleMeta { - insert_txn_id: 0, - delete_txn_id: 0, - is_deleted: false, - }; - let tuple_id = table_page.insert_tuple( - &meta, - &Tuple::new(schema.clone(), vec![1i8.into(), 1i16.into()]), - ); - assert_eq!(tuple_id, Some(0)); - assert_eq!(table_page.num_tuples, 1); - assert_eq!(table_page.num_deleted_tuples, 0); - assert_eq!(table_page.tuple_info.len(), 1); - assert_eq!( - table_page.tuple_info[tuple_id.unwrap() as usize].0, - BUSTUBX_PAGE_SIZE as u16 - 3 - ); - assert_eq!(table_page.tuple_info[tuple_id.unwrap() as usize].1, 3); - assert_eq!(table_page.tuple_info[tuple_id.unwrap() as usize].2, meta); - - let tuple_id = table_page.insert_tuple( - &meta, - &Tuple::new(schema.clone(), vec![1i8.into(), 1i16.into()]), - ); - assert_eq!(tuple_id, Some(1)); - assert_eq!(table_page.num_tuples, 2); - assert_eq!(table_page.num_deleted_tuples, 0); - assert_eq!(table_page.tuple_info.len(), 2); - assert_eq!( - table_page.tuple_info[tuple_id.unwrap() as usize].0, - BUSTUBX_PAGE_SIZE as u16 - 3 - 3 - ); - assert_eq!(table_page.tuple_info[tuple_id.unwrap() as usize].1, 3); - assert_eq!(table_page.tuple_info[tuple_id.unwrap() as usize].2, meta); - } - #[test] pub fn test_table_page_get_tuple() { let schema = Arc::new(Schema::new(vec![ @@ -388,18 +351,18 @@ mod tests { delete_txn_id: 0, is_deleted: false, }; - let tuple_id1 = table_page.insert_tuple( - &meta, - &Tuple::new(schema.clone(), vec![1i8.into(), 1i16.into()]), - ); - let tuple_id2 = table_page.insert_tuple( - &meta, - &Tuple::new(schema.clone(), vec![2i8.into(), 2i16.into()]), - ); - let tuple_id3 = table_page.insert_tuple( - &meta, - &Tuple::new(schema.clone(), vec![3i8.into(), 3i16.into()]), - ); + + let tuple1 = Tuple::new(schema.clone(), vec![1i8.into(), 1i16.into()]); + let tuple1_size = TupleCodec::encode(&tuple1).len() as u16; + let tuple_id1 = table_page.insert_tuple(&meta, &tuple1); + + let tuple2 = Tuple::new(schema.clone(), vec![2i8.into(), 2i16.into()]); + let tuple2_size = TupleCodec::encode(&tuple2).len() as u16; + let tuple_id2 = table_page.insert_tuple(&meta, &tuple2); + + let tuple3 = Tuple::new(schema.clone(), vec![3i8.into(), 3i16.into()]); + let tuple3_size = TupleCodec::encode(&tuple3).len() as u16; + let tuple_id3 = table_page.insert_tuple(&meta, &tuple3); let bytes = table_page.to_bytes(); let table_page2 = super::TablePage::from_bytes(schema.clone(), &bytes); @@ -407,24 +370,29 @@ mod tests { assert_eq!(table_page2.num_tuples, 3); assert_eq!(table_page2.num_deleted_tuples, 0); assert_eq!(table_page2.tuple_info.len(), 3); - assert_eq!(table_page2.tuple_info[0].0, BUSTUBX_PAGE_SIZE as u16 - 3); - assert_eq!(table_page2.tuple_info[0].1, 3); + + assert_eq!( + table_page2.tuple_info[0].0, + BUSTUBX_PAGE_SIZE as u16 - tuple1_size + ); + assert_eq!( + table_page2.tuple_info[0].1, + TupleCodec::encode(&tuple1).len() as u16 + ); assert_eq!(table_page2.tuple_info[0].2, meta); + assert_eq!( table_page2.tuple_info[1].0, - BUSTUBX_PAGE_SIZE as u16 - 3 - 3 + BUSTUBX_PAGE_SIZE as u16 - tuple1_size - tuple2_size ); - assert_eq!(table_page2.tuple_info[1].1, 3); + assert_eq!(table_page2.tuple_info[1].1, tuple2_size); assert_eq!(table_page2.tuple_info[1].2, meta); + assert_eq!( table_page2.tuple_info[2].0, - BUSTUBX_PAGE_SIZE as u16 - 3 - 3 - 3 + BUSTUBX_PAGE_SIZE as u16 - tuple1_size - tuple2_size - tuple3_size ); - assert_eq!(table_page2.tuple_info[2].1, 3); + assert_eq!(table_page2.tuple_info[2].1, tuple3_size); assert_eq!(table_page2.tuple_info[2].2, meta); - - let (tuple_meta, tuple) = table_page2.get_tuple(&Rid::new(0, tuple_id2.unwrap() as u32)); - assert_eq!(tuple_meta, meta); - assert_eq!(tuple.data, vec![2i8.into(), 2i16.into()]); } } diff --git a/tests/sqllogictest/slt/insert.slt b/tests/sqllogictest/slt/insert.slt index febc542..0aa04e8 100644 --- a/tests/sqllogictest/slt/insert.slt +++ b/tests/sqllogictest/slt/insert.slt @@ -2,11 +2,10 @@ statement ok create table t1 (a int, b int) statement ok -insert into t1 values (1, 1), (2, 3), (5, 4) +insert into t1 values (1, 1), (2, NULL) query II select * from t1 ---- 1 1 -2 3 -5 4 \ No newline at end of file +2 NULL \ No newline at end of file