Skip to content

Pluggable extension types: register custom logical types on the read/write registries #530

Open

Description

@dfa1

Rust lets users add a logical type by implementing ExtVTable (id(), validate_dtype(), metadata/scalar handling) and registering it on the session (session.dtypes().register(Vector)); see Adding a new logical type.

Current state (not supported)

  • ExtensionId is a closed enum (VORTEX_DATE/TIME/TIMESTAMP/UUID), so a third-party id cannot even be named.
  • Read side: ExtensionDecoders are singletons dispatched by a case in Chunk.as(), not registry-managed.
  • Write side: WriteRegistry.Builder#register(ExtensionEncoder) exists but can only re-implement the four built-ins.
  • CLAUDE.md says DType is pluggable only via Extension, but today that only works for wire-known ids, and docs/reference.md documents the gap.

Proposal

  • Make ExtensionId a sealed WellKnown/Custom type, like EncodingId/LayoutId.
  • Register ExtensionDecoder on ReadRegistry.Builder (built-ins via registerDefaults()), replacing the switch in Chunk.as().
  • Add a storage-dtype validation hook (Rust's validate_dtype) on both sides.
  • Unknown extension ids: decode as the storage array (Rust default), with an opt-in/out policy to decide.
  • Interop test: a Rust-written custom extension (e.g. a Vector over FixedSizeList<f32>) read by Java, and vice versa.
  • Update docs/reference.md, CLAUDE.md and the CHANGELOG in the same change.

Out of scope: compute functions on custom types (Rust vortex-tensor-style scalar functions).

Activity

  1. dfa1 commented on Oct 11, 2026

    @dfa1
    OwnerAuthor

    Write-side details

    WriteRegistry is mostly ready: it keeps a Map<ExtensionId, ExtensionEncoder> with register(ExtensionEncoder) / lookup(ExtensionId), so it works unchanged once ExtensionId is sealed with a Custom variant. What needs to change:

    • VortexWriter (~line 554) resolves the encoder with ExtensionId.parse(extDtype.extensionId()).map(defaultRegistry::lookup).orElse(null). parse only knows the four built-in ids, so an unknown id yields empty and the lookup is skipped. It must resolve unknown ids to ExtensionId.Custom.
    • When no encoder is found, impl is null and a Collection is passed through unconverted, which fails later with a confusing error. Fail fast with a VortexException naming the id, or accept only raw storage arrays.
    • Add the storage-dtype validation hook (Rust's validate_dtype) to ExtensionEncoder, checked at VortexWriter.create time.
    • ExtensionEncoder.extensionId(), dtype(boolean) and encodeAll are generic enough for custom types as they are.
    • DateTimePartsEncodingEncoder compares the timestamp id as a string and is unaffected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions