Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arrow-schema/src/extension/canonical/bool8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field = Field::new("", DataType::Int8, false).with_metadata(
[(EXTENSION_TYPE_METADATA_KEY.to_owned(), "".to_owned())]
Expand Down
2 changes: 1 addition & 1 deletion arrow-schema/src/extension/canonical/fixed_shape_tensor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field =
Field::new_fixed_size_list("", Field::new("", DataType::Float32, false), 3, false)
Expand Down
2 changes: 1 addition & 1 deletion arrow-schema/src/extension/canonical/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field = Field::new("", DataType::Int8, false).with_metadata(
[(EXTENSION_TYPE_METADATA_KEY.to_owned(), "{}".to_owned())]
Expand Down
2 changes: 1 addition & 1 deletion arrow-schema/src/extension/canonical/opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field = Field::new("", DataType::Null, false).with_metadata(
[(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field = make_valid_field_primitive(TimeUnit::Second)
.with_metadata([(EXTENSION_TYPE_METADATA_KEY.to_owned(), "".to_owned())].into());
Expand Down
2 changes: 1 addition & 1 deletion arrow-schema/src/extension/canonical/uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field = Field::new("", DataType::FixedSizeBinary(16), false);
field.extension_type::<Uuid>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn missing_name() {
let field = Field::new_struct(
"",
Expand Down
43 changes: 43 additions & 0 deletions arrow-schema/src/extension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod canonical;
pub use canonical::*;

use crate::{ArrowError, DataType};
use std::collections::HashMap;

/// The metadata key for the string name identifying an [`ExtensionType`].
pub const EXTENSION_TYPE_NAME_KEY: &str = "ARROW:extension:name";
Expand Down Expand Up @@ -255,4 +256,46 @@ pub trait ExtensionType: Sized {
/// This should return an error if the given data type is not supported by
/// this extension type.
fn try_new(data_type: &DataType, metadata: Self::Metadata) -> Result<Self, ArrowError>;

/// Construct this extension type from field metadata and data type.
///
/// This is a provided method that extracts extension type information from
/// metadata (using [`EXTENSION_TYPE_NAME_KEY`] and
/// [`EXTENSION_TYPE_METADATA_KEY`]) and delegates to [`Self::try_new`].
///
/// Returns an error if:
/// - The extension type name is missing or doesn't match [`Self::NAME`]
/// - Metadata deserialization fails
/// - The data type is not supported
///
/// This method enables extension type checking without requiring a full
/// [`Field`] instance, useful when only metadata and data type are available.
///
/// [`Field`]: crate::Field
fn try_new_from_field_metadata(
data_type: &DataType,
metadata: &HashMap<String, String>,
) -> Result<Self, ArrowError> {
// Check the extension name in the metadata
match metadata.get(EXTENSION_TYPE_NAME_KEY).map(|s| s.as_str()) {
// It should match the name of the given extension type
Some(name) if name == Self::NAME => {
// Deserialize the metadata and try to construct the extension type
let ext_metadata = metadata
.get(EXTENSION_TYPE_METADATA_KEY)
.map(|s| s.as_str());
let parsed = Self::deserialize_metadata(ext_metadata)?;
Self::try_new(data_type, parsed)
}
// Name mismatch
Some(name) => Err(ArrowError::InvalidArgumentError(format!(
"Extension type name mismatch: expected {}, got {name}",
Self::NAME
))),
// Name missing
None => Err(ArrowError::InvalidArgumentError(
"Extension type name missing".to_string(),
)),
}
}
}
20 changes: 1 addition & 19 deletions arrow-schema/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,25 +575,7 @@ impl Field {
/// }
/// ```
pub fn try_extension_type<E: ExtensionType>(&self) -> Result<E, ArrowError> {
// Check the extension name in the metadata
match self.extension_type_name() {
// It should match the name of the given extension type
Some(name) if name == E::NAME => {
// Deserialize the metadata and try to construct the extension
// type
E::deserialize_metadata(self.extension_type_metadata())
.and_then(|metadata| E::try_new(self.data_type(), metadata))
}
// Name mismatch
Some(name) => Err(ArrowError::InvalidArgumentError(format!(
"Field extension type name mismatch, expected {}, found {name}",
E::NAME
))),
// Name missing
None => Err(ArrowError::InvalidArgumentError(
"Field extension type name missing".to_owned(),
)),
}
E::try_new_from_field_metadata(self.data_type(), self.metadata())
}

/// Returns an instance of the given [`ExtensionType`] of this [`Field`],
Expand Down
4 changes: 2 additions & 2 deletions parquet/src/arrow/schema/virtual_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn row_number_missing_name() {
let field = Field::new("", DataType::Int64, false).with_metadata(
[(EXTENSION_TYPE_METADATA_KEY.to_owned(), "".to_owned())]
Expand Down Expand Up @@ -203,7 +203,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "Field extension type name missing")]
#[should_panic(expected = "Extension type name missing")]
fn row_group_index_missing_name() {
let field = Field::new("", DataType::Int64, false).with_metadata(
[(EXTENSION_TYPE_METADATA_KEY.to_owned(), "".to_owned())]
Expand Down
Loading