Repository: gluesql/gluesql
Pull request #92 (2020-10-12 05:31:02)

Implement ALTER TABLE - ADD, DROP, RENAME COLUMN & RENAME TABLE 
Implement ALTER TABLE functionality.

* ADD COLUMN
* DROP COLUMN
* RENAME COLUMN
* RENAME TABLE
are now supported.

`AlterTable` trait is optional, who wants to use `AlterTable` should enable `alter-table` feature.


Patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 85fd1df5..ae9d10aa 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -18,6 +18,12 @@ jobs:
     - uses: actions/checkout@v2
     - run: rustup component add clippy rustfmt
     - name: Clippy
+      run: cargo clippy -- -D warnings
+    - name: Clippy (alter-table)
+      run: cargo clippy --features alter-table -- -D warnings
+    - name: Clippy (sled-storage)
+      run: cargo clippy --features sled-storage -- -D warnings
+    - name: Clippy (all)
       run: cargo clippy --all-features -- -D warnings
     - name: Rustfmt
       run: cargo fmt --all -- --check
@@ -25,5 +31,7 @@ jobs:
       run: cargo build --all-features --verbose
     - name: Run tests
       run: cargo test --verbose
-    - name: Run tests with features
+    - name: Run tests with features (sled-storage)
+      run: cargo test --features sled-storage --verbose
+    - name: Run tests with all features
       run: cargo test --all-features --verbose
