use crate::blocking::blocking;
use crate::query::SqlQueryBuilder;
use crate::Result;
use apache_spark_connect as upstream;
use apache_spark_connect::udf::CommonInlineUserDefinedFunctionExpression;
use apache_spark_connect::{CommonInlineUserDefinedDataSourceExpression, ResourceProfile};
use std::fmt;
use std::ops::Deref;
#[derive(Clone, Debug, Default)]
pub struct SparkSessionBuilder {
remote: Option<String>,
}
impl SparkSessionBuilder {
pub fn remote(mut self, url: &str) -> Self {
self.remote = Some(url.to_string());
self
}
pub async fn get_or_create(&self) -> Result<SparkSession> {
let remote = self.remote.clone();
let inner = blocking(move || {
let builder = upstream::SparkSession::builder();
match remote {
Some(url) => builder.remote(&url),
None => builder,
}
.get_or_create()
})
.await?;
Ok(SparkSession { inner })
}
}
#[derive(Clone)]
pub struct SparkSession {
inner: upstream::SparkSession,
}
impl SparkSession {
pub fn builder() -> SparkSessionBuilder {
SparkSessionBuilder::default()
}
pub fn query(&self, query: &str) -> SqlQueryBuilder<'_> {
SqlQueryBuilder::new(self, query)
}
pub async fn run<F, T>(&self, f: F) -> Result<T>
where
F: FnOnce(upstream::SparkSession) -> Result<T> + Send + 'static,
T: Send + 'static,
{
let session = self.inner.clone();
blocking(move || f(session)).await
}
pub async fn version(&self) -> Result<String> {
self.run(|spark| spark.version()).await
}
pub async fn interrupt_all(&self) -> Result<Vec<String>> {
self.run(|spark| spark.interrupt_all()).await
}
pub async fn interrupt_tag(&self, tag: &str) -> Result<Vec<String>> {
let tag = tag.to_owned();
self.run(move |spark| spark.interrupt_tag(&tag)).await
}
pub async fn interrupt_operation(&self, operation_id: &str) -> Result<Vec<String>> {
let operation_id = operation_id.to_owned();
self.run(move |spark| spark.interrupt_operation(&operation_id)).await
}
pub async fn add_artifact(&self, path: &str) -> Result<()> {
let path = path.to_owned();
self.run(move |spark| spark.add_artifact(&path)).await
}
pub async fn add_artifacts(&self, paths: &[&str]) -> Result<()> {
let paths: Vec<String> = paths.iter().map(|path| path.to_string()).collect();
self.run(move |spark| spark.add_artifacts(&paths.iter().map(String::as_str).collect::<Vec<_>>()))
.await
}
pub async fn copy_from_local_to_fs(&self, local_path: &str, dest_path: &str) -> Result<()> {
let (local_path, dest_path) = (local_path.to_owned(), dest_path.to_owned());
self.run(move |spark| spark.copy_from_local_to_fs(&local_path, &dest_path)).await
}
pub async fn register_function(&self, udf: CommonInlineUserDefinedFunctionExpression) -> Result<()> {
self.run(move |spark| spark.register_function(udf)).await
}
pub async fn register_java_function(
&self,
name: &str,
java_class_name: &str,
return_type_ddl: Option<&str>,
aggregate: bool,
) -> Result<()> {
let (name, java_class_name) = (name.to_owned(), java_class_name.to_owned());
let return_type_ddl = return_type_ddl.map(str::to_owned);
self.run(move |spark| {
spark.register_java_function(&name, &java_class_name, return_type_ddl.as_deref(), aggregate)
})
.await
}
pub async fn register_data_source(
&self,
data_source: CommonInlineUserDefinedDataSourceExpression,
) -> Result<()> {
self.run(move |spark| spark.register_data_source(data_source)).await
}
pub async fn build_resource_profile(&self, profile: &ResourceProfile) -> Result<i32> {
let profile = profile.clone();
self.run(move |spark| spark.build_resource_profile(&profile)).await
}
pub async fn stop(&self) -> Result<()> {
self.run(|spark| spark.stop()).await
}
pub fn new_session(&self) -> SparkSession {
SparkSession { inner: self.inner.new_session() }
}
pub fn clone_session(&self) -> SparkSession {
SparkSession { inner: self.inner.clone_session() }
}
}
impl Deref for SparkSession {
type Target = upstream::SparkSession;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl fmt::Debug for SparkSession {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SparkSession")
.field("session_id", &self.session_id())
.finish()
}
}