use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use async_trait::async_trait;
use datafusion::catalog::{SchemaProvider, TableProvider};
use datafusion::error::{DataFusionError, Result as DataFusionResult};
use crate::client::OpenSearchClient;
use crate::table::OpenSearchTableFactory;
use crate::Result;
#[derive(Debug)]
pub struct OpenSearchSchemaProvider {
factory: OpenSearchTableFactory,
names: RwLock<Vec<String>>,
tables: RwLock<HashMap<String, Arc<dyn TableProvider>>>,
}
impl OpenSearchSchemaProvider {
pub async fn connect(client: OpenSearchClient) -> Result<Self> {
let names = client.indices().await?;
Ok(Self {
factory: OpenSearchTableFactory::new(client),
names: RwLock::new(names),
tables: RwLock::new(HashMap::new()),
})
}
pub async fn refresh(&self) -> Result<()> {
let names = self.factory.client().indices().await?;
self.tables.write().unwrap().retain(|k, _| names.contains(k));
*self.names.write().unwrap() = names;
Ok(())
}
pub fn client(&self) -> &OpenSearchClient {
self.factory.client()
}
}
#[async_trait]
impl SchemaProvider for OpenSearchSchemaProvider {
fn table_names(&self) -> Vec<String> {
self.names.read().unwrap().clone()
}
async fn table(&self, name: &str) -> DataFusionResult<Option<Arc<dyn TableProvider>>> {
if !self.table_exist(name) {
return Ok(None);
}
if let Some(t) = self.tables.read().unwrap().get(name) {
return Ok(Some(t.clone()));
}
let table = self
.factory
.table_provider(name)
.await
.map_err(|e| DataFusionError::External(Box::new(e)))?;
self.tables.write().unwrap().insert(name.to_string(), table.clone());
Ok(Some(table))
}
fn table_exist(&self, name: &str) -> bool {
self.names.read().unwrap().iter().any(|n| n == name)
}
}