diff --git a/Cargo.toml b/Cargo.toml
index 4801264f..d9394fc6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ keywords = ["sql-database", "sql", "functional", "no-mut-in-the-middle", "webass
 all-features = true
 
 [features]
+alter-table = []
 sled-storage = ["sled", "bincode"]
 
 [dependencies]
diff --git a/src/data/row.rs b/src/data/row.rs
index 1f7f883b..ea4c7848 100644
--- a/src/data/row.rs
+++ b/src/data/row.rs
@@ -18,9 +18,6 @@ pub enum RowError {
     #[error("literals have more values than target columns")]
     TooManyValues,
 
-    #[error("unsupported ast value type")]
-    UnsupportedAstValueType,
-
     #[error("conflict! row cannot be empty")]
     ConflictOnEmptyRow,
 }
@@ -64,22 +61,17 @@ impl Row {
 
                 let default = options
                     .iter()
-                    .find(|ColumnOptionDef { option, .. }| match option {
-                        ColumnOption::Default(_) => true,
-                        _ => false,
-                    });
+                    .filter_map(|ColumnOptionDef { option, .. }| match option {
+                        ColumnOption::Default(expr) => Some(expr),
+                        _ => None,
+                    })
+                    .next();
 
                 let expr = match (i, default) {
                     (Some(i), _) => values
                         .get(i)
                         .ok_or_else(|| RowError::LackOfRequiredValue(name.clone())),
-                    (
-                        None,
-                        Some(&ColumnOptionDef {
-                            option: ColumnOption::Default(ref expr),
-                            ..
-                        }),
-                    ) => Ok(expr),
+                    (None, Some(expr)) => Ok(expr),
                     (None, _) => Err(RowError::LackOfRequiredColumn(name.clone())),
                 }?;
 
@@ -87,11 +79,7 @@ impl Row {
                     .iter()
                     .any(|ColumnOptionDef { option, .. }| option == &ColumnOption::Null);
 
-                match expr {
-                    Expr::Value(literal) => Value::from_data_type(&data_type, nullable, literal),
-                    Expr::Identifier(Ident { value, .. }) => Ok(Value::Str(value.clone())),
-                    _ => Err(RowError::UnsupportedAstValueType.into()),
-                }
+                Value::from_expr(&data_type, nullable, expr)
             })
             .collect::<Result<_>>()
             .map(Self)
diff --git a/src/data/value.rs b/src/data/value.rs
index 6e65bdfb..769ff05f 100644
--- a/src/data/value.rs
+++ b/src/data/value.rs
@@ -5,7 +5,7 @@ use std::convert::{TryFrom, TryInto};
 use std::fmt::Debug;
 use thiserror::Error as ThisError;
 
-use sqlparser::ast::{DataType, Value as AstValue};
+use sqlparser::ast::{DataType, Expr, Ident, Value as AstValue};
 
 use crate::executor::GroupKey;
 use crate::result::{Error, Result};
@@ -18,6 +18,9 @@ pub enum ValueError {
     #[error("literal not supported yet")]
     LiteralNotSupported,
 
+    #[error("ast expr not supported: {0}")]
+    ExprNotSupported(String),
+
     #[error("failed to parse number")]
     FailedToParseNumber,
 
@@ -211,6 +214,14 @@ impl BoolToValue for bool {
 }
 
 impl Value {
+    pub fn from_expr(data_type: &DataType, nullable: bool, expr: &Expr) -> Result<Self> {
+        match expr {
+            Expr::Value(literal) => Value::from_data_type(&data_type, nullable, literal),
+            Expr::Identifier(Ident { value, .. }) => Ok(Value::Str(value.clone())),
+            _ => Err(ValueError::ExprNotSupported(expr.to_string()).into()),
+        }
+    }
+
     pub fn from_data_type(
         data_type: &DataType,
         nullable: bool,
diff --git a/src/executor/execute.rs b/src/executor/execute.rs
index 286d2fc3..7820bdd4 100644
--- a/src/executor/execute.rs
+++ b/src/executor/execute.rs
@@ -2,6 +2,8 @@ use serde::Serialize;
 use std::fmt::Debug;
 use thiserror::Error;
 
+#[cfg(feature = "alter-table")]
+use sqlparser::ast::AlterTableOperation;
 use sqlparser::ast::{ObjectType, SetExpr, Statement, Values};
 
 use super::fetch::{fetch, fetch_columns};
@@ -11,7 +13,7 @@ use super::update::Update;
 use crate::data::{get_name, Row, Schema};
 use crate::parse::Query;
 use crate::result::{MutResult, Result};
-use crate::store::{Store, StoreMut};
+use crate::store::{AlterTable, Store, StoreMut};
 
 #[derive(Error, Serialize, Debug, PartialEq)]
 pub enum ExecuteError {
@@ -23,6 +25,10 @@ pub enum ExecuteError {
 
     #[error("unsupported insert value type: {0}")]
     UnsupportedInsertValueType(String),
+
+    #[cfg(feature = "alter-table")]
+    #[error("unsupported alter table operation: {0}")]
+    UnsupportedAlterTableOperation(String),
 }
 
 #[derive(Serialize, Debug, PartialEq)]
@@ -33,9 +39,12 @@ pub enum Payload {
     Delete(usize),
     Update(usize),
     DropTable,
+
+    #[cfg(feature = "alter-table")]
+    AlterTable,
 }
 
-pub fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T>>(
+pub fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>(
     storage: U,
     query: &Query,
 ) -> MutResult<U, Payload> {
@@ -96,6 +105,37 @@ pub fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T>>(
             Ok((storage, Payload::DropTable))
         }
         Prepared::Select(rows) => Ok((storage, Payload::Select(rows))),
+
+        #[cfg(feature = "alter-table")]
+        Prepared::AlterTable(table_name, operation) => {
+            let result = match operation {
+                AlterTableOperation::RenameTable {
+                    table_name: new_table_name,
+                } => storage.rename_schema(table_name, &new_table_name.value),
+                AlterTableOperation::RenameColumn {
+                    old_column_name,
+                    new_column_name,
+                } => storage.rename_column(
+                    table_name,
+                    &old_column_name.value,
+                    &new_column_name.value,
+                ),
+                AlterTableOperation::AddColumn { column_def } => {
+                    storage.add_column(table_name, column_def)
+                }
+                AlterTableOperation::DropColumn {
+                    column_name,
+                    if_exists,
+                    ..
+                } => storage.drop_column(table_name, &column_name.value, *if_exists),
+                _ => Err((
+                    storage,
+                    ExecuteError::UnsupportedAlterTableOperation(operation.to_string()).into(),
+                )),
+            };
+
+            result.map(|(storage, _)| (storage, Payload::AlterTable))
+        }
     }
 }
 
@@ -106,6 +146,9 @@ enum Prepared<'a, T> {
     Update(Vec<(T, Row)>),
     Select(Vec<Row>),
     DropTable(Vec<&'a str>),
+
+    #[cfg(feature = "alter-table")]
+    AlterTable(&'a str, &'a AlterTableOperation),
 }
 
 fn prepare<'a, T: 'static + Debug>(
@@ -197,6 +240,14 @@ fn prepare<'a, T: 'static + Debug>(
 
             Ok(Prepared::DropTable(names))
         }
+
+        #[cfg(feature = "alter-table")]
+        Statement::AlterTable { name, operation } => {
+            let table_name = get_name(name)?;
+
+            Ok(Prepared::AlterTable(table_name, operation))
+        }
+
         _ => Err(ExecuteError::QueryNotSupported.into()),
     }
 }
diff --git a/src/result.rs b/src/result.rs
index c3c77473..46e49cbf 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -6,12 +6,20 @@ use crate::executor::{
     AggregateError, BlendContextError, BlendError, EvaluateError, ExecuteError, FilterContextError,
     FilterError, JoinError, LimitError, SelectError, UnionContextError, UpdateError,
 };
+
+#[cfg(feature = "alter-table")]
+use crate::store::AlterTableError;
 use crate::store::StoreError;
 
 #[derive(ThisError, Serialize, Debug)]
 pub enum Error {
     #[error(transparent)]
     Store(#[from] StoreError),
+
+    #[cfg(feature = "alter-table")]
+    #[error(transparent)]
+    AlterTable(#[from] AlterTableError),
+
     #[error(transparent)]
     #[serde(with = "stringify")]
     Storage(#[from] Box<dyn std::error::Error>),
@@ -57,6 +65,8 @@ impl PartialEq for Error {
 
         match (self, other) {
             (Store(e), Store(e2)) => e == e2,
+            #[cfg(feature = "alter-table")]
+            (AlterTable(e), AlterTable(e2)) => e == e2,
             (Execute(e), Execute(e2)) => e == e2,
             (Evaluate(e), Evaluate(e2)) => e == e2,
             (Select(e), Select(e2)) => e == e2,
diff --git a/src/storages/sled_storage.rs b/src/storages/sled_storage.rs
deleted file mode 100644
index 246adbfc..00000000
--- a/src/storages/sled_storage.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use sled::{self, Config, Db, IVec};
-use std::convert::TryFrom;
-use thiserror::Error as ThisError;
-
-use crate::{Error, MutResult, Result, Row, RowIter, Schema, Store, StoreError, StoreMut};
-
-#[derive(ThisError, Debug)]
-enum StorageError {
-    #[error(transparent)]
-    Store(#[from] StoreError),
-
-    #[error(transparent)]
-    Sled(#[from] sled::Error),
-    #[error(transparent)]
-    Bincode(#[from] bincode::Error),
-}
-
-impl Into<Error> for StorageError {
-    fn into(self) -> Error {
-        use StorageError::*;
-
-        match self {
-            Sled(e) => Error::Storage(Box::new(e)),
-            Bincode(e) => Error::Storage(e),
-            Store(e) => e.into(),
-        }
-    }
-}
-
-macro_rules! try_into {
-    ($expr: expr) => {
-        $expr.map_err(|e| {
-            let e: StorageError = e.into();
-            let e: Error = e.into();
-
-            e
-        })?
-    };
-}
-
-macro_rules! try_self {
-    ($self: expr, $expr: expr) => {
-        match $expr {
-            Err(e) => {
-                let e: StorageError = e.into();
-                let e: Error = e.into();
-
-                return Err(($self, e));
-            }
-            Ok(v) => v,
-        }
-    };
-}
-
-#[derive(Debug)]
-pub struct SledStorage {
-    tree: Db,
-}
-
-impl SledStorage {
-    pub fn new(filename: &str) -> Result<Self> {
-        let tree = try_into!(sled::open(filename));
-
-        Ok(Self { tree })
-    }
-}
-
-impl TryFrom<Config> for SledStorage {
-    type Error = Error;
-
-    fn try_from(config: Config) -> Result<Self> {
-        let tree = try_into!(config.open());
-
-        Ok(Self { tree })
-    }
-}
-
-impl StoreMut<IVec> for SledStorage {
-    fn generate_id(self, table_name: &str) -> MutResult<Self, IVec> {
-        let id = try_self!(self, self.tree.generate_id());
-        let id = format!("data/{}/{}", table_name, id);
-
-        Ok((self, IVec::from(id.as_bytes())))
-    }
-
-    fn insert_schema(self, schema: &Schema) -> MutResult<Self, ()> {
-        let key = format!("schema/{}", schema.table_name);
-        let key = key.as_bytes();
-        let value = try_self!(self, bincode::serialize(schema));
-
-        try_self!(self, self.tree.insert(key, value));
-
-        Ok((self, ()))
-    }
-
-    fn delete_schema(self, table_name: &str) -> MutResult<Self, ()> {
-        let prefix = format!("data/{}/", table_name);
-        let tree = &self.tree;
-
-        for item in tree.scan_prefix(prefix.as_bytes()) {
-            let (key, _) = try_self!(self, item);
-
-            try_self!(self, tree.remove(key));
-        }
-
-        let key = format!("schema/{}", table_name);
-        try_self!(self, tree.remove(key));
-
-        Ok((self, ()))
-    }
-
-    fn insert_data(self, key: &IVec, row: Row) -> MutResult<Self, ()> {
-        let value = try_self!(self, bincode::serialize(&row));
-
-        try_self!(self, self.tree.insert(key, value));
-
-        Ok((self, ()))
-    }
-
-    fn delete_data(self, key: &IVec) -> MutResult<Self, ()> {
-        try_self!(self, self.tree.remove(key));
-
-        Ok((self, ()))
-    }
-}
-
-impl Store<IVec> for SledStorage {
-    fn fetch_schema(&self, table_name: &str) -> Result<Schema> {
-        let key = format!("schema/{}", table_name);
-        let key = key.as_bytes();
-        let value = try_into!(self.tree.get(&key));
-        let value = value.ok_or(StoreError::SchemaNotFound)?;
-        let statement = try_into!(bincode::deserialize(&value));
-
-        Ok(statement)
-    }
-
-    fn scan_data(&self, table_name: &str) -> Result<RowIter<IVec>> {
-        let prefix = format!("data/{}/", table_name);
-
-        let result_set = self.tree.scan_prefix(prefix.as_bytes()).map(move |item| {
-            let (key, value) = try_into!(item);
-            let value = try_into!(bincode::deserialize(&value));
-
-            Ok((key, value))
-        });
-
-        Ok(Box::new(result_set))
-    }
-}
diff --git a/src/storages/sled_storage/alter_table.rs b/src/storages/sled_storage/alter_table.rs
new file mode 100644
index 00000000..976125d9
--- /dev/null
+++ b/src/storages/sled_storage/alter_table.rs
@@ -0,0 +1,245 @@
+use boolinator::Boolinator;
+use std::iter::once;
+use std::str;
+
+use sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, Ident, Value as AstValue};
+
+use super::{fetch_schema, AlterTableError, SledStorage, StorageError};
+use crate::utils::Vector;
+use crate::{try_into, AlterTable, Error, MutResult, Row, Schema, Value};
+
+macro_rules! try_self {
+    ($self: expr, $expr: expr) => {
+        match $expr {
+            Err(e) => {
+                return Err(($self, e.into()));
+            }
+            Ok(v) => v,
+        }
+    };
+}
+
+impl AlterTable for SledStorage {
+    fn rename_schema(self, table_name: &str, new_table_name: &str) -> MutResult<Self, ()> {
+        let (_, Schema { column_defs, .. }) = try_self!(self, fetch_schema(&self.tree, table_name));
+
+        let schema = Schema {
+            table_name: new_table_name.to_string(),
+            column_defs,
+        };
+
+        let tree = &self.tree;
+
+        // remove existing schema
+        let key = format!("schema/{}", table_name);
+        try_into!(self, tree.remove(key));
+
+        // insert new schema
+        let value = try_into!(self, bincode::serialize(&schema));
+        let key = format!("schema/{}", new_table_name);
+        let key = key.as_bytes();
+        try_into!(self, self.tree.insert(key, value));
+
+        // replace data
+        let prefix = format!("data/{}/", table_name);
+
+        for item in tree.scan_prefix(prefix.as_bytes()) {
+            let (key, value) = try_into!(self, item);
+
+            let new_key = try_into!(self, str::from_utf8(key.as_ref()));
+            let new_key = new_key.replace(table_name, new_table_name);
+            try_into!(self, tree.insert(new_key, value));
+
+            try_into!(self, tree.remove(key));
+        }
+
+        Ok((self, ()))
+    }
+
+    fn rename_column(
+        self,
+        table_name: &str,
+        old_column_name: &str,
+        new_column_name: &str,
+    ) -> MutResult<Self, ()> {
+        let (key, Schema { column_defs, .. }) =
+            try_self!(self, fetch_schema(&self.tree, table_name));
+
+        let i = column_defs
+            .iter()
+            .position(|column_def| column_def.name.value == old_column_name)
+            .ok_or(AlterTableError::RenamingColumnNotFound);
+        let i = try_into!(self, i);
+
+        let ColumnDef {
+            name: Ident { quote_style, .. },
+            data_type,
+            collation,
+            options,
+        } = column_defs[i].clone();
+
+        let column_def = ColumnDef {
+            name: Ident {
+                quote_style,
+                value: new_column_name.to_string(),
+            },
+            data_type,
+            collation,
+            options,
+        };
+        let column_defs = Vector::from(column_defs).update(i, column_def).into();
+
+        let schema = Schema {
+            table_name: table_name.to_string(),
+            column_defs,
+        };
+        let value = try_into!(self, bincode::serialize(&schema));
+        try_into!(self, self.tree.insert(key, value));
+
+        Ok((self, ()))
+    }
+
+    fn add_column(self, table_name: &str, column_def: &ColumnDef) -> MutResult<Self, ()> {
+        let (
+            key,
+            Schema {
+                table_name,
+                column_defs,
+            },
+        ) = try_self!(self, fetch_schema(&self.tree, table_name));
+
+        if column_defs
+            .iter()
+            .any(|ColumnDef { name, .. }| name.value == column_def.name.value)
+        {
+            let adding_column = column_def.name.value.to_string();
+
+            return Err((
+                self,
+                AlterTableError::AddingColumnAlreadyExists(adding_column).into(),
+            ));
+        }
+
+        let ColumnDef {
+            options, data_type, ..
+        } = column_def;
+
+        let nullable = options
+            .iter()
+            .any(|ColumnOptionDef { option, .. }| option == &ColumnOption::Null);
+        let default = options
+            .iter()
+            .filter_map(|ColumnOptionDef { option, .. }| match option {
+                ColumnOption::Default(expr) => Some(expr),
+                _ => None,
+            })
+            .map(|expr| Value::from_expr(&data_type, nullable, expr))
+            .next();
+
+        let value = match (default, nullable) {
+            (Some(value), _) => try_self!(self, value),
+            (None, true) => try_self!(
+                self,
+                Value::from_data_type(&data_type, nullable, &AstValue::Null)
+            ),
+            (None, false) => {
+                return Err((
+                    self,
+                    AlterTableError::DefaultValueRequired(column_def.to_string()).into(),
+                ));
+            }
+        };
+
+        // migrate data
+        let prefix = format!("data/{}/", table_name);
+
+        for item in self.tree.scan_prefix(prefix.as_bytes()) {
+            let (key, row) = try_into!(self, item);
+            let row: Row = try_into!(self, bincode::deserialize(&row));
+            let row = Row(row.0.into_iter().chain(once(value.clone())).collect());
+            let row = try_into!(self, bincode::serialize(&row));
+
+            try_into!(self, self.tree.insert(key, row));
+        }
+
+        // update schema
+        let column_defs = column_defs
+            .into_iter()
+            .chain(once(column_def.clone()))
+            .collect::<Vec<ColumnDef>>();
+
+        let schema = Schema {
+            table_name,
+            column_defs,
+        };
+        let schema_value = try_into!(self, bincode::serialize(&schema));
+        try_into!(self, self.tree.insert(key, schema_value));
+
+        Ok((self, ()))
+    }
+
+    fn drop_column(
+        self,
+        table_name: &str,
+        column_name: &str,
+        if_exists: bool,
+    ) -> MutResult<Self, ()> {
+        let (
+            key,
+            Schema {
+                table_name,
+                column_defs,
+            },
+        ) = try_self!(self, fetch_schema(&self.tree, table_name));
+
+        let index = column_defs
+            .iter()
+            .position(|ColumnDef { name, .. }| name.value == column_name);
+
+        let index = match (index, if_exists) {
+            (Some(index), _) => index,
+            (None, true) => {
+                return Ok((self, ()));
+            }
+            (None, false) => {
+                return Err((
+                    self,
+                    AlterTableError::DroppingColumnNotFound(column_name.to_string()).into(),
+                ));
+            }
+        };
+
+        // migrate data
+        let prefix = format!("data/{}/", table_name);
+
+        for item in self.tree.scan_prefix(prefix.as_bytes()) {
+            let (key, row) = try_into!(self, item);
+            let row: Row = try_into!(self, bincode::deserialize(&row));
+            let row = Row(row
+                .0
+                .into_iter()
+                .enumerate()
+                .filter_map(|(i, v)| (i != index).as_some(v))
+                .collect());
+            let row = try_into!(self, bincode::serialize(&row));
+
+            try_into!(self, self.tree.insert(key, row));
+        }
+
+        // update schema
+        let column_defs = column_defs
+            .into_iter()
+            .enumerate()
+            .filter_map(|(i, v)| (i != index).as_some(v))
+            .collect::<Vec<ColumnDef>>();
+
+        let schema = Schema {
+            table_name,
+            column_defs,
+        };
+        let schema_value = try_into!(self, bincode::serialize(&schema));
+        try_into!(self, self.tree.insert(key, schema_value));
+
+        Ok((self, ()))
+    }
+}
diff --git a/src/storages/sled_storage/mod.rs b/src/storages/sled_storage/mod.rs
new file mode 100644
index 00000000..dce0d445
--- /dev/null
+++ b/src/storages/sled_storage/mod.rs
@@ -0,0 +1,105 @@
+use sled::{self, Config, Db};
+use std::convert::TryFrom;
+use std::str;
+use thiserror::Error as ThisError;
+
+#[cfg(not(feature = "alter-table"))]
+use crate::AlterTable;
+#[cfg(feature = "alter-table")]
+use crate::AlterTableError;
+use crate::{Error, Result, Schema, StoreError};
+
+#[cfg(feature = "alter-table")]
+mod alter_table;
+#[cfg(not(feature = "alter-table"))]
+impl AlterTable for SledStorage {}
+
+mod store;
+
+#[derive(ThisError, Debug)]
+enum StorageError {
+    #[error(transparent)]
+    Store(#[from] StoreError),
+
+    #[cfg(feature = "alter-table")]
+    #[error(transparent)]
+    AlterTable(#[from] AlterTableError),
+
+    #[error(transparent)]
+    Sled(#[from] sled::Error),
+    #[error(transparent)]
+    Bincode(#[from] bincode::Error),
+    #[error(transparent)]
+    Str(#[from] str::Utf8Error),
+}
+
+impl Into<Error> for StorageError {
+    fn into(self) -> Error {
+        use StorageError::*;
+
+        match self {
+            Sled(e) => Error::Storage(Box::new(e)),
+            Bincode(e) => Error::Storage(e),
+            Str(e) => Error::Storage(Box::new(e)),
+            Store(e) => e.into(),
+
+            #[cfg(feature = "alter-table")]
+            AlterTable(e) => e.into(),
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! try_into {
+    ($expr: expr) => {
+        $expr.map_err(|e| {
+            let e: StorageError = e.into();
+            let e: Error = e.into();
+
+            e
+        })?
+    };
+    ($self: expr, $expr: expr) => {
+        match $expr {
+            Err(e) => {
+                let e: StorageError = e.into();
+                let e: Error = e.into();
+
+                return Err(($self, e));
+            }
+            Ok(v) => v,
+        }
+    };
+}
+
+#[derive(Debug)]
+pub struct SledStorage {
+    tree: Db,
+}
+
+impl SledStorage {
+    pub fn new(filename: &str) -> Result<Self> {
+        let tree = try_into!(sled::open(filename));
+
+        Ok(Self { tree })
+    }
+}
+
+impl TryFrom<Config> for SledStorage {
+    type Error = Error;
+
+    fn try_from(config: Config) -> Result<Self> {
+        let tree = try_into!(config.open());
+
+        Ok(Self { tree })
+    }
+}
+
+fn fetch_schema(tree: &Db, table_name: &str) -> Result<(String, Schema)> {
+    let key = format!("schema/{}", table_name);
+    let value = try_into!(tree.get(&key.as_bytes()));
+    let value = value.ok_or(StoreError::SchemaNotFound)?;
+    let schema = try_into!(bincode::deserialize(&value));
+
+    Ok((key, schema))
+}
diff --git a/src/storages/sled_storage/store.rs b/src/storages/sled_storage/store.rs
new file mode 100644
index 00000000..270ed119
--- /dev/null
+++ b/src/storages/sled_storage/store.rs
@@ -0,0 +1,73 @@
+use sled::IVec;
+
+use super::{fetch_schema, SledStorage, StorageError};
+use crate::try_into;
+use crate::{Error, MutResult, Result, Row, RowIter, Schema, Store, StoreMut};
+
+impl StoreMut<IVec> for SledStorage {
+    fn generate_id(self, table_name: &str) -> MutResult<Self, IVec> {
+        let id = try_into!(self, self.tree.generate_id());
+        let id = format!("data/{}/{}", table_name, id);
+
+        Ok((self, IVec::from(id.as_bytes())))
+    }
+
+    fn insert_schema(self, schema: &Schema) -> MutResult<Self, ()> {
+        let key = format!("schema/{}", schema.table_name);
+        let key = key.as_bytes();
+        let value = try_into!(self, bincode::serialize(schema));
+
+        try_into!(self, self.tree.insert(key, value));
+
+        Ok((self, ()))
+    }
+
+    fn delete_schema(self, table_name: &str) -> MutResult<Self, ()> {
+        let prefix = format!("data/{}/", table_name);
+        let tree = &self.tree;
+
+        for item in tree.scan_prefix(prefix.as_bytes()) {
+            let (key, _) = try_into!(self, item);
+
+            try_into!(self, tree.remove(key));
+        }
+
+        let key = format!("schema/{}", table_name);
+        try_into!(self, tree.remove(key));
+
+        Ok((self, ()))
+    }
+
+    fn insert_data(self, key: &IVec, row: Row) -> MutResult<Self, ()> {
+        let value = try_into!(self, bincode::serialize(&row));
+
+        try_into!(self, self.tree.insert(key, value));
+
+        Ok((self, ()))
+    }
+
+    fn delete_data(self, key: &IVec) -> MutResult<Self, ()> {
+        try_into!(self, self.tree.remove(key));
+
+        Ok((self, ()))
+    }
+}
+
+impl Store<IVec> for SledStorage {
+    fn fetch_schema(&self, table_name: &str) -> Result<Schema> {
+        fetch_schema(&self.tree, table_name).map(|(_, schema)| schema)
+    }
+
+    fn scan_data(&self, table_name: &str) -> Result<RowIter<IVec>> {
+        let prefix = format!("data/{}/", table_name);
+
+        let result_set = self.tree.scan_prefix(prefix.as_bytes()).map(move |item| {
+            let (key, value) = try_into!(item);
+            let value = try_into!(bincode::deserialize(&value));
+
+            Ok((key, value))
+        });
+
+        Ok(Box::new(result_set))
+    }
+}
diff --git a/src/store/alter_table.rs b/src/store/alter_table.rs
new file mode 100644
index 00000000..3bd52fe4
--- /dev/null
+++ b/src/store/alter_table.rs
@@ -0,0 +1,45 @@
+use serde::Serialize;
+use std::fmt::Debug;
+use thiserror::Error;
+
+use sqlparser::ast::ColumnDef;
+
+use crate::result::MutResult;
+
+#[derive(Error, Serialize, Debug, PartialEq)]
+pub enum AlterTableError {
+    #[error("Renaming column not found")]
+    RenamingColumnNotFound,
+
+    #[error("Default value is required: {0}")]
+    DefaultValueRequired(String),
+
+    #[error("Adding column already exists: {0}")]
+    AddingColumnAlreadyExists(String),
+
+    #[error("Dropping column not found: {0}")]
+    DroppingColumnNotFound(String),
+}
+
+pub trait AlterTable
+where
+    Self: Sized,
+{
+    fn rename_schema(self, table_name: &str, new_table_name: &str) -> MutResult<Self, ()>;
+
+    fn rename_column(
+        self,
+        table_name: &str,
+        old_column_name: &str,
+        new_column_name: &str,
+    ) -> MutResult<Self, ()>;
+
+    fn add_column(self, table_name: &str, column_def: &ColumnDef) -> MutResult<Self, ()>;
+
+    fn drop_column(
+        self,
+        table_name: &str,
+        column_name: &str,
+        if_exists: bool,
+    ) -> MutResult<Self, ()>;
+}
diff --git a/src/store.rs b/src/store/mod.rs
similarity index 80%
rename from src/store.rs
rename to src/store/mod.rs
index a9924ef9..076cb70b 100644
--- a/src/store.rs
+++ b/src/store/mod.rs
@@ -1,10 +1,17 @@
+#[cfg(feature = "alter-table")]
+mod alter_table;
+#[cfg(feature = "alter-table")]
+pub use alter_table::*;
+#[cfg(not(feature = "alter-table"))]
+pub trait AlterTable {}
+
 use serde::Serialize;
 use std::fmt::Debug;
 use std::marker::Sized;
 use thiserror::Error;
 
-use super::data::{Row, Schema};
-use super::result::{MutResult, Result};
+use crate::data::{Row, Schema};
+use crate::result::{MutResult, Result};
 
 #[derive(Error, Serialize, Debug, PartialEq)]
 pub enum StoreError {
diff --git a/src/utils/indexmap.rs b/src/utils/indexmap.rs
index 18b8d813..702e0a30 100644
--- a/src/utils/indexmap.rs
+++ b/src/utils/indexmap.rs
@@ -29,8 +29,19 @@ impl<K: Hash + Eq, V> IndexMap<K, V> {
     pub fn len(&self) -> usize {
         self.0.len()
     }
+}
+
+impl<K: Hash + Eq, V> Default for IndexMap<K, V> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<K: Hash + Eq, V> IntoIterator for IndexMap<K, V> {
+    type Item = (K, V);
+    type IntoIter = IntoIter<K, V>;
 
-    pub fn into_iter(self) -> IntoIter<K, V> {
+    fn into_iter(self) -> Self::IntoIter {
         self.0.into_iter()
     }
 }
diff --git a/src/utils/vector.rs b/src/utils/vector.rs
index de812561..f002d5a6 100644
--- a/src/utils/vector.rs
+++ b/src/utils/vector.rs
@@ -1,3 +1,4 @@
+use std::convert::{From, Into};
 use std::vec::IntoIter;
 
 pub struct Vector<T>(Vec<T>);
@@ -13,11 +14,41 @@ impl<T> Vector<T> {
         self
     }
 
+    #[cfg(all(feature = "alter-table", feature = "sled-storage"))]
+    pub fn update(mut self, i: usize, value: T) -> Self {
+        self.0[i] = value;
+
+        self
+    }
+
     pub fn get(&self, i: usize) -> Option<&T> {
         self.0.get(i)
     }
+}
+
+impl<T> Default for Vector<T> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
 
-    pub fn into_iter(self) -> IntoIter<T> {
+impl<T> IntoIterator for Vector<T> {
+    type Item = T;
+    type IntoIter = IntoIter<Self::Item>;
+
+    fn into_iter(self) -> Self::IntoIter {
         self.0.into_iter()
     }
 }
+
+impl<T> From<Vec<T>> for Vector<T> {
+    fn from(vector: Vec<T>) -> Self {
+        Self(vector)
+    }
+}
+
+impl<T> Into<Vec<T>> for Vector<T> {
+    fn into(self) -> Vec<T> {
+        self.0
+    }
+}


Test patch:
diff --git a/src/tests/alter_table.rs b/src/tests/alter_table.rs
new file mode 100644
index 00000000..a0d7bf7c
--- /dev/null
+++ b/src/tests/alter_table.rs
@@ -0,0 +1,118 @@
+use crate::*;
+use Value::*;
+
+fn run(mut tester: impl tests::Tester, test_cases: &[(&str, Result<Payload>)]) {
+    test_cases.iter().for_each(|(sql, expected)| {
+        let found = tester.run(sql);
+
+        assert_eq!(expected, &found);
+    });
+}
+
+pub fn rename(tester: impl tests::Tester) {
+    let test_cases = [
+        ("CREATE TABLE Foo (id INTEGER);", Ok(Payload::Create)),
+        (
+            "INSERT INTO Foo VALUES (1), (2), (3);",
+            Ok(Payload::Insert(3)),
+        ),
+        ("SELECT id FROM Foo", Ok(select!(I64; 1; 2; 3))),
+        ("ALTER TABLE Foo RENAME TO Bar;", Ok(Payload::AlterTable)),
+        ("SELECT id FROM Bar", Ok(select!(I64; 1; 2; 3))),
+        (
+            "ALTER TABLE Bar RENAME COLUMN id TO new_id",
+            Ok(Payload::AlterTable),
+        ),
+        ("SELECT new_id FROM Bar", Ok(select!(I64; 1; 2; 3))),
+        ("SELECT new_id FROM Bar", Ok(select!(I64; 1; 2; 3))),
+        (
+            "ALTER TABLE Bar RENAME COLUMN hello TO idid",
+            Err(AlterTableError::RenamingColumnNotFound.into()),
+        ),
+    ];
+
+    run(tester, &test_cases);
+}
+
+pub fn add_drop(tester: impl tests::Tester) {
+    let test_cases = [
+        ("CREATE TABLE Foo (id INTEGER);", Ok(Payload::Create)),
+        ("INSERT INTO Foo VALUES (1), (2);", Ok(Payload::Insert(2))),
+        ("SELECT * FROM Foo;", Ok(select!(I64; 1; 2))),
+        (
+            "ALTER TABLE Foo ADD COLUMN amount INTEGER",
+            Err(AlterTableError::DefaultValueRequired("amount INT".to_owned()).into()),
+        ),
+        (
+            "ALTER TABLE Foo ADD COLUMN id INTEGER",
+            Err(AlterTableError::AddingColumnAlreadyExists("id".to_owned()).into()),
+        ),
+        (
+            "ALTER TABLE Foo ADD COLUMN amount INTEGER DEFAULT 10",
+            Ok(Payload::AlterTable),
+        ),
+        ("SELECT * FROM Foo;", Ok(select!(I64 I64; 1 10; 2 10))),
+        (
+            "ALTER TABLE Foo ADD COLUMN opt BOOLEAN NULL",
+            Ok(Payload::AlterTable),
+        ),
+        (
+            "SELECT * FROM Foo;",
+            Ok(select!(
+                I64 I64 OptBool;
+                1   10  None;
+                2   10  None
+            )),
+        ),
+        (
+            "ALTER TABLE Foo ADD COLUMN opt2 BOOLEAN NULL DEFAULT true",
+            Ok(Payload::AlterTable),
+        ),
+        (
+            "SELECT * FROM Foo;",
+            Ok(select!(
+                I64 I64 OptBool OptBool;
+                1   10  None    Some(true);
+                2   10  None    Some(true)
+            )),
+        ),
+        (
+            "ALTER TABLE Foo ADD COLUMN something INTEGER DEFAULT (SELECT id FROM Bar LIMIT 1)",
+            Err(ValueError::ExprNotSupported("(SELECT id FROM Bar LIMIT 1)".to_owned()).into()),
+        ),
+        (
+            "ALTER TABLE Foo DROP COLUMN IF EXISTS something;",
+            Ok(Payload::AlterTable),
+        ),
+        (
+            "ALTER TABLE Foo DROP COLUMN something;",
+            Err(AlterTableError::DroppingColumnNotFound("something".to_owned()).into()),
+        ),
+        (
+            "ALTER TABLE Foo DROP COLUMN amount;",
+            Ok(Payload::AlterTable),
+        ),
+        (
+            "SELECT * FROM Foo;",
+            Ok(select!(
+                I64 OptBool OptBool;
+                1   None    Some(true);
+                2   None    Some(true)
+            )),
+        ),
+        (
+            "ALTER TABLE Foo DROP COLUMN IF EXISTS opt2;",
+            Ok(Payload::AlterTable),
+        ),
+        (
+            "SELECT * FROM Foo;",
+            Ok(select!(
+                I64 OptBool;
+                1   None;
+                2   None
+            )),
+        ),
+    ];
+
+    run(tester, &test_cases);
+}
diff --git a/src/tests/error.rs b/src/tests/error.rs
index d409f40f..28f63c3c 100644
--- a/src/tests/error.rs
+++ b/src/tests/error.rs
@@ -47,6 +47,14 @@ pub fn error(mut tester: impl tests::Tester) {
             ExecuteError::UnsupportedInsertValueType("SELECT id FROM TableA".to_owned()).into(),
             "INSERT INTO TableA SELECT id FROM TableA",
         ),
+        #[cfg(feature = "alter-table")]
+        (
+            ExecuteError::UnsupportedAlterTableOperation(
+                r#"ADD CONSTRAINT "hey" PRIMARY KEY (asdf)"#.to_owned(),
+            )
+            .into(),
+            r#"ALTER TABLE Foo ADD CONSTRAINT "hey" PRIMARY KEY (asdf);"#,
+        ),
     ];
 
     test_cases
diff --git a/src/tests/migrate.rs b/src/tests/migrate.rs
index cad6af55..2ec3011f 100644
--- a/src/tests/migrate.rs
+++ b/src/tests/migrate.rs
@@ -22,7 +22,7 @@ CREATE TABLE Test (
 
     let error_cases = vec![
         (
-            RowError::UnsupportedAstValueType.into(),
+            ValueError::ExprNotSupported("3 * 2".to_owned()).into(),
             "INSERT INTO Test (id, num) VALUES (3 * 2, 1);",
         ),
         (
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 5789bfcb..001b786a 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,4 +1,6 @@
 pub mod aggregate;
+#[cfg(feature = "alter-table")]
+pub mod alter_table;
 pub mod arithmetic;
 pub mod basic;
 pub mod blend;
@@ -53,5 +55,10 @@ macro_rules! generate_tests {
         glue!(sql_types, sql_types::sql_types);
         glue!(synthesize, synthesize::synthesize);
         glue!(filter, filter::filter);
+
+        #[cfg(feature = "alter-table")]
+        glue!(alter_table_rename, alter_table::rename);
+        #[cfg(feature = "alter-table")]
+        glue!(alter_table_add_drop, alter_table::add_drop);
     };
 }
diff --git a/src/tests/tester.rs b/src/tests/tester.rs
index 4c1a3ab5..0a2bd3d7 100644
--- a/src/tests/tester.rs
+++ b/src/tests/tester.rs
@@ -35,8 +35,10 @@ pub trait Tester {
             Payload::Insert(num) => println!("[Ok ] {} rows inserted.\n", num),
             Payload::Delete(num) => println!("[Ok ] {} rows deleted.\n", num),
             Payload::Update(num) => println!("[Ok ] {} rows updated.\n", num),
-            Payload::DropTable => println!("[Ok ] :)\n"),
-            Payload::Create => println!("[Ok ] :)\n"),
+            Payload::DropTable | Payload::Create => println!("[Ok ] :)\n"),
+
+            #[cfg(feature = "alter-table")]
+            Payload::AlterTable => println!("[Ok ] :)\n"),
         };
     }