#![allow(dead_code)]
#![allow(unreachable_pub)]
mod dv_conversion;
mod location;
pub(crate) mod stats;
use std::collections::HashMap;
use bytes::Bytes;
use delta_kernel_derive::{IntoStructData, ToSchema};
pub(crate) use location::resolve_amt_location;
use url::Url;
use crate::engine_data::EngineData;
use crate::expressions::{Scalar, StructData};
use crate::schema::derive_macro_utils::ToDataType;
use crate::schema::DataType;
use crate::Version;
pub(crate) const CONTENT_TYPE: &str = "contentType";
pub(crate) const LOCATION: &str = "location";
pub(crate) const FILE_FORMAT: &str = "fileFormat";
pub(crate) const TRACKING: &str = "tracking";
pub(crate) const DV_INFO: &str = "deletionVector";
pub(crate) const PARTITION_SPEC_ID: &str = "specId";
pub(crate) const PARTITION: &str = "partition";
pub(crate) const SORT_ORDER_ID: &str = "sortOrderId";
pub(crate) const RECORD_COUNT: &str = "recordCount";
pub(crate) const FILE_SIZE_IN_BYTES: &str = "fileSizeInBytes";
pub(crate) const CONTENT_STATS_FIELD_NAME: &str = "content_stats";
pub(crate) const MANIFEST_INFO: &str = "manifestInfo";
pub(crate) const KEY_METADATA: &str = "keyMetadata";
pub(crate) const SPLIT_OFFSETS: &str = "splitOffsets";
pub(crate) const EQUALITY_IDS: &str = "equalityIds";
pub(crate) const FORMAT_VERSION: &str = "formatVersion";
pub(crate) const TAGS: &str = "tags";
pub(crate) const LOWER_BOUND: &str = "lower_bound";
pub(crate) const UPPER_BOUND: &str = "upper_bound";
pub(crate) const TIGHT_BOUNDS: &str = "tight_bounds";
pub(crate) const VALUE_COUNT: &str = "value_count";
pub(crate) const NULL_VALUE_COUNT: &str = "null_value_count";
pub(crate) const NAN_VALUE_COUNT: &str = "nan_value_count";
pub(crate) const AVG_VALUE_SIZE_IN_BYTES: &str = "avg_value_size_in_bytes";
pub(super) struct ContentTreeNode {
data: Vec<Box<dyn EngineData>>,
version: Version,
table_root: Url,
path_in_log: String,
}
#[derive(Debug, Clone, ToSchema, IntoStructData)]
pub(crate) struct DeletionVectorInfo {
#[field_id = 155]
pub(crate) location: String,
#[field_id = 144]
pub(crate) offset: i64,
#[field_id = 145]
pub(crate) size_in_bytes: i64,
#[field_id = 156]
pub(crate) cardinality: i64,
}
#[derive(Debug, Clone, ToSchema, IntoStructData)]
pub struct TrackingInfo {
#[field_id = 0]
pub(crate) status: TrackingStatus,
#[field_id = 1]
pub snapshot_id: Option<i64>,
#[field_id = 5]
pub(crate) dv_snapshot_id: Option<i64>,
#[field_id = 3]
pub(crate) sequence_number: Option<i64>,
#[field_id = 4]
pub(crate) file_sequence_number: Option<i64>,
#[field_id = 142]
pub(crate) first_row_id: Option<i64>,
#[field_id = 6]
pub(crate) deleted_positions: Option<Bytes>,
#[field_id = 7]
pub(crate) replaced_positions: Option<Bytes>,
}
#[derive(Debug, Clone, ToSchema)]
pub(super) struct ContentTreeNodeEntry {
#[field_id = 134]
pub content_type: DataContentType,
#[field_id = 100]
pub location: String,
#[field_id = 101]
pub(crate) file_format: DataFileFormat,
#[field_id = 147]
pub tracking: TrackingInfo,
#[field_id = 148]
pub(crate) deletion_vector: Option<DeletionVectorInfo>,
#[field_id = 141]
pub(crate) spec_id: i32,
#[skip_schema]
#[field_id = 102]
pub(crate) partition: Option<StructData>,
#[field_id = 140]
pub(crate) sort_order_id: Option<i32>,
#[field_id = 103]
pub(crate) record_count: i64,
#[field_id = 104]
pub(crate) file_size_in_bytes: i64,
#[skip_schema]
#[field_id = 146]
pub(crate) content_stats: Option<StructData>,
#[field_id = 150]
pub(crate) manifest_info: Option<ManifestInfo>,
#[field_id = 131]
pub(crate) key_metadata: Option<Bytes>,
#[field_id = 132]
#[nested_field_id = 133]
pub(crate) split_offsets: Option<Vec<i64>>,
#[field_id = 135]
#[nested_field_id = 136]
pub(crate) equality_ids: Option<Vec<i32>>,
#[field_id = 157]
pub(crate) format_version: i32,
pub(crate) tags: Option<HashMap<String, Option<String>>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum DataContentType {
Data = 0,
PositionDeletes = 1,
EqualityDeletes = 2,
DataManifest = 3, DeleteManifest = 4, }
impl ToDataType for DataContentType {
fn to_data_type() -> DataType {
DataType::INTEGER
}
}
impl From<DataContentType> for Scalar {
fn from(value: DataContentType) -> Self {
Scalar::Integer(value as i32)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) enum DataFileFormat {
Parquet,
Puffin,
}
impl ToDataType for DataFileFormat {
fn to_data_type() -> DataType {
DataType::STRING
}
}
impl From<DataFileFormat> for Scalar {
fn from(value: DataFileFormat) -> Self {
match value {
DataFileFormat::Parquet => Scalar::String("parquet".to_string()),
DataFileFormat::Puffin => Scalar::String("puffin".to_string()),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TrackingStatus {
Existing = 0,
Added = 1,
Deleted = 2,
Replaced = 3,
}
impl ToDataType for TrackingStatus {
fn to_data_type() -> DataType {
DataType::INTEGER
}
}
impl From<TrackingStatus> for Scalar {
fn from(value: TrackingStatus) -> Self {
Scalar::Integer(value as i32)
}
}
#[derive(Debug, Clone, Default, PartialEq, ToSchema, IntoStructData)]
pub(crate) struct ManifestInfo {
#[field_id = 504]
pub(crate) added_files_count: i32,
#[field_id = 505]
pub(crate) existing_files_count: i32,
#[field_id = 506]
pub(crate) deleted_files_count: i32,
#[field_id = 520]
pub(crate) replaced_files_count: i32,
#[field_id = 512]
pub(crate) added_rows_count: i64,
#[field_id = 513]
pub(crate) existing_rows_count: i64,
#[field_id = 514]
pub(crate) deleted_rows_count: i64,
#[field_id = 521]
pub(crate) replaced_rows_count: i64,
#[field_id = 516]
pub(crate) min_sequence_number: i64,
#[field_id = 522]
pub(crate) dv: Option<Bytes>,
#[field_id = 523]
pub(crate) dv_cardinality: Option<i64>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::schema::{ColumnMetadataKey, MetadataValue, ToSchema};
#[test]
fn content_tree_node_entry_schema_field_contract() {
let schema = ContentTreeNodeEntry::to_schema();
let expected: &[(&str, Option<i64>, bool)] = &[
(CONTENT_TYPE, Some(134), false),
(LOCATION, Some(100), false),
(FILE_FORMAT, Some(101), false),
(TRACKING, Some(147), false),
(DV_INFO, Some(148), true),
(PARTITION_SPEC_ID, Some(141), false),
(SORT_ORDER_ID, Some(140), true),
(RECORD_COUNT, Some(103), false),
(FILE_SIZE_IN_BYTES, Some(104), false),
(MANIFEST_INFO, Some(150), true),
(KEY_METADATA, Some(131), true),
(SPLIT_OFFSETS, Some(132), true),
(EQUALITY_IDS, Some(135), true),
(FORMAT_VERSION, Some(157), false),
(TAGS, None, true),
];
let actual_names: Vec<&str> = schema.fields().map(|f| f.name().as_str()).collect();
let expected_names: Vec<&str> = expected.iter().map(|(name, _, _)| *name).collect();
assert_eq!(actual_names, expected_names, "schema fields drifted");
for &(name, field_id, nullable) in expected {
let field = schema.field(name).expect("field present in schema");
assert_eq!(
field.is_nullable(),
nullable,
"nullability mismatch for {name}"
);
assert_eq!(
field
.get_config_value(&ColumnMetadataKey::ParquetFieldId)
.cloned(),
field_id.map(MetadataValue::Number),
"field id mismatch for {name}",
);
}
}
}