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>>;
fn read_stream(&self, _ctx: &CopyFromReadContext) -> Result<Option<Box<dyn TableProducer>>> {
Ok(None)
}
}
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(),
started: false,
stream: None,
batches: Vec::new().into_iter(),
}))
}
}
struct CopyFromProducer {
inner: Arc<dyn CopyFromFunction>,
params: ProcessParams,
started: bool,
stream: Option<Box<dyn TableProducer>>,
batches: std::vec::IntoIter<RecordBatch>,
}
impl CopyFromProducer {
fn start(&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,
};
self.started = true;
match self.inner.read_stream(&ctx)? {
Some(producer) => self.stream = Some(producer),
None => self.batches = self.inner.read(&ctx, out)?.into_iter(),
}
Ok(())
}
}
impl TableProducer for CopyFromProducer {
fn next_batch(&mut self, out: &mut OutputCollector) -> Result<Option<RecordBatch>> {
if !self.started {
self.start(out)?;
}
match self.stream.as_mut() {
Some(producer) => producer.next_batch(out),
None => Ok(self.batches.next()),
}
}
}