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::Date64 => DataType::Date64,
187 TypeId::Time64 => DataType::Time64(arrow::datatypes::TimeUnit::Nanosecond),
188 TypeId::Interval => DataType::Interval(arrow::datatypes::IntervalUnit::MonthDayNano),
189 TypeId::Uuid => DataType::FixedSizeBinary(16),
190 TypeId::Json => DataType::Utf8,
191 TypeId::Array { .. } => DataType::Utf8,
192 TypeId::UInt8 => DataType::UInt8,
193 TypeId::UInt16 => DataType::UInt16,
194 TypeId::UInt32 => DataType::UInt32,
195 TypeId::UInt64 => DataType::UInt64,
196 TypeId::Float32 => DataType::Float32,
197 TypeId::Float64 => DataType::Float64,
198 TypeId::Bytes => DataType::Utf8,
199 TypeId::Embedding { dim } => DataType::FixedSizeList(
200 Arc::new(Field::new("item", DataType::Float32, true)),
201 *dim as i32,
202 ),
203 TypeId::Decimal128 { precision, scale } => DataType::Decimal128(*precision, *scale),
204 })
205}
206
207pub fn rows_to_batch(
209 rows: &[mongreldb_core::Row],
210 schema: &MongrelSchema,
211) -> Result<arrow::record_batch::RecordBatch> {
212 let fields: Vec<(u16, TypeId)> = schema.columns.iter().map(|c| (c.id, c.ty)).collect();
213 let arrays: Vec<ArrayRef> = fields
214 .iter()
215 .map(|(col_id, ty)| {
216 let vals: Vec<Value> = rows
217 .iter()
218 .map(|r| r.columns.get(col_id).cloned().unwrap_or(Value::Null))
219 .collect();
220 build_array(*ty, &vals)
221 })
222 .collect::<Result<_>>()?;
223 let arrow_fields: Vec<Field> = schema
224 .columns
225 .iter()
226 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
227 .collect();
228 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(arrow_fields)), arrays)
229 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
230}
231
232pub fn build_array(ty: TypeId, values: &[Value]) -> Result<ArrayRef> {
234 Ok(match ty {
235 TypeId::Int64 | TypeId::TimestampNanos => {
236 let mut b = Int64Builder::new();
237 for v in values {
238 match v {
239 Value::Int64(x) => b.append_value(*x),
240 _ => b.append_null(),
241 }
242 }
243 Arc::new(b.finish())
244 }
245 TypeId::Float64 => {
246 let mut b = Float64Builder::new();
247 for v in values {
248 match v {
249 Value::Float64(x) => b.append_value(*x),
250 _ => b.append_null(),
251 }
252 }
253 Arc::new(b.finish())
254 }
255 TypeId::Float32 => {
256 let mut b = arrow::array::Float32Builder::new();
257 for v in values {
258 match v {
259 Value::Float64(x) => b.append_value(*x as f32),
260 _ => b.append_null(),
261 }
262 }
263 Arc::new(b.finish())
264 }
265 TypeId::Bool => {
266 let mut b = BooleanBuilder::new();
267 for v in values {
268 match v {
269 Value::Bool(x) => b.append_value(*x),
270 _ => b.append_null(),
271 }
272 }
273 Arc::new(b.finish())
274 }
275 TypeId::Int32 | TypeId::Date32 => {
276 let mut b = arrow::array::Int32Builder::new();
277 for v in values {
278 match v {
279 Value::Int64(x) => b.append_value(*x as i32),
280 _ => b.append_null(),
281 }
282 }
283 Arc::new(b.finish())
284 }
285 TypeId::Bytes => {
286 let mut b = StringBuilder::new();
287 for v in values {
288 match v {
289 Value::Bytes(x) => b.append_value(String::from_utf8_lossy(x)),
290 _ => b.append_null(),
291 }
292 }
293 Arc::new(b.finish())
294 }
295 TypeId::Embedding { dim } => {
296 let fbb = Float32Builder::new();
297 let mut b = FixedSizeListBuilder::new(fbb, dim as i32);
298 for v in values {
299 match v {
300 Value::Embedding(x) if x.len() == dim as usize => {
301 for fv in x {
302 b.values().append_value(*fv);
303 }
304 b.append(true);
305 }
306 _ => {
307 for _ in 0..dim {
308 b.values().append_null();
309 }
310 b.append(false);
311 }
312 }
313 }
314 Arc::new(b.finish())
315 }
316 TypeId::Decimal128 { precision, scale } => {
317 let mut b = arrow::array::Decimal128Builder::new()
318 .with_precision_and_scale(precision, scale)
319 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))?;
320 for v in values {
321 match v {
322 Value::Decimal(d) => b.append_value(*d),
323 _ => b.append_null(),
324 }
325 }
326 Arc::new(b.finish())
327 }
328 TypeId::Uuid => {
329 let mut b = arrow::array::FixedSizeBinaryBuilder::new(16);
330 for v in values {
331 match v {
332 Value::Uuid(arr) => {
333 b.append_value(arr).ok();
334 }
335 _ => {
336 b.append_null();
337 }
338 }
339 }
340 Arc::new(b.finish())
341 }
342 TypeId::Json | TypeId::Array { .. } => {
343 let mut b = arrow::array::StringBuilder::new();
344 for v in values {
345 match v {
346 Value::Json(val) => b.append_value(String::from_utf8_lossy(val)),
347 Value::Bytes(val) => b.append_value(String::from_utf8_lossy(val)),
348 _ => b.append_null(),
349 }
350 }
351 Arc::new(b.finish())
352 }
353 _ => {
354 return Err(MongrelQueryError::Arrow(format!(
355 "unsupported column type {ty:?} for SQL projection"
356 )))
357 }
358 })
359}
360
361pub fn columns_to_batch(
364 columns: &[(u16, Vec<Value>)],
365 schema: &MongrelSchema,
366) -> Result<arrow::record_batch::RecordBatch> {
367 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(schema.columns.len());
369 for cdef in &schema.columns {
370 let vals = columns
371 .iter()
372 .find(|(id, _)| *id == cdef.id)
373 .map(|(_, v)| v.as_slice())
374 .unwrap_or(&[]);
375 arrays.push(build_array(cdef.ty, vals)?);
376 }
377 let arrow_fields: Vec<Field> = schema
378 .columns
379 .iter()
380 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
381 .collect();
382 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(arrow_fields)), arrays)
383 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
384}