1use std::sync::Arc;
18
19use api::v1::SemanticType;
20use common_error::ext::BoxedError;
21use common_recordbatch::error::{
22 ArrowComputeSnafu, DataTypesSnafu, ExternalSnafu, NewDfRecordBatchSnafu,
23};
24use common_recordbatch::{DfRecordBatch, RecordBatch};
25use datatypes::arrow::array::Array;
26use datatypes::arrow::datatypes::{DataType as ArrowDataType, Field};
27use datatypes::extension::json::is_json_extension_type;
28use datatypes::prelude::{ConcreteDataType, DataType};
29use datatypes::schema::{Schema, SchemaRef};
30use datatypes::value::Value;
31use datatypes::vectors::Helper;
32use datatypes::vectors::json::array::JsonArray;
33use snafu::{OptionExt, ResultExt};
34use store_api::metadata::{RegionMetadata, RegionMetadataRef};
35use store_api::storage::ColumnId;
36
37use crate::cache::CacheStrategy;
38use crate::error::{InvalidRequestSnafu, RecordBatchSnafu, Result};
39use crate::read::projection::{read_column_ids_from_projection, repeated_vector_with_cache};
40use crate::read::read_columns::ReadColumns;
41use crate::sst::parquet::flat_format::sst_column_id_indices;
42use crate::sst::parquet::format::FormatProjection;
43use crate::sst::{
44 FlatSchemaOptions, internal_fields, tag_maybe_to_dictionary_field, to_flat_sst_arrow_schema,
45 with_field_id,
46};
47
48pub struct FlatProjectionMapper {
53 metadata: RegionMetadataRef,
55 output_schema: SchemaRef,
57 read_cols: ReadColumns,
62 batch_schema: Vec<(ColumnId, ConcreteDataType)>,
67 is_empty_projection: bool,
69 batch_indices: Vec<usize>,
71 input_arrow_schema: datatypes::arrow::datatypes::SchemaRef,
73}
74
75impl FlatProjectionMapper {
76 pub fn new(
81 metadata: &RegionMetadataRef,
82 projection: impl IntoIterator<Item = usize>,
83 ) -> Result<Self> {
84 let projection: Vec<_> = projection.into_iter().collect();
85 let read_column_ids = read_column_ids_from_projection(metadata, &projection)?;
86 let read_cols = ReadColumns::from_deduped_column_ids(read_column_ids);
87 Self::new_with_read_columns(metadata, projection, read_cols)
88 }
89
90 pub fn new_with_read_columns(
92 metadata: &RegionMetadataRef,
93 projection: Vec<usize>,
94 read_cols: ReadColumns,
95 ) -> Result<Self> {
96 let is_empty_projection = projection.is_empty();
98
99 let mut col_schemas = Vec::with_capacity(projection.len());
101 let mut output_col_ids = Vec::with_capacity(projection.len());
103 for idx in &projection {
104 let col = metadata
105 .column_metadatas
106 .get(*idx)
107 .with_context(|| InvalidRequestSnafu {
108 region_id: metadata.region_id,
109 reason: format!("projection index {} is out of bound", idx),
110 })?;
111 output_col_ids.push(col.column_id);
112 col_schemas.push(col.column_schema.clone());
113 }
114
115 let id_to_index = sst_column_id_indices(metadata);
117
118 let format_projection = FormatProjection::compute_format_projection(
120 &id_to_index,
121 metadata.column_metadatas.len() + 3,
123 read_cols.clone(),
124 );
125
126 let batch_schema = flat_projected_columns(metadata, &format_projection);
127
128 let input_arrow_schema = compute_input_arrow_schema(metadata, &batch_schema);
130
131 let output_schema = if is_empty_projection {
133 Arc::new(Schema::new(vec![]))
134 } else {
135 Arc::new(Schema::new(col_schemas))
137 };
138
139 let batch_indices = if is_empty_projection {
140 vec![]
141 } else {
142 output_col_ids
143 .iter()
144 .map(|id| {
145 format_projection
147 .column_id_to_projected_index
148 .get(id)
149 .copied()
150 .with_context(|| {
151 let name = metadata
152 .column_by_id(*id)
153 .map(|column| column.column_schema.name.clone())
154 .unwrap_or_else(|| id.to_string());
155 InvalidRequestSnafu {
156 region_id: metadata.region_id,
157 reason: format!(
158 "output column {} is missing in read projection",
159 name
160 ),
161 }
162 })
163 })
164 .collect::<Result<Vec<_>>>()?
165 };
166
167 Ok(FlatProjectionMapper {
168 metadata: metadata.clone(),
169 output_schema,
170 read_cols,
171 batch_schema,
172 is_empty_projection,
173 batch_indices,
174 input_arrow_schema,
175 })
176 }
177
178 pub fn all(metadata: &RegionMetadataRef) -> Result<Self> {
180 FlatProjectionMapper::new(metadata, 0..metadata.column_metadatas.len())
181 }
182
183 pub(crate) fn metadata(&self) -> &RegionMetadataRef {
185 &self.metadata
186 }
187 pub(crate) fn read_columns(&self) -> &ReadColumns {
189 &self.read_cols
190 }
191
192 pub(crate) fn field_column_start(&self) -> usize {
194 for (idx, column_id) in self
195 .batch_schema
196 .iter()
197 .map(|(column_id, _)| column_id)
198 .enumerate()
199 {
200 if self
202 .metadata
203 .column_by_id(*column_id)
204 .unwrap()
205 .semantic_type
206 == SemanticType::Field
207 {
208 return idx;
209 }
210 }
211
212 self.batch_schema.len()
213 }
214
215 pub(crate) fn batch_schema(&self) -> &[(ColumnId, ConcreteDataType)] {
217 &self.batch_schema
218 }
219
220 pub(crate) fn input_arrow_schema(
224 &self,
225 compaction: bool,
226 ) -> datatypes::arrow::datatypes::SchemaRef {
227 if !compaction {
228 self.input_arrow_schema.clone()
229 } else {
230 to_flat_sst_arrow_schema(
232 &self.metadata,
233 &FlatSchemaOptions::from_encoding(self.metadata.primary_key_encoding),
234 )
235 }
236 }
237
238 pub(crate) fn output_schema(&self) -> SchemaRef {
242 self.output_schema.clone()
243 }
244
245 pub(crate) fn with_output_schema(&mut self, schema: SchemaRef) {
246 self.output_schema = schema;
247 }
248
249 pub(crate) fn convert(
253 &self,
254 batch: &datatypes::arrow::record_batch::RecordBatch,
255 cache_strategy: &CacheStrategy,
256 ) -> common_recordbatch::error::Result<RecordBatch> {
257 if self.is_empty_projection {
258 return RecordBatch::new_with_count(self.output_schema.clone(), batch.num_rows());
259 }
260 let mut arrays = Vec::with_capacity(self.output_schema.num_columns());
263 for (output_idx, index) in self.batch_indices.iter().enumerate() {
264 let mut array = batch.column(*index).clone();
265 if let ArrowDataType::Dictionary(_key_type, value_type) = array.data_type() {
267 if let Some(dict_array) = single_value_string_dictionary(
270 &array,
271 &self.output_schema.column_schemas()[output_idx].data_type,
272 value_type.as_ref(),
273 ) {
274 let dict_values = dict_array.values();
275 let value = if dict_values.is_null(0) {
276 Value::Null
277 } else {
278 Value::from(datatypes::arrow_array::string_array_value(dict_values, 0))
279 };
280
281 let repeated = repeated_vector_with_cache(
282 &self.output_schema.column_schemas()[output_idx].data_type,
283 &value,
284 batch.num_rows(),
285 cache_strategy,
286 )?;
287 array = repeated.to_arrow_array();
288 } else {
289 let casted = datatypes::arrow::compute::cast(&array, value_type)
290 .context(ArrowComputeSnafu)?;
291 array = casted;
292 }
293 }
294
295 let field = &self.output_schema.arrow_schema().fields()[output_idx];
296 if is_json_extension_type(field) {
297 array = JsonArray::from(&array)
298 .try_align(field.data_type())
299 .context(DataTypesSnafu)?;
300 }
301
302 arrays.push(array);
303 }
304
305 let df_record_batch =
306 DfRecordBatch::try_new(self.output_schema.arrow_schema().clone(), arrays)
307 .context(NewDfRecordBatchSnafu)?;
308 Ok(RecordBatch::from_df_record_batch(
309 self.output_schema.clone(),
310 df_record_batch,
311 ))
312 }
313
314 pub(crate) fn project_vectors(
316 &self,
317 batch: &datatypes::arrow::record_batch::RecordBatch,
318 ) -> common_recordbatch::error::Result<Vec<datatypes::vectors::VectorRef>> {
319 let mut columns = Vec::with_capacity(self.output_schema.num_columns());
320 for index in &self.batch_indices {
321 let mut array = batch.column(*index).clone();
322 if let datatypes::arrow::datatypes::DataType::Dictionary(_key_type, value_type) =
324 array.data_type()
325 {
326 let casted = datatypes::arrow::compute::cast(&array, value_type)
327 .context(ArrowComputeSnafu)?;
328 array = casted;
329 }
330 let vector = Helper::try_into_vector(array)
331 .map_err(BoxedError::new)
332 .context(ExternalSnafu)?;
333 columns.push(vector);
334 }
335 Ok(columns)
336 }
337}
338
339fn single_value_string_dictionary<'a>(
340 array: &'a Arc<dyn Array>,
341 output_type: &ConcreteDataType,
342 value_type: &ArrowDataType,
343) -> Option<&'a datatypes::arrow::array::DictionaryArray<datatypes::arrow::datatypes::UInt32Type>> {
344 if !matches!(
345 value_type,
346 ArrowDataType::Utf8 | ArrowDataType::LargeUtf8 | ArrowDataType::Utf8View
347 ) || !output_type.is_string()
348 {
349 return None;
350 }
351
352 let dict_array = array
353 .as_any()
354 .downcast_ref::<datatypes::arrow::array::DictionaryArray<
355 datatypes::arrow::datatypes::UInt32Type,
356 >>()?;
357
358 (dict_array.values().len() == 1 && dict_array.null_count() == 0).then_some(dict_array)
359}
360
361pub(crate) fn flat_projected_columns(
365 metadata: &RegionMetadata,
366 format_projection: &FormatProjection,
367) -> Vec<(ColumnId, ConcreteDataType)> {
368 let time_index = metadata.time_index_column();
369 let num_columns = if format_projection
370 .column_id_to_projected_index
371 .contains_key(&time_index.column_id)
372 {
373 format_projection.column_id_to_projected_index.len()
374 } else {
375 format_projection.column_id_to_projected_index.len() + 1
376 };
377 let mut schema = vec![None; num_columns];
378 for (column_id, index) in &format_projection.column_id_to_projected_index {
379 schema[*index] = Some((
381 *column_id,
382 metadata
383 .column_by_id(*column_id)
384 .unwrap()
385 .column_schema
386 .data_type
387 .clone(),
388 ));
389 }
390 if num_columns != format_projection.column_id_to_projected_index.len() {
391 schema[num_columns - 1] = Some((
392 time_index.column_id,
393 time_index.column_schema.data_type.clone(),
394 ));
395 }
396
397 schema.into_iter().map(|id_type| id_type.unwrap()).collect()
399}
400
401pub(crate) fn compute_input_arrow_schema(
406 metadata: &RegionMetadata,
407 batch_schema: &[(ColumnId, ConcreteDataType)],
408) -> datatypes::arrow::datatypes::SchemaRef {
409 let mut new_fields = Vec::with_capacity(batch_schema.len() + 3);
410 for (column_id, _) in batch_schema {
411 let column_metadata = metadata.column_by_id(*column_id).unwrap();
412 let field = Field::new(
413 &column_metadata.column_schema.name,
414 column_metadata.column_schema.data_type.as_arrow_type(),
415 column_metadata.column_schema.is_nullable(),
416 );
417 let field = with_field_id(field, *column_id);
418 if column_metadata.semantic_type == SemanticType::Tag {
419 new_fields.push(tag_maybe_to_dictionary_field(
420 &column_metadata.column_schema.data_type,
421 &Arc::new(field),
422 ));
423 } else {
424 new_fields.push(Arc::new(field));
425 }
426 }
427 new_fields.extend_from_slice(&internal_fields());
428
429 Arc::new(datatypes::arrow::datatypes::Schema::new(new_fields))
430}
431
432pub(crate) struct CompactionProjectionMapper {
435 mapper: FlatProjectionMapper,
436 assembler: DfBatchAssembler,
437}
438
439impl CompactionProjectionMapper {
440 pub(crate) fn try_new(metadata: &RegionMetadataRef) -> Result<Self> {
441 let projection = metadata
442 .column_metadatas
443 .iter()
444 .enumerate()
445 .filter_map(|(idx, col)| {
446 if matches!(col.semantic_type, SemanticType::Field) {
447 Some(idx)
448 } else {
449 None
450 }
451 })
452 .chain([metadata.time_index_column_pos()])
453 .collect::<Vec<_>>();
454
455 let read_col_ids = metadata.column_metadatas.iter().map(|col| col.column_id);
456 let read_cols = ReadColumns::from_deduped_column_ids(read_col_ids);
457 let mapper = FlatProjectionMapper::new_with_read_columns(metadata, projection, read_cols)?;
458 let assembler = DfBatchAssembler::new(mapper.output_schema());
459
460 Ok(Self { mapper, assembler })
461 }
462
463 pub(crate) fn project(&self, batch: DfRecordBatch) -> Result<DfRecordBatch> {
467 let columns = self
468 .mapper
469 .project_vectors(&batch)
470 .context(RecordBatchSnafu)?;
471 self.assembler
472 .build_df_record_batch_with_internal(&batch, columns)
473 .context(RecordBatchSnafu)
474 }
475}
476
477pub(crate) struct DfBatchAssembler {
479 output_arrow_schema_with_internal: datatypes::arrow::datatypes::SchemaRef,
480}
481
482impl DfBatchAssembler {
483 pub(crate) fn new(output_schema: SchemaRef) -> Self {
485 let fields = output_schema
486 .arrow_schema()
487 .fields()
488 .into_iter()
489 .chain(internal_fields().iter())
490 .cloned()
491 .collect::<Vec<_>>();
492 let output_arrow_schema_with_internal =
493 Arc::new(datatypes::arrow::datatypes::Schema::new(fields));
494 Self {
495 output_arrow_schema_with_internal,
496 }
497 }
498
499 pub(crate) fn build_df_record_batch_with_internal(
504 &self,
505 batch: &datatypes::arrow::record_batch::RecordBatch,
506 mut columns: Vec<datatypes::vectors::VectorRef>,
507 ) -> common_recordbatch::error::Result<DfRecordBatch> {
508 let num_columns = batch.columns().len();
509 let internal_indices = [num_columns - 3, num_columns - 2, num_columns - 1];
511 for index in internal_indices.iter() {
512 let array = batch.column(*index).clone();
513 let vector = Helper::try_into_vector(array)
514 .map_err(BoxedError::new)
515 .context(ExternalSnafu)?;
516 columns.push(vector);
517 }
518 RecordBatch::to_df_record_batch(self.output_arrow_schema_with_internal.clone(), columns)
519 }
520}