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

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


The following commit(s) were added to refs/heads/master by this push:
     new 775357ff3 [hive] Avoid using hive own FileSystem to access the 
location of paimon table when using HiveCatalog (#887)
775357ff3 is described below

commit 775357ff32d30973d6d5ff473f161804c6a1d65f
Author: wgcn <[email protected]>
AuthorDate: Thu Apr 27 16:00:35 2023 +0800

    [hive] Avoid using hive own FileSystem to access the location of paimon 
table when using HiveCatalog (#887)
---
 docs/content/how-to/creating-catalogs.md           |   7 +-
 .../generated/hive_catalog_configuration.html      |   9 +
 .../org/apache/paimon/catalog/AbstractCatalog.java |   3 +-
 .../java/org/apache/paimon/hive/HiveCatalog.java   |  57 +++++--
 .../org/apache/paimon/hive/HiveCatalogFactory.java |   8 +-
 .../org/apache/paimon/hive/HiveCatalogOptions.java |   9 +
 .../org/apache/paimon/hive/LocationHelper.java     |  38 +++++
 .../apache/paimon/hive/StorageLocationHelper.java  |  53 ++++++
 .../paimon/hive/TBPropertiesLocationHelper.java    |  63 +++++++
 .../org/apache/paimon/hive/HiveCatalogTest.java    |   3 +-
 .../apache/paimon/hive/LocationKeyExtractor.java   |  28 ++++
 .../paimon/hive/HiveCatalogLocationTest.java       | 183 +++++++++++++++++++++
 .../org/apache/paimon/hive/annotation/Minio.java   |  26 +++
 .../hive/runner/PaimonEmbeddedHiveRunner.java      |  58 ++++++-
 paimon-hive/pom.xml                                |  32 ++++
 15 files changed, 550 insertions(+), 27 deletions(-)

diff --git a/docs/content/how-to/creating-catalogs.md 
b/docs/content/how-to/creating-catalogs.md
index d24c8ea1c..72855899b 100644
--- a/docs/content/how-to/creating-catalogs.md
+++ b/docs/content/how-to/creating-catalogs.md
@@ -131,4 +131,9 @@ USE paimon.default;
 
 {{< /tab >}}
 
-{{< /tabs >}}
\ No newline at end of file
+{{< /tabs >}}
+
+If you are using a object storage , and you don't want that the location of 
paimon table/database is accessed by the filesystem of hive,
+which may lead to the error such as "No FileSystem for scheme: s3a".
+You can set location in the properties of table/database by the config of 
`location-in-properties`. See
+[setting the location of table/database in properties ]({{< ref 
"maintenance/configurations#HiveCatalogOptions" >}})
\ No newline at end of file
diff --git a/docs/layouts/shortcodes/generated/hive_catalog_configuration.html 
b/docs/layouts/shortcodes/generated/hive_catalog_configuration.html
index e7ec2f468..f7c3e0cbf 100644
--- a/docs/layouts/shortcodes/generated/hive_catalog_configuration.html
+++ b/docs/layouts/shortcodes/generated/hive_catalog_configuration.html
@@ -20,5 +20,14 @@
             <td>String</td>
             <td>File directory of the hive-site.xml , used to create 
HiveMetastoreClient and security authentication, such as Kerberos, LDAP, Ranger 
and so on</td>
         </tr>
+        <tr>
+            <td><h5>location-in-properties</h5></td>
+            <td style="word-wrap: break-word;">false</td>
+            <td>Boolean</td>
+            <td>Setting the location in properties of hive table/database.
+If you don't want to access the location by the filesystem of hive when using 
a object storage such as s3,oss
+you can set this option to true.
+</td>
+        </tr>
     </tbody>
 </table>
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java 
b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
index 3875f09ff..9e3d25b43 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
@@ -83,7 +83,8 @@ public abstract class AbstractCatalog implements Catalog {
         return FileStoreTableFactory.create(fileIO, 
getDataTableLocation(identifier), tableSchema);
     }
 
-    protected Path databasePath(String database) {
+    @VisibleForTesting
+    public Path databasePath(String database) {
         return new Path(warehouse(), database + DB_SUFFIX);
     }
 
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
index ea77d7e14..c9f42f137 100644
--- 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalog.java
@@ -18,6 +18,7 @@
 
 package org.apache.paimon.hive;
 
+import org.apache.paimon.annotation.VisibleForTesting;
 import org.apache.paimon.catalog.AbstractCatalog;
 import org.apache.paimon.catalog.CatalogLock;
 import org.apache.paimon.catalog.Identifier;
@@ -72,6 +73,7 @@ import java.util.stream.Collectors;
 
 import static org.apache.paimon.hive.HiveCatalogLock.acquireTimeout;
 import static org.apache.paimon.hive.HiveCatalogLock.checkMaxSleep;
+import static org.apache.paimon.hive.HiveCatalogOptions.LOCATION_IN_PROPERTIES;
 import static org.apache.paimon.options.CatalogOptions.LOCK_ENABLED;
 import static org.apache.paimon.options.CatalogOptions.TABLE_TYPE;
 import static org.apache.paimon.utils.Preconditions.checkState;
@@ -96,19 +98,36 @@ public class HiveCatalog extends AbstractCatalog {
     private final HiveConf hiveConf;
     private final String clientClassName;
     private final IMetaStoreClient client;
+    private final String warehouse;
 
-    public HiveCatalog(FileIO fileIO, HiveConf hiveConf, String 
clientClassName) {
-        super(fileIO);
-        this.hiveConf = hiveConf;
-        this.clientClassName = clientClassName;
-        this.client = createClient(hiveConf, clientClassName);
+    private LocationHelper locationHelper;
+
+    public HiveCatalog(FileIO fileIO, HiveConf hiveConf, String 
clientClassName, String warehouse) {
+        this(fileIO, hiveConf, clientClassName, Collections.emptyMap(), 
warehouse);
     }
 
     public HiveCatalog(
-            FileIO fileIO, HiveConf hiveConf, String clientClassName, 
Map<String, String> options) {
+            FileIO fileIO,
+            HiveConf hiveConf,
+            String clientClassName,
+            Map<String, String> options,
+            String warehouse) {
         super(fileIO, options);
         this.hiveConf = hiveConf;
         this.clientClassName = clientClassName;
+        this.warehouse = warehouse;
+
+        boolean needLocationInProperties =
+                hiveConf.getBoolean(
+                        LOCATION_IN_PROPERTIES.key(), 
LOCATION_IN_PROPERTIES.defaultValue());
+        if (needLocationInProperties) {
+            locationHelper = new TBPropertiesLocationHelper();
+        } else {
+            // set the warehouse location to the hiveConf
+            hiveConf.set(HiveConf.ConfVars.METASTOREWAREHOUSE.varname, 
warehouse);
+            locationHelper = new StorageLocationHelper();
+        }
+
         this.client = createClient(hiveConf, clientClassName);
     }
 
@@ -151,11 +170,13 @@ public class HiveCatalog extends AbstractCatalog {
             throws DatabaseAlreadyExistException {
         try {
             client.createDatabase(convertToDatabase(name));
+
+            locationHelper.createPathIfRequired(databasePath(name), fileIO);
         } catch (AlreadyExistsException e) {
             if (!ignoreIfExists) {
                 throw new DatabaseAlreadyExistException(name, e);
             }
-        } catch (TException e) {
+        } catch (TException | IOException e) {
             throw new RuntimeException("Failed to create database " + name, e);
         }
     }
@@ -167,12 +188,14 @@ public class HiveCatalog extends AbstractCatalog {
             if (!cascade && client.getAllTables(name).size() > 0) {
                 throw new DatabaseNotEmptyException(name);
             }
+
+            locationHelper.dropPathIfRequired(databasePath(name), fileIO);
             client.dropDatabase(name, true, false, true);
         } catch (NoSuchObjectException | UnknownDBException e) {
             if (!ignoreIfNotExists) {
                 throw new DatabaseNotExistException(name, e);
             }
-        } catch (TException e) {
+        } catch (TException | IOException e) {
             throw new RuntimeException("Failed to drop database " + name, e);
         }
     }
@@ -363,7 +386,7 @@ public class HiveCatalog extends AbstractCatalog {
 
     @Override
     protected String warehouse() {
-        return hiveConf.get(HiveConf.ConfVars.METASTOREWAREHOUSE.varname);
+        return warehouse;
     }
 
     private void checkIdentifierUpperCase(Identifier identifier) {
@@ -410,7 +433,7 @@ public class HiveCatalog extends AbstractCatalog {
     private Database convertToDatabase(String name) {
         Database database = new Database();
         database.setName(name);
-        database.setLocationUri(databasePath(name).toString());
+        locationHelper.specifyDatabaseLocation(databasePath(name), database);
         return database;
     }
 
@@ -444,19 +467,20 @@ public class HiveCatalog extends AbstractCatalog {
     }
 
     private void updateHmsTable(Table table, Identifier identifier, 
TableSchema schema) {
-        StorageDescriptor sd = convertToStorageDescriptor(identifier, schema);
+        StorageDescriptor sd = convertToStorageDescriptor(schema);
         table.setSd(sd);
+
+        // update location
+        locationHelper.specifyTableLocation(table, 
getDataTableLocation(identifier).toString());
     }
 
-    private StorageDescriptor convertToStorageDescriptor(
-            Identifier identifier, TableSchema schema) {
+    private StorageDescriptor convertToStorageDescriptor(TableSchema schema) {
         StorageDescriptor sd = new StorageDescriptor();
 
         sd.setCols(
                 schema.fields().stream()
                         .map(this::convertToFieldSchema)
                         .collect(Collectors.toList()));
-        sd.setLocation(getDataTableLocation(identifier).toString());
 
         sd.setInputFormat(INPUT_FORMAT_CLASS_NAME);
         sd.setOutputFormat(OUTPUT_FORMAT_CLASS_NAME);
@@ -469,6 +493,11 @@ public class HiveCatalog extends AbstractCatalog {
         return sd;
     }
 
+    @VisibleForTesting
+    IMetaStoreClient getHmsClient() {
+        return client;
+    }
+
     private FieldSchema convertToFieldSchema(DataField dataField) {
         return new FieldSchema(
                 dataField.name(),
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogFactory.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogFactory.java
index e8771df31..2db46caea 100644
--- 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogFactory.java
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogFactory.java
@@ -69,10 +69,14 @@ public class HiveCatalogFactory implements CatalogFactory {
         // always using user-set parameters overwrite hive-site.xml parameters
         context.options().toMap().forEach(hiveConf::set);
         hiveConf.set(HiveConf.ConfVars.METASTOREURIS.varname, uri);
-        hiveConf.set(HiveConf.ConfVars.METASTOREWAREHOUSE.varname, 
warehouse.toUri().toString());
 
         String clientClassName = context.options().get(METASTORE_CLIENT_CLASS);
 
-        return new HiveCatalog(fileIO, hiveConf, clientClassName, 
context.options().toMap());
+        return new HiveCatalog(
+                fileIO,
+                hiveConf,
+                clientClassName,
+                context.options().toMap(),
+                warehouse.toUri().toString());
     }
 }
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogOptions.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogOptions.java
index ceebdaa20..431e74dcf 100644
--- 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogOptions.java
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/HiveCatalogOptions.java
@@ -40,5 +40,14 @@ public final class HiveCatalogOptions {
                     .withDescription(
                             "File directory of the 
core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml. Currently, only 
local file system paths are supported.");
 
+    public static final ConfigOption<Boolean> LOCATION_IN_PROPERTIES =
+            ConfigOptions.key("location-in-properties")
+                    .booleanType()
+                    .defaultValue(false)
+                    .withDescription(
+                            "Setting the location in properties of hive 
table/database.\n"
+                                    + "If you don't want to access the 
location by the filesystem of hive when using a object storage such as s3,oss\n"
+                                    + "you can set this option to true.\n");
+
     private HiveCatalogOptions() {}
 }
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/LocationHelper.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/LocationHelper.java
new file mode 100644
index 000000000..409b79d58
--- /dev/null
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/LocationHelper.java
@@ -0,0 +1,38 @@
+/*
+ * 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.hive;
+
+import org.apache.paimon.fs.FileIO;
+import org.apache.paimon.fs.Path;
+
+import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.hadoop.hive.metastore.api.Table;
+
+import java.io.IOException;
+
+/** helper for specifying the storage location of hive table. */
+public interface LocationHelper {
+    void createPathIfRequired(Path dbPath, FileIO fileIO) throws IOException;
+
+    void dropPathIfRequired(Path path, FileIO fileIO) throws IOException;
+
+    void specifyTableLocation(Table table, String location);
+
+    void specifyDatabaseLocation(Path path, Database database);
+}
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/StorageLocationHelper.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/StorageLocationHelper.java
new file mode 100644
index 000000000..6d62d68f3
--- /dev/null
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/StorageLocationHelper.java
@@ -0,0 +1,53 @@
+/*
+ * 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.hive;
+
+import org.apache.paimon.fs.FileIO;
+import org.apache.paimon.fs.Path;
+
+import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.hadoop.hive.metastore.api.Table;
+
+import java.io.IOException;
+
+/** Helper for Setting Location in Hive Table Storage. */
+public final class StorageLocationHelper implements LocationHelper {
+
+    public StorageLocationHelper() {}
+
+    @Override
+    public void createPathIfRequired(Path dbPath, FileIO fileIO) throws 
IOException {
+        // do nothing
+    }
+
+    @Override
+    public void dropPathIfRequired(Path path, FileIO fileIO) throws 
IOException {
+        // do nothing
+    }
+
+    @Override
+    public void specifyTableLocation(Table table, String location) {
+        table.getSd().setLocation(location);
+    }
+
+    @Override
+    public void specifyDatabaseLocation(Path path, Database database) {
+        database.setLocationUri(path.toString());
+    }
+}
diff --git 
a/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/TBPropertiesLocationHelper.java
 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/TBPropertiesLocationHelper.java
new file mode 100644
index 000000000..a374907ed
--- /dev/null
+++ 
b/paimon-hive/paimon-hive-catalog/src/main/java/org/apache/paimon/hive/TBPropertiesLocationHelper.java
@@ -0,0 +1,63 @@
+/*
+ * 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.hive;
+
+import org.apache.paimon.fs.FileIO;
+import org.apache.paimon.fs.Path;
+
+import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.hadoop.hive.metastore.api.Table;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/** Helper for Setting Location in Hive Table Properties. */
+public final class TBPropertiesLocationHelper implements LocationHelper {
+
+    public TBPropertiesLocationHelper() {}
+
+    @Override
+    public void createPathIfRequired(Path path, FileIO fileIO) throws 
IOException {
+        if (!fileIO.exists(path)) {
+            fileIO.mkdirs(path);
+        }
+    }
+
+    @Override
+    public void dropPathIfRequired(Path path, FileIO fileIO) throws 
IOException {
+        if (fileIO.exists(path)) {
+            fileIO.delete(path, true);
+        }
+    }
+
+    @Override
+    public void specifyTableLocation(Table table, String location) {
+        
table.getParameters().put(LocationKeyExtractor.TBPROPERTIES_LOCATION_KEY, 
location);
+    }
+
+    @Override
+    public void specifyDatabaseLocation(Path path, Database database) {
+        HashMap<String, String> properties = new HashMap<>();
+        if (database.getParameters() != null) {
+            properties.putAll(database.getParameters());
+        }
+        properties.put(LocationKeyExtractor.TBPROPERTIES_LOCATION_KEY, 
path.toString());
+        database.setParameters(properties);
+    }
+}
diff --git 
a/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
 
b/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
index 6695e1d3e..b56ca222a 100644
--- 
a/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
+++ 
b/paimon-hive/paimon-hive-catalog/src/test/java/org/apache/paimon/hive/HiveCatalogTest.java
@@ -44,14 +44,13 @@ public class HiveCatalogTest extends CatalogTestBase {
         HiveConf hiveConf = new HiveConf();
         String jdoConnectionURL = "jdbc:derby:memory:" + UUID.randomUUID();
         hiveConf.setVar(METASTORECONNECTURLKEY, jdoConnectionURL + 
";create=true");
-        hiveConf.setVar(HiveConf.ConfVars.METASTOREWAREHOUSE, warehouse);
         HiveMetaStoreClient metaStoreClient = new 
HiveMetaStoreClient(hiveConf);
         String metastoreClientClass = 
"org.apache.hadoop.hive.metastore.HiveMetaStoreClient";
         try (MockedStatic<HiveCatalog> mocked = 
Mockito.mockStatic(HiveCatalog.class)) {
             mocked.when(() -> HiveCatalog.createClient(hiveConf, 
metastoreClientClass))
                     .thenReturn(metaStoreClient);
         }
-        catalog = new HiveCatalog(fileIO, hiveConf, metastoreClientClass);
+        catalog = new HiveCatalog(fileIO, hiveConf, metastoreClientClass, 
warehouse);
     }
 
     @Test
diff --git 
a/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/LocationKeyExtractor.java
 
b/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/LocationKeyExtractor.java
new file mode 100644
index 000000000..a5663a18d
--- /dev/null
+++ 
b/paimon-hive/paimon-hive-common/src/main/java/org/apache/paimon/hive/LocationKeyExtractor.java
@@ -0,0 +1,28 @@
+/*
+ * 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.hive;
+
+/**
+ * declaring the name of the key in the parameters of the Hive metastore 
table, which indicates
+ * where the Paimon table is stored.
+ */
+public class LocationKeyExtractor {
+    // special at the tbproperties with the name paimon_meta_location.
+    public static final String TBPROPERTIES_LOCATION_KEY = 
"paimon_meta_location";
+}
diff --git 
a/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/HiveCatalogLocationTest.java
 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/HiveCatalogLocationTest.java
new file mode 100644
index 000000000..138ca40bb
--- /dev/null
+++ 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/HiveCatalogLocationTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.hive;
+
+import org.apache.paimon.catalog.CatalogContext;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.fs.FileIO;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.hive.annotation.Minio;
+import org.apache.paimon.hive.runner.PaimonEmbeddedHiveRunner;
+import org.apache.paimon.options.CatalogOptions;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.s3.MinioTestContainer;
+import org.apache.paimon.schema.Schema;
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.RowType;
+
+import org.apache.paimon.shade.guava30.com.google.common.collect.Sets;
+
+import com.klarna.hiverunner.HiveShell;
+import com.klarna.hiverunner.annotations.HiveSQL;
+import org.apache.hadoop.hive.metastore.IMetaStoreClient;
+import org.apache.hadoop.hive.metastore.api.Table;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/** Test for HiveCatalog location. */
+@RunWith(PaimonEmbeddedHiveRunner.class)
+public class HiveCatalogLocationTest {
+    @HiveSQL(files = {})
+    private static HiveShell hiveShell;
+
+    @Minio private static MinioTestContainer minioTestContainer;
+
+    public static final String HIVE_CONF = "/hive-conf";
+
+    private HiveCatalog catalog;
+
+    private IMetaStoreClient hmsClient;
+
+    private String path;
+
+    private FileIO fileIO;
+
+    @Before
+    public void before() throws IOException {
+        path = minioTestContainer.getS3UriForDefaultBucket() + "/" + 
UUID.randomUUID();
+
+        Options conf = new Options();
+        conf.set(CatalogOptions.WAREHOUSE, path);
+        conf.set(CatalogOptions.METASTORE, "hive");
+        conf.set(CatalogOptions.URI, "");
+        conf.set(
+                HiveCatalogOptions.HIVE_CONF_DIR,
+                hiveShell.getBaseDir().getRoot().getPath() + HIVE_CONF);
+        conf.set(HiveCatalogOptions.LOCATION_IN_PROPERTIES, true);
+
+        for (Map.Entry<String, String> stringStringEntry :
+                minioTestContainer.getS3ConfigOptions().entrySet()) {
+            conf.set(stringStringEntry.getKey(), stringStringEntry.getValue());
+        }
+
+        // create CatalogContext using the options
+        CatalogContext catalogContext = CatalogContext.create(conf);
+
+        Path warehouse = new Path(path);
+        fileIO = getFileIO(catalogContext, warehouse);
+        fileIO.mkdirs(warehouse);
+
+        HiveCatalogFactory hiveCatalogFactory = new HiveCatalogFactory();
+        catalog = (HiveCatalog) hiveCatalogFactory.create(fileIO, warehouse, 
catalogContext);
+        hmsClient = catalog.getHmsClient();
+    }
+
+    private static FileIO getFileIO(CatalogContext catalogContext, Path 
warehouse) {
+        FileIO fileIO;
+        try {
+            fileIO = FileIO.get(warehouse, catalogContext);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        return fileIO;
+    }
+
+    @After
+    public void after() throws Exception {
+        catalog.close();
+    }
+
+    @Test
+    public void testDBLocation() throws Exception {
+
+        Set<String> dbs = Sets.newHashSet("db1", "db2", "db3", "db4", "db5");
+        List<Path> paths = new ArrayList<>();
+        for (String db : dbs) {
+            catalog.createDatabase(db, true);
+            Assert.assertNotNull(hmsClient.getDatabase(db));
+
+            Path actual = catalog.databasePath(db);
+            Path expected = new Path(this.path + "/" + db + ".db");
+            Assert.assertTrue(fileIO.exists(expected));
+            Assert.assertEquals(expected, actual);
+
+            paths.add(expected);
+        }
+
+        HashSet<String> dbsExpected = Sets.newHashSet("db1", "db2", "db3", 
"db4", "db5", "default");
+        Assert.assertEquals(Sets.newHashSet(catalog.listDatabases()), 
dbsExpected);
+
+        for (String db : dbs) {
+            catalog.dropDatabase(db, false, true);
+        }
+
+        for (Path p : paths) {
+            Assert.assertFalse(fileIO.exists(p));
+        }
+
+        Assert.assertEquals(Sets.newHashSet(catalog.listDatabases()), 
Sets.newHashSet("default"));
+    }
+
+    @Test
+    public void testTableLocation() throws Exception {
+
+        String db = "db";
+        String table = "table";
+
+        catalog.createDatabase(db, true);
+
+        RowType rowType = RowType.of(new DataType[] {DataTypes.INT()}, new 
String[] {"aaa"});
+        Identifier tableIdentifier = Identifier.create(db, table);
+
+        // create table
+        catalog.createTable(
+                tableIdentifier,
+                new Schema(
+                        rowType.getFields(),
+                        Collections.emptyList(),
+                        Collections.emptyList(),
+                        new HashMap<>(),
+                        ""),
+                false);
+
+        Table hmsClientTablea =
+                hmsClient.getTable(
+                        tableIdentifier.getDatabaseName(), 
tableIdentifier.getObjectName());
+        String location =
+                
hmsClientTablea.getParameters().get(LocationKeyExtractor.TBPROPERTIES_LOCATION_KEY);
+        String expected = this.path + "/" + db + ".db" + "/" + table;
+        Assert.assertTrue(fileIO.exists(new Path(expected)));
+        Assert.assertEquals(expected, location);
+    }
+}
diff --git 
a/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/annotation/Minio.java
 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/annotation/Minio.java
new file mode 100644
index 000000000..88c6eebc3
--- /dev/null
+++ 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/annotation/Minio.java
@@ -0,0 +1,26 @@
+/*
+ * 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.hive.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Annotation for Minio Container. */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Minio {}
diff --git 
a/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/runner/PaimonEmbeddedHiveRunner.java
 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/runner/PaimonEmbeddedHiveRunner.java
index 3d161cac9..fbb29cb3c 100644
--- 
a/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/runner/PaimonEmbeddedHiveRunner.java
+++ 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/runner/PaimonEmbeddedHiveRunner.java
@@ -18,6 +18,8 @@
 
 package org.apache.paimon.hive.runner;
 
+import org.apache.paimon.hive.annotation.Minio;
+import org.apache.paimon.s3.MinioTestContainer;
 import org.apache.paimon.utils.Preconditions;
 
 import org.apache.paimon.shade.guava30.com.google.common.io.Resources;
@@ -72,9 +74,7 @@ import static org.reflections.ReflectionUtils.withAnnotation;
 public class PaimonEmbeddedHiveRunner extends BlockJUnit4ClassRunner {
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(PaimonEmbeddedHiveRunner.class);
-    private HiveShellContainer container;
     private final HiveRunnerConfig config = new HiveRunnerConfig();
-    protected HiveServerContext context;
 
     public PaimonEmbeddedHiveRunner(Class<?> clazz) throws InitializationError 
{
         super(clazz);
@@ -85,21 +85,45 @@ public class PaimonEmbeddedHiveRunner extends 
BlockJUnit4ClassRunner {
         // need to load hive runner config before the context is inited
         loadAnnotatesHiveRunnerConfig(getTestClass().getJavaClass());
         final TemporaryFolder temporaryFolder = new TemporaryFolder();
-        context = new PaimonEmbeddedHiveServerContext(temporaryFolder, config);
         List<TestRule> rules = super.classRules();
         ExternalResource hiveShell =
                 new ExternalResource() {
+                    private HiveShellContainer innerContainer;
+
                     @Override
                     protected void before() throws Throwable {
-                        container =
-                                
createHiveServerContainer(getTestClass().getJavaClass(), context);
+                        PaimonEmbeddedHiveServerContext 
paimonEmbeddedHiveServerContext =
+                                new 
PaimonEmbeddedHiveServerContext(temporaryFolder, config);
+                        innerContainer =
+                                createHiveServerContainer(
+                                        getTestClass().getJavaClass(),
+                                        paimonEmbeddedHiveServerContext);
+                    }
+
+                    @Override
+                    protected void after() {
+                        tearDownHiveContainer(innerContainer);
+                    }
+                };
+
+        ExternalResource minio =
+                new ExternalResource() {
+                    public MinioTestContainer minioTestContainer;
+
+                    @Override
+                    protected void before() {
+                        minioTestContainer = 
setMinioTestContainer(getTestClass().getJavaClass());
                     }
 
                     @Override
                     protected void after() {
-                        tearDown();
+                        if (minioTestContainer != null && 
minioTestContainer.isRunning()) {
+                            minioTestContainer.close();
+                        }
                     }
                 };
+
+        rules.add(minio);
         rules.add(hiveShell);
         rules.add(temporaryFolder);
         return rules;
@@ -134,7 +158,7 @@ public class PaimonEmbeddedHiveRunner extends 
BlockJUnit4ClassRunner {
         }
     }
 
-    private void tearDown() {
+    private void tearDownHiveContainer(HiveShellContainer container) {
         if (container != null) {
             LOGGER.info("Tearing down {}", getName());
             try {
@@ -342,6 +366,26 @@ public class PaimonEmbeddedHiveRunner extends 
BlockJUnit4ClassRunner {
         }
     }
 
+    private MinioTestContainer setMinioTestContainer(final Class testClass) {
+        Set<Field> allFields = ReflectionUtils.getAllFields(testClass, 
withAnnotation(Minio.class));
+
+        Preconditions.checkState(
+                allFields.size() <= 1,
+                "At most one field of type MinioTestContainer should to be 
annotated with @MinIO");
+        MinioTestContainer minioTestContainer = null;
+        if (!allFields.isEmpty()) {
+            minioTestContainer = new MinioTestContainer();
+            minioTestContainer.start();
+
+            Field field = allFields.iterator().next();
+            Preconditions.checkState(
+                    ReflectionUtils.isOfType(field, MinioTestContainer.class),
+                    "Field annotated with @MinIO should be of type 
MinioTestContainer");
+            ReflectionUtils.setStaticField(testClass, field.getName(), 
minioTestContainer);
+        }
+        return minioTestContainer;
+    }
+
     /**
      * Used as a handle for the HiveShell field in the test case so that we 
may set it once the
      * HiveShell has been instantiated.
diff --git a/paimon-hive/pom.xml b/paimon-hive/pom.xml
index fc888bbb2..b0fc8bc17 100644
--- a/paimon-hive/pom.xml
+++ b/paimon-hive/pom.xml
@@ -48,6 +48,38 @@ under the License.
         <hive.version>2.3.9</hive.version>
         <hiverunner.version>4.0.0</hiverunner.version>
         <reflections.version>0.9.8</reflections.version>
+        <aws.version>1.12.319</aws.version>
     </properties>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-s3</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-s3</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-core</artifactId>
+            <version>${aws.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-s3</artifactId>
+            <version>${aws.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
 </project>


Reply via email to