use arrow::array::ArrayRef;
use tokio::io::AsyncWriteExt;
use crate::formats::SerializerState;
use crate::io::{ClickHouseBytesWrite, ClickHouseWrite};
use crate::{Result, Type};
pub(super) async fn serialize_nulls_async<W: ClickHouseWrite>(
type_hint: &Type,
writer: &mut W,
array: &ArrayRef,
_state: &mut SerializerState,
) -> Result<()> {
if matches!(type_hint.strip_null(), Type::Array(_) | Type::Map(_, _)) {
return Ok(());
}
if let Some(null_buffer) = array.nulls() {
let mut null_mask = vec![1_u8; array.len()]; for valid_idx in null_buffer.valid_indices() {
null_mask[valid_idx] = 0; }
writer.write_all(&null_mask).await?;
} else {
let nulls = vec![0_u8; array.len()];
writer.write_all(&nulls).await?;
}
Ok(())
}
pub(super) fn serialize_nulls<W: ClickHouseBytesWrite>(
type_hint: &Type,
writer: &mut W,
array: &ArrayRef,
_state: &mut SerializerState,
) {
if matches!(type_hint.strip_null(), Type::Array(_) | Type::Map(_, _)) {
return;
}
if let Some(null_buffer) = array.nulls() {
let mut null_mask = vec![1_u8; array.len()]; for valid_idx in null_buffer.valid_indices() {
null_mask[valid_idx] = 0; }
writer.put_slice(&null_mask);
} else {
let nulls = vec![0_u8; array.len()];
writer.put_slice(&nulls);
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use arrow::array::{Int32Array, ListArray, StringArray};
use arrow::datatypes::Int32Type;
use super::*;
type MockWriter = Vec<u8>;
#[tokio::test]
async fn test_write_nullability_with_nulls() {
let mut state = SerializerState::default();
let array = Arc::new(Int32Array::from(vec![Some(1), None, Some(3)])) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls_async(&Type::Int32, &mut writer, &array, &mut state).await.unwrap();
assert_eq!(writer, vec![0, 1, 0]); }
#[tokio::test]
async fn test_write_nullability_without_nulls() {
let mut state = SerializerState::default();
let array = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls_async(&Type::Int32, &mut writer, &array, &mut state).await.unwrap();
assert_eq!(writer, vec![0, 0, 0]); }
#[tokio::test]
async fn test_write_nullability_empty() {
let mut state = SerializerState::default();
let array = Arc::new(Int32Array::from(Vec::<i32>::new())) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls_async(&Type::Int32, &mut writer, &array, &mut state).await.unwrap();
assert!(writer.is_empty());
}
#[tokio::test]
async fn test_write_nullability_nullable_string() {
let mut state = SerializerState::default();
let array = Arc::new(StringArray::from(vec![Some("a"), None, Some("c")])) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls_async(&Type::String, &mut writer, &array, &mut state).await.unwrap();
assert_eq!(writer, vec![0, 1, 0]); }
#[tokio::test]
async fn test_write_nullability_nullable_array() {
let mut state = SerializerState::default();
let data = vec![
Some(vec![Some(0), Some(1), Some(2)]),
None,
Some(vec![Some(3), None, Some(5)]),
Some(vec![Some(6), Some(7)]),
];
let list_array = ListArray::from_iter_primitive::<Int32Type, _, _>(data);
let array = Arc::new(list_array) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls_async(
&Type::Nullable(Type::Array(Type::Int32.into()).into()),
&mut writer,
&array,
&mut state,
)
.await
.unwrap();
assert!(writer.is_empty());
}
}
#[cfg(test)]
mod tests_sync {
use std::sync::Arc;
use arrow::array::{Int32Array, ListArray, StringArray};
use arrow::datatypes::Int32Type;
use super::*;
type MockWriter = Vec<u8>;
#[test]
fn test_write_nullability_with_nulls() {
let mut state = SerializerState::default();
let array = Arc::new(Int32Array::from(vec![Some(1), None, Some(3)])) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls(&Type::Int32, &mut writer, &array, &mut state);
assert_eq!(writer, vec![0, 1, 0]); }
#[test]
fn test_write_nullability_without_nulls() {
let mut state = SerializerState::default();
let array = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls(&Type::Int32, &mut writer, &array, &mut state);
assert_eq!(writer, vec![0, 0, 0]); }
#[test]
fn test_write_nullability_empty() {
let mut state = SerializerState::default();
let array = Arc::new(Int32Array::from(Vec::<i32>::new())) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls(&Type::Int32, &mut writer, &array, &mut state);
assert!(writer.is_empty());
}
#[test]
fn test_write_nullability_nullable_string() {
let mut state = SerializerState::default();
let array = Arc::new(StringArray::from(vec![Some("a"), None, Some("c")])) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls(&Type::String, &mut writer, &array, &mut state);
assert_eq!(writer, vec![0, 1, 0]); }
#[test]
fn test_write_nullability_nullable_array() {
let mut state = SerializerState::default();
let data = vec![
Some(vec![Some(0), Some(1), Some(2)]),
None,
Some(vec![Some(3), None, Some(5)]),
Some(vec![Some(6), Some(7)]),
];
let list_array = ListArray::from_iter_primitive::<Int32Type, _, _>(data);
let array = Arc::new(list_array) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls(
&Type::Nullable(Type::Array(Type::Int32.into()).into()),
&mut writer,
&array,
&mut state,
);
assert!(writer.is_empty());
}
#[test]
fn test_write_nullability_array_type_variations() {
let mut state = SerializerState::default();
let data = vec![
Some(vec![Some(1i64), None, Some(3)]),
Some(vec![None, None]),
Some(vec![Some(10), Some(20), Some(30)]),
];
let list_array = ListArray::from_iter_primitive::<arrow::datatypes::Int64Type, _, _>(data);
let array = Arc::new(list_array) as ArrayRef;
let mut writer = MockWriter::new();
serialize_nulls(
&Type::Array(Type::Nullable(Type::Int64.into()).into()),
&mut writer,
&array,
&mut state,
);
assert!(writer.is_empty(), "Array type should not write null mask");
let data2 = vec![
Some(vec![Some(1i64), Some(2), Some(3)]),
None, Some(vec![Some(10), Some(20)]),
];
let list_array2 =
ListArray::from_iter_primitive::<arrow::datatypes::Int64Type, _, _>(data2);
let array2 = Arc::new(list_array2) as ArrayRef;
let mut writer2 = MockWriter::new();
serialize_nulls(
&Type::Nullable(Type::Array(Type::Int64.into()).into()),
&mut writer2,
&array2,
&mut state,
);
assert!(writer2.is_empty(), "Nullable(Array) should not write null mask");
let mut writer3 = MockWriter::new();
serialize_nulls(
&Type::Nullable(Type::Map(Type::String.into(), Type::Int32.into()).into()),
&mut writer3,
&array, &mut state,
);
assert!(writer3.is_empty(), "Nullable(Map) should not write null mask");
}
}