This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-mosaic.git
The following commit(s) were added to refs/heads/main by this push:
new 11fe80d fix(core): reject empty column names in writers (#93)
11fe80d is described below
commit 11fe80d16808f9893b30264f134b09f8c72c4dfe
Author: jianguotian <[email protected]>
AuthorDate: Wed Sep 30 07:16:12 2026 +0800
fix(core): reject empty column names in writers (#93)
---
core/src/schema.rs | 3 +++
core/src/writer.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/core/src/schema.rs b/core/src/schema.rs
index ecfc6d1..eef791d 100644
--- a/core/src/schema.rs
+++ b/core/src/schema.rs
@@ -47,6 +47,9 @@ impl MosaicSchema {
pub fn validate(columns: &[(String, DataType, bool)]) -> Result<(),
String> {
let mut seen = HashSet::new();
for (name, data_type, _nullable) in columns {
+ if name.is_empty() {
+ return Err("empty column name".to_string());
+ }
if !seen.insert(name.as_str()) {
return Err(format!("duplicate column name: {}", name));
}
diff --git a/core/src/writer.rs b/core/src/writer.rs
index a977049..d503a06 100644
--- a/core/src/writer.rs
+++ b/core/src/writer.rs
@@ -170,6 +170,19 @@ impl<S: OutputFile> MosaicWriter<S> {
schema: MosaicSchema,
options: WriterOptions,
) -> io::Result<Self> {
+ let columns = schema
+ .columns
+ .iter()
+ .map(|column| {
+ (
+ column.name.clone(),
+ column.data_type.clone(),
+ column.nullable,
+ )
+ })
+ .collect::<Vec<_>>();
+ MosaicSchema::validate(&columns)
+ .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput,
error))?;
let batch_col_map: Vec<usize> = (0..schema.columns.len()).collect();
Self::from_mosaic_schema_with_map(out, schema, options, batch_col_map)
}
@@ -892,6 +905,58 @@ mod tests {
assert_eq!(batch_column_map(&mosaic_schema), vec![1, 2, 0]);
}
+ #[test]
+ fn test_new_rejects_empty_column_name_before_writing() {
+ let arrow_schema = Schema::new(vec![Field::new("", DataType::Int32,
true)]);
+ let state = Arc::new(Mutex::new(FailingOutputState::default()));
+ let out = FailOnceOutputFile {
+ state: Arc::clone(&state),
+ fail: false,
+ };
+
+ let result = MosaicWriter::new(out, &arrow_schema,
WriterOptions::default());
+ let error = match result {
+ Ok(writer) => {
+ std::mem::forget(writer);
+ panic!("empty column name should be rejected");
+ }
+ Err(error) => error,
+ };
+
+ assert_eq!(io::ErrorKind::InvalidInput, error.kind());
+ assert!(error.to_string().contains("empty column name"));
+ let state = state.lock().unwrap();
+ assert_eq!(0, state.write_calls);
+ assert_eq!(0, state.flush_calls);
+ assert!(state.bytes.is_empty());
+ }
+
+ #[test]
+ fn test_from_mosaic_schema_rejects_empty_column_name_before_writing() {
+ let schema = MosaicSchema::new(vec![("".to_string(), DataType::Int32,
true)], 1);
+ let state = Arc::new(Mutex::new(FailingOutputState::default()));
+ let out = FailOnceOutputFile {
+ state: Arc::clone(&state),
+ fail: false,
+ };
+
+ let result = MosaicWriter::from_mosaic_schema(out, schema,
WriterOptions::default());
+ let error = match result {
+ Ok(writer) => {
+ std::mem::forget(writer);
+ panic!("empty column name should be rejected");
+ }
+ Err(error) => error,
+ };
+
+ assert_eq!(io::ErrorKind::InvalidInput, error.kind());
+ assert!(error.to_string().contains("empty column name"));
+ let state = state.lock().unwrap();
+ assert_eq!(0, state.write_calls);
+ assert_eq!(0, state.flush_calls);
+ assert!(state.bytes.is_empty());
+ }
+
#[test]
fn test_write_simple_file() {
let arrow_schema = Schema::new(vec![