fix(datatypes): preserve series frame field names

Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com>
This commit is contained in:
discord9
2026-10-15 20:37:16 +08:00
parent 489c9453cd
commit d4a3f67f65
2 changed files with 56 additions and 6 deletions
+3 -5
View File
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Finite Arrow batches for raw series chunks. Field names are ordinal (`f0`, `f1`, ...);
//! callers bind those positions to their logical names, which this frame cannot authenticate.
//! Finite Arrow batches for raw series chunks. Field names preserve the logical series schema in order.
#[cfg(test)]
mod tests;
@@ -50,7 +49,7 @@ pub enum RawLabelMode {
Explicit,
}
/// Builds the typed schema for a raw frame using ordinal domain fields.
/// Builds the typed schema for a raw frame, preserving logical field names and order.
pub fn raw_series_frame_schema(schema: &SeriesSchema, mode: RawLabelMode) -> Result<SchemaRef> {
if schema.eval_grid.is_some() {
return invalid_series("raw series schema cannot have an evaluation grid");
@@ -88,8 +87,7 @@ pub fn raw_series_frame_schema(schema: &SeriesSchema, mode: RawLabelMode) -> Res
let fields: Fields = schema
.fields
.iter()
.enumerate()
.map(|(i, _)| Field::new(format!("f{i}"), payload.clone(), false))
.map(|field| Field::new(field.name.as_ref(), payload.clone(), false))
.collect();
let mut columns = vec![
Field::new("read_key", DataType::Binary, false),
+53 -1
View File
@@ -339,6 +339,58 @@ fn sliced_histogram_frames_densify_without_duplicating_shared_table() {
);
}
#[test]
fn raw_frame_field_names_bind_payloads_to_their_schema() {
let schema = test_schema(&["cpu_usage", "cpu_idle"]);
let fields = SeriesFields::try_from_vec(vec![
Samples::from_float(Float64Array::from(vec![Some(1.0), Some(2.0)])),
Samples::from_float(Float64Array::from(vec![Some(10.0), Some(20.0)])),
])
.unwrap();
for mode in [RawLabelMode::Key, RawLabelMode::Explicit] {
let metric = (mode == RawLabelMode::Explicit).then(|| metric(Some("host"), &[]));
let chunk = RawSeriesChunk::try_new(
&schema,
Buffer::from(vec![0xaa_u8]),
metric,
RawTimeAxis::try_new(vec![100, 200].into()).unwrap(),
fields.clone(),
true,
)
.unwrap();
let batch = &pack_raw_series_chunk(&schema, mode, &chunk).unwrap()[0];
let payloads = batch
.column(3)
.as_any()
.downcast_ref::<StructArray>()
.unwrap();
assert_eq!(
payloads
.fields()
.iter()
.map(|field| field.name().as_str())
.collect::<Vec<_>>(),
["cpu_usage", "cpu_idle"]
);
let decoded = unpack_raw_series_frame(&schema, mode, batch).unwrap();
assert_eq!(decoded[0].read_key.as_slice(), &[0xaa]);
assert!(decoded[0].is_last);
let RawTimeAxis::Explicit(times) = &decoded[0].time;
assert_eq!(times.as_ref(), &[100, 200]);
assert_float_bits(decoded[0].fields.get(0).unwrap(), &[Some(1.0), Some(2.0)]);
assert_float_bits(decoded[0].fields.get(1).unwrap(), &[Some(10.0), Some(20.0)]);
assert!(
unpack_raw_series_frame(&test_schema(&["cpu_idle", "cpu_usage"]), mode, batch).is_err()
);
assert!(
unpack_raw_series_frame(&test_schema(&["cpu_usage", "cpu_busy"]), mode, batch).is_err()
);
}
}
#[test]
fn schema_and_key_explicit_round_trips_preserve_identity_and_bits() {
let schema = test_schema(&["value"]);
@@ -400,7 +452,7 @@ fn schema_and_key_explicit_round_trips_preserve_identity_and_bits() {
),
Field::new(
"fields",
DataType::Struct(vec![Field::new("f0", payload, false)].into()),
DataType::Struct(vec![Field::new("value", payload, false)].into()),
false,
),
],