Skip to content

Add conversions between the typed and dynamic column types - #26

Merged
emilk merged 7 commits into
release-0.6from
emilk/dyn-column-converts
Aug 27, 2026
Merged

Add conversions between the typed and dynamic column types#26
emilk merged 7 commits into
release-0.6from
emilk/dyn-column-converts

Conversation

@emilk

@emilk emilk commented Aug 27, 2026

Copy link
Copy Markdown
Member

Related

  • None

What

There was no way to go between Column<L>/ColumnDesc<Column<L>> and DynColumn/DynColumnDesc. Now there is:

  • ColumnDesc::to_dyn() -> DynColumnDesc, plus impl From<&ColumnDesc<C>> for DynColumnDesc. The declared metadata is dropped, since DynColumnDesc does not carry any.
  • Column::into_dyn(name) -> DynColumn, for L: ConcreteType. The field takes its name from the argument, its datatype and nullability from L, and its metadata from the column. Zero-copy: the array is moved.
  • DynColumn::try_into_column::<L>() -> Result<Column<L>, Error>, the inverse. Validates and downcasts (zero-copy), carrying over the field metadata.

Two things worth a look:

  • into_dyn needs an explicit name, because Column<L> does not store one. Passing it in beats inventing a placeholder.
  • try_into_column errors use record_type: "DynColumn", matching how Column::from_record_batch_and_name uses "Column".

Testing

New tests in tests/column.rs and tests/quiver.rs cover metadata round-tripping, Option<…> as the only source of field nullability, nested inner-field nullability, and that validation follows the array's nulls rather than the field's nullable flag.

Compatibility

Additive only.

🤖 Generated with Claude Code

emilk and others added 4 commits August 27, 2026 16:57
Also `impl From<&ColumnDesc<C>> for DynColumnDesc`. The declared
metadata is dropped, since `DynColumnDesc` does not carry any.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The arrow field takes its name from the argument, its datatype and
nullability from the logical type, and its metadata from the column.
Zero-copy: the array is moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Validates the array against the logical type and downcasts it
(zero-copy), carrying over the arrow field metadata. Errors name the
field, like the other by-name extractors do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers metadata round-tripping, `Option<…>` as the only source of field
nullability, nested inner-field nullability, and that validation follows
the array's nulls rather than the field's nullable flag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@emilk
emilk marked this pull request as ready for review August 27, 2026 15:01
emilk and others added 3 commits August 27, 2026 17:01
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`DynColumn` is not in scope in `column.rs`, so the link needs the
`crate::` path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`Field` is imported since `Column::into_dyn` uses it, so the explicit
`arrow::datatypes::Field` target makes `rustdoc::redundant_explicit_links`
fire under `cargo doc --document-private-items`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@emilk
emilk changed the base branch from main to release-0.6 August 27, 2026 15:08
@emilk
emilk marked this pull request as draft August 27, 2026 15:09
@emilk emilk mentioned this pull request Aug 27, 2026
@emilk
emilk marked this pull request as ready for review August 27, 2026 15:10
@emilk
emilk merged commit 1c283c2 into release-0.6 Aug 27, 2026
6 checks passed

@emilk emilk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-merge review, as part of reviewing the 0.6 stack for #30. Clippy and tests are clean at the tip of release-0.6, so everything below is a follow-up. This closes a real hole, and the test that pins which nullability wins — the array's nulls decide, not the field's nullable flag — is the subtle case and it is asserted three ways.

into_dyn is ConcreteType-only, so the multi-encoding types can't cross. AnyList/AnyUtf8/AnyBinary are parse-only and have no L::datatype(), but a Column<AnyUtf8> has an array and that array has a concrete datatype. self.as_arrow().data_type().clone() plus L::NULLABLE works for every LogicalType, and is more accurate for the concrete ones too (it reports what is there, not what L says should be). As it stands, exactly the users who parsed an unknown encoding have to hand-build the Field.

The metadata clone is now copied five times. field.metadata().iter().map(|(key, value)| (key.clone(), value.clone())).collect() appears here, in Column::extract_named, and three times in generated code (quiver.rs:437, :627, :787). A pub fn field_metadata(field: &Field) -> BTreeMap<String, String> in quiver_types covers all five and shrinks the expansion, which is the one place the copies also cost compile time.

Minor: to_dyn() dropping the declared metadata is documented but untested — with #32's whole-descriptor equality it is a two-line assertion, and "the metadata is dropped" is the kind of documented behavior that quietly becomes false.

— Claude

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant