1use arrow::array::{
4 ArrayRef, BooleanBuilder, FixedSizeListBuilder, Float32Builder, Float64Array, Float64Builder,
5 Int64Array, Int64Builder, StringBuilder,
6};
7use arrow::buffer::{BooleanBuffer, Buffer, NullBuffer};
8use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
9use mongreldb_core::columnar::NativeColumn;
10use mongreldb_core::memtable::Value;
11use mongreldb_core::schema::{Schema as MongrelSchema, TypeId};
12use std::sync::Arc;
13
14use crate::error::{MongrelQueryError, Result};
15
16fn bit_set(validity: &[u8], i: usize) -> bool {
17 (validity.get(i / 8).copied().unwrap_or(0) >> (i % 8)) & 1 == 1
18}
19
20fn all_bits_set(validity: &[u8], n: usize) -> bool {
22 if n == 0 {
23 return true;
24 }
25 let full = n / 8;
26 if !validity[..full].iter().all(|&b| b == 0xFF) {
27 return false;
28 }
29 if n % 8 != 0 {
30 let mask = (1u8 << (n % 8)) - 1;
31 (validity.get(full).copied().unwrap_or(0) & mask) == mask
32 } else {
33 true
34 }
35}
36
37pub fn native_to_array(ty: TypeId, col: &NativeColumn) -> Result<ArrayRef> {
41 Ok(match (ty, col) {
42 (TypeId::Int64 | TypeId::TimestampNanos, NativeColumn::Int64 { data, validity }) => {
43 if all_bits_set(validity, data.len()) {
44 Arc::new(Int64Array::new(data.clone().into(), None))
45 } else {
46 let mut b = Int64Builder::with_capacity(data.len());
47 for (i, v) in data.iter().enumerate() {
48 if bit_set(validity, i) {
49 b.append_value(*v);
50 } else {
51 b.append_null();
52 }
53 }
54 Arc::new(b.finish())
55 }
56 }
57 (TypeId::Float64, NativeColumn::Float64 { data, validity }) => {
58 if all_bits_set(validity, data.len()) {
59 Arc::new(Float64Array::new(data.clone().into(), None))
60 } else {
61 let mut b = Float64Builder::with_capacity(data.len());
62 for (i, v) in data.iter().enumerate() {
63 if bit_set(validity, i) {
64 b.append_value(*v);
65 } else {
66 b.append_null();
67 }
68 }
69 Arc::new(b.finish())
70 }
71 }
72 (TypeId::Bool, NativeColumn::Bool { data, validity }) => {
73 let mut b = BooleanBuilder::with_capacity(data.len());
74 for (i, v) in data.iter().enumerate() {
75 if bit_set(validity, i) {
76 b.append_value(*v != 0);
77 } else {
78 b.append_null();
79 }
80 }
81 Arc::new(b.finish())
82 }
83 (
84 TypeId::Bytes,
85 NativeColumn::Bytes {
86 offsets,
87 values,
88 validity,
89 },
90 ) => {
91 let n = offsets.len().saturating_sub(1);
92 let mut b = StringBuilder::with_capacity(n, values.len());
93 for i in 0..n {
94 if bit_set(validity, i) {
95 let lo = offsets[i] as usize;
96 let hi = offsets[i + 1] as usize;
97 b.append_value(String::from_utf8_lossy(&values[lo..hi]));
98 } else {
99 b.append_null();
100 }
101 }
102 Arc::new(b.finish())
103 }
104 _ => {
105 return Err(MongrelQueryError::Arrow(format!(
106 "native_to_array: unsupported (ty={ty:?})"
107 )))
108 }
109 })
110}
111
112pub fn native_to_array_owned(ty: TypeId, col: NativeColumn) -> Result<ArrayRef> {
118 Ok(match (ty, col) {
119 (TypeId::Int64 | TypeId::TimestampNanos, NativeColumn::Int64 { data, validity }) => {
120 let n = data.len();
121 Arc::new(Int64Array::new(data.into(), owned_nulls(validity, n)))
122 }
123 (TypeId::Float64, NativeColumn::Float64 { data, validity }) => {
124 let n = data.len();
125 Arc::new(Float64Array::new(data.into(), owned_nulls(validity, n)))
126 }
127 (ty, col) => native_to_array(ty, &col)?,
129 })
130}
131
132fn owned_nulls(validity: Vec<u8>, n: usize) -> Option<NullBuffer> {
137 if all_bits_set(&validity, n) {
138 None
139 } else {
140 let buffer: Buffer = validity.into();
141 Some(NullBuffer::new(BooleanBuffer::new(buffer, 0, n)))
142 }
143}
144
145pub fn native_columns_to_batch(
147 columns: &[(u16, NativeColumn)],
148 schema: &MongrelSchema,
149) -> Result<arrow::record_batch::RecordBatch> {
150 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(schema.columns.len());
151 for cdef in &schema.columns {
152 let col = columns
153 .iter()
154 .find(|(id, _)| *id == cdef.id)
155 .map(|(_, c)| c)
156 .ok_or_else(|| MongrelQueryError::Arrow(format!("missing column {}", cdef.id)))?;
157 arrays.push(native_to_array(cdef.ty, col)?);
158 }
159 let fields: Vec<Field> = schema
160 .columns
161 .iter()
162 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
163 .collect();
164 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(fields)), arrays)
165 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
166}
167
168pub fn arrow_schema(schema: &MongrelSchema) -> Result<SchemaRef> {
171 let fields: Result<Vec<Field>> = schema
172 .columns
173 .iter()
174 .map(|c| arrow_data_type(&c.ty).map(|dt| Field::new(&c.name, dt, true)))
175 .collect();
176 Ok(Arc::new(Schema::new(fields?)) as SchemaRef)
177}
178
179pub(crate) fn arrow_data_type(ty: &TypeId) -> Result<DataType> {
180 Ok(match ty {
181 TypeId::Bool => DataType::Boolean,
182 TypeId::Int8 => DataType::Int8,
183 TypeId::Int16 => DataType::Int16,
184 TypeId::Int32 | TypeId::Date32 => DataType::Int32,
185 TypeId::Int64 | TypeId::TimestampNanos => DataType::Int64,
186 TypeId::UInt8 => DataType::UInt8,
187 TypeId::UInt16 => DataType::UInt16,
188 TypeId::UInt32 => DataType::UInt32,
189 TypeId::UInt64 => DataType::UInt64,
190 TypeId::Float32 => DataType::Float32,
191 TypeId::Float64 => DataType::Float64,
192 TypeId::Bytes => DataType::Utf8,
193 TypeId::Embedding { dim } => DataType::FixedSizeList(
194 Arc::new(Field::new("item", DataType::Float32, true)),
195 *dim as i32,
196 ),
197 })
198}
199
200pub fn rows_to_batch(
202 rows: &[mongreldb_core::Row],
203 schema: &MongrelSchema,
204) -> Result<arrow::record_batch::RecordBatch> {
205 let fields: Vec<(u16, TypeId)> = schema.columns.iter().map(|c| (c.id, c.ty)).collect();
206 let arrays: Vec<ArrayRef> = fields
207 .iter()
208 .map(|(col_id, ty)| {
209 let vals: Vec<Value> = rows
210 .iter()
211 .map(|r| r.columns.get(col_id).cloned().unwrap_or(Value::Null))
212 .collect();
213 build_array(*ty, &vals)
214 })
215 .collect::<Result<_>>()?;
216 let arrow_fields: Vec<Field> = schema
217 .columns
218 .iter()
219 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
220 .collect();
221 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(arrow_fields)), arrays)
222 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
223}
224
225pub fn build_array(ty: TypeId, values: &[Value]) -> Result<ArrayRef> {
227 Ok(match ty {
228 TypeId::Int64 | TypeId::TimestampNanos => {
229 let mut b = Int64Builder::new();
230 for v in values {
231 match v {
232 Value::Int64(x) => b.append_value(*x),
233 _ => b.append_null(),
234 }
235 }
236 Arc::new(b.finish())
237 }
238 TypeId::Float64 => {
239 let mut b = Float64Builder::new();
240 for v in values {
241 match v {
242 Value::Float64(x) => b.append_value(*x),
243 _ => b.append_null(),
244 }
245 }
246 Arc::new(b.finish())
247 }
248 TypeId::Float32 => {
249 let mut b = arrow::array::Float32Builder::new();
250 for v in values {
251 match v {
252 Value::Float64(x) => b.append_value(*x as f32),
253 _ => b.append_null(),
254 }
255 }
256 Arc::new(b.finish())
257 }
258 TypeId::Bool => {
259 let mut b = BooleanBuilder::new();
260 for v in values {
261 match v {
262 Value::Bool(x) => b.append_value(*x),
263 _ => b.append_null(),
264 }
265 }
266 Arc::new(b.finish())
267 }
268 TypeId::Int32 | TypeId::Date32 => {
269 let mut b = arrow::array::Int32Builder::new();
270 for v in values {
271 match v {
272 Value::Int64(x) => b.append_value(*x as i32),
273 _ => b.append_null(),
274 }
275 }
276 Arc::new(b.finish())
277 }
278 TypeId::Bytes => {
279 let mut b = StringBuilder::new();
280 for v in values {
281 match v {
282 Value::Bytes(x) => b.append_value(String::from_utf8_lossy(x)),
283 _ => b.append_null(),
284 }
285 }
286 Arc::new(b.finish())
287 }
288 TypeId::Embedding { dim } => {
289 let fbb = Float32Builder::new();
290 let mut b = FixedSizeListBuilder::new(fbb, dim as i32);
291 for v in values {
292 match v {
293 Value::Embedding(x) if x.len() == dim as usize => {
294 for fv in x {
295 b.values().append_value(*fv);
296 }
297 b.append(true);
298 }
299 _ => {
300 for _ in 0..dim {
301 b.values().append_null();
302 }
303 b.append(false);
304 }
305 }
306 }
307 Arc::new(b.finish())
308 }
309 _ => {
310 return Err(MongrelQueryError::Arrow(format!(
311 "unsupported column type {ty:?} for SQL projection"
312 )))
313 }
314 })
315}
316
317pub fn columns_to_batch(
320 columns: &[(u16, Vec<Value>)],
321 schema: &MongrelSchema,
322) -> Result<arrow::record_batch::RecordBatch> {
323 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(schema.columns.len());
325 for cdef in &schema.columns {
326 let vals = columns
327 .iter()
328 .find(|(id, _)| *id == cdef.id)
329 .map(|(_, v)| v.as_slice())
330 .unwrap_or(&[]);
331 arrays.push(build_array(cdef.ty, vals)?);
332 }
333 let arrow_fields: Vec<Field> = schema
334 .columns
335 .iter()
336 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
337 .collect();
338 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(arrow_fields)), arrays)
339 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
340}