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};
15use crate::query_registry::RegisteredSqlQuery;
16
17fn bit_set(validity: &[u8], i: usize) -> bool {
18 (validity.get(i / 8).copied().unwrap_or(0) >> (i % 8)) & 1 == 1
19}
20
21fn all_bits_set(validity: &[u8], n: usize) -> bool {
23 if n == 0 {
24 return true;
25 }
26 let full = n / 8;
27 if !validity[..full].iter().all(|&b| b == 0xFF) {
28 return false;
29 }
30 if !n.is_multiple_of(8) {
31 let mask = (1u8 << (n % 8)) - 1;
32 (validity.get(full).copied().unwrap_or(0) & mask) == mask
33 } else {
34 true
35 }
36}
37
38pub fn native_to_array(ty: TypeId, col: &NativeColumn) -> Result<ArrayRef> {
42 native_to_array_with_query(ty, col, None)
43}
44
45pub(crate) fn native_to_array_with_query(
46 ty: TypeId,
47 col: &NativeColumn,
48 query: Option<&RegisteredSqlQuery>,
49) -> Result<ArrayRef> {
50 Ok(match (ty.clone(), col) {
51 (TypeId::Int64 | TypeId::TimestampNanos, NativeColumn::Int64 { data, validity }) => {
52 if all_bits_set(validity, data.len()) {
53 Arc::new(Int64Array::new(data.clone().into(), None))
54 } else {
55 let mut b = Int64Builder::with_capacity(data.len());
56 for (i, v) in data.iter().enumerate() {
57 checkpoint(query, i)?;
58 if bit_set(validity, i) {
59 b.append_value(*v);
60 } else {
61 b.append_null();
62 }
63 }
64 Arc::new(b.finish())
65 }
66 }
67 (TypeId::Float64, NativeColumn::Float64 { data, validity }) => {
68 if all_bits_set(validity, data.len()) {
69 Arc::new(Float64Array::new(data.clone().into(), None))
70 } else {
71 let mut b = Float64Builder::with_capacity(data.len());
72 for (i, v) in data.iter().enumerate() {
73 checkpoint(query, i)?;
74 if bit_set(validity, i) {
75 b.append_value(*v);
76 } else {
77 b.append_null();
78 }
79 }
80 Arc::new(b.finish())
81 }
82 }
83 (TypeId::Bool, NativeColumn::Bool { data, validity }) => {
84 let mut b = BooleanBuilder::with_capacity(data.len());
85 for (i, v) in data.iter().enumerate() {
86 checkpoint(query, i)?;
87 if bit_set(validity, i) {
88 b.append_value(*v != 0);
89 } else {
90 b.append_null();
91 }
92 }
93 Arc::new(b.finish())
94 }
95 (
96 TypeId::Bytes | TypeId::Enum { .. },
97 NativeColumn::Bytes {
98 offsets,
99 values,
100 validity,
101 },
102 ) => {
103 let n = offsets.len().saturating_sub(1);
104 let mut b = StringBuilder::with_capacity(n, values.len());
105 for i in 0..n {
106 checkpoint(query, i)?;
107 if bit_set(validity, i) {
108 let lo = offsets[i] as usize;
109 let hi = offsets[i + 1] as usize;
110 b.append_value(String::from_utf8_lossy(&values[lo..hi]));
111 } else {
112 b.append_null();
113 }
114 }
115 Arc::new(b.finish())
116 }
117 _ => {
118 return Err(MongrelQueryError::Arrow(format!(
119 "native_to_array: unsupported (ty={ty:?})"
120 )))
121 }
122 })
123}
124
125pub fn native_to_array_owned(ty: TypeId, col: NativeColumn) -> Result<ArrayRef> {
131 native_to_array_owned_with_query(ty, col, None)
132}
133
134pub(crate) fn native_to_array_owned_with_query(
135 ty: TypeId,
136 col: NativeColumn,
137 query: Option<&RegisteredSqlQuery>,
138) -> Result<ArrayRef> {
139 query.map(RegisteredSqlQuery::checkpoint).transpose()?;
140 Ok(match (ty, col) {
141 (TypeId::Int64 | TypeId::TimestampNanos, NativeColumn::Int64 { data, validity }) => {
142 let n = data.len();
143 Arc::new(Int64Array::new(data.into(), owned_nulls(validity, n)))
144 }
145 (TypeId::Float64, NativeColumn::Float64 { data, validity }) => {
146 let n = data.len();
147 Arc::new(Float64Array::new(data.into(), owned_nulls(validity, n)))
148 }
149 (ty, col) => native_to_array_with_query(ty, &col, query)?,
151 })
152}
153
154#[inline]
155fn checkpoint(query: Option<&RegisteredSqlQuery>, index: usize) -> Result<()> {
156 if index.is_multiple_of(256) {
157 query.map(RegisteredSqlQuery::checkpoint).transpose()?;
158 }
159 Ok(())
160}
161
162fn owned_nulls(validity: Vec<u8>, n: usize) -> Option<NullBuffer> {
167 if all_bits_set(&validity, n) {
168 None
169 } else {
170 let buffer: Buffer = validity.into();
171 Some(NullBuffer::new(BooleanBuffer::new(buffer, 0, n)))
172 }
173}
174
175pub fn native_columns_to_batch(
177 columns: &[(u16, NativeColumn)],
178 schema: &MongrelSchema,
179) -> Result<arrow::record_batch::RecordBatch> {
180 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(schema.columns.len());
181 for cdef in &schema.columns {
182 let col = columns
183 .iter()
184 .find(|(id, _)| *id == cdef.id)
185 .map(|(_, c)| c)
186 .ok_or_else(|| MongrelQueryError::Arrow(format!("missing column {}", cdef.id)))?;
187 arrays.push(native_to_array(cdef.ty.clone(), col)?);
188 }
189 let fields: Vec<Field> = schema
190 .columns
191 .iter()
192 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
193 .collect();
194 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(fields)), arrays)
195 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
196}
197
198pub fn arrow_schema(schema: &MongrelSchema) -> Result<SchemaRef> {
201 let fields: Result<Vec<Field>> = schema
202 .columns
203 .iter()
204 .map(|c| arrow_data_type(&c.ty).map(|dt| Field::new(&c.name, dt, true)))
205 .collect();
206 Ok(Arc::new(Schema::new(fields?)) as SchemaRef)
207}
208
209pub(crate) fn arrow_data_type(ty: &TypeId) -> Result<DataType> {
210 Ok(match ty {
211 TypeId::Bool => DataType::Boolean,
212 TypeId::Int8 => DataType::Int8,
213 TypeId::Int16 => DataType::Int16,
214 TypeId::Int32 | TypeId::Date32 => DataType::Int32,
215 TypeId::Int64 | TypeId::TimestampNanos => DataType::Int64,
216 TypeId::Date64 => DataType::Date64,
217 TypeId::Time64 => DataType::Time64(arrow::datatypes::TimeUnit::Nanosecond),
218 TypeId::Interval => DataType::Interval(arrow::datatypes::IntervalUnit::MonthDayNano),
219 TypeId::Uuid => DataType::FixedSizeBinary(16),
220 TypeId::Json => DataType::Utf8,
221 TypeId::Array { .. } => DataType::Utf8,
222 TypeId::UInt8 => DataType::UInt8,
223 TypeId::UInt16 => DataType::UInt16,
224 TypeId::UInt32 => DataType::UInt32,
225 TypeId::UInt64 => DataType::UInt64,
226 TypeId::Float32 => DataType::Float32,
227 TypeId::Float64 => DataType::Float64,
228 TypeId::Bytes => DataType::Utf8,
229 TypeId::Embedding { dim } => DataType::FixedSizeList(
230 Arc::new(Field::new("item", DataType::Float32, true)),
231 *dim as i32,
232 ),
233 TypeId::Decimal128 { precision, scale } => DataType::Decimal128(*precision, *scale),
234 TypeId::Enum { .. } => DataType::Utf8,
235 })
236}
237
238pub fn rows_to_batch(
240 rows: &[mongreldb_core::Row],
241 schema: &MongrelSchema,
242) -> Result<arrow::record_batch::RecordBatch> {
243 let fields: Vec<(u16, TypeId)> = schema
244 .columns
245 .iter()
246 .map(|c| (c.id, c.ty.clone()))
247 .collect();
248 let arrays: Vec<ArrayRef> = fields
249 .iter()
250 .map(|(col_id, ty)| {
251 let vals: Vec<Value> = rows
252 .iter()
253 .map(|r| r.columns.get(col_id).cloned().unwrap_or(Value::Null))
254 .collect();
255 build_array(ty.clone(), &vals)
256 })
257 .collect::<Result<_>>()?;
258 let arrow_fields: Vec<Field> = schema
259 .columns
260 .iter()
261 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
262 .collect();
263 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(arrow_fields)), arrays)
264 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
265}
266
267pub fn build_array(ty: TypeId, values: &[Value]) -> Result<ArrayRef> {
269 Ok(match ty {
270 TypeId::Int64 | TypeId::TimestampNanos => {
271 let mut b = Int64Builder::new();
272 for v in values {
273 match v {
274 Value::Int64(x) => b.append_value(*x),
275 _ => b.append_null(),
276 }
277 }
278 Arc::new(b.finish())
279 }
280 TypeId::Float64 => {
281 let mut b = Float64Builder::new();
282 for v in values {
283 match v {
284 Value::Float64(x) => b.append_value(*x),
285 _ => b.append_null(),
286 }
287 }
288 Arc::new(b.finish())
289 }
290 TypeId::Float32 => {
291 let mut b = arrow::array::Float32Builder::new();
292 for v in values {
293 match v {
294 Value::Float64(x) => b.append_value(*x as f32),
295 _ => b.append_null(),
296 }
297 }
298 Arc::new(b.finish())
299 }
300 TypeId::Bool => {
301 let mut b = BooleanBuilder::new();
302 for v in values {
303 match v {
304 Value::Bool(x) => b.append_value(*x),
305 _ => b.append_null(),
306 }
307 }
308 Arc::new(b.finish())
309 }
310 TypeId::Int32 | TypeId::Date32 => {
311 let mut b = arrow::array::Int32Builder::new();
312 for v in values {
313 match v {
314 Value::Int64(x) => b.append_value(*x as i32),
315 _ => b.append_null(),
316 }
317 }
318 Arc::new(b.finish())
319 }
320 TypeId::Bytes | TypeId::Enum { .. } => {
321 let mut b = StringBuilder::new();
322 for v in values {
323 match v {
324 Value::Bytes(x) => b.append_value(String::from_utf8_lossy(x)),
325 _ => b.append_null(),
326 }
327 }
328 Arc::new(b.finish())
329 }
330 TypeId::Embedding { dim } => {
331 let fbb = Float32Builder::new();
332 let mut b = FixedSizeListBuilder::new(fbb, dim as i32);
333 for v in values {
334 match v {
335 Value::Embedding(x) if x.len() == dim as usize => {
336 for fv in x {
337 b.values().append_value(*fv);
338 }
339 b.append(true);
340 }
341 _ => {
342 for _ in 0..dim {
343 b.values().append_null();
344 }
345 b.append(false);
346 }
347 }
348 }
349 Arc::new(b.finish())
350 }
351 TypeId::Decimal128 { precision, scale } => {
352 let mut b = arrow::array::Decimal128Builder::new()
353 .with_precision_and_scale(precision, scale)
354 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))?;
355 for v in values {
356 match v {
357 Value::Decimal(d) => b.append_value(*d),
358 _ => b.append_null(),
359 }
360 }
361 Arc::new(b.finish())
362 }
363 TypeId::Uuid => {
364 let mut b = arrow::array::FixedSizeBinaryBuilder::new(16);
365 for v in values {
366 match v {
367 Value::Uuid(arr) => {
368 b.append_value(arr).ok();
369 }
370 _ => {
371 b.append_null();
372 }
373 }
374 }
375 Arc::new(b.finish())
376 }
377 TypeId::Json | TypeId::Array { .. } => {
378 let mut b = arrow::array::StringBuilder::new();
379 for v in values {
380 match v {
381 Value::Json(val) => b.append_value(String::from_utf8_lossy(val)),
382 Value::Bytes(val) => b.append_value(String::from_utf8_lossy(val)),
383 _ => b.append_null(),
384 }
385 }
386 Arc::new(b.finish())
387 }
388 _ => {
389 return Err(MongrelQueryError::Arrow(format!(
390 "unsupported column type {ty:?} for SQL projection"
391 )))
392 }
393 })
394}
395
396pub fn columns_to_batch(
399 columns: &[(u16, Vec<Value>)],
400 schema: &MongrelSchema,
401) -> Result<arrow::record_batch::RecordBatch> {
402 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(schema.columns.len());
404 for cdef in &schema.columns {
405 let vals = columns
406 .iter()
407 .find(|(id, _)| *id == cdef.id)
408 .map(|(_, v)| v.as_slice())
409 .unwrap_or(&[]);
410 arrays.push(build_array(cdef.ty.clone(), vals)?);
411 }
412 let arrow_fields: Vec<Field> = schema
413 .columns
414 .iter()
415 .map(|c| Field::new(&c.name, arrow_data_type(&c.ty).unwrap(), true))
416 .collect();
417 arrow::record_batch::RecordBatch::try_new(Arc::new(Schema::new(arrow_fields)), arrays)
418 .map_err(|e| MongrelQueryError::Arrow(e.to_string()))
419}