This is an automated email from the ASF dual-hosted git repository.

nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git


The following commit(s) were added to refs/heads/main by this push:
     new 4c384d5  [Improvement] Refactor server module (#48)
4c384d5 is described below

commit 4c384d5b82bd275c203b5c1d42113cbd00d01f20
Author: s7monk <[email protected]>
AuthorDate: Sat Oct 14 15:11:21 2023 +0800

    [Improvement] Refactor server module (#48)
---
 paimon-web-server/pom.xml                          |   4 +
 .../web/server/controller/CatalogController.java   |  74 ++--
 .../web/server/controller/DatabaseController.java  |  69 +--
 .../web/server/controller/TableController.java     | 484 ++++++++++++++++-----
 .../DatabaseInfo.java => enums/CatalogMode.java}   |  27 +-
 .../{DatabaseInfo.java => AlterTableRequest.java}  |  14 +-
 .../paimon/web/server/data/model/DatabaseInfo.java |   2 +
 .../paimon/web/server/data/model/TableColumn.java  |  12 +-
 .../web/server/data/result/enums/Status.java       |  10 +-
 .../paimon/web/server/util/CatalogUtils.java       |  44 --
 .../web/server/util/DataTypeConvertUtils.java      | 181 ++++++--
 .../DatabaseInfo.java => util/PaimonDataType.java} |  14 +-
 .../paimon/web/server/util/PaimonServiceUtils.java |  66 +++
 ...{application-dev.yml => application-dev-h2.yml} |   0
 ...lication-prod.yml => application-dev-mysql.yml} |   6 +-
 .../src/main/resources/application-prod.yml        |   6 +-
 .../src/main/resources/application.yml             |   2 +-
 .../src/main/resources/i18n/messages.properties    |  15 +-
 ...missionTest.java => CatalogControllerTest.java} |  89 ++--
 .../web/server/controller/ControllerTestBase.java  | 199 +++++++++
 ...issionTest.java => DatabaseControllerTest.java} |  83 ++--
 .../web/server/controller/PermissionTest.java      |  68 +--
 .../server/controller/SysMenuControllerTest.java   |  68 +--
 .../server/controller/SysRoleControllerTest.java   |  72 +--
 .../web/server/controller/TableControllerTest.java | 322 ++++++++++++++
 pom.xml                                            |   1 +
 scripts/sql/paimon-mysql.sql                       | 174 ++++++++
 27 files changed, 1493 insertions(+), 613 deletions(-)

diff --git a/paimon-web-server/pom.xml b/paimon-web-server/pom.xml
index 3c4fa57..02ec9b7 100644
--- a/paimon-web-server/pom.xml
+++ b/paimon-web-server/pom.xml
@@ -76,6 +76,10 @@ under the License.
                     <groupId>org.junit.vintage</groupId>
                     <artifactId>junit-vintage-engine</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>junit</groupId>
