diff --git a/Cargo.toml b/Cargo.toml index 61774a7c..02eac4dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "kip-sql" -version = "0.0.1-alpha.5" +version = "0.0.1-alpha.6" edition = "2021" authors = ["Kould ", "Xwg "] description = "build the SQL layer of KipDB database" diff --git a/src/binder/alter_table.rs b/src/binder/alter_table.rs index 139e0cdb..8c4c1aaf 100644 --- a/src/binder/alter_table.rs +++ b/src/binder/alter_table.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use super::Binder; use crate::binder::{lower_case_name, split_name, BindError}; use crate::planner::operator::alter_table::add_column::AddColumnOperator; +use crate::planner::operator::alter_table::drop_column::DropColumnOperator; use crate::planner::operator::scan::ScanOperator; use crate::planner::operator::Operator; use crate::planner::LogicalPlan; @@ -18,13 +19,13 @@ impl<'a, T: Transaction> Binder<'a, T> { ) -> Result { let table_name: Arc = Arc::new(split_name(&lower_case_name(name))?.1.to_string()); - let plan = match operation { - AlterTableOperation::AddColumn { - column_keyword: _, - if_not_exists, - column_def, - } => { - if let Some(table) = self.context.table(table_name.clone()) { + if let Some(table) = self.context.table(table_name.clone()) { + let plan = match operation { + AlterTableOperation::AddColumn { + column_keyword: _, + if_not_exists, + column_def, + } => { let plan = ScanOperator::build(table_name.clone(), table); LogicalPlan { @@ -35,37 +36,49 @@ impl<'a, T: Transaction> Binder<'a, T> { }), childrens: vec![plan], } - } else { - return Err(BindError::InvalidTable(format!( - "not found table {}", - table_name - ))); } - } - AlterTableOperation::DropColumn { - column_name: _, - if_exists: _, - cascade: _, - } => todo!(), - AlterTableOperation::DropPrimaryKey => todo!(), - AlterTableOperation::RenameColumn { - old_column_name: _, - new_column_name: _, - } => todo!(), - AlterTableOperation::RenameTable { table_name: _ } => todo!(), - AlterTableOperation::ChangeColumn { - old_name: _, - new_name: _, - data_type: _, - options: _, - } => todo!(), - AlterTableOperation::AlterColumn { - column_name: _, - op: _, - } => todo!(), - _ => todo!(), - }; + AlterTableOperation::DropColumn { + column_name, + if_exists, + .. + } => { + let plan = ScanOperator::build(table_name.clone(), table); + let column_name = column_name.value.clone(); + + LogicalPlan { + operator: Operator::DropColumn(DropColumnOperator { + table_name, + if_exists: *if_exists, + column_name, + }), + childrens: vec![plan], + } + } + AlterTableOperation::DropPrimaryKey => todo!(), + AlterTableOperation::RenameColumn { + old_column_name: _, + new_column_name: _, + } => todo!(), + AlterTableOperation::RenameTable { table_name: _ } => todo!(), + AlterTableOperation::ChangeColumn { + old_name: _, + new_name: _, + data_type: _, + options: _, + } => todo!(), + AlterTableOperation::AlterColumn { + column_name: _, + op: _, + } => todo!(), + _ => todo!(), + }; - Ok(plan) + Ok(plan) + } else { + Err(BindError::InvalidTable(format!( + "not found table {}", + table_name + ))) + } } } diff --git a/src/catalog/table.rs b/src/catalog/table.rs index bcdb2178..2dd7398d 100644 --- a/src/catalog/table.rs +++ b/src/catalog/table.rs @@ -29,11 +29,11 @@ impl TableCatalog { } #[allow(dead_code)] - pub(crate) fn get_column_id_by_name(&self, name: &String) -> Option { + pub(crate) fn get_column_id_by_name(&self, name: &str) -> Option { self.column_idxs.get(name).cloned() } - pub(crate) fn get_column_by_name(&self, name: &String) -> Option<&ColumnRef> { + pub(crate) fn get_column_by_name(&self, name: &str) -> Option<&ColumnRef> { let id = self.column_idxs.get(name)?; self.columns.get(id) } @@ -65,10 +65,22 @@ impl TableCatalog { Ok(col_id) } - pub(crate) fn add_index_meta(&mut self, mut index: IndexMeta) -> &IndexMeta { + pub(crate) fn add_index_meta( + &mut self, + name: String, + column_ids: Vec, + is_unique: bool, + is_primary: bool, + ) -> &IndexMeta { let index_id = self.indexes.len(); - index.id = index_id as u32; + let index = IndexMeta { + id: index_id as u32, + column_ids, + name, + is_unique, + is_primary, + }; self.indexes.push(Arc::new(index)); &self.indexes[index_id] diff --git a/src/execution/executor/ddl/alter_table/add_column.rs b/src/execution/executor/ddl/alter_table/add_column.rs index b475efad..64b2891e 100644 --- a/src/execution/executor/ddl/alter_table/add_column.rs +++ b/src/execution/executor/ddl/alter_table/add_column.rs @@ -6,6 +6,7 @@ use futures_async_stream::try_stream; use std::cell::RefCell; use std::sync::Arc; +use crate::types::index::Index; use crate::{ execution::executor::Executor, planner::operator::alter_table::add_column::AddColumnOperator, storage::Transaction, @@ -23,7 +24,7 @@ impl From<(AddColumnOperator, BoxedExecutor)> for AddColumn { } impl Executor for AddColumn { - fn execute(self, transaction: &RefCell) -> crate::execution::executor::BoxedExecutor { + fn execute(self, transaction: &RefCell) -> BoxedExecutor { unsafe { self._execute(transaction.as_ptr().as_mut().unwrap()) } } } @@ -31,27 +32,46 @@ impl Executor for AddColumn { impl AddColumn { #[try_stream(boxed, ok = Tuple, error = ExecutorError)] async fn _execute(self, transaction: &mut T) { - let _ = transaction.add_column(&self.op)?; - let AddColumnOperator { - table_name, column, .. + table_name, + column, + if_not_exists, } = &self.op; + let mut unique_values = column.desc().is_unique.then(|| Vec::new()); #[for_await] for tuple in self.input { let mut tuple: Tuple = tuple?; - let is_overwrite = true; tuple.columns.push(Arc::new(column.clone())); if let Some(value) = column.default_value() { + if let Some(unique_values) = &mut unique_values { + unique_values.push((tuple.id.clone().unwrap(), value.clone())); + } tuple.values.push(value); } else { tuple.values.push(Arc::new(DataValue::Null)); } + transaction.append(table_name, tuple, true)?; + } + let col_id = transaction.add_column(table_name, column, *if_not_exists)?; - transaction.append(table_name, tuple, is_overwrite)?; + // Unique Index + if let (Some(unique_values), Some(unique_meta)) = ( + unique_values, + transaction + .table(table_name.clone()) + .and_then(|table| table.get_unique_index(&col_id)) + .cloned(), + ) { + for (tuple_id, value) in unique_values { + let index = Index { + id: unique_meta.id, + column_values: vec![value], + }; + transaction.add_index(&table_name, index, vec![tuple_id], true)?; + } } - transaction.remove_cache(&table_name)?; let tuple_builder = TupleBuilder::new_result(); let tuple = tuple_builder.push_result("ALTER TABLE SUCCESS", "1")?; diff --git a/src/execution/executor/ddl/alter_table/drop_column.rs b/src/execution/executor/ddl/alter_table/drop_column.rs new file mode 100644 index 00000000..63a751e8 --- /dev/null +++ b/src/execution/executor/ddl/alter_table/drop_column.rs @@ -0,0 +1,76 @@ +use crate::binder::BindError; +use crate::execution::executor::{BoxedExecutor, Executor}; +use crate::execution::ExecutorError; +use crate::planner::operator::alter_table::drop_column::DropColumnOperator; +use crate::storage::Transaction; +use crate::types::tuple::Tuple; +use crate::types::tuple_builder::TupleBuilder; +use futures_async_stream::try_stream; +use std::cell::RefCell; + +pub struct DropColumn { + op: DropColumnOperator, + input: BoxedExecutor, +} + +impl From<(DropColumnOperator, BoxedExecutor)> for DropColumn { + fn from((op, input): (DropColumnOperator, BoxedExecutor)) -> Self { + Self { op, input } + } +} + +impl Executor for DropColumn { + fn execute(self, transaction: &RefCell) -> BoxedExecutor { + unsafe { self._execute(transaction.as_ptr().as_mut().unwrap()) } + } +} + +impl DropColumn { + #[try_stream(boxed, ok = Tuple, error = ExecutorError)] + async fn _execute(self, transaction: &mut T) { + let DropColumnOperator { + table_name, + column_name, + if_exists, + } = &self.op; + let mut option_column_index = None; + + #[for_await] + for tuple in self.input { + let mut tuple: Tuple = tuple?; + + if option_column_index.is_none() { + if let Some((column_index, is_primary)) = tuple + .columns + .iter() + .enumerate() + .find(|(_, column)| column.name() == column_name) + .map(|(i, column)| (i, column.desc.is_primary)) + { + if is_primary { + Err(BindError::InvalidColumn( + "drop of primary key column is not allowed.".to_owned(), + ))?; + } + option_column_index = Some(column_index); + } + } + if option_column_index.is_none() && *if_exists { + return Ok(()); + } + let column_index = option_column_index + .ok_or_else(|| BindError::InvalidColumn("not found column".to_string()))?; + + let _ = tuple.columns.remove(column_index); + let _ = tuple.values.remove(column_index); + + transaction.append(table_name, tuple, true)?; + } + transaction.drop_column(table_name, column_name, *if_exists)?; + + let tuple_builder = TupleBuilder::new_result(); + let tuple = tuple_builder.push_result("ALTER TABLE SUCCESS", "1")?; + + yield tuple; + } +} diff --git a/src/execution/executor/ddl/alter_table/mod.rs b/src/execution/executor/ddl/alter_table/mod.rs index 4c008951..413c5a01 100644 --- a/src/execution/executor/ddl/alter_table/mod.rs +++ b/src/execution/executor/ddl/alter_table/mod.rs @@ -1 +1,2 @@ pub mod add_column; +pub mod drop_column; diff --git a/src/execution/executor/mod.rs b/src/execution/executor/mod.rs index 38334a8d..05de2e42 100644 --- a/src/execution/executor/mod.rs +++ b/src/execution/executor/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod dml; pub(crate) mod dql; pub(crate) mod show; +use crate::execution::executor::ddl::alter_table::drop_column::DropColumn; use crate::execution::executor::ddl::create_table::CreateTable; use crate::execution::executor::ddl::drop_table::DropTable; use crate::execution::executor::ddl::truncate::Truncate; @@ -110,6 +111,10 @@ pub fn build(plan: LogicalPlan, transaction: &RefCell) -> Box let input = build(childrens.remove(0), transaction); AddColumn::from((op, input)).execute(transaction) } + Operator::DropColumn(op) => { + let input = build(childrens.remove(0), transaction); + DropColumn::from((op, input)).execute(transaction) + } Operator::CreateTable(op) => CreateTable::from(op).execute(transaction), Operator::DropTable(op) => DropTable::from(op).execute(transaction), Operator::Truncate(op) => Truncate::from(op).execute(transaction), diff --git a/src/optimizer/rule/column_pruning.rs b/src/optimizer/rule/column_pruning.rs index ce16c9bf..54977fbb 100644 --- a/src/optimizer/rule/column_pruning.rs +++ b/src/optimizer/rule/column_pruning.rs @@ -116,7 +116,8 @@ impl ColumnPruning { | Operator::Show(_) | Operator::CopyFromFile(_) | Operator::CopyToFile(_) - | Operator::AddColumn(_) => (), + | Operator::AddColumn(_) + | Operator::DropColumn(_) => (), } } diff --git a/src/planner/operator/alter_table/drop_column.rs b/src/planner/operator/alter_table/drop_column.rs new file mode 100644 index 00000000..ea0a20da --- /dev/null +++ b/src/planner/operator/alter_table/drop_column.rs @@ -0,0 +1,8 @@ +use crate::catalog::TableName; + +#[derive(Debug, PartialEq, Clone)] +pub struct DropColumnOperator { + pub table_name: TableName, + pub column_name: String, + pub if_exists: bool, +} diff --git a/src/planner/operator/alter_table/mod.rs b/src/planner/operator/alter_table/mod.rs index 4c008951..413c5a01 100644 --- a/src/planner/operator/alter_table/mod.rs +++ b/src/planner/operator/alter_table/mod.rs @@ -1 +1,2 @@ pub mod add_column; +pub mod drop_column; diff --git a/src/planner/operator/mod.rs b/src/planner/operator/mod.rs index 634957e4..5c443043 100644 --- a/src/planner/operator/mod.rs +++ b/src/planner/operator/mod.rs @@ -18,6 +18,7 @@ pub mod update; pub mod values; use crate::catalog::ColumnRef; +use crate::planner::operator::alter_table::drop_column::DropColumnOperator; use crate::planner::operator::copy_from_file::CopyFromFileOperator; use crate::planner::operator::copy_to_file::CopyToFileOperator; use crate::planner::operator::create_table::CreateTableOperator; @@ -55,6 +56,7 @@ pub enum Operator { Delete(DeleteOperator), // DDL AddColumn(AddColumnOperator), + DropColumn(DropColumnOperator), CreateTable(CreateTableOperator), DropTable(DropTableOperator), Truncate(TruncateOperator), diff --git a/src/storage/kip.rs b/src/storage/kip.rs index c432f712..2025cdfe 100644 --- a/src/storage/kip.rs +++ b/src/storage/kip.rs @@ -1,17 +1,18 @@ use crate::catalog::{ColumnCatalog, ColumnRef, TableCatalog, TableName}; use crate::expression::simplify::ConstantBinary; -use crate::planner::operator::alter_table::add_column::AddColumnOperator; use crate::storage::table_codec::TableCodec; use crate::storage::{ tuple_projection, Bounds, IndexIter, Iter, Projections, Storage, StorageError, Transaction, }; -use crate::types::index::{Index, IndexMeta, IndexMetaRef}; +use crate::types::index::{Index, IndexMetaRef}; use crate::types::tuple::{Tuple, TupleId}; +use crate::types::ColumnId; use kip_db::kernel::lsm::iterator::Iter as KipDBIter; use kip_db::kernel::lsm::mvcc::{CheckType, TransactionIter}; use kip_db::kernel::lsm::storage::Config; use kip_db::kernel::lsm::{mvcc, storage}; use kip_db::kernel::utils::lru_cache::ShardingLruCache; +use kip_db::KernelError; use std::collections::hash_map::RandomState; use std::collections::{Bound, VecDeque}; use std::path::PathBuf; @@ -42,7 +43,7 @@ impl Storage for KipStorage { Ok(KipTransaction { tx, - cache: ShardingLruCache::new(32, 16, RandomState::default())?, + cache: ShardingLruCache::new(8, 2, RandomState::default())?, }) } } @@ -162,22 +163,21 @@ impl Transaction for KipTransaction { Ok(()) } - fn add_column(&mut self, op: &AddColumnOperator) -> Result<(), StorageError> { - let AddColumnOperator { - table_name, - if_not_exists, - column, - } = op; - + fn add_column( + &mut self, + table_name: &TableName, + column: &ColumnCatalog, + if_not_exists: bool, + ) -> Result { if let Some(mut catalog) = self.table(table_name.clone()).cloned() { if !column.nullable && column.default_value().is_none() { - return Err(StorageError::NeedNullAble); + return Err(StorageError::NeedNullAbleOrDefault); } for col in catalog.all_columns() { if col.name() == column.name() { - if *if_not_exists { - return Ok(()); + if if_not_exists { + return Ok(col.id().unwrap()); } else { return Err(StorageError::DuplicateColumn); } @@ -187,14 +187,12 @@ impl Transaction for KipTransaction { let col_id = catalog.add_column(column.clone())?; if column.desc.is_unique { - let meta = IndexMeta { - id: 0, - column_ids: vec![col_id], - name: format!("uk_{}", column.name()), - is_unique: true, - is_primary: false, - }; - let meta_ref = catalog.add_index_meta(meta); + let meta_ref = catalog.add_index_meta( + format!("uk_{}", column.name()), + vec![col_id], + true, + false, + ); let (key, value) = TableCodec::encode_index_meta(table_name, meta_ref)?; self.tx.set(key, value); } @@ -202,10 +200,46 @@ impl Transaction for KipTransaction { let column = catalog.get_column_by_id(&col_id).unwrap(); let (key, value) = TableCodec::encode_column(&table_name, column)?; self.tx.set(key, value); + self.cache.remove(table_name); + + Ok(col_id) + } else { + Err(StorageError::TableNotFound) + } + } + + fn drop_column( + &mut self, + table_name: &TableName, + column_name: &str, + if_exists: bool, + ) -> Result<(), StorageError> { + if let Some(catalog) = self.table(table_name.clone()).cloned() { + let column = catalog.get_column_by_name(column_name).unwrap(); + + if let Some(index_meta) = catalog.get_unique_index(&column.id().unwrap()) { + let (index_meta_key, _) = TableCodec::encode_index_meta(table_name, index_meta)?; + self.tx.remove(&index_meta_key)?; + + let (index_min, index_max) = TableCodec::index_bound(table_name, &index_meta.id); + Self::_drop_data(&mut self.tx, &index_min, &index_max)?; + } + let (key, _) = TableCodec::encode_column(&table_name, column)?; + + match self.tx.remove(&key) { + Ok(_) => (), + Err(KernelError::KeyNotFound) => { + if !if_exists { + Err(KernelError::KeyNotFound)?; + } + } + err => err?, + } + self.cache.remove(table_name); Ok(()) } else { - return Err(StorageError::TableNotFound); + Err(StorageError::TableNotFound) } } @@ -245,23 +279,14 @@ impl Transaction for KipTransaction { return Err(StorageError::TableNotFound); } } - self.drop_data(table_name)?; - let (min, max) = TableCodec::columns_bound(table_name); - let mut iter = self.tx.iter(Bound::Included(&min), Bound::Included(&max))?; - let mut col_keys = vec![]; + let (column_min, column_max) = TableCodec::columns_bound(table_name); + Self::_drop_data(&mut self.tx, &column_min, &column_max)?; - while let Some((key, value_option)) = iter.try_next()? { - if value_option.is_some() { - col_keys.push(key); - } - } - drop(iter); + let (index_meta_min, index_meta_max) = TableCodec::index_meta_bound(table_name); + Self::_drop_data(&mut self.tx, &index_meta_min, &index_meta_max)?; - for col_key in col_keys { - self.tx.remove(&col_key)? - } self.tx .remove(&TableCodec::encode_root_table_key(table_name))?; @@ -322,11 +347,6 @@ impl Transaction for KipTransaction { Ok(()) } - - fn remove_cache(&self, key: &String) -> Result<(), StorageError> { - self.cache.remove(key); - Ok(()) - } } impl KipTransaction { @@ -401,14 +421,12 @@ impl KipTransaction { let prefix = if is_primary { "pk" } else { "uk" }; if let Some(col_id) = col.id() { - let meta = IndexMeta { - id: 0, - column_ids: vec![col_id], - name: format!("{}_{}", prefix, col.name()), - is_unique: col.desc.is_unique, + let meta_ref = table.add_index_meta( + format!("{}_{}", prefix, col.name()), + vec![col_id], + col.desc.is_unique, is_primary, - }; - let meta_ref = table.add_index_meta(meta); + ); let (key, value) = TableCodec::encode_index_meta(&table_name, meta_ref)?; tx.set(key, value); diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 96506d7f..3ce43192 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -4,12 +4,12 @@ mod table_codec; use crate::catalog::{CatalogError, ColumnCatalog, TableCatalog, TableName}; use crate::expression::simplify::ConstantBinary; use crate::expression::ScalarExpression; -use crate::planner::operator::alter_table::add_column::AddColumnOperator; use crate::storage::table_codec::TableCodec; use crate::types::errors::TypeError; use crate::types::index::{Index, IndexMetaRef}; use crate::types::tuple::{Tuple, TupleId}; use crate::types::value::ValueRef; +use crate::types::ColumnId; use kip_db::kernel::lsm::iterator::Iter as DBIter; use kip_db::kernel::lsm::mvcc; use kip_db::KernelError; @@ -68,7 +68,21 @@ pub trait Transaction: Sync + Send + 'static { ) -> Result<(), StorageError>; fn delete(&mut self, table_name: &str, tuple_id: TupleId) -> Result<(), StorageError>; - fn add_column(&mut self, op: &AddColumnOperator) -> Result<(), StorageError>; + + fn add_column( + &mut self, + table_name: &TableName, + column: &ColumnCatalog, + if_not_exists: bool, + ) -> Result; + + fn drop_column( + &mut self, + table_name: &TableName, + column: &str, + if_exists: bool, + ) -> Result<(), StorageError>; + fn create_table( &mut self, table_name: TableName, @@ -84,9 +98,6 @@ pub trait Transaction: Sync + Send + 'static { #[allow(async_fn_in_trait)] async fn commit(self) -> Result<(), StorageError>; - fn remove_cache(&self, _key: &String) -> Result<(), StorageError> { - Ok(()) - } } enum IndexValue { @@ -320,8 +331,8 @@ pub enum StorageError { #[error("The some column already exists")] DuplicateColumn, - #[error("Add column need nullable")] - NeedNullAble, + #[error("Add column must be nullable or specify a default value")] + NeedNullAbleOrDefault, #[error("The table already exists")] TableExists, diff --git a/tests/slt/alter_table.slt b/tests/slt/alter_table.slt index ded8ff9c..2aa8883e 100644 --- a/tests/slt/alter_table.slt +++ b/tests/slt/alter_table.slt @@ -1,19 +1,59 @@ statement ok -create table alter_table(id int primary key, v1 int) +create table t1(id int primary key, v1 int unique) statement ok -insert into alter_table values (1,1), (2,2), (3,3), (4,4) +insert into t1 values (1,1), (2,2), (3,3), (4,4) statement ok -alter table alter_table add column da int null +alter table t1 add column v2 int null + +statement ok +alter table t1 add column if not exists v3 int default 0 + +statement ok +alter table t1 add column if not exists v3 int default 0 + +statement error +alter table t1 add column v4 int + +query IIII rowsort +select * from t1 +---- +1 1 null 0 +2 2 null 0 +3 3 null 0 +4 4 null 0 + +statement ok +alter table t1 drop column if exists v1 + +statement ok +alter table t1 drop column if exists v1 + +statement error +alter table t1 drop column id + +query IIII rowsort +select * from t1 +---- +1 null 0 +2 null 0 +3 null 0 +4 null 0 + +statement ok +create table t2(id int primary key, v1 int) + +statement ok +insert into t2 values (1,1) + +statement error +alter table t2 add column v2 int default 0 unique query IIII rowsort -select * from alter_table +select * from t2 ---- -1 1 null -2 2 null -3 3 null -4 4 null +1 1 0 statement ok -drop table alter_table +drop table t1 diff --git a/tests/slt/basic_test.slt b/tests/slt/basic_test.slt index 63d18f1c..2ab998d0 100644 --- a/tests/slt/basic_test.slt +++ b/tests/slt/basic_test.slt @@ -87,4 +87,7 @@ statement ok select CAST(id AS VARCHAR) from t statement ok -drop table t \ No newline at end of file +drop table if exists t + +statement ok +drop table if exists t \ No newline at end of file