This is an automated email from the ASF dual-hosted git repository.
tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-datafusion.git
The following commit(s) were added to refs/heads/master by this push:
new b62527712 feat: use ObjectStoreProvider to provide s3 and gcs object
stores to the cli (#3540)
b62527712 is described below
commit b6252771248da117dd3a5976d134a66a4bb60431
Author: gorkem <[email protected]>
AuthorDate: Fri Sep 23 12:28:12 2022 -0700
feat: use ObjectStoreProvider to provide s3 and gcs object stores to the
cli (#3540)
---
datafusion-cli/Cargo.lock | 388 ++++++++++++++++++++++++++++++++++-
datafusion-cli/Cargo.toml | 2 +
datafusion-cli/src/lib.rs | 1 +
datafusion-cli/src/main.rs | 18 +-
datafusion-cli/src/object_storage.rs | 150 ++++++++++++++
5 files changed, 549 insertions(+), 10 deletions(-)
diff --git a/datafusion-cli/Cargo.lock b/datafusion-cli/Cargo.lock
index 87ef19773..bdb0fdbae 100644
--- a/datafusion-cli/Cargo.lock
+++ b/datafusion-cli/Cargo.lock
@@ -260,6 +260,7 @@ dependencies = [
"iana-time-zone",
"num-integer",
"num-traits",
+ "serde",
"winapi",
]
@@ -462,8 +463,10 @@ dependencies = [
"dirs",
"env_logger",
"mimalloc",
+ "object_store",
"rustyline",
"tokio",
+ "url",
]
[[package]]
@@ -608,6 +611,15 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+[[package]]
+name = "encoding_rs"
+version = "0.8.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "endian-type"
version = "0.1.2"
@@ -699,6 +711,12 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
[[package]]
name = "form_urlencoded"
version = "1.1.0"
@@ -824,6 +842,25 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+[[package]]
+name = "h2"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
[[package]]
name = "half"
version = "2.1.0"
@@ -858,22 +895,92 @@ dependencies = [
"libc",
]
+[[package]]
+name = "http"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa 1.0.3",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+[[package]]
+name = "hyper"
+version = "0.14.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa 1.0.3",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
+dependencies = [
+ "http",
+ "hyper",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+]
+
[[package]]
name = "iana-time-zone"
-version = "0.1.48"
+version = "0.1.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0"
+checksum = "3bbaead50122b06e9a973ac20bc7445074d99ad9a0a0654934876908a9cec82c"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
- "once_cell",
"wasm-bindgen",
"winapi",
]
@@ -919,6 +1026,12 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06"
+[[package]]
+name = "ipnet"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
+
[[package]]
name = "itertools"
version = "0.10.5"
@@ -942,9 +1055,9 @@ checksum =
"6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "jobserver"
-version = "0.1.24"
+version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
+checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
dependencies = [
"libc",
]
@@ -1118,6 +1231,12 @@ dependencies = [
"libmimalloc-sys",
]
+[[package]]
+name = "mime"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+
[[package]]
name = "miniz_oxide"
version = "0.5.4"
@@ -1127,6 +1246,18 @@ dependencies = [
"adler",
]
+[[package]]
+name = "mio"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys",
+]
+
[[package]]
name = "multiversion"
version = "0.6.1"
@@ -1261,12 +1392,20 @@ source =
"registry+https://github.com/rust-lang/crates.io-index"
checksum = "2168fee79ee3e7695905bc3a48777d807f82d956f821186fa7a2601c1295a73e"
dependencies = [
"async-trait",
+ "base64",
"bytes",
"chrono",
"futures",
"itertools",
"parking_lot",
"percent-encoding",
+ "quick-xml",
+ "rand",
+ "reqwest",
+ "ring",
+ "rustls-pemfile",
+ "serde",
+ "serde_json",
"snafu",
"tokio",
"tracing",
@@ -1422,6 +1561,16 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "quick-xml"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37dddbbe9df96afafcb8027fcf263971b726530e12f0787f620a7ba5b4846081"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
[[package]]
name = "quote"
version = "1.0.21"
@@ -1523,6 +1672,61 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "reqwest"
+version = "0.11.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-rustls",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls",
+ "rustls-pemfile",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "tokio",
+ "tokio-rustls",
+ "tokio-util",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots",
+ "winreg",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
[[package]]
name = "rustix"
version = "0.35.10"
@@ -1537,6 +1741,27 @@ dependencies = [
"windows-sys",
]
+[[package]]
+name = "rustls"
+version = "0.20.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
+dependencies = [
+ "log",
+ "ring",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
+dependencies = [
+ "base64",
+]
+
[[package]]
name = "rustversion"
version = "1.0.9"
@@ -1587,6 +1812,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
[[package]]
name = "seq-macro"
version = "0.3.1"
@@ -1595,18 +1830,18 @@ checksum =
"0772c5c30e1a0d91f6834f8e545c69281c099dfa9a3ac58d96a9fd629c8d4898"
[[package]]
name = "serde"
-version = "1.0.144"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
+checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.144"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
+checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
@@ -1624,6 +1859,18 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa 1.0.3",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "sha2"
version = "0.10.6"
@@ -1678,6 +1925,22 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451"
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
[[package]]
name = "sqlparser"
version = "0.23.0"
@@ -1833,12 +2096,16 @@ checksum =
"0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95"
dependencies = [
"autocfg",
"bytes",
+ "libc",
"memchr",
+ "mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
+ "socket2",
"tokio-macros",
+ "winapi",
]
[[package]]
@@ -1852,6 +2119,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "tokio-rustls"
+version = "0.23.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
+dependencies = [
+ "rustls",
+ "tokio",
+ "webpki",
+]
+
[[package]]
name = "tokio-stream"
version = "0.1.10"
@@ -1863,6 +2141,26 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tokio-util"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
[[package]]
name = "tracing"
version = "0.1.36"
@@ -1895,6 +2193,12 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "try-lock"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+
[[package]]
name = "typenum"
version = "1.15.0"
@@ -1934,6 +2238,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
[[package]]
name = "url"
version = "2.3.1"
@@ -1977,6 +2287,16 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -2008,6 +2328,18 @@ dependencies = [
"wasm-bindgen-shared",
]
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
@@ -2037,6 +2369,35 @@ version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
+dependencies = [
+ "webpki",
+]
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -2111,6 +2472,15 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+[[package]]
+name = "winreg"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
diff --git a/datafusion-cli/Cargo.toml b/datafusion-cli/Cargo.toml
index 2c70224ec..e74b81a36 100644
--- a/datafusion-cli/Cargo.toml
+++ b/datafusion-cli/Cargo.toml
@@ -35,5 +35,7 @@ datafusion = { path = "../datafusion/core", version =
"12.0.0" }
dirs = "4.0.0"
env_logger = "0.9"
mimalloc = { version = "0.1", default-features = false }
+object_store = { version = "0.5.0", features = ["aws", "gcp"] }
rustyline = "10.0"
tokio = { version = "1.0", features = ["macros", "rt", "rt-multi-thread",
"sync", "parking_lot"] }
+url = "2.2"
diff --git a/datafusion-cli/src/lib.rs b/datafusion-cli/src/lib.rs
index 743556533..44d8f0610 100644
--- a/datafusion-cli/src/lib.rs
+++ b/datafusion-cli/src/lib.rs
@@ -22,5 +22,6 @@ pub mod command;
pub mod exec;
pub mod functions;
pub mod helper;
+pub mod object_storage;
pub mod print_format;
pub mod print_options;
diff --git a/datafusion-cli/src/main.rs b/datafusion-cli/src/main.rs
index 98ae274ca..cbb74bbf9 100644
--- a/datafusion-cli/src/main.rs
+++ b/datafusion-cli/src/main.rs
@@ -16,15 +16,19 @@
// under the License.
use clap::Parser;
+use datafusion::datasource::object_store::ObjectStoreRegistry;
use datafusion::error::{DataFusionError, Result};
use datafusion::execution::context::SessionConfig;
+use datafusion::execution::runtime_env::{RuntimeConfig, RuntimeEnv};
use datafusion::prelude::SessionContext;
+use datafusion_cli::object_storage::DatafusionCliObjectStoreProvider;
use datafusion_cli::{
exec, print_format::PrintFormat, print_options::PrintOptions,
DATAFUSION_CLI_VERSION,
};
use mimalloc::MiMalloc;
use std::env;
use std::path::Path;
+use std::sync::Arc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
@@ -98,7 +102,9 @@ pub async fn main() -> Result<()> {
session_config = session_config.with_batch_size(batch_size);
};
- let mut ctx = SessionContext::with_config(session_config.clone());
+ let runtime_env = create_runtime_env()?;
+ let mut ctx =
+ SessionContext::with_config_rt(session_config.clone(),
Arc::new(runtime_env));
let mut print_options = PrintOptions {
format: args.format,
@@ -120,6 +126,7 @@ pub async fn main() -> Result<()> {
files
}
};
+
if !files.is_empty() {
exec::exec_from_files(files, &mut ctx, &print_options).await;
Ok(())
@@ -134,6 +141,15 @@ pub async fn main() -> Result<()> {
}
}
+fn create_runtime_env() -> Result<RuntimeEnv> {
+ let object_store_provider = DatafusionCliObjectStoreProvider {};
+ let object_store_registry =
+
ObjectStoreRegistry::new_with_provider(Some(Arc::new(object_store_provider)));
+ let rn_config =
+
RuntimeConfig::new().with_object_store_registry(Arc::new(object_store_registry));
+ return RuntimeEnv::new(rn_config);
+}
+
fn is_valid_file(dir: &str) -> std::result::Result<(), String> {
if Path::new(dir).is_file() {
Ok(())
diff --git a/datafusion-cli/src/object_storage.rs
b/datafusion-cli/src/object_storage.rs
new file mode 100644
index 000000000..861830d81
--- /dev/null
+++ b/datafusion-cli/src/object_storage.rs
@@ -0,0 +1,150 @@
+// 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.
+
+use datafusion::error::Result;
+use std::{env, str::FromStr, sync::Arc};
+
+use datafusion::{datasource::object_store::ObjectStoreProvider,
error::DataFusionError};
+use object_store::{aws::AmazonS3Builder, gcp::GoogleCloudStorageBuilder};
+use url::Url;
+
+#[derive(Debug, PartialEq, Eq, clap::ArgEnum, Clone)]
+pub enum ObjectStoreScheme {
+ S3,
+ GCS,
+}
+
+impl FromStr for ObjectStoreScheme {
+ type Err = DataFusionError;
+
+ fn from_str(input: &str) -> Result<Self> {
+ match input {
+ "s3" => Ok(ObjectStoreScheme::S3),
+ "gcs" => Ok(ObjectStoreScheme::GCS),
+ _ => Err(DataFusionError::Execution(format!(
+ "Unsupported object store scheme {}",
+ input
+ ))),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct DatafusionCliObjectStoreProvider {}
+
+/// ObjectStoreProvider for S3 and GCS
+impl ObjectStoreProvider for DatafusionCliObjectStoreProvider {
+ fn get_by_url(&self, url: &Url) -> Result<Arc<dyn
object_store::ObjectStore>> {
+ ObjectStoreScheme::from_str(url.scheme()).map(|scheme| match scheme {
+ ObjectStoreScheme::S3 => build_s3_object_store(url),
+ ObjectStoreScheme::GCS => build_gcs_object_store(url),
+ })?
+ }
+}
+
+fn build_s3_object_store(url: &Url) -> Result<Arc<dyn
object_store::ObjectStore>> {
+ let host = get_host_name(url)?;
+ match AmazonS3Builder::from_env().with_bucket_name(host).build() {
+ Ok(s3) => Ok(Arc::new(s3)),
+ Err(err) => Err(DataFusionError::Execution(err.to_string())),
+ }
+}
+
+fn build_gcs_object_store(url: &Url) -> Result<Arc<dyn
object_store::ObjectStore>> {
+ let host = get_host_name(url)?;
+ let mut builder = GoogleCloudStorageBuilder::new().with_bucket_name(host);
+
+ if let Some(path) = env::var("GCP_SERVICE_ACCOUNT_PATH").ok() {
+ builder = builder.with_service_account_path(path);
+ }
+ match builder.build() {
+ Ok(gcs) => Ok(Arc::new(gcs)),
+ Err(err) => Err(DataFusionError::Execution(err.to_string())),
+ }
+}
+
+fn get_host_name(url: &Url) -> Result<&str> {
+ url.host_str().ok_or(DataFusionError::Execution(format!(
+ "Not able to parse hostname from url, {}",
+ url.as_str()
+ )))
+}
+
+#[cfg(test)]
+mod tests {
+ use std::{env, str::FromStr};
+
+ use datafusion::datasource::object_store::ObjectStoreProvider;
+ use url::Url;
+
+ use super::DatafusionCliObjectStoreProvider;
+
+ #[test]
+ fn s3_provider_no_host() {
+ let no_host_url = "s3:///";
+ let provider = DatafusionCliObjectStoreProvider {};
+ let err = provider
+ .get_by_url(&Url::from_str(no_host_url).unwrap())
+ .unwrap_err();
+ assert!(err
+ .to_string()
+ .contains("Not able to parse hostname from url"))
+ }
+
+ #[test]
+ fn gcs_provider_no_host() {
+ let no_host_url = "gcs:///";
+ let provider = DatafusionCliObjectStoreProvider {};
+ let err = provider
+ .get_by_url(&Url::from_str(no_host_url).unwrap())
+ .unwrap_err();
+ assert!(err
+ .to_string()
+ .contains("Not able to parse hostname from url"))
+ }
+
+ #[test]
+ fn unknown_object_store_type() {
+ let unknown = "unknown://bucket_name/path";
+ let provider = DatafusionCliObjectStoreProvider {};
+ let err = provider
+ .get_by_url(&Url::from_str(unknown).unwrap())
+ .unwrap_err();
+ assert!(err
+ .to_string()
+ .contains("Unsupported object store scheme unknown"))
+ }
+
+ #[test]
+ fn s3_build_error() {
+ let s3 = "s3://bucket_name/path";
+ let provider = DatafusionCliObjectStoreProvider {};
+ let err = provider
+ .get_by_url(&Url::from_str(s3).unwrap())
+ .unwrap_err();
+ assert!(err.to_string().contains("Generic S3 error: Missing region"));
+ }
+
+ #[test]
+ fn s3_success() {
+ let s3 = "s3://bucket_name/path";
+ env::set_var("AWS_DEFAULT_REGION", "us-east-1");
+ let provider = DatafusionCliObjectStoreProvider {};
+ assert!(provider.get_by_url(&Url::from_str(s3).unwrap()).is_ok());
+ env::remove_var("AWS_DEFAULT_REGION");
+ }
+}