use super::{
options::ParquetMultiTableInput,
remote::{
RemoteParquetBackendBuildStatus, RemoteParquetSource, RemoteReadMode, remote_parquet_backend_build_status,
},
};
use crate::core::error::{Error, ErrorCode, Result};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParquetReadEvidence {
pub requested_remote_read_mode: Option<RemoteReadMode>,
pub input_kind: &'static str,
pub backend: &'static str,
pub fallback_reason: &'static str,
pub remote_bytes: Option<u64>,
pub remote_range_count: Option<u64>,
pub device_read_support: &'static str,
pub backend_build_status: RemoteParquetBackendBuildStatus,
pub pruning: Option<ParquetPruningEvidence>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ParquetPruningEvidence {
pub input_row_groups: u64,
pub row_groups_after_stats_filter: Option<u64>,
pub row_groups_after_bloom_filter: Option<u64>,
}
impl ParquetReadEvidence {
pub(super) fn remote_path_reader(source: &RemoteParquetSource) -> Self {
Self {
requested_remote_read_mode: Some(source.read_mode()),
input_kind: "remote_path",
backend: "libcudf_path_reader_unverified",
fallback_reason: "not_reported_by_libcudf",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_reported_by_libcudf",
backend_build_status: remote_parquet_backend_build_status(),
pruning: None,
}
}
pub(super) fn local_path_reader() -> Self {
Self {
requested_remote_read_mode: None,
input_kind: "local_path",
backend: "libcudf_path_reader",
fallback_reason: "not_applicable",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_applicable",
backend_build_status: RemoteParquetBackendBuildStatus::Unknown,
pruning: None,
}
}
pub(crate) fn local_path_footer_materialized() -> Self {
Self {
requested_remote_read_mode: None,
input_kind: "local_path",
backend: "parquet_footer_materialized",
fallback_reason: "all_null_columns_elided",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_applicable",
backend_build_status: RemoteParquetBackendBuildStatus::Unknown,
pruning: None,
}
}
pub(crate) fn local_path_multi_footer_materialized() -> Self {
Self {
requested_remote_read_mode: None,
input_kind: "local_path_multi",
backend: "parquet_footer_materialized",
fallback_reason: "all_null_columns_elided",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_applicable",
backend_build_status: RemoteParquetBackendBuildStatus::Unknown,
pruning: None,
}
}
pub(super) fn host_buffer() -> Self {
Self {
requested_remote_read_mode: None,
input_kind: "host_buffer",
backend: "host_buffer",
fallback_reason: "not_applicable",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_applicable",
backend_build_status: RemoteParquetBackendBuildStatus::Unknown,
pruning: None,
}
}
pub(super) fn with_pruning(mut self, pruning: Option<ParquetPruningEvidence>) -> Self {
self.pruning = pruning;
self
}
}
pub(super) fn empty_remote_read_evidence_raw() -> cudf_sys::cudf_io_remote_parquet_read_evidence_t {
cudf_sys::cudf_io_remote_parquet_read_evidence_t {
backend: 0,
fallback_reason: 1,
supports_device_read: 0,
device_read_used: 0,
remote_bytes: -1,
remote_range_count: -1,
host_read_bytes: -1,
host_read_count: -1,
device_read_bytes: -1,
device_read_count: -1,
}
}
pub(super) fn empty_pruning_evidence_raw() -> cudf_sys::cudf_io_parquet_pruning_evidence_t {
cudf_sys::cudf_io_parquet_pruning_evidence_t {
input_row_groups: -1,
has_row_groups_after_stats_filter: 0,
row_groups_after_stats_filter: -1,
has_row_groups_after_bloom_filter: 0,
row_groups_after_bloom_filter: -1,
}
}
pub(super) fn pruning_evidence_from_raw(
raw: cudf_sys::cudf_io_parquet_pruning_evidence_t,
) -> Option<ParquetPruningEvidence> {
let input_row_groups = u64::try_from(raw.input_row_groups).ok()?;
Some(ParquetPruningEvidence {
input_row_groups,
row_groups_after_stats_filter: if raw.has_row_groups_after_stats_filter != 0 {
u64::try_from(raw.row_groups_after_stats_filter).ok()
} else {
None
},
row_groups_after_bloom_filter: if raw.has_row_groups_after_bloom_filter != 0 {
u64::try_from(raw.row_groups_after_bloom_filter).ok()
} else {
None
},
})
}
pub(super) fn remote_read_evidence_from_raw(
source: Option<&RemoteParquetSource>,
input_kind: &'static str,
raw: cudf_sys::cudf_io_remote_parquet_read_evidence_t,
) -> ParquetReadEvidence {
let requested_remote_read_mode = source.map(RemoteParquetSource::read_mode);
let backend = match raw.backend {
1 => "cudf_sys_kvikio_remote_datasource",
_ => "unavailable",
};
let fallback_reason = match raw.fallback_reason {
0 => "none",
_ => "backend_unavailable",
};
let device_read_support = if raw.supports_device_read == 0 {
"unsupported"
} else if raw.device_read_used != 0 {
"supported_used"
} else {
"supported_not_used"
};
ParquetReadEvidence {
requested_remote_read_mode,
input_kind,
backend,
fallback_reason,
remote_bytes: u64::try_from(raw.remote_bytes).ok(),
remote_range_count: u64::try_from(raw.remote_range_count).ok(),
device_read_support,
backend_build_status: remote_parquet_backend_build_status(),
pruning: None,
}
}
pub(super) fn validate_required_remote_evidence(
evidence: &ParquetReadEvidence,
required_mode: Option<RemoteReadMode>,
operation: &'static str,
) -> Result<()> {
let Some(required_mode) = required_mode.filter(|mode| mode.requires_explicit_kvikio_datasource()) else {
return Ok(());
};
let explicit_kvikio_backend = evidence.backend == "cudf_sys_kvikio_remote_datasource";
let no_fallback = evidence.fallback_reason == "none";
if required_mode == RemoteReadMode::RequireKvikio {
if explicit_kvikio_backend && no_fallback {
return Ok(());
}
return Err(Error::new_with(
ErrorCode::UnsupportedOperation,
operation,
"RequireKvikio remote Parquet read did not produce required explicit KvikIO datasource evidence",
)
.with_detail("resource", "remote_parquet_read_evidence")
.with_detail("reason", "require_kvikio_evidence_unavailable")
.with_detail("remote_read_mode", required_mode.as_str())
.with_detail("input_kind", evidence.input_kind)
.with_detail("backend", evidence.backend)
.with_detail("fallback_reason", evidence.fallback_reason)
.with_detail("remote_backend_build_status", evidence.backend_build_status.as_str()));
}
let has_remote_bytes = evidence.remote_bytes.is_some_and(|bytes| bytes > 0);
let has_remote_ranges = evidence.remote_range_count.is_some_and(|ranges| ranges > 0);
let device_read_capable = matches!(evidence.device_read_support, "supported_used" | "supported_not_used");
let used_device_read = evidence.device_read_support == "supported_used";
let payload_device_read_required = remote_payload_device_read_required(evidence);
let direct_device_read_proven = used_device_read || (device_read_capable && !payload_device_read_required);
if explicit_kvikio_backend && no_fallback && has_remote_bytes && has_remote_ranges && direct_device_read_proven {
return Ok(());
}
Err(Error::new_with(
ErrorCode::UnsupportedOperation,
operation,
"RequireDirect remote Parquet read did not produce required direct remote evidence",
)
.with_detail("resource", "remote_parquet_read_evidence")
.with_detail("reason", "require_direct_evidence_unavailable")
.with_detail("remote_read_mode", required_mode.as_str())
.with_detail("input_kind", evidence.input_kind)
.with_detail("backend", evidence.backend)
.with_detail("fallback_reason", evidence.fallback_reason)
.with_detail("remote_bytes_positive", has_remote_bytes.to_string())
.with_detail("remote_range_count_positive", has_remote_ranges.to_string())
.with_detail("device_read_support", evidence.device_read_support)
.with_detail("device_read_capable", device_read_capable.to_string())
.with_detail("device_read_used", used_device_read.to_string())
.with_detail("payload_device_read_required", payload_device_read_required.to_string())
.with_detail("remote_backend_build_status", evidence.backend_build_status.as_str()))
}
fn remote_payload_device_read_required(evidence: &ParquetReadEvidence) -> bool {
let Some(pruning) = evidence.pruning else {
return true;
};
if pruning.input_row_groups == 0 {
return false;
}
let remaining_row_groups = pruning
.row_groups_after_bloom_filter
.or(pruning.row_groups_after_stats_filter)
.unwrap_or(pruning.input_row_groups);
remaining_row_groups > 0
}
pub(super) fn multi_table_read_evidence(inputs: &[ParquetMultiTableInput]) -> Option<ParquetReadEvidence> {
let remote_sources = inputs.iter().filter_map(ParquetMultiTableInput::remote_source).collect::<Vec<_>>();
if remote_sources.is_empty() {
return Some(ParquetReadEvidence {
requested_remote_read_mode: None,
input_kind: "local_path_multi",
backend: "libcudf_path_reader",
fallback_reason: "not_applicable",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_applicable",
backend_build_status: RemoteParquetBackendBuildStatus::Unknown,
pruning: None,
});
}
let requested_remote_read_mode = remote_sources
.first()
.map(|source| source.read_mode())
.filter(|first_mode| remote_sources.iter().all(|source| source.read_mode() == *first_mode));
Some(ParquetReadEvidence {
requested_remote_read_mode,
input_kind: if remote_sources.len() == inputs.len() { "remote_path_multi" } else { "mixed_path_multi" },
backend: "libcudf_path_reader_unverified",
fallback_reason: "not_reported_by_libcudf",
remote_bytes: None,
remote_range_count: None,
device_read_support: "not_reported_by_libcudf",
backend_build_status: remote_parquet_backend_build_status(),
pruning: None,
})
}