1use std::any::Any;
8use std::sync::Arc;
9
10use async_trait::async_trait;
11use datafusion::arrow::array::{
12 Array, BinaryArray, BooleanArray, Float64Array, Int64Array, RecordBatch, StringArray,
13 TimestampMicrosecondArray,
14};
15use datafusion::arrow::buffer::{BooleanBuffer, NullBuffer};
16use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit};
17use datafusion::catalog::{Session, TableProvider};
18use datafusion::error::DataFusionError;
19use datafusion::logical_expr::{Expr, TableType};
20use datafusion::physical_plan::ExecutionPlan;
21use nodedb_columnar::delete_bitmap::DeleteBitmap;
22use nodedb_columnar::reader::{DecodedColumn, SegmentReader};
23use nodedb_types::Namespace;
24use nodedb_types::columnar::{ColumnType, ColumnarSchema};
25
26use crate::storage::engine::StorageEngine;
27
28pub struct ColumnarTableProvider<S: StorageEngine> {
33 collection: String,
34 arrow_schema: SchemaRef,
35 columnar_schema: ColumnarSchema,
36 storage: Arc<S>,
37 segment_ids: Vec<u32>,
39 delete_bitmaps: Vec<(u32, DeleteBitmap)>,
41}
42
43impl<S: StorageEngine> std::fmt::Debug for ColumnarTableProvider<S> {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct("ColumnarTableProvider")
46 .field("collection", &self.collection)
47 .field("segments", &self.segment_ids.len())
48 .finish()
49 }
50}
51
52impl<S: StorageEngine> ColumnarTableProvider<S> {
53 pub fn new(
55 collection: String,
56 schema: &ColumnarSchema,
57 storage: Arc<S>,
58 segment_ids: Vec<u32>,
59 delete_bitmaps: Vec<(u32, DeleteBitmap)>,
60 ) -> Self {
61 let arrow_schema = columnar_schema_to_arrow(schema);
62 Self {
63 collection,
64 arrow_schema,
65 columnar_schema: schema.clone(),
66 storage,
67 segment_ids,
68 delete_bitmaps,
69 }
70 }
71
72 fn scan_to_batches(
74 &self,
75 projection: Option<&Vec<usize>>,
76 limit: Option<usize>,
77 ) -> Result<Vec<RecordBatch>, DataFusionError> {
78 let col_indices: Vec<usize> = match projection {
79 Some(proj) => proj.clone(),
80 None => (0..self.columnar_schema.columns.len()).collect(),
81 };
82
83 let projected_schema = if projection.is_some() {
84 Arc::new(
85 self.arrow_schema
86 .project(&col_indices)
87 .map_err(|e| DataFusionError::Execution(format!("project schema: {e}")))?,
88 )
89 } else {
90 self.arrow_schema.clone()
91 };
92
93 let mut batches: Vec<RecordBatch> = Vec::new();
95 let mut total_rows = 0usize;
96
97 for &seg_id in &self.segment_ids {
98 let seg_key = format!("{}:seg:{}", self.collection, seg_id);
99 let seg_bytes = tokio::task::block_in_place(|| {
100 let handle = tokio::runtime::Handle::current();
101 handle.block_on(async {
102 self.storage
103 .get(Namespace::Columnar, seg_key.as_bytes())
104 .await
105 })
106 })
107 .map_err(|e| DataFusionError::Execution(format!("storage read: {e}")))?;
108
109 let Some(seg_bytes) = seg_bytes else {
110 continue;
111 };
112
113 let reader = SegmentReader::open(&seg_bytes)
114 .map_err(|e| DataFusionError::Execution(format!("open segment: {e}")))?;
115
116 let empty_bitmap = DeleteBitmap::new();
118 let bitmap = self
119 .delete_bitmaps
120 .iter()
121 .find(|(id, _)| *id == seg_id)
122 .map(|(_, bm)| bm)
123 .unwrap_or(&empty_bitmap);
124
125 let seg_columns: Vec<DecodedColumn> = col_indices
127 .iter()
128 .map(|&idx| reader.read_column_with_deletes(idx, &[], bitmap))
129 .collect::<Result<Vec<_>, _>>()
130 .map_err(|e| DataFusionError::Execution(format!("read columns: {e}")))?;
131
132 let seg_row_count = reader.row_count() as usize;
133
134 let mut arrow_arrays: Vec<Arc<dyn Array>> = Vec::with_capacity(col_indices.len());
136 for (i, decoded) in seg_columns.into_iter().enumerate() {
137 let col_schema_idx = col_indices[i];
138 let col_type = &self.columnar_schema.columns[col_schema_idx].column_type;
139 let arr = decoded_to_arrow(decoded, col_type, seg_row_count)?;
140 arrow_arrays.push(arr);
141 }
142
143 let batch = RecordBatch::try_new(projected_schema.clone(), arrow_arrays)
144 .map_err(|e| DataFusionError::Execution(format!("build batch: {e}")))?;
145
146 batches.push(batch);
147 total_rows += batches.last().map_or(0, |b| b.num_rows());
148 if let Some(lim) = limit
149 && total_rows >= lim
150 {
151 break;
152 }
153 }
154
155 if batches.is_empty() {
156 return Ok(vec![RecordBatch::new_empty(projected_schema)]);
157 }
158
159 Ok(batches)
160 }
161}
162
163#[async_trait]
164impl<S: StorageEngine> TableProvider for ColumnarTableProvider<S> {
165 fn as_any(&self) -> &dyn Any {
166 self
167 }
168
169 fn schema(&self) -> SchemaRef {
170 Arc::clone(&self.arrow_schema)
171 }
172
173 fn table_type(&self) -> TableType {
174 TableType::Base
175 }
176
177 async fn scan(
178 &self,
179 state: &dyn Session,
180 projection: Option<&Vec<usize>>,
181 _filters: &[Expr],
182 limit: Option<usize>,
183 ) -> datafusion::error::Result<Arc<dyn ExecutionPlan>> {
184 let batches = self.scan_to_batches(projection, limit)?;
185 let schema = if let Some(proj) = projection {
186 Arc::new(self.arrow_schema.project(proj)?)
187 } else {
188 self.arrow_schema.clone()
189 };
190 let mem_table = datafusion::datasource::MemTable::try_new(schema, vec![batches])?;
191 mem_table.scan(state, None, &[], limit).await
192 }
193}
194
195fn columnar_schema_to_arrow(schema: &ColumnarSchema) -> SchemaRef {
197 let fields: Vec<Field> = schema
198 .columns
199 .iter()
200 .map(|col| {
201 let dt = match &col.column_type {
202 ColumnType::Int64 => DataType::Int64,
203 ColumnType::Float64 => DataType::Float64,
204 ColumnType::String => DataType::Utf8,
205 ColumnType::Bool => DataType::Boolean,
206 ColumnType::Bytes | ColumnType::Geometry => DataType::Binary,
207 ColumnType::Timestamp => DataType::Timestamp(TimeUnit::Microsecond, None),
208 ColumnType::Decimal | ColumnType::Uuid => DataType::Utf8,
209 ColumnType::Vector(_) => DataType::Binary,
210 };
211 Field::new(&col.name, dt, col.nullable)
212 })
213 .collect();
214 Arc::new(Schema::new(fields))
215}
216
217fn decoded_to_arrow(
219 decoded: DecodedColumn,
220 col_type: &ColumnType,
221 _row_count: usize,
222) -> Result<Arc<dyn Array>, DataFusionError> {
223 match decoded {
224 DecodedColumn::Int64 { values, valid } => {
225 let null_buf = build_null_buffer(&valid);
226 let arr = Int64Array::new(values.into(), null_buf);
227 Ok(Arc::new(arr))
228 }
229 DecodedColumn::Float64 { values, valid } => {
230 let null_buf = build_null_buffer(&valid);
231 let arr = Float64Array::new(values.into(), null_buf);
232 Ok(Arc::new(arr))
233 }
234 DecodedColumn::Timestamp { values, valid } => {
235 let null_buf = build_null_buffer(&valid);
236 let arr = TimestampMicrosecondArray::new(values.into(), null_buf);
237 Ok(Arc::new(arr))
238 }
239 DecodedColumn::Bool { values, valid } => {
240 let null_buf = build_null_buffer(&valid);
241 let bool_buf = BooleanBuffer::from(values);
242 let arr = BooleanArray::new(bool_buf, null_buf);
243 Ok(Arc::new(arr))
244 }
245 DecodedColumn::Binary {
246 data,
247 offsets,
248 valid,
249 } => {
250 match col_type {
251 ColumnType::String | ColumnType::Uuid | ColumnType::Decimal => {
252 let null_buf = build_null_buffer(&valid);
254 let mut strs: Vec<Option<&str>> = Vec::with_capacity(valid.len());
255 for (i, &is_valid) in valid.iter().enumerate() {
256 if is_valid && i + 1 < offsets.len() {
257 let start = offsets[i] as usize;
258 let end = offsets[i + 1] as usize;
259 strs.push(Some(std::str::from_utf8(&data[start..end]).unwrap_or("")));
260 } else {
261 strs.push(None);
262 }
263 }
264 let arr = StringArray::from(strs);
265 if null_buf.is_some() {
266 let data = arr
267 .into_data()
268 .into_builder()
269 .null_bit_buffer(null_buf.map(|nb| nb.into_inner().into_inner()))
270 .build()
271 .map_err(|e| {
272 DataFusionError::Execution(format!("build string array: {e}"))
273 })?;
274 Ok(Arc::new(StringArray::from(data)))
275 } else {
276 Ok(Arc::new(arr))
277 }
278 }
279 _ => {
280 let mut blobs: Vec<Option<&[u8]>> = Vec::with_capacity(valid.len());
282 for (i, &is_valid) in valid.iter().enumerate() {
283 if is_valid && i + 1 < offsets.len() {
284 let start = offsets[i] as usize;
285 let end = offsets[i + 1] as usize;
286 blobs.push(Some(&data[start..end]));
287 } else {
288 blobs.push(None);
289 }
290 }
291 let arr = BinaryArray::from(blobs);
292 Ok(Arc::new(arr))
293 }
294 }
295 }
296 }
297}
298
299fn build_null_buffer(valid: &[bool]) -> Option<NullBuffer> {
301 let null_count = valid.iter().filter(|&&v| !v).count();
302 if null_count == 0 {
303 return None;
304 }
305 Some(NullBuffer::new(BooleanBuffer::from(valid.to_vec())))
306}