use anyhow::{Context, Result};
use bytes::Bytes;
use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
use crate::config::RuntimeConfig;
use crate::parsers::{ParseResult, structured::constants::StructuredEncoding};
use crate::results::{ColumnarCommonFields, ParquetMetadata};
use super::utils as columnar_utils;
pub fn extract_parquet_metadata(
mmap: &[u8],
_stats: &ParseResult,
config: &RuntimeConfig,
) -> Result<ParquetMetadata> {
let bytes = Bytes::copy_from_slice(mmap);
let builder = ParquetRecordBatchReaderBuilder::try_new(bytes).context("open Parquet")?;
let num_row_groups = Some(builder.metadata().num_row_groups());
let row_count_total = builder
.metadata()
.file_metadata()
.num_rows()
.try_into()
.unwrap_or(usize::MAX);
let schema_ref = builder.schema().clone();
let column_names = columnar_utils::schema_column_names(schema_ref.as_ref());
let arrow_field_types = columnar_utils::schema_arrow_dtype_strings(schema_ref.as_ref());
let column_count = column_names.len();
let file_bytes = mmap.len() as u64;
let max_sample = columnar_utils::tabular_effective_sample_rows(
config.max_tabular_sample_rows,
file_bytes,
column_count.max(1),
Some(row_count_total),
);
let mut reader = builder.build().context("build Parquet reader")?;
let sample_data = columnar_utils::record_batches_to_string_sample(
reader.by_ref(),
max_sample,
config,
"read Parquet batch",
)?;
let ts = columnar_utils::tabular_stats_from_sample(&sample_data, column_count, config);
let columns = columnar_utils::columns_from_tabular_sample(
column_count,
Some(column_names),
ts,
Some(arrow_field_types),
);
Ok(ParquetMetadata {
common: ColumnarCommonFields {
row_count: Some(row_count_total),
column_count: Some(column_count),
stats_rows_sampled: Some(sample_data.len()),
encoding: Some(StructuredEncoding::TABULAR_BINARY.to_string()),
columns,
},
num_row_groups,
})
}
crate::no_template_mining!(
extract_parquet_templates,
"Parquet is columnar binary; no text template mining."
);