1use std::sync::Arc;
5
6use datafusion::arrow::array::{
7 Array, BooleanArray, Float32Array, Float64Array, Int16Array, Int32Array, Int64Array, Int8Array, LargeListArray,
8 LargeStringArray, ListArray, StringArray, StringViewArray, StructArray, UInt16Array, UInt32Array, UInt64Array,
9 UInt8Array,
10};
11use datafusion::arrow::datatypes::{DataType, Field, Schema};
12use datafusion::arrow::record_batch::RecordBatch;
13use fv_value::Value;
14
15use crate::row::Row;
16
17pub fn infer_type(rows: &[Row], col: &str) -> DataType {
20 infer(rows, col)
21}
22
23fn infer(rows: &[Row], col: &str) -> DataType {
24 let (mut num, mut boolean, mut string, mut other) = (false, false, false, false);
25 for r in rows {
26 match r.get(col) {
27 Value::Null => {}
28 Value::Num(_) => num = true,
29 Value::Bool(_) => boolean = true,
30 Value::Str(_) => string = true,
31 _ => other = true,
32 }
33 }
34 if other || string {
35 DataType::Utf8
36 } else if boolean && !num {
37 DataType::Boolean
38 } else {
39 DataType::Float64
40 }
41}
42
43fn scalar_to_string(v: &Value) -> Option<String> {
44 match v {
45 Value::Null => None,
46 Value::Str(s) => Some(s.clone()),
47 Value::Bool(b) => Some(if *b { "true" } else { "false" }.to_string()),
48 Value::Num(n) => Some(if n.fract() == 0.0 && n.is_finite() {
49 format!("{}", *n as i64)
50 } else {
51 format!("{n}")
52 }),
53 other => Some(format!("{other:?}")),
55 }
56}
57
58pub fn rows_to_batch(rows: &[Row], columns: &[String]) -> Result<RecordBatch, String> {
60 let mut fields = Vec::with_capacity(columns.len());
61 let mut arrays: Vec<Arc<dyn Array>> = Vec::with_capacity(columns.len());
62 for col in columns {
63 let dt = infer(rows, col);
64 fields.push(Field::new(col, dt.clone(), true));
65 let arr: Arc<dyn Array> = match dt {
66 DataType::Float64 => Arc::new(Float64Array::from(
67 rows.iter().map(|r| r.get(col).as_num()).collect::<Vec<_>>(),
68 )),
69 DataType::Boolean => Arc::new(BooleanArray::from(
70 rows.iter()
71 .map(|r| match r.get(col) {
72 Value::Bool(b) => Some(b),
73 _ => None,
74 })
75 .collect::<Vec<_>>(),
76 )),
77 _ => Arc::new(StringArray::from(
78 rows.iter().map(|r| scalar_to_string(&r.get(col))).collect::<Vec<_>>(),
79 )),
80 };
81 arrays.push(arr);
82 }
83 let schema = Arc::new(Schema::new(fields));
84 RecordBatch::try_new(schema, arrays).map_err(|e| e.to_string())
85}
86
87pub fn batch_to_rows(batch: &RecordBatch) -> Vec<Row> {
89 let schema = batch.schema();
90 let mut rows = vec![Row::new(); batch.num_rows()];
91 for (c, field) in schema.fields().iter().enumerate() {
92 let col = batch.column(c);
93 let name = field.name();
94 for (i, row) in rows.iter_mut().enumerate() {
95 let v = read_cell(col, i);
96 row.0.push((name.clone(), v));
97 }
98 }
99 rows
100}
101
102pub fn array_value(col: &Arc<dyn Array>, i: usize) -> Value {
104 read_cell(col, i)
105}
106
107fn read_cell(col: &Arc<dyn Array>, i: usize) -> Value {
108 if col.is_null(i) {
109 return Value::Null;
110 }
111 match col.data_type() {
114 DataType::Float64 => Value::Num(col.as_any().downcast_ref::<Float64Array>().unwrap().value(i)),
115 DataType::Float32 => Value::Num(col.as_any().downcast_ref::<Float32Array>().unwrap().value(i) as f64),
116 DataType::Int64 => Value::Num(col.as_any().downcast_ref::<Int64Array>().unwrap().value(i) as f64),
117 DataType::Int32 => Value::Num(col.as_any().downcast_ref::<Int32Array>().unwrap().value(i) as f64),
118 DataType::Int16 => Value::Num(col.as_any().downcast_ref::<Int16Array>().unwrap().value(i) as f64),
119 DataType::Int8 => Value::Num(col.as_any().downcast_ref::<Int8Array>().unwrap().value(i) as f64),
120 DataType::UInt64 => Value::Num(col.as_any().downcast_ref::<UInt64Array>().unwrap().value(i) as f64),
121 DataType::UInt32 => Value::Num(col.as_any().downcast_ref::<UInt32Array>().unwrap().value(i) as f64),
122 DataType::UInt16 => Value::Num(col.as_any().downcast_ref::<UInt16Array>().unwrap().value(i) as f64),
123 DataType::UInt8 => Value::Num(col.as_any().downcast_ref::<UInt8Array>().unwrap().value(i) as f64),
124 DataType::Boolean => Value::Bool(col.as_any().downcast_ref::<BooleanArray>().unwrap().value(i)),
125 DataType::Utf8 => Value::Str(col.as_any().downcast_ref::<StringArray>().unwrap().value(i).to_string()),
126 DataType::LargeUtf8 => Value::Str(
127 col.as_any()
128 .downcast_ref::<LargeStringArray>()
129 .unwrap()
130 .value(i)
131 .to_string(),
132 ),
133 DataType::Utf8View => Value::Str(
137 col.as_any()
138 .downcast_ref::<StringViewArray>()
139 .unwrap()
140 .value(i)
141 .to_string(),
142 ),
143 DataType::BinaryView => Value::Null, DataType::Struct(fields) => {
148 let arr = col.as_any().downcast_ref::<StructArray>().unwrap();
149 Value::Obj(
150 fields
151 .iter()
152 .enumerate()
153 .map(|(c, f)| (f.name().clone(), read_cell(arr.column(c), i)))
154 .collect(),
155 )
156 }
157 DataType::List(_) => read_list(&col.as_any().downcast_ref::<ListArray>().unwrap().value(i)),
158 DataType::LargeList(_) => read_list(&col.as_any().downcast_ref::<LargeListArray>().unwrap().value(i)),
159 _ => Value::Null,
160 }
161}
162
163fn read_list(sub: &Arc<dyn Array>) -> Value {
165 Value::List((0..sub.len()).map(|j| read_cell(sub, j)).collect())
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use datafusion::arrow::array::ArrayRef;
172 use datafusion::arrow::datatypes::{Field, Float64Type};
173
174 fn num(n: f64) -> Value {
175 Value::Num(n)
176 }
177
178 #[test]
179 fn decodes_a_list_column_to_value_list() {
180 let list: ArrayRef = Arc::new(ListArray::from_iter_primitive::<Float64Type, _, _>(vec![
181 Some(vec![Some(1.0), Some(2.0)]),
182 Some(vec![Some(3.0)]),
183 ]));
184 assert_eq!(array_value(&list, 0), Value::List(vec![num(1.0), num(2.0)]));
185 assert_eq!(array_value(&list, 1), Value::List(vec![num(3.0)]));
186 }
187
188 #[test]
189 fn decodes_a_struct_column_to_value_obj() {
190 let ty: ArrayRef = Arc::new(StringArray::from(vec!["Polygon"]));
191 let coords: ArrayRef = Arc::new(ListArray::from_iter_primitive::<Float64Type, _, _>(vec![Some(vec![
192 Some(1.0),
193 Some(2.0),
194 ])]));
195 let s: ArrayRef = Arc::new(StructArray::from(vec![
196 (Arc::new(Field::new("type", DataType::Utf8, true)), ty),
197 (
198 Arc::new(Field::new("coordinates", coords.data_type().clone(), true)),
199 coords,
200 ),
201 ]));
202 let got = array_value(&s, 0);
203 let want = Value::Obj(
204 [
205 ("type".to_string(), Value::Str("Polygon".to_string())),
206 ("coordinates".to_string(), Value::List(vec![num(1.0), num(2.0)])),
207 ]
208 .into_iter()
209 .collect(),
210 );
211 assert_eq!(got, want);
212 }
213
214 #[test]
215 fn decodes_nested_lists_of_lists() {
216 let inner = ListArray::from_iter_primitive::<Float64Type, _, _>(vec![
218 Some(vec![Some(1.0), Some(2.0)]),
219 Some(vec![Some(3.0), Some(4.0)]),
220 ]);
221 use datafusion::arrow::buffer::OffsetBuffer;
223 let field = Arc::new(Field::new("item", inner.data_type().clone(), true));
224 let outer: ArrayRef = Arc::new(ListArray::new(
225 field,
226 OffsetBuffer::new(vec![0, 2].into()),
227 Arc::new(inner),
228 None,
229 ));
230 assert_eq!(
231 array_value(&outer, 0),
232 Value::List(vec![
233 Value::List(vec![num(1.0), num(2.0)]),
234 Value::List(vec![num(3.0), num(4.0)]),
235 ])
236 );
237 }
238
239 #[test]
240 fn a_null_nested_cell_reads_null() {
241 let list: ArrayRef = Arc::new(ListArray::from_iter_primitive::<Float64Type, _, _>(vec![
242 Some(vec![Some(1.0)]),
243 None,
244 ]));
245 assert_eq!(array_value(&list, 1), Value::Null);
246 }
247}