Skip to content

Commit

Permalink
Support null storage
Browse files Browse the repository at this point in the history
  • Loading branch information
lewiszlw committed Feb 6, 2024
1 parent 1031403 commit 05d4ad0
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 127 deletions.
1 change: 1 addition & 0 deletions bustubx/src/common/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
47 changes: 1 addition & 46 deletions bustubx/src/expression/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,52 +23,7 @@ impl ExprTrait for Cast {

fn evaluate(&self, tuple: &Tuple) -> BustubxResult<ScalarValue> {
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<Column> {
Expand Down
5 changes: 4 additions & 1 deletion bustubx/src/planner/logical_planner/bind_expr.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion bustubx/src/planner/logical_planner/plan_create_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())),
)
Expand Down
2 changes: 2 additions & 0 deletions bustubx/src/storage/codec/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Check warning on line 8 in bustubx/src/storage/codec/mod.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `table_page::TablePageCodec`
pub use tuple::TupleCodec;

// data + consumed offset
Expand Down
1 change: 1 addition & 0 deletions bustubx/src/storage/codec/table_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct TablePageCodec;
120 changes: 44 additions & 76 deletions bustubx/src/storage/table_page.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::buffer::{PageId, BUSTUBX_PAGE_SIZE, INVALID_PAGE_ID};

Check warning on line 1 in bustubx/src/storage/table_page.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `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};

Expand Down Expand Up @@ -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).
Expand All @@ -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);
Expand All @@ -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);
}

Expand All @@ -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);
}
Expand Down Expand Up @@ -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![
Expand Down Expand Up @@ -388,43 +351,48 @@ 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);
assert_eq!(table_page2.next_page_id, 1);
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()]);
}
}
5 changes: 2 additions & 3 deletions tests/sqllogictest/slt/insert.slt
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 NULL

0 comments on commit 05d4ad0

Please sign in to comment.