+                    <artifactId>junit</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
index cdc0bf1..64e166f 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/CatalogController.java
@@ -18,13 +18,16 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.web.api.catalog.CatalogCreator;
+import org.apache.paimon.web.api.catalog.PaimonServiceFactory;
+import org.apache.paimon.web.server.data.enums.CatalogMode;
 import org.apache.paimon.web.server.data.model.CatalogInfo;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.service.CatalogService;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -45,46 +48,39 @@ public class CatalogController {
     @Autowired private CatalogService catalogService;
 
     /**
-     * Create a filesystem catalog.
+     * Create a catalog.
      *
-     * @param catalogInfo The catalogInfo for the filesystem catalog.
+     * @param catalogInfo The catalogInfo for the catalog.
      * @return The created catalog.
      */
-    @PostMapping("/createFilesystemCatalog")
-    public R<Void> createFilesystemCatalog(@RequestBody CatalogInfo 
catalogInfo) {
+    @PostMapping("/create")
+    public R<Void> createCatalog(@RequestBody CatalogInfo catalogInfo) {
         if (!catalogService.checkCatalogNameUnique(catalogInfo)) {
             return R.failed(Status.CATALOG_NAME_IS_EXIST, 
catalogInfo.getCatalogName());
         }
 
         try {
-            CatalogCreator.createFilesystemCatalog(catalogInfo.getWarehouse());
+            if 
(catalogInfo.getCatalogType().equalsIgnoreCase(CatalogMode.FILESYSTEM.getMode()))
 {
+                PaimonServiceFactory.createFileSystemCatalogService(
+                        catalogInfo.getCatalogName(), 
catalogInfo.getWarehouse());
+            } else if 
(catalogInfo.getCatalogType().equalsIgnoreCase(CatalogMode.HIVE.getMode())) {
+                if (StringUtils.isNotBlank(catalogInfo.getHiveConfDir())) {
+                    PaimonServiceFactory.createHiveCatalogService(
+                            catalogInfo.getCatalogName(),
+                            catalogInfo.getWarehouse(),
+                            catalogInfo.getHiveUri(),
+                            catalogInfo.getHiveConfDir());
+                } else {
+                    PaimonServiceFactory.createHiveCatalogService(
+                            catalogInfo.getCatalogName(),
+                            catalogInfo.getWarehouse(),
+                            catalogInfo.getHiveUri(),
+                            null);
+                }
+            }
             return catalogService.save(catalogInfo) ? R.succeed() : R.failed();
         } catch (Exception e) {
-            e.printStackTrace();
-            return R.failed(Status.CATALOG_CREATE_ERROR);
-        }
-    }
-
-    /**
-     * Create a hive catalog.
-     *
-     * @param catalogInfo The information for the hive catalog.
-     * @return The created catalog.
-     */
-    @PostMapping("/createHiveCatalog")
-    public R<Void> createHiveCatalog(@RequestBody CatalogInfo catalogInfo) {
-        if (!catalogService.checkCatalogNameUnique(catalogInfo)) {
-            return R.failed(Status.CATALOG_NAME_IS_EXIST, 
catalogInfo.getCatalogName());
-        }
-
-        try {
-            CatalogCreator.createHiveCatalog(
-                    catalogInfo.getWarehouse(),
-                    catalogInfo.getHiveUri(),
-                    catalogInfo.getHiveConfDir());
-            return catalogService.save(catalogInfo) ? R.succeed() : R.failed();
-        } catch (Exception e) {
-            e.printStackTrace();
+            log.error("Exception with creating catalog.", e);
             return R.failed(Status.CATALOG_CREATE_ERROR);
         }
     }
@@ -101,13 +97,17 @@ public class CatalogController {
     }
 
     /**
-     * Removes a catalog by its ID.
+     * Removes a catalog with given catalog name.
      *
-     * @param catalogId The ID of the catalog to be removed.
-     * @return A response indicating the success or failure of the removal 
operation.
+     * @param catalogName The catalog name.
+     * @return A response indicating the success or failure of the operation.
      */
-    @DeleteMapping("/{catalogId}")
-    public R<Void> remove(@PathVariable Integer catalogId) {
-        return catalogService.removeById(catalogId) ? R.succeed() : R.failed();
+    @DeleteMapping("/remove/{catalogName}")
+    public R<Void> removeCatalog(@PathVariable String catalogName) {
+        QueryWrapper<CatalogInfo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("catalog_name", catalogName);
+        return catalogService.remove(queryWrapper)
+                ? R.succeed()
+                : R.failed(Status.CATALOG_REMOVE_ERROR);
     }
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
index 83d0584..9ab10db 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/DatabaseController.java
@@ -18,20 +18,21 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.catalog.Catalog;
-import org.apache.paimon.web.api.database.DatabaseManager;
+import org.apache.paimon.web.api.catalog.PaimonService;
 import org.apache.paimon.web.server.data.model.CatalogInfo;
 import org.apache.paimon.web.server.data.model.DatabaseInfo;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.service.CatalogService;
-import org.apache.paimon.web.server.util.CatalogUtils;
+import org.apache.paimon.web.server.util.PaimonServiceUtils;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -54,18 +55,18 @@ public class DatabaseController {
      * @param databaseInfo The DatabaseInfo object containing the details of 
the new database.
      * @return R<Void/> indicating the result of the operation.
      */
-    @PostMapping("/createDatabase")
+    @PostMapping("/create")
     public R<Void> createDatabase(@RequestBody DatabaseInfo databaseInfo) {
         try {
-            CatalogInfo catalogInfo = getCatalogInfo(databaseInfo);
-            Catalog catalog = CatalogUtils.getCatalog(catalogInfo);
-            if (DatabaseManager.databaseExists(catalog, 
databaseInfo.getDatabaseName())) {
+            CatalogInfo catalogInfo = 
getCatalogInfo(databaseInfo.getCatalogName());
+            PaimonService service = 
PaimonServiceUtils.getPaimonService(catalogInfo);
+            if (service.databaseExists(databaseInfo.getDatabaseName())) {
                 return R.failed(Status.DATABASE_NAME_IS_EXIST, 
databaseInfo.getDatabaseName());
             }
-            DatabaseManager.createDatabase(catalog, 
databaseInfo.getDatabaseName());
+            service.createDatabase(databaseInfo.getDatabaseName());
             return R.succeed();
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error("Exception with creating database.", e);
             return R.failed(Status.DATABASE_CREATE_ERROR);
         }
     }
@@ -79,17 +80,18 @@ public class DatabaseController {
     public R<List<DatabaseInfo>> getAllDatabases() {
         List<DatabaseInfo> databaseInfoList = new ArrayList<>();
         List<CatalogInfo> catalogInfoList = catalogService.list();
-        if (catalogInfoList.size() > 0) {
+        if (!CollectionUtils.isEmpty(catalogInfoList)) {
             catalogInfoList.forEach(
                     item -> {
-                        Catalog catalog = CatalogUtils.getCatalog(item);
-                        List<String> list = 
DatabaseManager.listDatabase(catalog);
+                        PaimonService service = 
PaimonServiceUtils.getPaimonService(item);
+                        List<String> list = service.listDatabases();
                         list.forEach(
                                 databaseName -> {
                                     DatabaseInfo info =
                                             DatabaseInfo.builder()
                                                     .databaseName(databaseName)
                                                     .catalogId(item.getId())
+                                                    
.catalogName(item.getCatalogName())
                                                     .description("")
                                                     .build();
                                     databaseInfoList.add(info);
@@ -99,34 +101,37 @@ public class DatabaseController {
         return R.succeed(databaseInfoList);
     }
 
-    /**
-     * Retrieves the associated CatalogInfo object based on the given 
DatabaseInfo object.
-     *
-     * @param databaseInfo The DatabaseInfo object for which to retrieve the 
associated CatalogInfo.
-     * @return The associated CatalogInfo object, or null if it doesn't exist.
-     */
-    private CatalogInfo getCatalogInfo(DatabaseInfo databaseInfo) {
-        LambdaQueryWrapper<CatalogInfo> queryWrapper = new 
LambdaQueryWrapper<>();
-        queryWrapper.eq(CatalogInfo::getId, databaseInfo.getCatalogId());
-        return catalogService.getOne(queryWrapper);
-    }
-
     /**
      * Removes a database by its name.
      *
-     * @param databaseInfo The information of the database to be removed.
+     * @param databaseName The database to be removed.
+     * @param catalogName The catalog to which the database to be removed 
belongs.
      * @return A response indicating the success or failure of the removal 
operation.
      * @throws RuntimeException if the database is not found or it is not 
empty.
      */
-    @DeleteMapping("/delete")
-    public R<Void> remove(@RequestBody DatabaseInfo databaseInfo) {
+    @DeleteMapping("/drop/{databaseName}/{catalogName}")
+    public R<Void> dropDatabase(
+            @PathVariable String databaseName, @PathVariable String 
catalogName) {
         try {
-            CatalogInfo catalogInfo = getCatalogInfo(databaseInfo);
-            Catalog catalog = CatalogUtils.getCatalog(catalogInfo);
-            DatabaseManager.dropDatabase(catalog, 
databaseInfo.getDatabaseName());
+            CatalogInfo catalogInfo = getCatalogInfo(catalogName);
+            PaimonService service = 
PaimonServiceUtils.getPaimonService(catalogInfo);
+            service.dropDatabase(databaseName);
             return R.succeed();
-        } catch (Catalog.DatabaseNotEmptyException | 
Catalog.DatabaseNotExistException e) {
-            throw new RuntimeException(e);
+        } catch (Exception e) {
+            log.error("Exception with dropping database.", e);
+            return R.failed(Status.DATABASE_DROP_ERROR);
         }
     }
+
+    /**
+     * Retrieves the associated CatalogInfo object based on the given catalog 
name.
+     *
+     * @param catalogName The catalog name.
+     * @return The associated CatalogInfo object, or null if it doesn't exist.
+     */
+    private CatalogInfo getCatalogInfo(String catalogName) {
+        LambdaQueryWrapper<CatalogInfo> queryWrapper = new 
LambdaQueryWrapper<>();
+        queryWrapper.eq(CatalogInfo::getCatalogName, catalogName);
+        return catalogService.getOne(queryWrapper);
+    }
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
index 33eaa1c..07cc45f 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/TableController.java
@@ -18,34 +18,41 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.catalog.Catalog;
 import org.apache.paimon.table.Table;
 import org.apache.paimon.types.DataField;
-import org.apache.paimon.web.api.database.DatabaseManager;
+import org.apache.paimon.web.api.catalog.PaimonService;
 import org.apache.paimon.web.api.table.ColumnMetadata;
-import org.apache.paimon.web.api.table.TableManager;
+import org.apache.paimon.web.api.table.TableChange;
 import org.apache.paimon.web.api.table.TableMetadata;
+import org.apache.paimon.web.server.data.model.AlterTableRequest;
 import org.apache.paimon.web.server.data.model.CatalogInfo;
 import org.apache.paimon.web.server.data.model.TableColumn;
 import org.apache.paimon.web.server.data.model.TableInfo;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.service.CatalogService;
-import org.apache.paimon.web.server.util.CatalogUtils;
 import org.apache.paimon.web.server.util.DataTypeConvertUtils;
+import org.apache.paimon.web.server.util.PaimonDataType;
+import org.apache.paimon.web.server.util.PaimonServiceUtils;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /** Table api controller. */
 @Slf4j
@@ -53,6 +60,9 @@ import java.util.Map;
 @RequestMapping("/api/table")
 public class TableController {
 
+    private static final String FIELDS_PREFIX = "FIELDS";
+    private static final String DEFAULT_VALUE_SUFFIX = "default-value";
+
     @Autowired private CatalogService catalogService;
 
     /**
@@ -61,12 +71,30 @@ public class TableController {
      * @param tableInfo The TableInfo object containing information about the 
table.
      * @return R<Void/> indicating the success or failure of the operation.
      */
-    @PostMapping("/createTable")
+    @PostMapping("/create")
     public R<Void> createTable(@RequestBody TableInfo tableInfo) {
         try {
-            Catalog catalog = 
CatalogUtils.getCatalog(getCatalogInfo(tableInfo.getCatalogName()));
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(tableInfo.getCatalogName()));
             List<String> partitionKeys = tableInfo.getPartitionKey();
+
             Map<String, String> tableOptions = tableInfo.getTableOptions();
+            List<TableColumn> tableColumns = tableInfo.getTableColumns();
+            if (!CollectionUtils.isEmpty(tableColumns)) {
+                for (TableColumn tableColumn : tableColumns) {
+                    if (tableColumn.getDefaultValue() != null
+                            && !tableColumn.getDefaultValue().equals("")) {
+                        tableOptions.put(
+                                FIELDS_PREFIX
+                                        + "."
+                                        + tableColumn.getField()
+                                        + "."
+                                        + DEFAULT_VALUE_SUFFIX,
+                                tableColumn.getDefaultValue());
+                    }
+                }
+            }
+
             TableMetadata tableMetadata =
                     TableMetadata.builder()
                             .columns(buildColumns(tableInfo))
@@ -75,19 +103,285 @@ public class TableController {
                             .options(tableOptions)
                             .comment(tableInfo.getDescription())
                             .build();
-            if (TableManager.tableExists(
-                    catalog, tableInfo.getDatabaseName(), 
tableInfo.getTableName())) {
+            if (service.tableExists(tableInfo.getDatabaseName(), 
tableInfo.getTableName())) {
                 return R.failed(Status.TABLE_NAME_IS_EXIST, 
tableInfo.getTableName());
             }
-            TableManager.createTable(
-                    catalog, tableInfo.getDatabaseName(), 
tableInfo.getTableName(), tableMetadata);
+            service.createTable(
+                    tableInfo.getDatabaseName(), tableInfo.getTableName(), 
tableMetadata);
             return R.succeed();
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error("Exception with creating table.", e);
             return R.failed(Status.TABLE_CREATE_ERROR);
         }
     }
 
+    /**
+     * Adds a column to the table.
+     *
+     * @param tableInfo The information of the table, including the catalog 
name, database name,
+     *     table name, and table columns.
+     * @return A response indicating the success or failure of the operation.
+     */
+    @PostMapping("/column/add")
+    public R<Void> addColumn(@RequestBody TableInfo tableInfo) {
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(tableInfo.getCatalogName()));
+            List<TableColumn> tableColumns = tableInfo.getTableColumns();
+            List<TableChange> tableChanges = new ArrayList<>();
+            Map<String, String> options = new HashMap<>();
+            for (TableColumn tableColumn : tableColumns) {
+                if (tableColumn.getDefaultValue() != null
+                        && !tableColumn.getDefaultValue().equals("")) {
+                    options.put(
+                            FIELDS_PREFIX
+                                    + "."
+                                    + tableColumn.getField()
+                                    + "."
+                                    + DEFAULT_VALUE_SUFFIX,
+                            tableColumn.getDefaultValue());
+                }
+                ColumnMetadata columnMetadata =
+                        new ColumnMetadata(
+                                tableColumn.getField(),
+                                DataTypeConvertUtils.convert(
+                                        new PaimonDataType(
+                                                
tableColumn.getDataType().getType(),
+                                                true,
+                                                
tableColumn.getDataType().getPrecision(),
+                                                
tableColumn.getDataType().getScale())),
+                                tableColumn.getComment());
+                TableChange.AddColumn add = TableChange.add(columnMetadata);
+                tableChanges.add(add);
+            }
+
+            if (options.size() > 0) {
+                for (Map.Entry<String, String> entry : options.entrySet()) {
+                    TableChange.SetOption setOption =
+                            TableChange.set(entry.getKey(), entry.getValue());
+                    tableChanges.add(setOption);
+                }
+            }
+            service.alterTable(tableInfo.getDatabaseName(), 
tableInfo.getTableName(), tableChanges);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with adding column.", e);
+            return R.failed(Status.TABLE_ADD_COLUMN_ERROR);
+        }
+    }
+
+    /**
+     * Drops a column from a table.
+     *
+     * @param catalogName The name of the catalog.
+     * @param databaseName The name of the database.
+     * @param tableName The name of the table.
+     * @param columnName The name of the column to be dropped.
+     * @return The result indicating the success or failure of the operation.
+     */
+    
@DeleteMapping("/column/drop/{catalogName}/{databaseName}/{tableName}/{columnName}")
+    public R<Void> dropColumn(
+            @PathVariable String catalogName,
+            @PathVariable String databaseName,
+            @PathVariable String tableName,
+            @PathVariable String columnName) {
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(catalogName));
+            List<TableChange> tableChanges = new ArrayList<>();
+            TableChange.DropColumn dropColumn = 
TableChange.dropColumn(columnName);
+            tableChanges.add(dropColumn);
+            service.alterTable(databaseName, tableName, tableChanges);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with dropping column.", e);
+            return R.failed(Status.TABLE_DROP_COLUMN_ERROR);
+        }
+    }
+
+    /**
+     * Modify a column in a table.
+     *
+     * @param catalogName The name of the catalog.
+     * @param databaseName The name of the database.
+     * @param tableName The name of the table.
+     * @param alterTableRequest The param of the alter table request.
+     * @return A response indicating the success or failure of the operation.
+     */
+    @PostMapping("/alter")
+    public R<Void> alterTable(
+            @RequestParam String catalogName,
+            @RequestParam String databaseName,
+            @RequestParam String tableName,
+            @RequestBody AlterTableRequest alterTableRequest) {
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(catalogName));
+
+            TableColumn oldColumn = alterTableRequest.getOldColumn();
+            TableColumn newColumn = alterTableRequest.getNewColumn();
+
+            List<TableChange> tableChanges = createTableChanges(oldColumn, 
newColumn);
+
+            if (!Objects.equals(newColumn.getField(), oldColumn.getField())) {
+                ColumnMetadata columnMetadata =
+                        new ColumnMetadata(
+                                oldColumn.getField(),
+                                
DataTypeConvertUtils.convert(oldColumn.getDataType()),
+                                oldColumn.getComment());
+
+                TableChange.ModifyColumnName modifyColumnName =
+                        TableChange.modifyColumnName(columnMetadata, 
newColumn.getField());
+                List<TableChange> modifyNameTableChanges = new ArrayList<>();
+                modifyNameTableChanges.add(modifyColumnName);
+                service.alterTable(databaseName, tableName, 
modifyNameTableChanges);
+            }
+
+            service.alterTable(databaseName, tableName, tableChanges);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with altering table.", e);
+            return R.failed(Status.TABLE_AlTER_COLUMN_ERROR);
+        }
+    }
+
+    private List<TableChange> createTableChanges(TableColumn oldColumn, 
TableColumn newColumn) {
+        ColumnMetadata columnMetadata =
+                new ColumnMetadata(
+                        newColumn.getField(),
+                        DataTypeConvertUtils.convert(oldColumn.getDataType()),
+                        oldColumn.getComment());
+
+        TableChange.ModifyColumnType modifyColumnType =
+                TableChange.modifyColumnType(
+                        columnMetadata, 
DataTypeConvertUtils.convert(newColumn.getDataType()));
+
+        TableChange.ModifyColumnComment modifyColumnComment =
+                TableChange.modifyColumnComment(columnMetadata, 
newColumn.getComment());
+
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(modifyColumnType);
+        tableChanges.add(modifyColumnComment);
+
+        return tableChanges;
+    }
+
+    /**
+     * Adds options to a table.
+     *
+     * @param tableInfo An object containing table information.
+     * @return If the options are successfully added, returns a successful 
result object. If an
+     *     exception occurs, returns a result object with an error status.
+     */
+    @PostMapping("/option/add")
+    public R<Void> addOption(@RequestBody TableInfo tableInfo) {
+        List<TableChange> tableChanges = new ArrayList<>();
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(tableInfo.getCatalogName()));
+            Map<String, String> tableOptions = tableInfo.getTableOptions();
+            for (Map.Entry<String, String> entry : tableOptions.entrySet()) {
+                TableChange.SetOption setOption = 
TableChange.set(entry.getKey(), entry.getValue());
+                tableChanges.add(setOption);
+            }
+            service.alterTable(tableInfo.getDatabaseName(), 
tableInfo.getTableName(), tableChanges);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with adding option.", e);
+            return R.failed(Status.TABLE_ADD_OPTION_ERROR);
+        }
+    }
+
+    /**
+     * Removes an option from a table.
+     *
+     * @param catalogName The name of the catalog.
+     * @param databaseName The name of the database.
+     * @param tableName The name of the table.
+     * @param key The key of the option to be removed.
+     * @return Returns a {@link R} object indicating the success or failure of 
the operation. If the
+     *     option is successfully removed, the result will be a successful 
response with no data. If
+     *     an error occurs during the operation, the result will be a failed 
response with an error
+     *     code. Possible error codes: {@link 
Status#TABLE_REMOVE_OPTION_ERROR}.
+     */
+    @PostMapping("/option/remove")
+    public R<Void> removeOption(
+            @RequestParam String catalogName,
+            @RequestParam String databaseName,
+            @RequestParam String tableName,
+            @RequestParam String key) {
+        List<TableChange> tableChanges = new ArrayList<>();
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(catalogName));
+            TableChange.RemoveOption removeOption = TableChange.remove(key);
+            tableChanges.add(removeOption);
+            service.alterTable(databaseName, tableName, tableChanges);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with removing option.", e);
+            return R.failed(Status.TABLE_REMOVE_OPTION_ERROR);
+        }
+    }
+
+    /**
+     * Drops a table from the specified database in the given catalog.
+     *
+     * @param catalogName The name of the catalog from which the table will be 
dropped.
+     * @param databaseName The name of the database from which the table will 
be dropped.
+     * @param tableName The name of the table to be dropped.
+     * @return A Response object indicating the success or failure of the 
operation. If the
+     *     operation is successful, the response will be R.succeed(). If the 
operation fails, the
+     *     response will be R.failed() with Status.TABLE_DROP_ERROR.
+     * @throws RuntimeException If there is an error during the operation, a 
RuntimeException is
+     *     thrown with the error message.
+     */
+    @DeleteMapping("/drop/{catalogName}/{databaseName}/{tableName}")
+    public R<Void> dropTable(
+            @PathVariable String catalogName,
+            @PathVariable String databaseName,
+            @PathVariable String tableName) {
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(catalogName));
+            service.dropTable(databaseName, tableName);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with dropping table.", e);
+            return R.failed(Status.TABLE_DROP_ERROR);
+        }
+    }
+
+    /**
+     * Renames a table in the specified database of the given catalog.
+     *
+     * @param catalogName The name of the catalog where the table resides.
+     * @param databaseName The name of the database where the table resides.
+     * @param fromTableName The current name of the table to be renamed.
+     * @param toTableName The new name for the table.
+     * @return A Response object indicating the success or failure of the 
operation. If the
+     *     operation is successful, the response will be R.succeed(). If the 
operation fails, the
+     *     response will be R.failed() with Status.TABLE_RENAME_ERROR.
+     * @throws RuntimeException If there is an error during the operation, a 
RuntimeException is
+     *     thrown with the error message.
+     */
+    @PostMapping("/rename")
+    public R<Void> renameTable(
+            @RequestParam String catalogName,
+            @RequestParam String databaseName,
+            @RequestParam String fromTableName,
+            @RequestParam String toTableName) {
+        try {
+            PaimonService service =
+                    
PaimonServiceUtils.getPaimonService(getCatalogInfo(catalogName));
+            service.renameTable(databaseName, fromTableName, toTableName);
+            return R.succeed();
+        } catch (Exception e) {
+            log.error("Exception with renaming table.", e);
+            return R.failed(Status.TABLE_RENAME_ERROR);
+        }
+    }
+
     /**
      * Handler method for the "/getAllTables" endpoint. Retrieves information 
about all tables and
      * returns a response containing the table details.
@@ -98,102 +392,71 @@ public class TableController {
     public R<List<TableInfo>> getAllTables() {
         List<TableInfo> tableInfoList = new ArrayList<>();
         List<CatalogInfo> catalogInfoList = catalogService.list();
-        if (catalogInfoList.size() > 0) {
-            catalogInfoList.forEach(
-                    item -> {
-                        Catalog catalog = CatalogUtils.getCatalog(item);
-                        List<String> databaseList = 
DatabaseManager.listDatabase(catalog);
-                        if (databaseList.size() > 0) {
-                            databaseList.forEach(
-                                    db -> {
-                                        try {
-                                            List<String> tables =
-                                                    
TableManager.listTables(catalog, db);
-                                            if (tables.size() > 0) {
-                                                tables.forEach(
-                                                        t -> {
-                                                            try {
-                                                                Table table =
-                                                                        
TableManager.getTable(
-                                                                               
 catalog, db, t);
-                                                                if (table != 
null) {
-                                                                    
List<String> primaryKeys =
-                                                                            
table.primaryKeys();
-                                                                    
List<DataField> fields =
-                                                                            
table.rowType()
-                                                                               
     .getFields();
-                                                                    
List<TableColumn> tableColumns =
-                                                                            
new ArrayList<>();
-                                                                    if 
(fields.size() > 0) {
-                                                                        
fields.forEach(
-                                                                               
 field -> {
-                                                                               
     TableColumn
-                                                                               
                     .TableColumnBuilder
-                                                                               
             builder =
-                                                                               
                     TableColumn
-                                                                               
                             .builder()
-                                                                               
                             .field(
-                                                                               
                                     field
-                                                                               
                                             .name())
-                                                                               
                             .dataType(
-                                                                               
                                     DataTypeConvertUtils
-                                                                               
                                             .fromPaimonType(
-                                                                               
                                                     field
-                                                                               
                                                             .type()))
-                                                                               
                             .comment(
-                                                                               
                                     field
-                                                                               
                                             .description());
-                                                                               
     if (primaryKeys
-                                                                               
                             .size()
-                                                                               
                     > 0
-                                                                               
             && primaryKeys
-                                                                               
                     .contains(
-                                                                               
                             field
-                                                                               
                                     .name())) {
-                                                                               
         builder
-                                                                               
                 .isPK(
-                                                                               
                         true);
-                                                                               
     }
-                                                                               
     tableColumns
-                                                                               
             .add(
-                                                                               
                     builder
-                                                                               
                             .build());
-                                                                               
 });
-                                                                    }
-                                                                    TableInfo 
tableInfo =
-                                                                            
TableInfo.builder()
-                                                                               
     .catalogName(
-                                                                               
             item
-                                                                               
                     .getCatalogName())
-                                                                               
     .databaseName(
-                                                                               
             db)
-                                                                               
     .tableName(
-                                                                               
             table
-                                                                               
                     .name())
-                                                                               
     .partitionKey(
-                                                                               
             table
-                                                                               
                     .partitionKeys())
-                                                                               
     .tableOptions(
-                                                                               
             table
-                                                                               
                     .options())
-                                                                               
     .tableColumns(
-                                                                               
             tableColumns)
-                                                                               
     .build();
-                                                                    
tableInfoList.add(tableInfo);
-                                                                }
-                                                            } catch (
-                                                                    
Catalog.TableNotExistException
-                                                                            e) 
{
-                                                                throw new 
RuntimeException(e);
-                                                            }
-                                                        });
+        if (!CollectionUtils.isEmpty(catalogInfoList)) {
+            for (CatalogInfo item : catalogInfoList) {
+                PaimonService service = 
PaimonServiceUtils.getPaimonService(item);
+                List<String> databaseList = service.listDatabases();
+                if (!CollectionUtils.isEmpty(databaseList)) {
+                    for (String db : databaseList) {
+                        try {
+                            List<String> tables = service.listTables(db);
+                            if (!CollectionUtils.isEmpty(tables)) {
+                                for (String t : tables) {
+                                    try {
+                                        Table table = service.getTable(db, t);
+                                        if (table != null) {
+                                            List<String> primaryKeys = 
table.primaryKeys();
+                                            List<DataField> fields = 
table.rowType().getFields();
+                                            List<TableColumn> tableColumns = 
new ArrayList<>();
+                                            Map<String, String> options = 
table.options();
+                                            if 
(!CollectionUtils.isEmpty(fields)) {
+                                                for (DataField field : fields) 
{
+                                                    String key =
+                                                            FIELDS_PREFIX
+                                                                    + "."
+                                                                    + 
field.name()
+                                                                    + "."
+                                                                    + 
DEFAULT_VALUE_SUFFIX;
+                                                    PaimonDataType dataType =
+                                                            
DataTypeConvertUtils.fromPaimonType(
+                                                                    
field.type());
+                                                    
TableColumn.TableColumnBuilder builder =
+                                                            
TableColumn.builder()
+                                                                    
.field(field.name())
+                                                                    
.dataType(dataType)
+                                                                    
.comment(field.description());
+                                                    if (primaryKeys.size() > 0
+                                                            && 
primaryKeys.contains(field.name())) {
+                                                        builder.isPk(true);
+                                                    }
+                                                    if (options.get(key) != 
null) {
+                                                        
builder.defaultValue(options.get(key));
+                                                    }
+                                                    
tableColumns.add(builder.build());
+                                                }
                                             }
-                                        } catch 
(Catalog.DatabaseNotExistException e) {
-                                            throw new RuntimeException(e);
+                                            TableInfo tableInfo =
+                                                    TableInfo.builder()
+                                                            
.catalogName(item.getCatalogName())
+                                                            .databaseName(db)
+                                                            
.tableName(table.name())
+                                                            
.partitionKey(table.partitionKeys())
+                                                            
.tableOptions(table.options())
+                                                            
.tableColumns(tableColumns)
+                                                            .build();
+                                            tableInfoList.add(tableInfo);
                                         }
-                                    });
+                                    } catch (Exception e) {
+                                        throw new RuntimeException(e);
+                                    }
+                                }
+                            }
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
                         }
-                    });
+                    }
+                }
+            }
         }
         return R.succeed(tableInfoList);
     }
@@ -207,10 +470,10 @@ public class TableController {
     private List<String> buildPrimaryKeys(TableInfo tableInfo) {
         List<String> primaryKeys = new ArrayList<>();
         List<TableColumn> tableColumns = tableInfo.getTableColumns();
-        if (tableColumns != null && tableColumns.size() > 0) {
+        if (!CollectionUtils.isEmpty(tableColumns)) {
             tableColumns.forEach(
                     item -> {
-                        if (item.isPK()) {
+                        if (item.isPk()) {
                             primaryKeys.add(item.getField());
                         }
                     });
@@ -227,13 +490,18 @@ public class TableController {
     private List<ColumnMetadata> buildColumns(TableInfo tableInfo) {
         List<ColumnMetadata> columns = new ArrayList<>();
         List<TableColumn> tableColumns = tableInfo.getTableColumns();
-        if (tableColumns != null && tableColumns.size() > 0) {
+        if (!CollectionUtils.isEmpty(tableColumns)) {
             tableColumns.forEach(
                     item -> {
                         ColumnMetadata columnMetadata =
                                 new ColumnMetadata(
                                         item.getField(),
-                                        
DataTypeConvertUtils.convert(item.getDataType()),
+                                        DataTypeConvertUtils.convert(
+                                                new PaimonDataType(
+                                                        
item.getDataType().getType(),
+                                                        
item.getDataType().isNullable(),
+                                                        
item.getDataType().getPrecision(),
+                                                        
item.getDataType().getScale())),
                                         item.getComment() != null ? 
item.getComment() : null);
                         columns.add(columnMetadata);
                     });
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/enums/CatalogMode.java
similarity index 68%
copy from 
paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
copy to 
paimon-web-server/src/main/java/org/apache/paimon/web/server/data/enums/CatalogMode.java
index 0f93b78..2dc029a 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/enums/CatalogMode.java
@@ -16,23 +16,20 @@
  * limitations under the License.
  */
 
-package org.apache.paimon.web.server.data.model;
+package org.apache.paimon.web.server.data.enums;
 
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+/** Enum representing different catalog modes. */
+public enum CatalogMode {
+    FILESYSTEM("filesystem"),
+    HIVE("hive");
 
-/** Database table model. */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class DatabaseInfo {
+    private final String mode;
 
-    private String databaseName;
+    CatalogMode(String mode) {
+        this.mode = mode;
+    }
 
-    private Integer catalogId;
-
-    private String description;
+    public String getMode() {
+        return mode;
+    }
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/AlterTableRequest.java
similarity index 84%
copy from 
paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
copy to 
paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/AlterTableRequest.java
index 0f93b78..73f1873 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/AlterTableRequest.java
@@ -19,20 +19,16 @@
 package org.apache.paimon.web.server.data.model;
 
 import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-/** Database table model. */
+/** Alter table request. */
 @Data
-@Builder
-@NoArgsConstructor
 @AllArgsConstructor
-public class DatabaseInfo {
-
-    private String databaseName;
+@NoArgsConstructor
+public class AlterTableRequest {
 
-    private Integer catalogId;
+    private TableColumn oldColumn;
 
-    private String description;
+    private TableColumn newColumn;
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
index 0f93b78..5789ec0 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
@@ -34,5 +34,7 @@ public class DatabaseInfo {
 
     private Integer catalogId;
 
+    private String catalogName;
+
     private String description;
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
index 0593dea..62ea3d2 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/TableColumn.java
@@ -18,11 +18,15 @@
 
 package org.apache.paimon.web.server.data.model;
 
+import org.apache.paimon.web.server.util.PaimonDataType;
+
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import javax.annotation.Nullable;
+
 /** TableColumn model. */
 @Data
 @Builder
@@ -32,11 +36,11 @@ public class TableColumn {
 
     private String field;
 
-    private String dataType;
+    private PaimonDataType dataType;
 
-    private String comment;
+    @Nullable private String comment;
 
-    private boolean isPK;
+    @Nullable private boolean isPk;
 
-    private String defaultValue;
+    @Nullable private String defaultValue;
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
index c6e5788..5239e85 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
@@ -56,15 +56,23 @@ public enum Status {
     /** ------------catalog-----------------. */
     CATALOG_NAME_IS_EXIST(10301, "catalog.name.exist"),
     CATALOG_CREATE_ERROR(10302, "catalog.create.error"),
+    CATALOG_REMOVE_ERROR(10303, "catalog.remove.error"),
 
     /** ------------database-----------------. */
     DATABASE_NAME_IS_EXIST(10401, "database.name.exist"),
     DATABASE_CREATE_ERROR(10402, "database.create.error"),
+    DATABASE_DROP_ERROR(10403, "database.drop.error"),
 
     /** ------------table-----------------. */
     TABLE_NAME_IS_EXIST(10501, "table.name.exist"),
     TABLE_CREATE_ERROR(10502, "table.create.error"),
-    ;
+    TABLE_ADD_COLUMN_ERROR(10503, "table.add.column.error"),
+    TABLE_ADD_OPTION_ERROR(10504, "table.add.option.error"),
+    TABLE_REMOVE_OPTION_ERROR(10505, "table.remove.option.error"),
+    TABLE_DROP_COLUMN_ERROR(10506, "table.drop.column.error"),
+    TABLE_AlTER_COLUMN_ERROR(10507, "table.alter.column.error"),
+    TABLE_DROP_ERROR(10510, "table.drop.error"),
+    TABLE_RENAME_ERROR(10510, "table.rename.error");
 
     private final int code;
     private final String msg;
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/CatalogUtils.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/CatalogUtils.java
deleted file mode 100644
index c19668e..0000000
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/CatalogUtils.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.paimon.web.server.util;
-
-import org.apache.paimon.catalog.Catalog;
-import org.apache.paimon.web.api.catalog.CatalogCreator;
-import org.apache.paimon.web.server.data.model.CatalogInfo;
-
-/** catalog util. */
-public class CatalogUtils {
-
-    /**
-     * Get a Catalog based on the provided CatalogInfo.
-     *
-     * @param catalogInfo The CatalogInfo object containing the catalog 
details.
-     * @return The created Catalog object.
-     */
-    public static Catalog getCatalog(CatalogInfo catalogInfo) {
-        if ("filesystem".equals(catalogInfo.getCatalogType())) {
-            return 
CatalogCreator.createFilesystemCatalog(catalogInfo.getWarehouse());
-        } else {
-            return CatalogCreator.createHiveCatalog(
-                    catalogInfo.getWarehouse(),
-                    catalogInfo.getHiveUri(),
-                    catalogInfo.getHiveConfDir());
-        }
-    }
-}
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
index aa4f9f9..298cc83 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/DataTypeConvertUtils.java
@@ -18,74 +18,167 @@
 
 package org.apache.paimon.web.server.util;
 
+import org.apache.paimon.types.BigIntType;
+import org.apache.paimon.types.BinaryType;
+import org.apache.paimon.types.BooleanType;
+import org.apache.paimon.types.CharType;
 import org.apache.paimon.types.DataType;
-import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.DateType;
+import org.apache.paimon.types.DecimalType;
+import org.apache.paimon.types.DoubleType;
+import org.apache.paimon.types.FloatType;
+import org.apache.paimon.types.IntType;
+import org.apache.paimon.types.LocalZonedTimestampType;
+import org.apache.paimon.types.SmallIntType;
+import org.apache.paimon.types.TimeType;
+import org.apache.paimon.types.TimestampType;
+import org.apache.paimon.types.TinyIntType;
+import org.apache.paimon.types.VarBinaryType;
+import org.apache.paimon.types.VarCharType;
 
 /** data type convert util. */
 public class DataTypeConvertUtils {
 
-    public static DataType convert(String type) {
-        switch (type) {
+    public static DataType convert(PaimonDataType type) {
+        switch (type.getType()) {
             case "INT":
-                return DataTypes.INT();
+                return new IntType(type.isNullable());
             case "TINYINT":
-                return DataTypes.TINYINT();
+                return new TinyIntType(type.isNullable());
             case "SMALLINT":
-                return DataTypes.SMALLINT();
+                return new SmallIntType(type.isNullable());
             case "BIGINT":
-                return DataTypes.BIGINT();
+                return new BigIntType(type.isNullable());
+            case "CHAR":
+                return new CharType(
+                        type.isNullable(), type.getPrecision() > 0 ? 
type.getPrecision() : 1);
+            case "VARCHAR":
+                return new VarCharType(
+                        type.isNullable(), type.getPrecision() > 0 ? 
type.getPrecision() : 1);
             case "STRING":
-                return DataTypes.STRING();
+                return new VarCharType(type.isNullable(), Integer.MAX_VALUE);
+            case "BINARY":
+                return new BinaryType(
+                        type.isNullable(), type.getPrecision() > 0 ? 
type.getPrecision() : 1);
+            case "VARBINARY":
+                return new VarBinaryType(
+                        type.isNullable(), type.getPrecision() > 0 ? 
type.getPrecision() : 1);
             case "DOUBLE":
-                return DataTypes.DOUBLE();
+                return new DoubleType(type.isNullable());
             case "BOOLEAN":
-                return DataTypes.BOOLEAN();
+                return new BooleanType(type.isNullable());
             case "DATE":
-                return DataTypes.DATE();
+                return new DateType(type.isNullable());
             case "TIME":
-                return DataTypes.TIME();
+                return new TimeType(type.isNullable(), 0);
+            case "TIME(precision)":
+                return new TimeType(type.isNullable(), type.getPrecision());
             case "TIMESTAMP":
-                return DataTypes.TIMESTAMP();
+                return new TimestampType(type.isNullable(), 0);
+            case "TIMESTAMP(precision)":
+                return new TimestampType(type.isNullable(), 
type.getPrecision());
+            case "TIMESTAMP_MILLIS":
+                return new TimestampType(type.isNullable(), 3);
             case "BYTES":
-                return DataTypes.BYTES();
+                return new VarBinaryType(type.isNullable(), 0);
             case "FLOAT":
-                return DataTypes.FLOAT();
+                return new FloatType(type.isNullable());
             case "DECIMAL":
-                return DataTypes.DECIMAL(38, 0);
+                return new DecimalType(type.isNullable(), type.getPrecision(), 
type.getScale());
+            case "TIMESTAMP_WITH_LOCAL_TIME_ZONE":
+                return new LocalZonedTimestampType(type.isNullable(), 0);
+            case "TIMESTAMP_WITH_LOCAL_TIME_ZONE(precision)":
+                return new LocalZonedTimestampType(type.isNullable(), 
type.getPrecision());
             default:
                 throw new RuntimeException("Invalid type: " + type);
         }
     }
 
-    public static String fromPaimonType(DataType dataType) {
-        if (dataType.equals(DataTypes.INT())) {
-            return "INT";
-        } else if (dataType.equals(DataTypes.TINYINT())) {
-            return "TINYINT";
-        } else if (dataType.equals(DataTypes.SMALLINT())) {
-            return "SMALLINT";
-        } else if (dataType.equals(DataTypes.BIGINT())) {
-            return "BIGINT";
-        } else if (dataType.equals(DataTypes.STRING())) {
-            return "STRING";
-        } else if (dataType.equals(DataTypes.DOUBLE())) {
-            return "DOUBLE";
-        } else if (dataType.equals(DataTypes.BOOLEAN())) {
-            return "BOOLEAN";
-        } else if (dataType.equals(DataTypes.DATE())) {
-            return "DATE";
-        } else if (dataType.equals(DataTypes.TIME())) {
-            return "TIME";
-        } else if (dataType.equals(DataTypes.TIMESTAMP())) {
-            return "TIMESTAMP";
-        } else if (dataType.equals(DataTypes.BYTES())) {
-            return "BYTES";
-        } else if (dataType.equals(DataTypes.FLOAT())) {
-            return "FLOAT";
-        } else if (dataType.equals(DataTypes.DECIMAL(38, 0))) {
-            return "DECIMAL";
+    public static PaimonDataType fromPaimonType(DataType dataType) {
+        if (dataType instanceof IntType) {
+            return new PaimonDataType("INT", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof TinyIntType) {
+            return new PaimonDataType("TINYINT", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof SmallIntType) {
+            return new PaimonDataType("SMALLINT", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof BigIntType) {
+            return new PaimonDataType("BIGINT", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof VarCharType) {
+            VarCharType varCharType = (VarCharType) dataType;
+            if (varCharType.getLength() == Integer.MAX_VALUE) {
+                return new PaimonDataType("STRING", varCharType.isNullable(), 
0, 0);
+            } else {
+                return new PaimonDataType(
+                        "VARCHAR", varCharType.isNullable(), 
varCharType.getLength(), 0);
+            }
+        } else if (dataType instanceof CharType) {
+            CharType charType = (CharType) dataType;
+            return new PaimonDataType("CHAR", charType.isNullable(), 
charType.getLength(), 0);
+        } else if (dataType instanceof BinaryType) {
+            BinaryType binaryType = (BinaryType) dataType;
+            return new PaimonDataType("BINARY", binaryType.isNullable(), 
binaryType.getLength(), 0);
+        } else if (dataType instanceof VarBinaryType) {
+            VarBinaryType varBinaryType = (VarBinaryType) dataType;
+            if (varBinaryType.getLength() == Integer.MAX_VALUE) {
+                return new PaimonDataType(
+                        "BYTES", varBinaryType.isNullable(), 
varBinaryType.getLength(), 0);
+            } else {
+                return new PaimonDataType(
+                        "VARBINARY", varBinaryType.isNullable(), 
varBinaryType.getLength(), 0);
+            }
+        } else if (dataType instanceof DoubleType) {
+            return new PaimonDataType("DOUBLE", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof BooleanType) {
+            return new PaimonDataType("BOOLEAN", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof DateType) {
+            return new PaimonDataType("DATE", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof TimeType) {
+            TimeType timeType = (TimeType) dataType;
+            if (timeType.getPrecision() == 0) {
+                return new PaimonDataType("TIME", timeType.isNullable(), 0, 0);
+            } else {
+                return new PaimonDataType(
+                        "TIME(precision)", timeType.isNullable(), 
timeType.getPrecision(), 0);
+            }
+        } else if (dataType instanceof TimestampType) {
+            TimestampType timestampType = (TimestampType) dataType;
+            if (timestampType.getPrecision() == 0) {
+                return new PaimonDataType("TIMESTAMP", 
timestampType.isNullable(), 0, 0);
+            } else if (timestampType.getPrecision() == 3) {
+                return new PaimonDataType("TIMESTAMP_MILLIS", 
timestampType.isNullable(), 3, 0);
+            } else {
+                return new PaimonDataType(
+                        "TIMESTAMP(precision)",
+                        timestampType.isNullable(),
+                        timestampType.getPrecision(),
+                        0);
+            }
+        } else if (dataType instanceof FloatType) {
+            return new PaimonDataType("FLOAT", dataType.isNullable(), 0, 0);
+        } else if (dataType instanceof DecimalType) {
+            DecimalType decimalType = (DecimalType) dataType;
+            return new PaimonDataType(
+                    "DECIMAL",
+                    decimalType.isNullable(),
+                    decimalType.getPrecision(),
+                    decimalType.getScale());
+        } else if (dataType instanceof LocalZonedTimestampType) {
+            LocalZonedTimestampType localZonedTimestampType = 
(LocalZonedTimestampType) dataType;
+            if (localZonedTimestampType.getPrecision() == 0) {
+                return new PaimonDataType(
+                        "TIMESTAMP_WITH_LOCAL_TIME_ZONE",
+                        localZonedTimestampType.isNullable(),
+                        0,
+                        0);
+            } else {
+                return new PaimonDataType(
+                        "TIMESTAMP_WITH_LOCAL_TIME_ZONE(precision)",
+                        localZonedTimestampType.isNullable(),
+                        localZonedTimestampType.getPrecision(),
+                        0);
+            }
         } else {
-            return "UNKNOWN";
+            return new PaimonDataType("UNKNOWN", dataType.isNullable(), 0, 0);
         }
     }
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/PaimonDataType.java
similarity index 81%
copy from 
paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
copy to 
paimon-web-server/src/main/java/org/apache/paimon/web/server/util/PaimonDataType.java
index 0f93b78..76567ee 100644
--- 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/DatabaseInfo.java
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/PaimonDataType.java
@@ -16,23 +16,25 @@
  * limitations under the License.
  */
 
-package org.apache.paimon.web.server.data.model;
+package org.apache.paimon.web.server.util;
 
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-/** Database table model. */
+/** Paimon data type. */
 @Data
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class DatabaseInfo {
+public class PaimonDataType {
 
-    private String databaseName;
+    private String type;
 
-    private Integer catalogId;
+    private boolean isNullable;
 
-    private String description;
+    private Integer precision;
+
+    private Integer scale;
 }
diff --git 
a/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/PaimonServiceUtils.java
 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/PaimonServiceUtils.java
new file mode 100644
index 0000000..f4e3410
--- /dev/null
+++ 
b/paimon-web-server/src/main/java/org/apache/paimon/web/server/util/PaimonServiceUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.web.server.util;
+
+import org.apache.paimon.web.api.catalog.PaimonService;
+import org.apache.paimon.web.api.catalog.PaimonServiceFactory;
+import org.apache.paimon.web.server.data.enums.CatalogMode;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+
+import org.apache.commons.lang3.StringUtils;
+
+/** Paimon Service util. */
+public class PaimonServiceUtils {
+
+    /**
+     * Get a Paimon Service based on the provided CatalogInfo.
+     *
+     * @param catalogInfo The CatalogInfo object containing the catalog 
details.
+     * @return The created PaimonService object.
+     */
+    public static PaimonService getPaimonService(CatalogInfo catalogInfo) {
+        PaimonService service;
+        if 
(catalogInfo.getCatalogType().equalsIgnoreCase(CatalogMode.FILESYSTEM.getMode()))
 {
+            service =
+                    PaimonServiceFactory.createFileSystemCatalogService(
+                            catalogInfo.getCatalogName(), 
catalogInfo.getWarehouse());
+        } else if 
(catalogInfo.getCatalogType().equalsIgnoreCase(CatalogMode.HIVE.getMode())) {
+            if (StringUtils.isNotBlank(catalogInfo.getHiveConfDir())) {
+                service =
+                        PaimonServiceFactory.createHiveCatalogService(
+                                catalogInfo.getCatalogName(),
+                                catalogInfo.getWarehouse(),
+                                catalogInfo.getHiveUri(),
+                                catalogInfo.getHiveConfDir());
+            } else {
+                service =
+                        PaimonServiceFactory.createHiveCatalogService(
+                                catalogInfo.getCatalogName(),
+                                catalogInfo.getWarehouse(),
+                                catalogInfo.getHiveUri(),
+                                null);
+            }
+        } else {
+            service =
+                    PaimonServiceFactory.createFileSystemCatalogService(
+                            catalogInfo.getCatalogName(), 
catalogInfo.getWarehouse());
+        }
+        return service;
+    }
+}
diff --git a/paimon-web-server/src/main/resources/application-dev.yml 
b/paimon-web-server/src/main/resources/application-dev-h2.yml
similarity index 100%
rename from paimon-web-server/src/main/resources/application-dev.yml
rename to paimon-web-server/src/main/resources/application-dev-h2.yml
diff --git a/paimon-web-server/src/main/resources/application-prod.yml 
b/paimon-web-server/src/main/resources/application-dev-mysql.yml
similarity index 67%
copy from paimon-web-server/src/main/resources/application-prod.yml
copy to paimon-web-server/src/main/resources/application-dev-mysql.yml
index 1337972..67b2fb6 100644
--- a/paimon-web-server/src/main/resources/application-prod.yml
+++ b/paimon-web-server/src/main/resources/application-dev-mysql.yml
@@ -15,7 +15,7 @@
 
 spring:
   datasource:
-    url: 
jdbc:mysql://${MYSQL_ADDR:124.221.249.188:3887}/${MYSQL_DATABASE:paimon}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
-    username: ${MYSQL_USERNAME:root}
-    password: ${MYSQL_PASSWORD:Zhumingye520!@#.}
+    url: 
jdbc:mysql://${MYSQL_ADDR:127.0.0.1:3306}/${MYSQL_DATABASE:paimon}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+    username: ${MYSQL_USERNAME:username}
+    password: ${MYSQL_PASSWORD:password}
     driver-class-name: com.mysql.cj.jdbc.Driver
\ No newline at end of file
diff --git a/paimon-web-server/src/main/resources/application-prod.yml 
b/paimon-web-server/src/main/resources/application-prod.yml
index 1337972..67b2fb6 100644
--- a/paimon-web-server/src/main/resources/application-prod.yml
+++ b/paimon-web-server/src/main/resources/application-prod.yml
@@ -15,7 +15,7 @@
 
 spring:
   datasource:
-    url: 
jdbc:mysql://${MYSQL_ADDR:124.221.249.188:3887}/${MYSQL_DATABASE:paimon}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
-    username: ${MYSQL_USERNAME:root}
-    password: ${MYSQL_PASSWORD:Zhumingye520!@#.}
+    url: 
jdbc:mysql://${MYSQL_ADDR:127.0.0.1:3306}/${MYSQL_DATABASE:paimon}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+    username: ${MYSQL_USERNAME:username}
+    password: ${MYSQL_PASSWORD:password}
     driver-class-name: com.mysql.cj.jdbc.Driver
\ No newline at end of file
diff --git a/paimon-web-server/src/main/resources/application.yml 
b/paimon-web-server/src/main/resources/application.yml
index c978065..db7c962 100644
--- a/paimon-web-server/src/main/resources/application.yml
+++ b/paimon-web-server/src/main/resources/application.yml
@@ -22,7 +22,7 @@ spring:
   application:
     name: Paimon-Web-UI
   profiles:
-    active: dev
+    active: dev-mysql
   messages:
     basename: i18n/messages
     encoding: UTF-8
diff --git a/paimon-web-server/src/main/resources/i18n/messages.properties 
b/paimon-web-server/src/main/resources/i18n/messages.properties
index 86a4bc8..37610ec 100644
--- a/paimon-web-server/src/main/resources/i18n/messages.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages.properties
@@ -34,8 +34,17 @@ menu.in.used=This menu is in used
 menu.name.exist=This menu name is exist:{0}
 menu.path.invalid=This menu path is invalid:{0}
 catalog.name.exist=This catalog name {0} is exist
-catalog.create.error=An exception is returned when calling the Paimon API to 
create a Catalog.
+catalog.create.error=Exception calling Paimon Catalog API to create a Catalog.
+catalog.remove.error=Exception calling Paimon Catalog API to remove a Catalog.
 database.name.exist=This database name {0} is exist.
-database.create.error=An exception is returned when calling the Paimon API to 
create a Database.
+database.create.error=Exception calling Paimon Catalog API to create a 
Database.
+database.drop.error=Exception calling Paimon Catalog API to drop a Database.
 table.name.exist=This table name {0} is exist.
-table.create.error=An exception is returned when calling the Paimon API to 
create a Table.
+table.create.error=Exception calling Paimon Catalog API to create a Table.
+table.add.column.error=Exception calling Paimon Catalog API to add a Column.
+table.drop.column.error=Exception calling Paimon Catalog API to drop a Column.
+table.add.option.error=Exception calling Paimon Catalog API to add an option.
+table.remove.option.error=Exception calling Paimon Catalog API to remove an 
option.
+table.alter.column.error=Exception calling Paimon Catalog API to alter a 
column.
+table.drop.error=Exception calling Paimon Catalog API to drop a table.
+table.rename.error=Exception calling Paimon Catalog API to rename a table.
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/CatalogControllerTest.java
similarity index 51%
copy from 
paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
copy to 
paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/CatalogControllerTest.java
index 21d22dd..e96ee13 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/CatalogControllerTest.java
@@ -18,56 +18,49 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.web.server.data.dto.LoginDto;
-import org.apache.paimon.web.server.data.model.User;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
 import org.apache.paimon.web.server.data.result.R;
-import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.util.ObjectMapperUtils;
-import org.apache.paimon.web.server.util.StringUtils;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.io.TempDir;
 import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
-/** Test of permission service. */
+/** Test for CatalogController. */
 @SpringBootTest
 @AutoConfigureMockMvc
-public class PermissionTest {
-    private static final String loginPath = "/api/login";
-    private static final String logoutPath = "/api/logout";
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class CatalogControllerTest extends ControllerTestBase {
 
-    private static final String getUserPath = "/api/user";
+    private static final String catalogPath = "/api/catalog";
 
-    @Value("${spring.application.name}")
-    private String tokenName;
+    @TempDir java.nio.file.Path tempFile;
 
-    @Autowired private MockMvc mockMvc;
+    private static final String catalogName = "testCatalog";
 
-    private String token;
-
-    @BeforeEach
-    public void before() throws Exception {
-        LoginDto login = new LoginDto();
-        login.setUsername("common");
-        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
+    @Test
+    public void testCreateCatalog() throws Exception {
+        CatalogInfo catalogInfo = new CatalogInfo();
+        catalogInfo.setCatalogType("filesystem");
+        catalogInfo.setCatalogName(catalogName);
+        catalogInfo.setWarehouse(tempFile.toUri().toString());
+        catalogInfo.setDelete(false);
 
-        String result =
+        String responseString =
                 mockMvc.perform(
-                                MockMvcRequestBuilders.post(loginPath)
-                                        
.content(ObjectMapperUtils.toJSON(login))
+                                MockMvcRequestBuilders.post(catalogPath + 
"/create")
+                                        .cookie(cookie)
+                                        
.content(ObjectMapperUtils.toJSON(catalogInfo))
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -75,46 +68,32 @@ public class PermissionTest {
                         .andReturn()
                         .getResponse()
                         .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-
-        assertTrue(StringUtils.isNotBlank(r.getData().toString()));
 
-        this.token = r.getData().toString();
-    }
-
-    @AfterEach
-    public void after() throws Exception {
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(logoutPath)
-                                        .header(tokenName, token)
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
         assertEquals(200, r.getCode());
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.delete(catalogPath + "/remove/" + 
catalogName)
+                        .cookie(cookie)
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
     }
 
     @Test
-    public void testNoPermission() throws Exception {
+    public void testGetAllCatalogs() throws Exception {
         String responseString =
                 mockMvc.perform(
-                                MockMvcRequestBuilders.get(getUserPath + "/" + 
1)
-                                        .header(tokenName, token)
+                                MockMvcRequestBuilders.get(catalogPath + 
"/getAllCatalogs")
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
+                        .andExpect(MockMvcResultMatchers.status().isOk())
                         .andDo(MockMvcResultHandlers.print())
                         .andReturn()
                         .getResponse()
                         .getContentAsString();
 
-        R<User> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<User>>() {});
-        assertEquals(Status.FORBIDDEN.getCode(), r.getCode());
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
     }
 }
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/ControllerTestBase.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/ControllerTestBase.java
new file mode 100644
index 0000000..d0bb0a5
--- /dev/null
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/ControllerTestBase.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.web.server.controller;
+
+import org.apache.paimon.web.server.data.dto.LoginDto;
+import org.apache.paimon.web.server.data.model.CatalogInfo;
+import org.apache.paimon.web.server.data.model.DatabaseInfo;
+import org.apache.paimon.web.server.data.model.TableColumn;
+import org.apache.paimon.web.server.data.model.TableInfo;
+import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.util.ObjectMapperUtils;
+import org.apache.paimon.web.server.util.PaimonDataType;
+import org.apache.paimon.web.server.util.StringUtils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.io.TempDir;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockCookie;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** ControllerTestBase. */
+@SpringBootTest
+@AutoConfigureMockMvc
+public class ControllerTestBase {
+
+    private static final String loginPath = "/api/login";
+    private static final String logoutPath = "/api/logout";
+    private static final String catalogPath = "/api/catalog";
+    private static final String databasePath = "/api/database";
+    private static final String tablePath = "/api/table";
+
+    @Value("${spring.application.name}")
+    private String tokenName;
+
+    @Autowired public MockMvc mockMvc;
+
+    public static MockCookie cookie;
+
+    @TempDir java.nio.file.Path tempFile;
+
+    private static final String catalogName = "paimon_catalog";
+
+    private static final String databaseName = "paimon_database";
+
+    private static final String tableName = "paimon_table";
+
+    @BeforeEach
+    public void before() throws Exception {
+        LoginDto login = new LoginDto();
+        login.setUsername("admin");
+        login.setPassword("admin");
+
+        MockHttpServletResponse response =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(loginPath)
+                                        
.content(ObjectMapperUtils.toJSON(login))
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse();
+        String result = response.getContentAsString();
+        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
+        assertEquals(200, r.getCode());
+
+        assertTrue(StringUtils.isNotBlank(r.getData().toString()));
+
+        cookie = (MockCookie) response.getCookie(tokenName);
+
+        // create default catalog
+        CatalogInfo catalogInfo = new CatalogInfo();
+        catalogInfo.setCatalogType("filesystem");
+        catalogInfo.setCatalogName(catalogName);
+        catalogInfo.setWarehouse(tempFile.toUri().toString());
+        catalogInfo.setDelete(false);
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.post(catalogPath + "/create")
+                        .cookie(cookie)
+                        .content(ObjectMapperUtils.toJSON(catalogInfo))
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+
+        // create default database
+        DatabaseInfo databaseInfo = new DatabaseInfo();
+        databaseInfo.setDatabaseName(databaseName);
+        databaseInfo.setCatalogName(catalogName);
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.post(databasePath + "/create")
+                        .cookie(cookie)
+                        .content(ObjectMapperUtils.toJSON(databaseInfo))
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+
+        // create default table
+        List<TableColumn> tableColumns = new ArrayList<>();
+        TableColumn id =
+                new TableColumn("id", 
PaimonDataType.builder().type("INT").build(), "", false, "0");
+        TableColumn name =
+                new TableColumn(
+                        "name", 
PaimonDataType.builder().type("STRING").build(), "", false, "0");
+        tableColumns.add(id);
+        tableColumns.add(name);
+        TableInfo tableInfo =
+                TableInfo.builder()
+                        .catalogName(catalogName)
+                        .databaseName(databaseName)
+                        .tableName(tableName)
+                        .tableColumns(tableColumns)
+                        .partitionKey(Lists.newArrayList())
+                        .tableOptions(Maps.newHashMap())
+                        .build();
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.post(tablePath + "/create")
+                        .cookie(cookie)
+                        .content(ObjectMapperUtils.toJSON(tableInfo))
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+    }
+
+    @AfterEach
+    public void after() throws Exception {
+        String result =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(logoutPath)
+                                        .cookie(cookie)
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
+        assertEquals(200, r.getCode());
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.delete(
+                                tablePath
+                                        + "/drop/"
+                                        + catalogName
+                                        + "/"
+                                        + databaseName
+                                        + "/"
+                                        + "test_table")
+                        .cookie(cookie)
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.delete(
+                                databasePath + "/drop/" + databaseName + "/" + 
catalogName)
+                        .cookie(cookie)
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.delete(catalogPath + "/remove/" + 
catalogName)
+                        .cookie(cookie)
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+    }
+}
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/DatabaseControllerTest.java
similarity index 51%
copy from 
paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
copy to 
paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/DatabaseControllerTest.java
index 21d22dd..d23e8fd 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/DatabaseControllerTest.java
@@ -18,56 +18,41 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.web.server.data.dto.LoginDto;
-import org.apache.paimon.web.server.data.model.User;
+import org.apache.paimon.web.server.data.model.DatabaseInfo;
 import org.apache.paimon.web.server.data.result.R;
-import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.util.ObjectMapperUtils;
-import org.apache.paimon.web.server.util.StringUtils;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
-/** Test of permission service. */
+/** Test for DatabaseController. */
 @SpringBootTest
 @AutoConfigureMockMvc
-public class PermissionTest {
-    private static final String loginPath = "/api/login";
-    private static final String logoutPath = "/api/logout";
+public class DatabaseControllerTest extends ControllerTestBase {
 
-    private static final String getUserPath = "/api/user";
+    private static final String databasePath = "/api/database";
 
-    @Value("${spring.application.name}")
-    private String tokenName;
+    private static final String catalogName = "paimon_catalog";
 
-    @Autowired private MockMvc mockMvc;
-
-    private String token;
-
-    @BeforeEach
-    public void before() throws Exception {
-        LoginDto login = new LoginDto();
-        login.setUsername("common");
-        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
+    @Test
+    public void testCreateDatabase() throws Exception {
+        DatabaseInfo databaseInfo = new DatabaseInfo();
+        databaseInfo.setDatabaseName("test_db");
+        databaseInfo.setCatalogName(catalogName);
 
-        String result =
+        String responseString =
                 mockMvc.perform(
-                                MockMvcRequestBuilders.post(loginPath)
-                                        
.content(ObjectMapperUtils.toJSON(login))
+                                MockMvcRequestBuilders.post(databasePath + 
"/create")
+                                        .cookie(cookie)
+                                        
.content(ObjectMapperUtils.toJSON(databaseInfo))
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -75,46 +60,32 @@ public class PermissionTest {
                         .andReturn()
                         .getResponse()
                         .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
 
-        assertTrue(StringUtils.isNotBlank(r.getData().toString()));
-
-        this.token = r.getData().toString();
-    }
-
-    @AfterEach
-    public void after() throws Exception {
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(logoutPath)
-                                        .header(tokenName, token)
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
         assertEquals(200, r.getCode());
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.delete(databasePath + "/drop/" + 
"test_db/" + catalogName)
+                        .cookie(cookie)
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
     }
 
     @Test
-    public void testNoPermission() throws Exception {
+    public void testGetDatabases() throws Exception {
         String responseString =
                 mockMvc.perform(
-                                MockMvcRequestBuilders.get(getUserPath + "/" + 
1)
-                                        .header(tokenName, token)
+                                MockMvcRequestBuilders.get(databasePath + 
"/getAllDatabases")
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
+                        .andExpect(MockMvcResultMatchers.status().isOk())
                         .andDo(MockMvcResultHandlers.print())
                         .andReturn()
                         .getResponse()
                         .getContentAsString();
 
-        R<User> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<User>>() {});
-        assertEquals(Status.FORBIDDEN.getCode(), r.getCode());
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
     }
 }
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
index 21d22dd..1e59b35 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
@@ -18,103 +18,43 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.User;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.result.enums.Status;
 import org.apache.paimon.web.server.util.ObjectMapperUtils;
-import org.apache.paimon.web.server.util.StringUtils;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /** Test of permission service. */
 @SpringBootTest
 @AutoConfigureMockMvc
-public class PermissionTest {
-    private static final String loginPath = "/api/login";
-    private static final String logoutPath = "/api/logout";
-
+public class PermissionTest extends ControllerTestBase {
     private static final String getUserPath = "/api/user";
 
-    @Value("${spring.application.name}")
-    private String tokenName;
-
-    @Autowired private MockMvc mockMvc;
-
-    private String token;
-
-    @BeforeEach
-    public void before() throws Exception {
-        LoginDto login = new LoginDto();
-        login.setUsername("common");
-        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
-
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(loginPath)
-                                        
.content(ObjectMapperUtils.toJSON(login))
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-
-        assertTrue(StringUtils.isNotBlank(r.getData().toString()));
-
-        this.token = r.getData().toString();
-    }
-
-    @AfterEach
-    public void after() throws Exception {
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(logoutPath)
-                                        .header(tokenName, token)
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-    }
-
     @Test
     public void testNoPermission() throws Exception {
         String responseString =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(getUserPath + "/" + 
1)
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
+                        
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful())
                         .andDo(MockMvcResultHandlers.print())
                         .andReturn()
                         .getResponse()
                         .getContentAsString();
 
         R<User> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<User>>() {});
-        assertEquals(Status.FORBIDDEN.getCode(), r.getCode());
+        assertEquals(Status.SUCCESS.getCode(), r.getCode());
     }
 }
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
index 6d9f09e..16cef62 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysMenuControllerTest.java
@@ -18,24 +18,17 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.SysMenu;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.data.tree.TreeSelect;
 import org.apache.paimon.web.server.data.vo.RoleMenuTreeselectVo;
 import org.apache.paimon.web.server.util.ObjectMapperUtils;
-import org.apache.paimon.web.server.util.StringUtils;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@@ -49,67 +42,16 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 /** Test for SysMenuController. */
 @SpringBootTest
 @AutoConfigureMockMvc
-public class SysMenuControllerTest {
+public class SysMenuControllerTest extends ControllerTestBase {
 
     private static final String menuPath = "/api/menu";
-    private static final String loginPath = "/api/login";
-    private static final String logoutPath = "/api/logout";
-
-    @Value("${spring.application.name}")
-    private String tokenName;
-
-    @Autowired private MockMvc mockMvc;
-
-    private String token;
-
-    @BeforeEach
-    public void before() throws Exception {
-        LoginDto login = new LoginDto();
-        login.setUsername("admin");
-        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
-
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(loginPath)
-                                        
.content(ObjectMapperUtils.toJSON(login))
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-
-        assertTrue(StringUtils.isNotBlank(r.getData().toString()));
-
-        this.token = r.getData().toString();
-    }
-
-    @AfterEach
-    public void after() throws Exception {
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(logoutPath)
-                                        .header(tokenName, token)
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-    }
 
     @Test
     public void testList() throws Exception {
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(menuPath + "/list")
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -129,7 +71,7 @@ public class SysMenuControllerTest {
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(menuPath + "/1")
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -148,7 +90,7 @@ public class SysMenuControllerTest {
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(menuPath + 
"/treeselect")
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -168,7 +110,7 @@ public class SysMenuControllerTest {
         String result =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(menuPath + 
"/roleMenuTreeselect/1")
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
index 216e269..4f3276b 100644
--- 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/SysRoleControllerTest.java
@@ -18,26 +18,19 @@
 
 package org.apache.paimon.web.server.controller;
 
-import org.apache.paimon.web.server.data.dto.LoginDto;
 import org.apache.paimon.web.server.data.model.SysRole;
 import org.apache.paimon.web.server.data.result.PageR;
 import org.apache.paimon.web.server.data.result.R;
 import org.apache.paimon.web.server.util.ObjectMapperUtils;
-import org.apache.paimon.web.server.util.StringUtils;
 
 import com.fasterxml.jackson.core.type.TypeReference;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestMethodOrder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@@ -50,64 +43,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 @SpringBootTest
 @AutoConfigureMockMvc
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-public class SysRoleControllerTest {
+public class SysRoleControllerTest extends ControllerTestBase {
 
     private static final String rolePath = "/api/role";
-    private static final String loginPath = "/api/login";
-    private static final String logoutPath = "/api/logout";
 
     private static final int roleId = 3;
     private static final String roleName = "test";
 
-    @Value("${spring.application.name}")
-    private String tokenName;
-
-    @Autowired private MockMvc mockMvc;
-
-    private String token;
-
-    @BeforeEach
-    public void before() throws Exception {
-        LoginDto login = new LoginDto();
-        login.setUsername("admin");
-        login.setPassword("21232f297a57a5a743894a0e4a801fc3");
-
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(loginPath)
-                                        
.content(ObjectMapperUtils.toJSON(login))
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-
-        assertTrue(StringUtils.isNotBlank(r.getData().toString()));
-
-        this.token = r.getData().toString();
-    }
-
-    @AfterEach
-    public void after() throws Exception {
-        String result =
-                mockMvc.perform(
-                                MockMvcRequestBuilders.post(logoutPath)
-                                        .header(tokenName, token)
-                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
-                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
-                        .andExpect(MockMvcResultMatchers.status().isOk())
-                        .andDo(MockMvcResultHandlers.print())
-                        .andReturn()
-                        .getResponse()
-                        .getContentAsString();
-        R<?> r = ObjectMapperUtils.fromJSON(result, R.class);
-        assertEquals(200, r.getCode());
-    }
-
     @Test
     @Order(1)
     public void testAddRole() throws Exception {
@@ -122,7 +64,7 @@ public class SysRoleControllerTest {
 
         mockMvc.perform(
                         MockMvcRequestBuilders.post(rolePath)
-                                .header(tokenName, token)
+                                .cookie(cookie)
                                 .content(ObjectMapperUtils.toJSON(sysRole))
                                 .contentType(MediaType.APPLICATION_JSON_VALUE)
                                 .accept(MediaType.APPLICATION_JSON_VALUE))
@@ -136,7 +78,7 @@ public class SysRoleControllerTest {
         String responseString =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(rolePath + "/" + 
roleId)
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -167,7 +109,7 @@ public class SysRoleControllerTest {
 
         mockMvc.perform(
                         MockMvcRequestBuilders.put(rolePath)
-                                .header(tokenName, token)
+                                .cookie(cookie)
                                 .content(ObjectMapperUtils.toJSON(sysRole))
                                 .contentType(MediaType.APPLICATION_JSON_VALUE)
                                 .accept(MediaType.APPLICATION_JSON_VALUE))
@@ -176,7 +118,7 @@ public class SysRoleControllerTest {
         String responseString =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(rolePath + "/" + 
roleId)
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -198,7 +140,7 @@ public class SysRoleControllerTest {
         String delResponseString =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.delete(rolePath + "/" + 
roleId)
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
@@ -217,7 +159,7 @@ public class SysRoleControllerTest {
         String responseString =
                 mockMvc.perform(
                                 MockMvcRequestBuilders.get(rolePath + "/list")
-                                        .header(tokenName, token)
+                                        .cookie(cookie)
                                         
.contentType(MediaType.APPLICATION_JSON_VALUE)
                                         
.accept(MediaType.APPLICATION_JSON_VALUE))
                         .andExpect(MockMvcResultMatchers.status().isOk())
diff --git 
a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/TableControllerTest.java
 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/TableControllerTest.java
new file mode 100644
index 0000000..f31d1d5
--- /dev/null
+++ 
b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/TableControllerTest.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.web.server.controller;
+
+import org.apache.paimon.web.server.data.model.AlterTableRequest;
+import org.apache.paimon.web.server.data.model.TableColumn;
+import org.apache.paimon.web.server.data.model.TableInfo;
+import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.util.ObjectMapperUtils;
+import org.apache.paimon.web.server.util.PaimonDataType;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/** Test for TableController. */
+public class TableControllerTest extends ControllerTestBase {
+
+    private static final String tablePath = "/api/table";
+
+    private static final String catalogName = "paimon_catalog";
+
+    private static final String databaseName = "paimon_database";
+
+    private static final String tableName = "paimon_table";
+
+    @Test
+    public void testCreateTable() throws Exception {
+        List<TableColumn> tableColumns = new ArrayList<>();
+        TableColumn id =
+                new TableColumn("id", 
PaimonDataType.builder().type("INT").build(), "", false, "0");
+        TableColumn name =
+                new TableColumn(
+                        "name", 
PaimonDataType.builder().type("STRING").build(), "", false, "0");
+        tableColumns.add(id);
+        tableColumns.add(name);
+        TableInfo tableInfo =
+                TableInfo.builder()
+                        .catalogName(catalogName)
+                        .databaseName(databaseName)
+                        .tableName("test_table")
+                        .tableColumns(tableColumns)
+                        .partitionKey(Lists.newArrayList())
+                        .tableOptions(Maps.newHashMap())
+                        .build();
+
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(tablePath + 
"/create")
+                                        .cookie(cookie)
+                                        
.content(ObjectMapperUtils.toJSON(tableInfo))
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+
+        mockMvc.perform(
+                MockMvcRequestBuilders.delete(
+                                tablePath
+                                        + "/drop/"
+                                        + catalogName
+                                        + "/"
+                                        + databaseName
+                                        + "/"
+                                        + "test_table")
+                        .cookie(cookie)
+                        .contentType(MediaType.APPLICATION_JSON_VALUE)
+                        .accept(MediaType.APPLICATION_JSON_VALUE));
+    }
+
+    @Test
+    public void testAddColumn() throws Exception {
+        List<TableColumn> tableColumns = new ArrayList<>();
+        TableColumn age =
+                new TableColumn(
+                        "age",
+                        
PaimonDataType.builder().type("INT").isNullable(true).build(),
+                        "",
+                        false,
+                        "0");
+        tableColumns.add(age);
+        TableInfo tableInfo =
+                TableInfo.builder()
+                        .catalogName(catalogName)
+                        .databaseName(databaseName)
+                        .tableName(tableName)
+                        .tableColumns(tableColumns)
+                        .partitionKey(Lists.newArrayList())
+                        .tableOptions(Maps.newHashMap())
+                        .build();
+
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(tablePath + 
"/column/add")
+                                        .cookie(cookie)
+                                        
.content(ObjectMapperUtils.toJSON(tableInfo))
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+
+    @Test
+    public void testDropColumn() throws Exception {
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.delete(
+                                                tablePath
+                                                        + "/drop/"
+                                                        + catalogName
+                                                        + "/"
+                                                        + databaseName
+                                                        + "/"
+                                                        + tableName
+                                                        + "/"
+                                                        + "name")
+                                        .cookie(cookie)
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+
+    @Test
+    public void testAlterTable() throws Exception {
+        TableColumn oldColumn =
+                new TableColumn("id", 
PaimonDataType.builder().type("INT").build(), "", false, "0");
+
+        TableColumn newColumn =
+                new TableColumn(
+                        "age", 
PaimonDataType.builder().type("BIGINT").build(), "", false, "0");
+
+        AlterTableRequest alterTableRequest = new AlterTableRequest();
+        alterTableRequest.setOldColumn(oldColumn);
+        alterTableRequest.setNewColumn(newColumn);
+
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(tablePath + 
"/alter")
+                                        .cookie(cookie)
+                                        .param("catalogName", catalogName)
+                                        .param("databaseName", databaseName)
+                                        .param("tableName", tableName)
+                                        
.content(ObjectMapperUtils.toJSON(alterTableRequest))
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+
+    @Test
+    public void testAddOption() throws Exception {
+        Map<String, String> option = new HashMap<>();
+        option.put("bucket", "2");
+
+        TableInfo tableInfo =
+                TableInfo.builder()
+                        .catalogName(catalogName)
+                        .databaseName(databaseName)
+                        .tableName(tableName)
+                        .tableColumns(Lists.newArrayList())
+                        .partitionKey(Lists.newArrayList())
+                        .tableOptions(option)
+                        .build();
+
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(tablePath + 
"/option/add")
+                                        .cookie(cookie)
+                                        
.content(ObjectMapperUtils.toJSON(tableInfo))
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+
+    @Test
+    public void testRemoveOption() throws Exception {
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(tablePath + 
"/option/remove")
+                                        .cookie(cookie)
+                                        .param("catalogName", catalogName)
+                                        .param("databaseName", databaseName)
+                                        .param("tableName", tableName)
+                                        .param("key", "bucket")
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+
+    @Test
+    public void testRenameTable() throws Exception {
+        List<TableColumn> tableColumns = new ArrayList<>();
+        TableColumn id =
+                new TableColumn("id", 
PaimonDataType.builder().type("INT").build(), "", false, "0");
+        TableColumn name =
+                new TableColumn(
+                        "name", 
PaimonDataType.builder().type("STRING").build(), "", false, "0");
+        tableColumns.add(id);
+        tableColumns.add(name);
+        TableInfo tableInfo =
+                TableInfo.builder()
+                        .catalogName(catalogName)
+                        .databaseName(databaseName)
+                        .tableName("test_table_01")
+                        .tableColumns(tableColumns)
+                        .partitionKey(Lists.newArrayList())
+                        .tableOptions(Maps.newHashMap())
+                        .build();
+
+        mockMvc.perform(
+                        MockMvcRequestBuilders.post(tablePath + "/create")
+                                .cookie(cookie)
+                                .content(ObjectMapperUtils.toJSON(tableInfo))
+                                .contentType(MediaType.APPLICATION_JSON_VALUE)
+                                .accept(MediaType.APPLICATION_JSON_VALUE))
+                .andExpect(MockMvcResultMatchers.status().isOk());
+
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.post(tablePath + 
"/rename")
+                                        .cookie(cookie)
+                                        .param("catalogName", catalogName)
+                                        .param("databaseName", databaseName)
+                                        .param("fromTableName", 
"test_table_01")
+                                        .param("toTableName", "test_table_02")
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+
+    @Test
+    public void testGetTables() throws Exception {
+        String responseString =
+                mockMvc.perform(
+                                MockMvcRequestBuilders.get(tablePath + 
"/getAllTables")
+                                        .cookie(cookie)
+                                        
.contentType(MediaType.APPLICATION_JSON_VALUE)
+                                        
.accept(MediaType.APPLICATION_JSON_VALUE))
+                        .andExpect(MockMvcResultMatchers.status().isOk())
+                        .andDo(MockMvcResultHandlers.print())
+                        .andReturn()
+                        .getResponse()
+                        .getContentAsString();
+
+        R<Void> r = ObjectMapperUtils.fromJSON(responseString, new 
TypeReference<R<Void>>() {});
+        assertEquals(200, r.getCode());
+    }
+}
diff --git a/pom.xml b/pom.xml
index 151ffe2..9ff1baa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -358,6 +358,7 @@ under the License.
                         <!-- artifacts created during release process -->
                         <exclude>release/**</exclude>
                         <exclude>paimon-web-ui/node_modules/**</exclude>
+                        <exclude>paimon-web-ui/dist/**</exclude>
                         <exclude>paimon-web-ui-new/node_modules/**</exclude>
                     </excludes>
                 </configuration>
diff --git a/scripts/sql/paimon-mysql.sql b/scripts/sql/paimon-mysql.sql
new file mode 100644
index 0000000..d3b0044
--- /dev/null
+++ b/scripts/sql/paimon-mysql.sql
@@ -0,0 +1,174 @@
+--  Licensed to the Apache Software Foundation (ASF) under one or more
+--  contributor license agreements.  See the NOTICE file distributed with
+--  this work for additional information regarding copyright ownership.
+--  The ASF licenses this file to You under the Apache License, Version 2.0
+--  (the "License"); you may not use this file except in compliance with
+--  the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+--  Unless required by applicable law or agreed to in writing, software
+--  distributed under the License is distributed on an "AS IS" BASIS,
+--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+--  See the License for the specific language governing permissions and
+--  limitations under the License.
+
+DROP TABLE IF EXISTS `user`;
+CREATE TABLE if not exists `user`
+(
+    `id`          int(11)      NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 
'ID',
+    `username`    varchar(50)  NOT NULL COMMENT 'username',
+    `password`    varchar(50)  NULL     DEFAULT NULL COMMENT 'password',
+    `nickname`    varchar(50)  NULL     DEFAULT NULL COMMENT 'nickname',
+    `user_type`   int          NOT NULL DEFAULT 0 COMMENT 'login type 
(0:LOCAL,1:LDAP)',
+    `url`         varchar(100) NULL     DEFAULT NULL COMMENT 'avatar url',
+    `mobile`      varchar(20)  NULL     DEFAULT NULL COMMENT 'mobile phone',
+    `email`       varchar(100) NULL     DEFAULT NULL COMMENT 'email',
+    `enabled`     tinyint(1)   NOT NULL DEFAULT 1 COMMENT 'is enable',
+    `is_delete`   tinyint(1)   NOT NULL DEFAULT 0 COMMENT 'is delete',
+    `create_time` datetime(0)  NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'create time',
+    `update_time` datetime(0)  NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'update time'
+    ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `tenant`;
+CREATE TABLE if not exists `tenant`
+(
+    `id`          int(11)      NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 
'ID',
+    `name`        varchar(64)  NULL     DEFAULT NULL COMMENT 'tenant name',
+    `description` varchar(255) NULL     DEFAULT NULL COMMENT 'tenant 
description',
+    `is_delete`   tinyint(1)   NOT NULL DEFAULT 0 COMMENT 'is delete',
+    `create_time` datetime(0)  NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'create time',
+    `update_time` datetime(0)  NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'update time'
+    ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `user_tenant`;
+CREATE TABLE if not exists `user_tenant`
+(
+    `id`          int(11)     NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
+    `user_id`     int(11)     NOT NULL COMMENT 'user id',
+    `tenant_id`   int(11)     NOT NULL COMMENT 'tenant id',
+    `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create 
time',
+    `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update 
time'
+    ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `sys_role`;
+CREATE TABLE if not exists `sys_role`
+(
+    `id`          int(11)      not null auto_increment primary key comment 
'id',
+    `role_name`   varchar(30)  not null comment 'role name',
+    `role_key`    varchar(100) not null comment 'role key',
+    `sort`        int(4)       not null comment 'sort',
+    `enabled`     tinyint(1)   NOT NULL DEFAULT 1 COMMENT 'is enable',
+    `is_delete`   tinyint(1)   NOT NULL DEFAULT 0 COMMENT 'is delete',
+    `remark`      varchar(500)          default null comment 'remark',
+    `create_time` datetime(0)  NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'create time',
+    `update_time` datetime(0)  NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'update time'
+    ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `sys_menu`;
+CREATE TABLE if not exists `sys_menu`
+(
+    `id`          int(11)  not null auto_increment primary key comment 'id',
+    `menu_name`   varchar(50) not null comment 'menu name',
+    `parent_id`   int(11)           default 0 comment 'parent id',
+    `sort`        int(4)               default 0 comment 'sort',
+    `path`        varchar(200)         default '' comment 'route path',
+    `query`       varchar(255)         default null comment 'route params',
+    `is_cache`    int(1)               default 0 comment 'is cache(0:cache 
1:no_cache)',
+    `type`        char(1)              default '' comment 'menu 
type(M:directory C:menu F:button)',
+    `visible`     char(1)              default 0 comment 'is visible(0:display 
1:hide)',
+    `component`   varchar(255)         default null comment 'component path',
+    `is_frame`    int(1)               default 0 comment 'is frame',
+    `enabled`     tinyint(1)  NOT NULL DEFAULT 1 COMMENT 'is enable',
+    `is_delete`   tinyint(1)  NOT NULL DEFAULT 0 COMMENT 'is delete',
+    `perms`       varchar(100)         default null comment 'menu perms',
+    `icon`        varchar(100)         default '#' comment 'menu icon',
+    `remark`      varchar(500)         default '' comment 'remark',
+    `create_time` datetime(0) NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'create time',
+    `update_time` datetime(0) NULL     DEFAULT CURRENT_TIMESTAMP COMMENT 
'update time'
+    ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `user_role`;
+CREATE TABLE if not exists `user_role`
+(
+    `id`          int(11)     not null auto_increment primary key comment 'id',
+    `user_id`     int(11)     not null comment 'user id',
+    `role_id`     int(11)     not null comment 'role id',
+    `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create 
time',
+    `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update 
time',
+    unique key `idx_user_role` (`user_id`, `role_id`)
+    ) ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `role_menu`;
+CREATE TABLE if not exists `role_menu`
+(
+    `id`          int(11)     not null auto_increment primary key comment 'id',
+    `role_id`     int(11)     not null comment 'role id',
+    `menu_id`     int(11)     not null comment 'menu id',
+    `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create 
time',
+    `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update 
time',
+    unique key `idx_role_menu` (`role_id`, `menu_id`)
+    )  ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `catalog`;
+CREATE TABLE if not exists `catalog`
+(
+    `id`          int(11)     not null auto_increment primary key comment 'id',
+    `catalog_type`     varchar(50)  not null comment 'catalog type',
+    `catalog_name`     varchar(100)     not null comment 'catalog name',
+    `warehouse`     varchar(200)     not null comment 'warehouse',
+    `hive_uri`     varchar(200)     comment 'hive uri',
+    `hive_conf_dir`     varchar(100)   comment 'catalog name',
+    `is_delete`   tinyint(1)   NOT NULL DEFAULT 0 COMMENT 'is delete',
+    `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create 
time',
+    `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'update 
time'
+    )  ENGINE = InnoDB DEFAULT CHARSET=utf8;
+
+
+INSERT INTO `user` ( id, username, password, nickname, mobile
+                   , email, enabled, is_delete)
+VALUES ( 1, 'admin', '21232f297a57a5a743894a0e4a801fc3', 'Admin', 0
+       , '[email protected]', 1, 0);
+INSERT INTO `user` (id, username, password, nickname, mobile, email, enabled, 
is_delete)
+VALUES (2, 'common', '21232f297a57a5a743894a0e4a801fc3', 'common', 0, 
'[email protected]', 1, 0);
+
+INSERT INTO `tenant` (id, name, description)
+VALUES (1, 'DefaultTenant', 'DefaultTenant');
+
+INSERT INTO `user_tenant` (`id`, `user_id`, `tenant_id`)
+VALUES (1, 1, 1);
+
+insert into sys_role (id, role_name, role_key, sort)
+values (1, 'admin', 'admin', 1),
+       (2, 'common', 'common', 2);
+
+insert into sys_menu (id, menu_name, parent_id, sort, path, component, 
is_frame, type, perms, icon, remark)
+values (1, 'all', 0, 1, 'system', null, 1, 'M', 'system', 'admin', 'system 
root path'),
+       (100, 'user manager', 1, 1, 'user', 'user/index', 1, 'C', 
'system:user:list', 'user', 'user manager'),
+       (1000, 'user query', 100, 1, '', '', 1, 'F', 'system:user:query', '#', 
''),
+       (1001, 'user add', 100, 2, '', '', 1, 'F', 'system:user:add', '#', ''),
+       (1002, 'user edit', 100, 3, '', '', 1, 'F', 'system:user:edit', '#', 
''),
+       (1003, 'user del', 100, 4, '', '', 1, 'F', 'system:user:remove', '#', 
''),
+       (1004, 'user reset', 100, 5, '', '', 1, 'F', 'system:user:resetPwd', 
'#', ''),
+       (200, 'role manager', 1, 1, 'role', 'role/index', 1, 'C', 
'system:role:list', 'role', 'role manager'),
+       (2000, 'role query', 200, 1, '', '', 1, 'F', 'system:role:query', '#', 
''),
+       (2001, 'role add', 200, 2, '', '', 1, 'F', 'system:role:add', '#', ''),
+       (2002, 'role edit', 200, 3, '', '', 1, 'F', 'system:role:edit', '#', 
''),
+       (2003, 'role del', 200, 4, '', '', 1, 'F', 'system:role:remove', '#', 
''),
+       (300, 'menu manager', 1, 1, 'menu', 'menu/index', 1, 'C', 
'system:menu:list', 'menu', 'menu manager'),
+       (3000, 'menu query', 300, 1, '', '', 1, 'F', 'system:menu:query', '#', 
''),
+       (3001, 'menu add', 300, 2, '', '', 1, 'F', 'system:menu:add', '#', ''),
+       (3002, 'menu edit', 300, 3, '', '', 1, 'F', 'system:menu:edit', '#', 
''),
+       (3003, 'menu del', 300, 4, '', '', 1, 'F', 'system:menu:remove', '#', 
'');
+
+insert into user_role (id, user_id, role_id)
+values (1, 1, 1), (2, 2, 2);
+
+insert into role_menu (role_id, menu_id)
+values (1, 1),
+       (1, 100),
+       (1, 1000),
+       (1, 1001),
+       (1, 1002),
+       (1, 1003),
+       (1, 1004);


Reply via email to