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