use std::sync::Arc;
use arrow_array::RecordBatch;
use arrow_schema::SchemaRef;
use vgi_rpc::{OutputCollector, Result, RpcError};
use crate::arguments::Arguments;
use crate::function::{ArgSpec, BindParams, BindResponse, FunctionMetadata, ProcessParams};
use crate::table_function::{TableFunction, TableProducer};
pub struct CopyFromReadContext<'a> {
pub path: &'a str,
pub options: &'a Arguments,
pub expected_schema: &'a SchemaRef,
pub params: &'a ProcessParams,
}
pub trait CopyFromFunction: Send + Sync {
fn format(&self) -> &str;
fn handler_name(&self) -> &str;
fn comment(&self) -> Option<String> {
None
}
fn metadata(&self) -> FunctionMetadata {
FunctionMetadata::default()
}
fn argument_specs(&self) -> Vec<ArgSpec>;
fn secret_lookups(&self, _params: &BindParams) -> Vec<crate::secrets::SecretLookup> {
Vec::new()
}
fn read(
&self,
ctx: &CopyFromReadContext,
out: &mut OutputCollector,
) -> Result<Vec<RecordBatch>>;
}
pub struct CopyFromTable(pub Arc<dyn CopyFromFunction>);
impl CopyFromTable {
fn missing_ctx_error(&self) -> RpcError {
RpcError::value_error(format!(
"{} is a COPY FROM format reader; invoke it via \
COPY <table> FROM '<path>' (FORMAT {}), not as a table function.",
self.0.handler_name(),
self.0.format()
))
}
}
impl TableFunction for CopyFromTable {
fn name(&self) -> &str {
self.0.handler_name()
}
fn metadata(&self) -> FunctionMetadata {
self.0.metadata()
}
fn argument_specs(&self) -> Vec<ArgSpec> {
self.0.argument_specs()
}
fn secret_lookups(&self, params: &BindParams) -> Vec<crate::secrets::SecretLookup> {
self.0.secret_lookups(params)
}
fn on_bind(&self, params: &BindParams) -> Result<BindResponse> {
let cf = params
.copy_from
.as_ref()
.ok_or_else(|| self.missing_ctx_error())?;
let output_schema = crate::ipc::read_schema(&cf.expected_schema.0)?;
Ok(BindResponse {
output_schema,
opaque_data: Vec::new(),
})
}
fn producer(&self, params: &ProcessParams) -> Result<Box<dyn TableProducer>> {
if params.copy_from.is_none() {
return Err(self.missing_ctx_error());
}
Ok(Box::new(CopyFromProducer {
inner: self.0.clone(),
params: params.clone(),
done: false,
batches: Vec::new().into_iter(),
}))
}
}
struct CopyFromProducer {
inner: Arc<dyn CopyFromFunction>,
params: ProcessParams,
done: bool,
batches: std::vec::IntoIter<RecordBatch>,
}
impl CopyFromProducer {
fn fill(&mut self, out: &mut OutputCollector) -> Result<()> {
let cf =
self.params.copy_from.clone().ok_or_else(|| {
RpcError::value_error("COPY FROM context missing at process time")
})?;
let expected_schema = self.params.output_schema.clone();
let ctx = CopyFromReadContext {
path: &cf.file_path,
options: &self.params.arguments,
expected_schema: &expected_schema,
params: &self.params,
};
let batches = self.inner.read(&ctx, out)?;
self.batches = batches.into_iter();
self.done = true;
Ok(())
}
}
impl TableProducer for CopyFromProducer {
fn next_batch(&mut self, out: &mut OutputCollector) -> Result<Option<RecordBatch>> {
if !self.done {
self.fill(out)?;
}
Ok(self.batches.next())
}
}