use rudb_common::{Error, Field, Result, Value};
use rudb_pipeline::{Morsel, Progress, Source};
use rudb_plan::{Plan, Slice};
use rudb_vector::{Chunk, VECTOR_SIZE, Vector};
use crate::schema::Schema;
use crate::source::{Handout, position};
#[derive(Debug)]
pub(crate) struct Metadata {
schema: Schema,
chunks: Vec<Chunk>,
handout: Handout,
}
impl Metadata {
pub(crate) fn new(
name: &str,
all: &[Field],
rows: &[Vec<Value>],
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Self> {
let wanted = plan.field_list(columns).to_vec();
let mut positions = Vec::with_capacity(wanted.len());
for field in &wanted {
let position =
all.iter().position(|held| held.name == field.name).ok_or_else(|| {
Error::internal(format!("{name}() has no column named {}", field.name))
})?;
positions.push(position);
}
if let Some(row) = rows.iter().find(|row| row.len() != all.len()) {
return Err(Error::internal(format!(
"{name}() has {} columns and built a row of {}",
all.len(),
row.len()
)));
}
let schema = Schema::numbered(wanted, index);
let types = schema.types();
let mut chunks = Vec::new();
let mut start = 0;
while start < rows.len() {
let end = (start + VECTOR_SIZE).min(rows.len());
let mut built = Vec::with_capacity(types.len());
for (wanted, ty) in positions.iter().zip(&types) {
let column: Vec<Value> =
rows[start..end].iter().map(|row| row[*wanted].clone()).collect();
built.push(Vector::from_values(ty.clone(), &column)?);
}
chunks.push(Chunk::with_rows(built, end - start)?);
start = end;
}
let handout = Handout::new(chunks.len());
Ok(Self { schema, chunks, handout })
}
pub(crate) fn schema(&self) -> &Schema {
&self.schema
}
}
impl Source for Metadata {
fn morsel(&self) -> Option<Morsel> {
self.handout.take()
}
fn morsels(&self, _threads: usize) -> Option<usize> {
Some(self.handout.total())
}
fn read(&self, morsel: &mut Morsel, out: &mut Chunk) -> Result<Progress> {
*out = match self.chunks.get(position(morsel)) {
Some(chunk) => chunk.clone(),
None => Chunk::empty(&self.schema.types()),
};
morsel.advance(1);
Ok(Progress::Done)
}
}
pub(crate) fn text(value: &str) -> Value {
Value::Varchar(value.to_string())
}