use std::fs::File;
use std::io::BufReader;
use std::rc::Rc;
use super::super::arrow::array::*;
use super::super::arrow::datatypes::*;
use super::super::exec::*;
use super::super::rel::*;
extern crate csv;
use super::super::csv::StringRecord;
pub struct CsvRelation {
schema: Schema,
iter: Box<Iterator<Item = Result<StringRecord, csv::Error>>>,
}
impl CsvRelation {
pub fn open(file: File, schema: Schema) -> Result<Self, ExecutionError> {
let buf_reader = BufReader::with_capacity(8 * 1024 * 1024, file);
let csv_reader = csv::Reader::from_reader(buf_reader);
let record_iter = csv_reader.into_records();
let iter: Box<Iterator<Item = Result<StringRecord, csv::Error>>> = Box::new(record_iter);
Ok(CsvRelation { schema, iter })
}
}
impl SimpleRelation for CsvRelation {
fn scan<'a>(
&'a mut self,
_ctx: &'a ExecutionContext,
) -> Box<Iterator<Item = Result<Box<Batch>, ExecutionError>> + 'a> {
let batch_iter: Box<Iterator<Item = Result<Box<Batch>, ExecutionError>>> =
Box::new(CsvIterator {
schema: &self.schema,
iter: &mut self.iter,
});
batch_iter
}
fn schema<'a>(&'a self) -> &'a Schema {
&self.schema
}
}
struct CsvIterator<'a> {
schema: &'a Schema,
iter: &'a mut Box<Iterator<Item = Result<StringRecord, csv::Error>>>,
}
impl<'a> CsvIterator<'a> {
fn parse_record(&self, r: &StringRecord) -> Vec<ScalarValue> {
assert_eq!(self.schema.columns.len(), r.len());
let values = self.schema
.columns
.iter()
.zip(r.into_iter())
.map(|(c, s)| match c.data_type {
DataType::Boolean => ScalarValue::Boolean(s.parse::<bool>().unwrap()),
DataType::Float32 => ScalarValue::Float32(s.parse::<f32>().unwrap()),
DataType::Float64 => ScalarValue::Float64(s.parse::<f64>().unwrap()),
DataType::Int32 => ScalarValue::Int32(s.parse::<i32>().unwrap()),
DataType::Int64 => ScalarValue::Int64(s.parse::<i64>().unwrap()),
DataType::Utf8 => ScalarValue::Utf8(s.to_string()),
_ => panic!("csv unsupported type"),
})
.collect();
values
}
}
impl<'a> Iterator for CsvIterator<'a> {
type Item = Result<Box<Batch>, ExecutionError>;
fn next(&mut self) -> Option<Self::Item> {
let max_size = 1000;
let mut rows: Vec<Vec<ScalarValue>> = Vec::with_capacity(max_size);
for _ in 0..max_size {
match self.iter.next() {
Some(r) => rows.push(self.parse_record(&r.unwrap())),
None => break,
}
}
if rows.is_empty() {
return None;
}
let mut columns: Vec<Rc<Value>> = Vec::with_capacity(self.schema.columns.len());
for i in 0..self.schema.columns.len() {
let name = format!("col{}", i + 1);
let field = Field::new(&name, self.schema.columns[i].data_type.clone(), false);
let values: Array = match self.schema.columns[i].data_type {
DataType::Boolean => Array::from(
rows.iter()
.map(|row| match &row[i] {
&ScalarValue::Boolean(v) => v,
_ => panic!(),
})
.collect::<Vec<bool>>(),
),
DataType::Float32 => Array::from(
rows.iter()
.map(|row| match &row[i] {
&ScalarValue::Float32(v) => v,
_ => panic!(),
})
.collect::<Vec<f32>>(),
),
DataType::Float64 => Array::from(
rows.iter()
.map(|row| match &row[i] {
&ScalarValue::Float64(v) => v,
_ => panic!(),
})
.collect::<Vec<f64>>(),
),
DataType::Int32 => Array::from(
rows.iter()
.map(|row| match &row[i] {
&ScalarValue::Int32(v) => v,
_ => panic!(),
})
.collect::<Vec<i32>>(),
),
DataType::Int64 => Array::from(
rows.iter()
.map(|row| match &row[i] {
&ScalarValue::Int64(v) => v,
_ => panic!(),
})
.collect::<Vec<i64>>(),
),
DataType::Utf8 => {
Array::from(
rows.iter()
.map(|row| match &row[i] {
&ScalarValue::Utf8(ref v) => v.clone(),
_ => panic!(),
})
.collect::<Vec<String>>(),
)
}
_ => unimplemented!(),
};
columns.push(Rc::new(Value::Column(Rc::new(field), Rc::new(values))));
}
Some(Ok(Box::new(ColumnBatch {
row_count: rows.len(),
columns,
})))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_batch() {
let schema = Schema::new(vec![
Field::new("city", DataType::Utf8, false),
Field::new("lat", DataType::Float64, false),
Field::new("lng", DataType::Float64, false),
]);
let file = File::open("test/data/uk_cities.csv").unwrap();
let buf_reader = BufReader::with_capacity(8 * 1024 * 1024, file);
let csv_reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_reader(buf_reader);
let record_iter = csv_reader.into_records();
let mut foo: Box<Iterator<Item = Result<StringRecord, csv::Error>>> = Box::new(record_iter);
let mut it = CsvIterator {
schema: &schema,
iter: &mut foo,
};
let batch: Box<Batch> = it.next().unwrap().unwrap();
assert_eq!(3, batch.col_count());
assert_eq!(37, batch.row_count());
let row = batch.row_slice(0);
assert_eq!(
vec![
ScalarValue::Utf8("Elgin, Scotland, the UK".to_string()),
ScalarValue::Float64(57.653484),
ScalarValue::Float64(-3.335724),
],
row
);
let _names = batch.column(0);
}
}