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 aa93eb6  [Improvement] Refactor api module (#45)
aa93eb6 is described below

commit aa93eb671baff1ec37d1d2471b2c45f1c9848b21
Author: s7monk <[email protected]>
AuthorDate: Tue Oct 10 21:05:00 2023 +0800

    [Improvement] Refactor api module (#45)
---
 .../paimon/web/api/catalog/PaimonService.java      | 300 +++++++++++++
 .../web/api/catalog/PaimonServiceFactory.java      |  55 +++
 .../ColumnException.java}                          |  42 +-
 .../web/api/exception/DatabaseException.java       |  50 +++
 .../TableException.java}                           |  42 +-
 .../paimon/web/api/table/ColumnMetadata.java       |  12 +
 .../apache/paimon/web/api/table/TableChange.java   | 480 +++++++++++++++++++++
 .../paimon/web/api/catalog/PaimonServiceTest.java  | 309 +++++++++++++
 8 files changed, 1236 insertions(+), 54 deletions(-)

diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/catalog/PaimonService.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/catalog/PaimonService.java
new file mode 100644
index 0000000..034505d
--- /dev/null
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/catalog/PaimonService.java
@@ -0,0 +1,300 @@
+/*
+ * 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.api.catalog;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.schema.Schema;
+import org.apache.paimon.schema.SchemaChange;
+import org.apache.paimon.schema.SchemaManager;
+import org.apache.paimon.table.Table;
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.web.api.exception.ColumnException;
+import org.apache.paimon.web.api.exception.DatabaseException;
+import org.apache.paimon.web.api.exception.TableException;
+import org.apache.paimon.web.api.table.ColumnMetadata;
+import org.apache.paimon.web.api.table.TableChange;
+import org.apache.paimon.web.api.table.TableMetadata;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Paimon service. */
+public class PaimonService {
+    private final Catalog catalog;
+
+    private final String name;
+
+    public PaimonService(Catalog catalog, String name) {
+        this.catalog = catalog;
+        this.name = name;
+    }
+
+    public Catalog catalog() {
+        return catalog;
+    }
+
+    public String catalogName() {
+        return name;
+    }
+
+    public List<String> listDatabases() {
+        return catalog.listDatabases();
+    }
+
+    public boolean databaseExists(String databaseName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        return catalog.databaseExists(databaseName);
+    }
+
+    public void createDatabase(String databaseName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+
+        try {
+            catalog.createDatabase(databaseName, false);
+        } catch (Catalog.DatabaseAlreadyExistException e) {
+            throw new DatabaseException.DatabaseAlreadyExistsException(
+                    String.format(
+                            "The database '%s' already exists in the 
catalog.", databaseName));
+        }
+    }
+
+    public void dropDatabase(String databaseName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        try {
+            catalog.dropDatabase(databaseName, false, true);
+        } catch (Catalog.DatabaseNotExistException e) {
+            throw new DatabaseException.DatabaseNotExistException(
+                    String.format(
+                            "The database '%s' does not exist in the 
catalog.", databaseName));
+        } catch (Catalog.DatabaseNotEmptyException e) {
+            throw new DatabaseException.DatabaseNotEmptyException(
+                    String.format("The database '%s' is not empty.", 
databaseName));
+        }
+    }
+
+    public List<String> listTables(String databaseName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        try {
+            return catalog.listTables(databaseName);
+        } catch (Catalog.DatabaseNotExistException e) {
+            throw new DatabaseException.DatabaseNotExistException(
+                    String.format(
+                            "The database '%s' does not exist in the 
catalog.", databaseName));
+        }
+    }
+
+    public boolean tableExists(String databaseName, String tableName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        Preconditions.checkNotNull(tableName, "Table name cannot be null.");
+
+        Identifier identifier = Identifier.create(databaseName, tableName);
+        return catalog.tableExists(identifier);
+    }
+
+    public void createTable(String databaseName, String tableName, 
TableMetadata tableMetadata) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        Preconditions.checkNotNull(tableName, "Table name cannot be null.");
+
+        Schema.Builder schemaBuilder =
+                Schema.newBuilder()
+                        .partitionKeys(
+                                tableMetadata.partitionKeys() == null
+                                        ? ImmutableList.of()
+                                        : 
ImmutableList.copyOf(tableMetadata.partitionKeys()))
+                        .primaryKey(
+                                tableMetadata.primaryKeys() == null
+                                        ? ImmutableList.of()
+                                        : 
ImmutableList.copyOf(tableMetadata.primaryKeys()))
+                        .comment(tableMetadata.comment() == null ? "" : 
tableMetadata.comment())
+                        .options(tableMetadata.options());
+
+        for (ColumnMetadata column : tableMetadata.columns()) {
+            schemaBuilder.column(column.name(), column.type(), 
column.description());
+        }
+
+        Schema schema = schemaBuilder.build();
+
+        Identifier identifier = Identifier.create(databaseName, tableName);
+
+        try {
+            catalog.createTable(identifier, schema, false);
+        } catch (Catalog.TableAlreadyExistException e) {
+            throw new TableException.TableAlreadyExistException(
+                    String.format("The table '%s' already exists in the 
database.", tableName));
+        } catch (Catalog.DatabaseNotExistException e) {
+            throw new DatabaseException.DatabaseNotExistException(
+                    String.format(
+                            "The database '%s' does not exist in the 
catalog.", databaseName));
+        }
+    }
+
+    public Table getTable(String databaseName, String tableName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        Preconditions.checkNotNull(tableName, "Table name cannot be null.");
+
+        Identifier identifier = Identifier.create(databaseName, tableName);
+        try {
+            return catalog.getTable(identifier);
+        } catch (Catalog.TableNotExistException e) {
+            throw new TableException.TableNotExistException(
+                    String.format("The table '%s' does not exist in the 
database.", tableName));
+        }
+    }
+
+    public void dropTable(String databaseName, String tableName) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        Preconditions.checkNotNull(tableName, "Table name cannot be null.");
+
+        Identifier identifier = Identifier.create(databaseName, tableName);
+        try {
+            catalog.dropTable(identifier, false);
+        } catch (Catalog.TableNotExistException e) {
+            throw new TableException.TableNotExistException(
+                    String.format("The table '%s' does not exist in the 
database.", tableName));
+        }
+    }
+
+    public void renameTable(String databaseName, String fromTable, String 
toTable) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        Preconditions.checkNotNull(fromTable, "From table name cannot be 
null.");
+        Preconditions.checkNotNull(toTable, "To table name cannot be null.");
+
+        Identifier fromTableIdentifier = Identifier.create(databaseName, 
fromTable);
+        Identifier toTableIdentifier = Identifier.create(databaseName, 
toTable);
+        try {
+            catalog.renameTable(fromTableIdentifier, toTableIdentifier, false);
+        } catch (Catalog.TableNotExistException e) {
+            throw new TableException.TableNotExistException(
+                    String.format("The table '%s' does not exist in the 
database.", fromTable));
+        } catch (Catalog.TableAlreadyExistException e) {
+            throw new TableException.TableAlreadyExistException(
+                    String.format("The table '%s' already exists in the 
database.", toTable));
+        }
+    }
+
+    public void alterTable(String databaseName, String tableName, 
List<TableChange> tableChanges) {
+        Preconditions.checkNotNull(databaseName, "Database name cannot be 
null.");
+        Preconditions.checkNotNull(tableName, "Table name cannot be null.");
+
+        if (!tableExists(databaseName, tableName)) {
+            return;
+        }
+
+        Identifier identifier = Identifier.create(databaseName, tableName);
+
+        List<SchemaChange> changes = new ArrayList<>();
+        if (null != tableChanges) {
+            List<SchemaChange> schemaChanges =
+                    tableChanges.stream()
+                            .flatMap(tableChange -> 
toSchemaChange(tableChange).stream())
+                            .collect(Collectors.toList());
+            changes.addAll(schemaChanges);
+        }
+
+        try {
+            catalog.alterTable(identifier, changes, false);
+        } catch (Catalog.ColumnAlreadyExistException e) {
+            throw new 
ColumnException.ColumnAlreadyExistException(e.getMessage());
+        } catch (Catalog.TableNotExistException e) {
+            throw new TableException.TableNotExistException(
+                    String.format("The table '%s' does not exist in the 
database.", tableName));
+        } catch (Catalog.ColumnNotExistException e) {
+            throw new ColumnException.ColumnNotExistException(e.getMessage());
+        }
+    }
+
+    private List<SchemaChange> toSchemaChange(TableChange change) {
+        List<SchemaChange> schemaChanges = new ArrayList<>();
+        if (change instanceof TableChange.AddColumn) {
+            TableChange.AddColumn add = (TableChange.AddColumn) change;
+            String comment = add.getColumn().description();
+            SchemaChange.Move move = getMove(add.getPosition(), 
add.getColumn().name());
+            schemaChanges.add(
+                    SchemaChange.addColumn(
+                            add.getColumn().name(), add.getColumn().type(), 
comment, move));
+            return schemaChanges;
+        } else if (change instanceof TableChange.DropColumn) {
+            TableChange.DropColumn drop = (TableChange.DropColumn) change;
+            schemaChanges.add(SchemaChange.dropColumn(drop.getColumnName()));
+            return schemaChanges;
+        } else if (change instanceof TableChange.ModifyColumnName) {
+            TableChange.ModifyColumnName modify = 
(TableChange.ModifyColumnName) change;
+            schemaChanges.add(
+                    SchemaChange.renameColumn(
+                            modify.getOldColumnName(), 
modify.getNewColumnName()));
+            return schemaChanges;
+        } else if (change instanceof TableChange.ModifyColumnType) {
+            TableChange.ModifyColumnType modify = 
(TableChange.ModifyColumnType) change;
+            DataType newColumnType = modify.getNewType();
+            DataType oldColumnType = modify.getOldColumn().type();
+            if (newColumnType.isNullable() != oldColumnType.isNullable()) {
+                schemaChanges.add(
+                        SchemaChange.updateColumnNullability(
+                                modify.getNewColumn().name(), 
newColumnType.isNullable()));
+            }
+            schemaChanges.add(
+                    
SchemaChange.updateColumnType(modify.getOldColumn().name(), newColumnType));
+            return schemaChanges;
+        } else if (change instanceof TableChange.ModifyColumnPosition) {
+            TableChange.ModifyColumnPosition modify = 
(TableChange.ModifyColumnPosition) change;
+            SchemaChange.Move move = getMove(modify.getNewPosition(), 
modify.getNewColumn().name());
+            schemaChanges.add(SchemaChange.updateColumnPosition(move));
+            return schemaChanges;
+        } else if (change instanceof TableChange.ModifyColumnComment) {
+            TableChange.ModifyColumnComment modify = 
(TableChange.ModifyColumnComment) change;
+            schemaChanges.add(
+                    SchemaChange.updateColumnComment(
+                            modify.getNewColumn().name(), 
modify.getNewComment()));
+            return schemaChanges;
+        } else if (change instanceof TableChange.SetOption) {
+            TableChange.SetOption setOption = (TableChange.SetOption) change;
+            String key = setOption.getKey();
+            String value = setOption.getValue();
+
+            SchemaManager.checkAlterTablePath(key);
+
+            schemaChanges.add(SchemaChange.setOption(key, value));
+            return schemaChanges;
+        } else if (change instanceof TableChange.RemoveOption) {
+            TableChange.RemoveOption removeOption = (TableChange.RemoveOption) 
change;
+            
schemaChanges.add(SchemaChange.removeOption(removeOption.getKey()));
+            return schemaChanges;
+        } else {
+            throw new UnsupportedOperationException(
+                    "Change is not supported: " + change.getClass());
+        }
+    }
+
+    private SchemaChange.Move getMove(TableChange.ColumnPosition 
columnPosition, String fieldName) {
+        SchemaChange.Move move = null;
+        if (columnPosition instanceof TableChange.First) {
+            move = SchemaChange.Move.first(fieldName);
+        } else if (columnPosition instanceof TableChange.After) {
+            move =
+                    SchemaChange.Move.after(
+                            fieldName, ((TableChange.After) 
columnPosition).column());
+        }
+        return move;
+    }
+}
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/catalog/PaimonServiceFactory.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/catalog/PaimonServiceFactory.java
new file mode 100644
index 0000000..6edaaae
--- /dev/null
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/catalog/PaimonServiceFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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.api.catalog;
+
+import org.apache.paimon.catalog.CatalogContext;
+import org.apache.paimon.catalog.CatalogFactory;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.web.api.common.CatalogProperties;
+import org.apache.paimon.web.api.common.MetastoreType;
+
+import org.apache.commons.lang3.StringUtils;
+
+/** Paimon service factory. */
+public class PaimonServiceFactory {
+
+    public static PaimonService createFileSystemCatalogService(String name, 
String warehouse) {
+        Options options = new Options();
+        options.set(CatalogProperties.WAREHOUSE, warehouse + "/" + name);
+
+        CatalogContext context = CatalogContext.create(options);
+
+        return new PaimonService(CatalogFactory.createCatalog(context), name);
+    }
+
+    public static PaimonService createHiveCatalogService(
+            String name, String warehouse, String uri, String hiveConfDir) {
+        Options options = new Options();
+        options.set(CatalogProperties.WAREHOUSE, warehouse + "/" + name);
+
+        options.set(CatalogProperties.METASTORE, 
MetastoreType.HIVE.toString());
+        options.set(CatalogProperties.URI, uri);
+        if (StringUtils.isNotBlank(hiveConfDir)) {
+            options.set(CatalogProperties.HIVE_CONF_DIR, hiveConfDir);
+        }
+        CatalogContext context = CatalogContext.create(options);
+
+        return new PaimonService(CatalogFactory.createCatalog(context), name);
+    }
+}
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/ColumnException.java
similarity index 53%
copy from 
paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
copy to 
paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/ColumnException.java
index 163f50d..43c7b22 100644
--- 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/ColumnException.java
@@ -16,40 +16,28 @@
  * limitations under the License.
  */
 
-package org.apache.paimon.web.api.table;
+package org.apache.paimon.web.api.exception;
 
-import org.apache.paimon.types.DataType;
+/** Column exception. */
+public class ColumnException extends RuntimeException {
 
-import javax.annotation.Nullable;
-
-/** table metadata. */
-public class ColumnMetadata {
-
-    private final String name;
-
-    private final DataType type;
-
-    private final @Nullable String description;
-
-    public ColumnMetadata(String name, DataType type) {
-        this(name, type, null);
+    public ColumnException(String message) {
+        super(message);
     }
 
-    public ColumnMetadata(String name, DataType type, String description) {
-        this.name = name;
-        this.type = type;
-        this.description = description;
-    }
+    /** The exception that the column has already existed. */
+    public static class ColumnAlreadyExistException extends ColumnException {
 
-    public String name() {
-        return this.name;
+        public ColumnAlreadyExistException(String message) {
+            super(message);
+        }
     }
 
-    public DataType type() {
-        return this.type;
-    }
+    /** The exception that the column does not exist. */
+    public static class ColumnNotExistException extends ColumnException {
 
-    public String description() {
-        return this.description;
+        public ColumnNotExistException(String message) {
+            super(message);
+        }
     }
 }
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/DatabaseException.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/DatabaseException.java
new file mode 100644
index 0000000..3c3468e
--- /dev/null
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/DatabaseException.java
@@ -0,0 +1,50 @@
+/*
+ * 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.api.exception;
+
+/** Database exception. */
+public class DatabaseException extends RuntimeException {
+    public DatabaseException(String message) {
+        super(message);
+    }
+
+    /** The exception that the database already exists. */
+    public static class DatabaseAlreadyExistsException extends 
DatabaseException {
+
+        public DatabaseAlreadyExistsException(String message) {
+            super(message);
+        }
+    }
+
+    /** The exception that the database is not empty. */
+    public static class DatabaseNotEmptyException extends DatabaseException {
+
+        public DatabaseNotEmptyException(String message) {
+            super(message);
+        }
+    }
+
+    /** The exception that the database does not exist. */
+    public static class DatabaseNotExistException extends DatabaseException {
+
+        public DatabaseNotExistException(String message) {
+            super(message);
+        }
+    }
+}
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/TableException.java
similarity index 53%
copy from 
paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
copy to 
paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/TableException.java
index 163f50d..a7b7546 100644
--- 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/exception/TableException.java
@@ -16,40 +16,28 @@
  * limitations under the License.
  */
 
-package org.apache.paimon.web.api.table;
+package org.apache.paimon.web.api.exception;
 
-import org.apache.paimon.types.DataType;
+/** Table exception. */
+public class TableException extends RuntimeException {
 
-import javax.annotation.Nullable;
-
-/** table metadata. */
-public class ColumnMetadata {
-
-    private final String name;
-
-    private final DataType type;
-
-    private final @Nullable String description;
-
-    public ColumnMetadata(String name, DataType type) {
-        this(name, type, null);
+    public TableException(String message) {
+        super(message);
     }
 
-    public ColumnMetadata(String name, DataType type, String description) {
-        this.name = name;
-        this.type = type;
-        this.description = description;
-    }
+    /** The exception that the table already exists. */
+    public static class TableAlreadyExistException extends TableException {
 
-    public String name() {
-        return this.name;
+        public TableAlreadyExistException(String message) {
+            super(message);
+        }
     }
 
-    public DataType type() {
-        return this.type;
-    }
+    /** The exception that the table does not exist. */
+    public static class TableNotExistException extends TableException {
 
-    public String description() {
-        return this.description;
+        public TableNotExistException(String message) {
+            super(message);
+        }
     }
 }
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
index 163f50d..4d09c6e 100644
--- 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/ColumnMetadata.java
@@ -31,6 +31,10 @@ public class ColumnMetadata {
 
     private final @Nullable String description;
 
+    public ColumnMetadata(String name) {
+        this(name, null, null);
+    }
+
     public ColumnMetadata(String name, DataType type) {
         this(name, type, null);
     }
@@ -52,4 +56,12 @@ public class ColumnMetadata {
     public String description() {
         return this.description;
     }
+
+    public ColumnMetadata copy(DataType newDataType) {
+        return new ColumnMetadata(this.name, newDataType, this.description);
+    }
+
+    public ColumnMetadata setComment(String comment) {
+        return comment == null ? this : new ColumnMetadata(this.name, 
this.type, comment);
+    }
 }
diff --git 
a/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableChange.java 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableChange.java
new file mode 100644
index 0000000..ec0f392
--- /dev/null
+++ 
b/paimon-web-api/src/main/java/org/apache/paimon/web/api/table/TableChange.java
@@ -0,0 +1,480 @@
+/*
+ * 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.api.table;
+
+import org.apache.paimon.types.DataType;
+
+import javax.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * This change represents the modification of the table including adding, 
modifying and dropping
+ * column etc.
+ */
+public interface TableChange {
+    static AddColumn add(ColumnMetadata column) {
+        return new AddColumn(column, null);
+    }
+
+    static AddColumn add(ColumnMetadata column, @Nullable ColumnPosition 
position) {
+        return new AddColumn(column, position);
+    }
+
+    static ModifyColumn modify(
+            ColumnMetadata oldColumn,
+            ColumnMetadata newColumn,
+            @Nullable ColumnPosition columnPosition) {
+        return new ModifyColumn(oldColumn, newColumn, columnPosition);
+    }
+
+    static ModifyColumnType modifyColumnType(ColumnMetadata oldColumn, 
DataType newType) {
+        return new ModifyColumnType(oldColumn, newType);
+    }
+
+    static ModifyColumnName modifyColumnName(ColumnMetadata oldColumn, String 
newName) {
+        return new ModifyColumnName(oldColumn, newName);
+    }
+
+    static ModifyColumnComment modifyColumnComment(ColumnMetadata oldColumn, 
String newComment) {
+        return new ModifyColumnComment(oldColumn, newComment);
+    }
+
+    static ModifyColumnPosition modifyColumnPosition(
+            ColumnMetadata oldColumn, ColumnPosition columnPosition) {
+        return new ModifyColumnPosition(oldColumn, columnPosition);
+    }
+
+    static DropColumn dropColumn(String columnName) {
+        return new DropColumn(columnName);
+    }
+
+    static SetOption set(String key, String value) {
+        return new SetOption(key, value);
+    }
+
+    static RemoveOption remove(String key) {
+        return new RemoveOption(key);
+    }
+
+    /** A table change to add a column. */
+    class AddColumn implements TableChange {
+
+        private final ColumnMetadata column;
+        private final ColumnPosition position;
+
+        private AddColumn(ColumnMetadata column, ColumnPosition position) {
+            this.column = column;
+            this.position = position;
+        }
+
+        public ColumnMetadata getColumn() {
+            return column;
+        }
+
+        @Nullable
+        public ColumnPosition getPosition() {
+            return position;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof AddColumn)) {
+                return false;
+            }
+            AddColumn addColumn = (AddColumn) o;
+            return Objects.equals(column, addColumn.column)
+                    && Objects.equals(position, addColumn.position);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(column, position);
+        }
+
+        @Override
+        public String toString() {
+            return "AddColumn{" + "column=" + column + ", position=" + 
position + '}';
+        }
+    }
+
+    /** A base schema change to modify a column. */
+    class ModifyColumn implements TableChange {
+
+        protected final ColumnMetadata oldColumn;
+        protected final ColumnMetadata newColumn;
+
+        protected final @Nullable ColumnPosition newPosition;
+
+        public ModifyColumn(
+                ColumnMetadata oldColumn,
+                ColumnMetadata newColumn,
+                @Nullable ColumnPosition newPosition) {
+            this.oldColumn = oldColumn;
+            this.newColumn = newColumn;
+            this.newPosition = newPosition;
+        }
+
+        public ColumnMetadata getOldColumn() {
+            return oldColumn;
+        }
+
+        public ColumnMetadata getNewColumn() {
+            return newColumn;
+        }
+
+        public @Nullable ColumnPosition getNewPosition() {
+            return newPosition;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof ModifyColumn)) {
+                return false;
+            }
+            ModifyColumn that = (ModifyColumn) o;
+            return Objects.equals(oldColumn, that.oldColumn)
+                    && Objects.equals(newColumn, that.newColumn)
+                    && Objects.equals(newPosition, that.newPosition);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(oldColumn, newColumn, newPosition);
+        }
+
+        @Override
+        public String toString() {
+            return "ModifyColumn{"
+                    + "oldColumn="
+                    + oldColumn
+                    + ", newColumn="
+                    + newColumn
+                    + ", newPosition="
+                    + newPosition
+                    + '}';
+        }
+    }
+
+    /** A table change that modify the column data type. */
+    class ModifyColumnType extends ModifyColumn {
+
+        private ModifyColumnType(ColumnMetadata oldColumn, DataType newType) {
+            super(oldColumn, oldColumn.copy(newType), null);
+        }
+
+        /** Get the column type for the new column. */
+        public DataType getNewType() {
+            return newColumn.type();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return (o instanceof ModifyColumnType) && super.equals(o);
+        }
+
+        @Override
+        public String toString() {
+            return "ModifyColumnType{" + "Column=" + oldColumn + ", newType=" 
+ getNewType() + '}';
+        }
+    }
+
+    /** A table change to modify the column name. */
+    class ModifyColumnName extends ModifyColumn {
+
+        private ModifyColumnName(ColumnMetadata oldColumn, String newName) {
+            super(oldColumn, createNewColumn(oldColumn, newName), null);
+        }
+
+        private static ColumnMetadata createNewColumn(ColumnMetadata 
oldColumn, String newName) {
+            return new ColumnMetadata(newName, oldColumn.type(), 
oldColumn.description());
+        }
+
+        public String getOldColumnName() {
+            return oldColumn.name();
+        }
+
+        /** Returns the new column name after renaming the column name. */
+        public String getNewColumnName() {
+            return newColumn.name();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return (o instanceof ModifyColumnName) && super.equals(o);
+        }
+
+        @Override
+        public String toString() {
+            return "ModifyColumnName{"
+                    + "Column="
+                    + oldColumn
+                    + ", newName="
+                    + getNewColumnName()
+                    + '}';
+        }
+    }
+
+    /** A table change to modify the column comment. */
+    class ModifyColumnComment extends ModifyColumn {
+
+        private final String newComment;
+
+        private ModifyColumnComment(ColumnMetadata oldColumn, String 
newComment) {
+            super(oldColumn, oldColumn.setComment(newComment), null);
+            this.newComment = newComment;
+        }
+
+        /** Get the new comment for the column. */
+        public String getNewComment() {
+            return newComment;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return (o instanceof ModifyColumnComment) && super.equals(o);
+        }
+
+        @Override
+        public String toString() {
+            return "ModifyColumnComment{"
+                    + "Column="
+                    + oldColumn
+                    + ", newComment="
+                    + newComment
+                    + '}';
+        }
+    }
+
+    /** A table change to modify the column position. */
+    class ModifyColumnPosition extends ModifyColumn {
+
+        public ModifyColumnPosition(ColumnMetadata oldColumn, ColumnPosition 
newPosition) {
+            super(oldColumn, oldColumn, newPosition);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return (o instanceof ModifyColumnPosition) && super.equals(o);
+        }
+
+        @Override
+        public String toString() {
+            return "ModifyColumnPosition{"
+                    + "Column="
+                    + oldColumn
+                    + ", newPosition="
+                    + newPosition
+                    + '}';
+        }
+    }
+
+    /** A table change to drop the column. */
+    class DropColumn implements TableChange {
+
+        private final String columnName;
+
+        private DropColumn(String columnName) {
+            this.columnName = columnName;
+        }
+
+        /** Returns the column name. */
+        public String getColumnName() {
+            return columnName;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof DropColumn)) {
+                return false;
+            }
+            DropColumn that = (DropColumn) o;
+            return Objects.equals(columnName, that.columnName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(columnName);
+        }
+
+        @Override
+        public String toString() {
+            return "DropColumn{" + "columnName=" + columnName + '}';
+        }
+    }
+
+    /** A table change to set the table option. */
+    class SetOption implements TableChange {
+
+        private final String key;
+        private final String value;
+
+        private SetOption(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        /** Returns the Option key to set. */
+        public String getKey() {
+            return key;
+        }
+
+        /** Returns the Option value to set. */
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof SetOption)) {
+                return false;
+            }
+            SetOption setOption = (SetOption) o;
+            return Objects.equals(key, setOption.key) && Objects.equals(value, 
setOption.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(key, value);
+        }
+
+        @Override
+        public String toString() {
+            return "SetOption{" + "key=" + key + ", value=" + value + '}';
+        }
+    }
+
+    /** A table change to remove the table option. */
+    class RemoveOption implements TableChange {
+
+        private final String key;
+
+        public RemoveOption(String key) {
+            this.key = key;
+        }
+
+        /** Returns the Option key to reset. */
+        public String getKey() {
+            return key;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof RemoveOption)) {
+                return false;
+            }
+            RemoveOption that = (RemoveOption) o;
+            return Objects.equals(key, that.key);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(key);
+        }
+
+        @Override
+        public String toString() {
+            return "RemoveOption{" + "key=" + key + '}';
+        }
+    }
+
+    /** The position of the modified or added column. */
+    interface ColumnPosition {
+
+        /** Get the position to place the column at the first. */
+        static ColumnPosition first() {
+            return First.INSTANCE;
+        }
+
+        /** Get the position to place the column after the specified column. */
+        static ColumnPosition after(String column) {
+            return new After(column);
+        }
+    }
+
+    /** Column position FIRST means the specified column should be the first 
column. */
+    final class First implements ColumnPosition {
+        private static final First INSTANCE = new First();
+
+        private First() {}
+
+        @Override
+        public String toString() {
+            return "FIRST";
+        }
+    }
+
+    /** Column position AFTER means the specified column should be put after 
the given `column`. */
+    final class After implements ColumnPosition {
+        private final String column;
+
+        private After(String column) {
+            this.column = column;
+        }
+
+        public String column() {
+            return column;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof After)) {
+                return false;
+            }
+            After after = (After) o;
+            return Objects.equals(column, after.column);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(column);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("AFTER %s", escapeIdentifier(column));
+        }
+
+        private static String escapeBackticks(String s) {
+            return s.replace("`", "``");
+        }
+
+        private static String escapeIdentifier(String s) {
+            return "`" + escapeBackticks(s) + "`";
+        }
+    }
+}
diff --git 
a/paimon-web-api/src/test/java/org/apache/paimon/web/api/catalog/PaimonServiceTest.java
 
b/paimon-web-api/src/test/java/org/apache/paimon/web/api/catalog/PaimonServiceTest.java
new file mode 100644
index 0000000..37af5de
--- /dev/null
+++ 
b/paimon-web-api/src/test/java/org/apache/paimon/web/api/catalog/PaimonServiceTest.java
@@ -0,0 +1,309 @@
+/*
+ * 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.api.catalog;
+
+import org.apache.paimon.table.Table;
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.web.api.exception.DatabaseException;
+import org.apache.paimon.web.api.exception.TableException;
+import org.apache.paimon.web.api.table.ColumnMetadata;
+import org.apache.paimon.web.api.table.TableChange;
+import org.apache.paimon.web.api.table.TableMetadata;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/** The test class of catalog creator in {@link PaimonService}. */
+public class PaimonServiceTest {
+
+    private String warehouse;
+
+    private PaimonService service;
+
+    @TempDir private Path tempFile;
+
+    private final String db = "test_default_db";
+
+    @BeforeEach
+    public void before() {
+        warehouse = tempFile.toUri().toString();
+        service = 
PaimonServiceFactory.createFileSystemCatalogService("paimon", warehouse);
+        service.createDatabase(db);
+    }
+
+    @AfterEach
+    public void after() {
+        if (service.databaseExists(db)) {
+            service.dropDatabase(db);
+        }
+    }
+
+    @Test
+    public void testDatabaseExists() {
+        boolean exists = service.databaseExists(db);
+        assertThat(exists).isTrue();
+    }
+
+    @Test
+    public void testListDatabase() {
+        service.createDatabase("db1");
+        List<String> databases = service.listDatabases();
+        assertThat(databases).contains("test_default_db", "db1");
+        dropDatabase("db1");
+    }
+
+    @Test
+    public void testCreateDatabase() {
+        service.createDatabase("test_db");
+        boolean exists = service.databaseExists("test_db");
+        assertThat(exists).isTrue();
+
+        
assertThatExceptionOfType(DatabaseException.DatabaseAlreadyExistsException.class)
+                .isThrownBy(() -> service.createDatabase("test_db"))
+                .withMessage("The database 'test_db' already exists in the 
catalog.");
+
+        dropDatabase("test_db");
+    }
+
+    @Test
+    public void testDropDatabase() {
+        service.createDatabase("db2");
+        boolean exists = service.databaseExists("db2");
+        assertThat(exists).isTrue();
+        service.dropDatabase("db2");
+        boolean notExist = service.databaseExists("db2");
+        assertThat(notExist).isFalse();
+    }
+
+    @Test
+    public void testTableExists() {
+        createTable(db, "tb1");
+        boolean exists = service.tableExists(db, "tb1");
+        boolean notExists = service.tableExists(db, "tb_not");
+        assertThat(exists).isTrue();
+        assertThat(notExists).isFalse();
+    }
+
+    @Test
+    public void testListTables() {
+        createTable(db, "tb1");
+        createTable(db, "tb2");
+        List<String> tables = service.listTables(db);
+        assertThat(tables).contains("tb1", "tb2");
+    }
+
+    @Test
+    public void testCreateTable() {
+        createTable(db, "tb1");
+        boolean exists = service.tableExists(db, "tb1");
+        assertThat(exists).isTrue();
+
+        
assertThatExceptionOfType(TableException.TableAlreadyExistException.class)
+                .isThrownBy(() -> createTable(db, "tb1"))
+                .withMessage("The table 'tb1' already exists in the 
database.");
+
+        
assertThatExceptionOfType(DatabaseException.DatabaseNotExistException.class)
+                .isThrownBy(() -> createTable("db1", "tb1"))
+                .withMessage("The database 'db1' does not exist in the 
catalog.");
+    }
+
+    @Test
+    public void testGetTable() {
+        createTable(db, "tb1");
+        Table tb1 = service.getTable(db, "tb1");
+        assertThat(tb1).isInstanceOf(Table.class);
+        assertThat(tb1.name()).isEqualTo("tb1");
+
+        assertThatExceptionOfType(TableException.TableNotExistException.class)
+                .isThrownBy(() -> service.getTable(db, "tb2"))
+                .withMessage("The table 'tb2' does not exist in the 
database.");
+    }
+
+    @Test
+    public void testRenameTable() {
+        createTable(db, "tb1");
+        createTable(db, "tb3");
+        createTable(db, "tb4");
+        createTable(db, "tb6");
+        assertThat(service.tableExists(db, "tb1")).isTrue();
+        service.renameTable(db, "tb1", "tb2");
+        assertThat(service.tableExists(db, "tb1")).isFalse();
+        assertThat(service.tableExists(db, "tb2")).isTrue();
+
+        
assertThatExceptionOfType(TableException.TableAlreadyExistException.class)
+                .isThrownBy(() -> service.renameTable(db, "tb3", "tb4"))
+                .withMessage("The table 'tb4' already exists in the 
database.");
+
+        assertThatExceptionOfType(TableException.TableNotExistException.class)
+                .isThrownBy(() -> service.renameTable(db, "tb5", "tb7"))
+                .withMessage("The table 'tb5' does not exist in the 
database.");
+    }
+
+    @Test
+    public void testDropTable() {
+        createTable(db, "tb1");
+        assertThat(service.tableExists(db, "tb1")).isTrue();
+        service.dropTable(db, "tb1");
+        assertThat(service.tableExists(db, "tb1")).isFalse();
+
+        assertThatExceptionOfType(TableException.TableNotExistException.class)
+                .isThrownBy(() -> service.dropTable(db, "tb5"))
+                .withMessage("The table 'tb5' does not exist in the 
database.");
+    }
+
+    @Test
+    public void testAddColumn() {
+        createTable(db, "tb1");
+        ColumnMetadata age = new ColumnMetadata("age", DataTypes.INT());
+        TableChange.ColumnPosition columnPosition = 
TableChange.ColumnPosition.after("id");
+        TableChange.AddColumn add = TableChange.add(age, columnPosition);
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(add);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        List<String> fieldNames = tb1.rowType().getFieldNames();
+        assertThat(fieldNames).contains("id", "age", "name");
+    }
+
+    @Test
+    public void testModifyColumnType() {
+        createTable(db, "tb1");
+        ColumnMetadata id = new ColumnMetadata("id", DataTypes.INT());
+        TableChange.ModifyColumnType modifyColumnType =
+                TableChange.modifyColumnType(id, DataTypes.BIGINT());
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(modifyColumnType);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        List<DataType> fieldTypes = tb1.rowType().getFieldTypes();
+        assertThat(fieldTypes).contains(DataTypes.BIGINT(), 
DataTypes.STRING());
+    }
+
+    @Test
+    public void testModifyColumnName() {
+        createTable(db, "tb1");
+        ColumnMetadata id = new ColumnMetadata("id", DataTypes.INT());
+        TableChange.ModifyColumnName modifyColumnName = 
TableChange.modifyColumnName(id, "age");
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(modifyColumnName);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        List<String> fieldNames = tb1.rowType().getFieldNames();
+        assertThat(fieldNames).contains("age", "name");
+    }
+
+    @Test
+    public void testModifyColumnComment() {
+        createTable(db, "tb1");
+        ColumnMetadata id = new ColumnMetadata("id", DataTypes.INT());
+        TableChange.ModifyColumnComment modifyColumnComment =
+                TableChange.modifyColumnComment(id, "id");
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(modifyColumnComment);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        String description = tb1.rowType().getFields().get(0).description();
+        assertThat(description).isEqualTo("id");
+    }
+
+    @Test
+    public void testModifyColumnPosition() {
+        createTable(db, "tb1");
+        ColumnMetadata name = new ColumnMetadata("name", DataTypes.STRING());
+        TableChange.ColumnPosition columnPosition = 
TableChange.ColumnPosition.first();
+        TableChange.ModifyColumnPosition modifyColumnPosition =
+                TableChange.modifyColumnPosition(name, columnPosition);
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(modifyColumnPosition);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        String columnName = tb1.rowType().getFields().get(0).name();
+        assertThat(columnName).isEqualTo("name");
+    }
+
+    @Test
+    public void testDropColumn() {
+        createTable(db, "tb1");
+        TableChange.DropColumn dropColumn = TableChange.dropColumn("id");
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(dropColumn);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        List<String> fieldNames = tb1.rowType().getFieldNames();
+        assertThat(fieldNames).contains("name");
+        assertThat(fieldNames).doesNotContain("id");
+    }
+
+    @Test
+    public void testSetOption() {
+        createTable(db, "tb1");
+        TableChange.SetOption setOption = TableChange.set("bucket", "2");
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(setOption);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        String bucket = tb1.options().get("bucket");
+        assertThat(bucket).isEqualTo("2");
+    }
+
+    @Test
+    public void testRemoveOption() {
+        createTable(db, "tb1");
+        TableChange.SetOption setOption = TableChange.set("bucket", "2");
+        List<TableChange> tableChanges = new ArrayList<>();
+        tableChanges.add(setOption);
+        service.alterTable(db, "tb1", tableChanges);
+        Table tb1 = service.getTable(db, "tb1");
+        String bucket = tb1.options().get("bucket");
+        assertThat(bucket).isEqualTo("2");
+
+        TableChange.RemoveOption resetOption = TableChange.remove("bucket");
+        List<TableChange> changes = new ArrayList<>();
+        changes.add(resetOption);
+        service.alterTable(db, "tb1", changes);
+        Table tb = service.getTable(db, "tb1");
+        assertThat(tb.options().get("bucket")).isEqualTo(null);
+    }
+
+    private void createTable(String databaseName, String tableName) {
+        List<ColumnMetadata> columns = new ArrayList<>();
+        ColumnMetadata id = new ColumnMetadata("id", DataTypes.INT());
+        ColumnMetadata name = new ColumnMetadata("name", DataTypes.STRING());
+        columns.add(id);
+        columns.add(name);
+        TableMetadata tableMetadata = 
TableMetadata.builder().columns(columns).build();
+        service.createTable(databaseName, tableName, tableMetadata);
+    }
+
+    private void dropDatabase(String databaseName) {
+        if (service.databaseExists(databaseName)) {
+            service.dropDatabase(databaseName);
+        }
+    }
+}

Reply via email to