diff --git a/benchmarks/string-bench/src/serialized.rs b/benchmarks/string-bench/src/serialized.rs index 29d2115ed49..bb9f5fbe872 100644 --- a/benchmarks/string-bench/src/serialized.rs +++ b/benchmarks/string-bench/src/serialized.rs @@ -348,6 +348,8 @@ mod tests { use vortex::VortexSessionDefault; use vortex::array::Canonical; use vortex::array::VortexSessionExecute; + use vortex::editions::CORE_2026_08_3; + use vortex::editions::EditionSessionExt; use vortex::io::runtime::BlockingRuntime; use vortex::io::runtime::current::CurrentThreadRuntime; use vortex::io::session::RuntimeSessionExt; @@ -404,6 +406,7 @@ mod tests { let (column, expected_uncompressed_bytes) = crate::repeated_fixture(); let runtime = CurrentThreadRuntime::new(); let session = VortexSession::default().with_handle(runtime.handle()); + session.enable_edition(CORE_2026_08_3)?; for (encoder, expected_label) in [ (StringEncoder::OnPair, "onpair-12"), diff --git a/docs/specs/editions.md b/docs/specs/editions.md index 52c3d1c2b97..b3c6d721107 100644 --- a/docs/specs/editions.md +++ b/docs/specs/editions.md @@ -9,10 +9,9 @@ guarantee applies to that serialized contract, not to the in-memory implementati Editions belong to independently versioned families and are cumulative within a family. Each edition includes all components from the preceding edition in that family, plus any newly added components. A writer selects at most one -edition from each family and may use the union of their components. For example, selecting `core2026.07.0` and -`unstable2026.06.0` allows stable components released through July 2026 and unstable components released through June - -2026. +edition from each family and may use the union of their components. For example, selecting `core2026.08.1` and +`preview2026.06.0` allows stable components released through August 2026 and preview components released through +June 2026. The first frozen edition, `core2025.05.0`, contains the components that Vortex `0.36.0` could write. This marks the start of the Vortex file format's stability guarantee. Every Vortex release from `0.36.0` onward can read @@ -81,7 +80,7 @@ the previous selection. You can change the default configuration to: - **Target an older `core` edition** when the file must remain readable by an older Vortex deployment. -- **Enable another family** to use components outside `core`. Vortex currently defines `unstable`, `spatial`, and +- **Enable another family** to use components outside `core`. Vortex currently defines `preview`, `spatial`, and `json` in addition to `core`. Sessions created without the Vortex facade must register and enable their editions before writing files. The lower-level @@ -210,43 +209,49 @@ Minimum Vortex release: `0.54.0`. - `array`: `fastlanes.rle`, `vortex.fixed_size_list`, `vortex.listview`, `vortex.masked` -#### `core2026.07.0` - -Minimum Vortex release: `0.65.0`. - -- `array`: `vortex.variant` -- `dtype`: `vortex.uuid` - #### `core2026.08.0` Minimum Vortex release: `0.84.0`. -- `array`: `vortex.map` - `layout`: `vortex.zoned` - `aggregate`: `vortex.bounded_max`, `vortex.bounded_min`, `vortex.max`, `vortex.min`, `vortex.nan_count`, `vortex.null_count` +#### `core2026.08.1` + +Minimum Vortex release: `0.84.0`. + +- `array`: `vortex.map` + ### Draft editions Draft component lists may change and have no minimum reader or permanent compatibility guarantee. -#### `unstable2025.05.0` +#### `core2026.08.2` + +- `array`: `vortex.parquet.variant`, `vortex.variant` +- `dtype`: `vortex.uuid` + +#### `core2026.08.3` + +- `array`: `vortex.onpair` + +#### `preview2025.05.0` - `array`: `fastlanes.delta` -#### `unstable2026.02.0` +#### `preview2026.02.0` - `array`: `vortex.zstd_buffers` -#### `unstable2026.04.0` +#### `preview2026.04.0` -- `array`: `vortex.parquet.variant`, `vortex.patched`, `vortex.tensor.cosine_similarity`, - `vortex.tensor.inner_product`, `vortex.tensor.l2_norm`, `vortex.tensor.normalized` +- `array`: `vortex.patched`, `vortex.tensor.cosine_similarity`, `vortex.tensor.inner_product`, + `vortex.tensor.l2_norm`, `vortex.tensor.normalized` - `dtype`: `vortex.tensor.fixed_shape_tensor`, `vortex.tensor.vector` -#### `unstable2026.06.0` +#### `preview2026.06.0` -- `array`: `vortex.onpair` - `layout`: `vortex.list` #### `spatial2026.08.0` diff --git a/vortex-datafusion/src/persistent/format.rs b/vortex-datafusion/src/persistent/format.rs index 4e59821dbfa..4cf6ab5c9bf 100644 --- a/vortex-datafusion/src/persistent/format.rs +++ b/vortex-datafusion/src/persistent/format.rs @@ -320,6 +320,19 @@ impl VortexFormatFactory { } } + /// Creates a factory with an explicit session and session-driven table options. + /// + /// Formats created by this factory start from the DataFusion session's `vortex` options, + /// falling back to [`VortexTableOptions::default`]. Table-level `OPTIONS(...)` are still + /// applied last. + pub fn new_with_session(session: VortexSession) -> Self { + Self { + session, + options: None, + expression_convertor: None, + } + } + /// Creates a factory with an explicit session and factory-level options. /// /// The supplied options become the complete starting value for every diff --git a/vortex-datafusion/src/persistent/tests.rs b/vortex-datafusion/src/persistent/tests.rs index 41d8141165f..35dd745461d 100644 --- a/vortex-datafusion/src/persistent/tests.rs +++ b/vortex-datafusion/src/persistent/tests.rs @@ -26,6 +26,8 @@ use vortex::array::arrays::VarBinArray; use vortex::array::validity::Validity; use vortex::buffer::Buffer; use vortex::buffer::buffer; +use vortex::editions::CORE_2026_08_3; +use vortex::editions::EditionSessionExt; use vortex::file::OpenOptionsSessionExt; use vortex::file::WriteOptionsSessionExt; use vortex::io::VortexWrite; @@ -497,8 +499,8 @@ async fn arrow_uuid_extension_roundtrip() -> anyhow::Result<()> { use vortex_arrow::ArrowSessionExt; let ctx = TestSessionContext::default(); - // Default vortex session has importer/exporter for Arrow UUID let session = VortexSession::default(); + session.enable_edition(CORE_2026_08_3)?; let mut uuid_field = Field::new("id", DataType::FixedSizeBinary(16), false); uuid_field.try_with_extension_type(Uuid)?; @@ -565,6 +567,7 @@ async fn arrow_uuid_extension_roundtrip_nested_struct() -> anyhow::Result<()> { let ctx = TestSessionContext::default(); let session = VortexSession::default(); + session.enable_edition(CORE_2026_08_3)?; let mut inner_uuid_field = Field::new("id", DataType::FixedSizeBinary(16), false); inner_uuid_field.try_with_extension_type(Uuid)?; diff --git a/vortex-duckdb/src/lib.rs b/vortex-duckdb/src/lib.rs index b16d41e9e75..c8d35eb74e9 100644 --- a/vortex-duckdb/src/lib.rs +++ b/vortex-duckdb/src/lib.rs @@ -12,8 +12,11 @@ use std::sync::OnceLock; use vortex::VortexSessionDefault; use vortex::cloud::Registry; +use vortex::editions::CORE_2026_08_3; +use vortex::editions::EditionSessionExt; use vortex::error::VortexExpect; use vortex::error::VortexResult; +use vortex::error::vortex_err; use vortex::io::runtime::BlockingRuntime; use vortex::io::runtime::current::CurrentThreadRuntime; use vortex::io::session::RuntimeSessionExt; @@ -48,6 +51,10 @@ static RUNTIME: LazyLock = LazyLock::new(CurrentThreadRunt static REGISTRY: LazyLock = LazyLock::new(Registry::new); static SESSION: LazyLock = LazyLock::new(|| { let session = VortexSession::default().with_handle(RUNTIME.handle()); + session + .enable_edition(CORE_2026_08_3) + .map_err(|error| vortex_err!("{error}")) + .vortex_expect("DuckDB-supported draft core edition is registered"); vortex_spatial::initialize(&session); session }); diff --git a/vortex-jni/src/session.rs b/vortex-jni/src/session.rs index 081f55e9b56..8132fd8213a 100644 --- a/vortex-jni/src/session.rs +++ b/vortex-jni/src/session.rs @@ -7,6 +7,10 @@ use jni::EnvUnowned; use jni::objects::JClass; use jni::sys::jlong; use vortex::VortexSessionDefault; +use vortex::editions::CORE_2026_08_3; +use vortex::editions::EditionSessionExt; +use vortex::error::VortexExpect; +use vortex::error::vortex_err; use vortex::io::runtime::BlockingRuntime; use vortex::io::session::RuntimeSessionExt; use vortex::session::VortexSession; @@ -17,6 +21,10 @@ use crate::RUNTIME; /// an opaque pointer that Java must pass to [`Java_dev_vortex_jni_NativeSession_free`]. pub(crate) fn new_session() -> Box { let session = VortexSession::default().with_handle(RUNTIME.handle()); + session + .enable_edition(CORE_2026_08_3) + .map_err(|error| vortex_err!("{error}")) + .vortex_expect("JNI-supported draft core edition is registered"); vortex_parquet_variant::initialize(&session); vortex_spatial::initialize(&session); Box::new(session) diff --git a/vortex-python/src/io.rs b/vortex-python/src/io.rs index 7d117d2b940..a9a1d2017ad 100644 --- a/vortex-python/src/io.rs +++ b/vortex-python/src/io.rs @@ -313,7 +313,7 @@ impl PyVortexWriteOptions { /// >>> vx.io.VortexWriteOptions.default().write(sprl, "chonky.vortex") /// >>> import os /// >>> os.path.getsize('chonky.vortex') - /// 215716 + /// 215684 /// /// Wow, Vortex manages to use about two bytes per integer! So advanced. So tiny. /// @@ -323,7 +323,7 @@ impl PyVortexWriteOptions { /// /// >>> vx.io.VortexWriteOptions.compact().write(sprl, "tiny.vortex") /// >>> os.path.getsize('tiny.vortex') - /// 54920 + /// 54888 /// /// Random numbers are not (usually) composed of random bytes! #[staticmethod] diff --git a/vortex-sqllogictest/bin/sqllogictests-runner.rs b/vortex-sqllogictest/bin/sqllogictests-runner.rs index fd5aa0cec60..a0ad4a74e00 100644 --- a/vortex-sqllogictest/bin/sqllogictests-runner.rs +++ b/vortex-sqllogictest/bin/sqllogictests-runner.rs @@ -21,6 +21,10 @@ use sqllogictest::harness::Arguments; use sqllogictest::harness::Failed; use sqllogictest::harness::Trial; use sqllogictest::strict_column_validator; +use vortex::VortexSessionDefault; +use vortex::editions::CORE_2026_08_3; +use vortex::editions::EditionSessionExt; +use vortex::session::VortexSession; use vortex_datafusion::VortexFormatFactory; use vortex_datafusion::VortexTableOptions; use vortex_sqllogictest::duckdb::DuckDB; @@ -64,7 +68,9 @@ fn drive_datafusion(path: &Path, work_dir: &Path, mode: Mode) -> anyhow::Result< let rt = build_runtime()?; rt.block_on(async { let config = SessionConfig::default().with_option_extension(VortexTableOptions::default()); - let factory = Arc::new(VortexFormatFactory::new()); + let vortex_session = VortexSession::default(); + vortex_session.enable_edition(CORE_2026_08_3)?; + let factory = Arc::new(VortexFormatFactory::new_with_session(vortex_session)); let session_state_builder = SessionStateBuilder::new() .with_config(config) .with_default_features() diff --git a/vortex-test/compat-gen/src/adapter.rs b/vortex-test/compat-gen/src/adapter.rs index be23417e44b..89cc43c678c 100644 --- a/vortex-test/compat-gen/src/adapter.rs +++ b/vortex-test/compat-gen/src/adapter.rs @@ -87,11 +87,21 @@ pub fn write_compressed( pub fn write_compressed_to_bytes( chunk: ArrayRef, strategy: Arc, +) -> VortexResult { + write_compressed_to_bytes_with_session(&VortexSession::default(), chunk, strategy) +} + +/// Write a `.vortex` file into memory using a caller-provided session and layout strategy. +pub fn write_compressed_to_bytes_with_session( + session: &VortexSession, + chunk: ArrayRef, + strategy: Arc, ) -> VortexResult { let stream = ArrayStreamAdapter::new(chunk.dtype().clone(), stream::iter([Ok(chunk)])); + let session = session.clone(); - runtime()?.block_on(async { - let session = VortexSession::default().with_tokio(); + runtime()?.block_on(async move { + let session = session.with_tokio(); let mut bytes = Vec::new(); let _summary = session .write_options() diff --git a/vortex-test/compat-gen/src/fixtures/arrays/datasets/mod.rs b/vortex-test/compat-gen/src/fixtures/arrays/datasets/mod.rs index 1f84d0edeea..f3d506539fd 100644 --- a/vortex-test/compat-gen/src/fixtures/arrays/datasets/mod.rs +++ b/vortex-test/compat-gen/src/fixtures/arrays/datasets/mod.rs @@ -16,10 +16,15 @@ pub fn fixtures() -> Vec> { #[cfg(test)] mod tests { + use vortex::VortexSessionDefault; use vortex::compressor::BtrBlocksCompressorBuilder; + use vortex::editions::CORE_2026_08_3; + use vortex::editions::EditionSessionExt; use vortex::file::WriteStrategyBuilder; - use vortex_array::array_session; + use vortex::session::VortexSession; use vortex_arrow::ArrowSessionExt; + use vortex_error::VortexResult; + use vortex_error::vortex_err; use super::fixtures; use crate::adapter; @@ -29,28 +34,32 @@ mod tests { } #[test] - fn roundtrip_non_clickbench_fixtures_to_bytes() { - let session = array_session(); + fn roundtrip_non_clickbench_fixtures_to_bytes() -> VortexResult<()> { + let session = VortexSession::default(); + session + .enable_edition(CORE_2026_08_3) + .map_err(|error| vortex_err!("{error}"))?; for dataset in fixtures() .into_iter() .filter(|fixture| !is_clickbench_fixture(fixture.name())) { - let array = dataset.build(&session.arrow()).unwrap(); - let regular_bytes = adapter::write_compressed_to_bytes( + let array = dataset.build(&session.arrow())?; + let regular_bytes = adapter::write_compressed_to_bytes_with_session( + &session, array.clone(), WriteStrategyBuilder::default().build(), - ) - .unwrap(); - let _regular = adapter::read_file(regular_bytes).unwrap(); + )?; + let _regular = adapter::read_file(regular_bytes)?; - let compact_bytes = adapter::write_compressed_to_bytes( + let compact_bytes = adapter::write_compressed_to_bytes_with_session( + &session, array, WriteStrategyBuilder::default() .with_btrblocks_builder(BtrBlocksCompressorBuilder::default().with_compact()) .build(), - ) - .unwrap(); - let _compact = adapter::read_file(compact_bytes).unwrap(); + )?; + let _compact = adapter::read_file(compact_bytes)?; } + Ok(()) } } diff --git a/vortex/src/editions/core/mod.rs b/vortex/src/editions/core/mod.rs index 00e295e542e..84a0dcb8a05 100644 --- a/vortex/src/editions/core/mod.rs +++ b/vortex/src/editions/core/mod.rs @@ -9,11 +9,14 @@ pub mod v2025_05; pub mod v2025_06; pub mod v2025_10; -pub mod v2026_07; pub mod v2026_08; +pub mod v2026_08_2; +pub mod v2026_08_3; pub use v2025_05::CORE_2025_05_0; pub use v2025_06::CORE_2025_06_0; pub use v2025_10::CORE_2025_10_0; -pub use v2026_07::CORE_2026_07_0; -pub use v2026_08::CORE_2026_08; +pub use v2026_08::CORE_2026_08_0; +pub use v2026_08::CORE_2026_08_1; +pub use v2026_08_2::CORE_2026_08_2; +pub use v2026_08_3::CORE_2026_08_3; diff --git a/vortex/src/editions/core/v2026_08.rs b/vortex/src/editions/core/v2026_08.rs index 589f6c969b5..07979c9976f 100644 --- a/vortex/src/editions/core/v2026_08.rs +++ b/vortex/src/editions/core/v2026_08.rs @@ -1,17 +1,17 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! The August 2026 core edition adding canonical Map arrays and zoned layouts. +//! The frozen August 2026 core editions. use vortex_edition::Edition; use vortex_edition::EditionDeclaration; use vortex_edition::EditionId; use vortex_edition::EditionMember; -/// The August 2026 core edition containing canonical Map arrays and zoned layouts. -pub const CORE_2026_08: EditionId = EditionId::new("core", 2026, 8, 0); +/// The August 2026 core edition containing zoned layouts. +pub const CORE_2026_08_0: EditionId = EditionId::new("core", 2026, 8, 0); -/// The declaration of [`CORE_2026_08`] and the components that join the family at it. +/// The declaration of [`CORE_2026_08_0`] and the components that join the family at it. /// /// The aggregates are the set the default writer records in zone maps. A strategy asking for an /// aggregate outside this set fails the write instead of producing zone maps an older reader would @@ -20,13 +20,12 @@ pub const CORE_2026_08: EditionId = EditionId::new("core", 2026, 8, 0); /// `vortex.sum` is deliberately not a member: zone maps prune, a zone sum does not, and its /// null-on-empty semantics were changed and reverted within a single week. The writer no longer /// records it, so the two stay consistent. -pub static DECLARATION: EditionDeclaration = EditionDeclaration { +pub static DECLARATION_0: EditionDeclaration = EditionDeclaration { edition: Edition { - id: CORE_2026_08, + id: CORE_2026_08_0, min_vortex_version: Some("0.84.0"), }, added: &[ - EditionMember::array(&"vortex.map"), EditionMember::layout(&"vortex.zoned"), EditionMember::aggregate(&"vortex.bounded_max"), EditionMember::aggregate(&"vortex.bounded_min"), @@ -36,3 +35,15 @@ pub static DECLARATION: EditionDeclaration = EditionDeclaration { EditionMember::aggregate(&"vortex.null_count"), ], }; + +/// The second August 2026 edition of the `core` family, adding OnPair arrays. +pub const CORE_2026_08_1: EditionId = EditionId::new("core", 2026, 8, 1); + +/// The declaration of [`CORE_2026_08_1`] and the components that join the family at it. +pub static DECLARATION_1: EditionDeclaration = EditionDeclaration { + edition: Edition { + id: CORE_2026_08_1, + min_vortex_version: Some("0.84.0"), + }, + added: &[EditionMember::array(&"vortex.onpair")], +}; diff --git a/vortex/src/editions/core/v2026_08_2.rs b/vortex/src/editions/core/v2026_08_2.rs new file mode 100644 index 00000000000..6c423b2f838 --- /dev/null +++ b/vortex/src/editions/core/v2026_08_2.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! The August 2026 draft core edition adding canonical Map arrays. + +use vortex_edition::Edition; +use vortex_edition::EditionDeclaration; +use vortex_edition::EditionId; +use vortex_edition::EditionMember; + +/// The third August 2026 edition of the `core` family. +pub const CORE_2026_08_2: EditionId = EditionId::new("core", 2026, 8, 2); + +/// The declaration of [`CORE_2026_08_2`] and the components that join the family at it. +pub static DECLARATION: EditionDeclaration = EditionDeclaration { + edition: Edition { + id: CORE_2026_08_2, + min_vortex_version: None, + }, + added: &[EditionMember::array(&"vortex.map")], +}; diff --git a/vortex/src/editions/core/v2026_07.rs b/vortex/src/editions/core/v2026_08_3.rs similarity index 54% rename from vortex/src/editions/core/v2026_07.rs rename to vortex/src/editions/core/v2026_08_3.rs index b5f0664a24d..20c3e092b77 100644 --- a/vortex/src/editions/core/v2026_07.rs +++ b/vortex/src/editions/core/v2026_08_3.rs @@ -1,23 +1,24 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! The `core` edition adding stable components released through July 2026. +//! The August 2026 draft core edition adding Variant arrays and UUID extension dtypes. use vortex_edition::Edition; use vortex_edition::EditionDeclaration; use vortex_edition::EditionId; use vortex_edition::EditionMember; -/// The July 2026 edition of the `core` family. -pub const CORE_2026_07_0: EditionId = EditionId::new("core", 2026, 7, 0); +/// The fourth August 2026 edition of the `core` family. +pub const CORE_2026_08_3: EditionId = EditionId::new("core", 2026, 8, 3); -/// The declaration of [`CORE_2026_07_0`] and the components that join the family at it. +/// The declaration of [`CORE_2026_08_3`] and the components that join the family at it. pub static DECLARATION: EditionDeclaration = EditionDeclaration { edition: Edition { - id: CORE_2026_07_0, - min_vortex_version: Some("0.65.0"), + id: CORE_2026_08_3, + min_vortex_version: None, }, added: &[ + EditionMember::array(&"vortex.parquet.variant"), EditionMember::array(&"vortex.variant"), EditionMember::dtype(&"vortex.uuid"), ], diff --git a/vortex/src/editions/mod.rs b/vortex/src/editions/mod.rs index 2837762e8ff..a6dd8ee7fe9 100644 --- a/vortex/src/editions/mod.rs +++ b/vortex/src/editions/mod.rs @@ -13,14 +13,14 @@ //! its declared members, so an empty set permits no components of that kind. //! //! The default file writer resolves the session's enabled editions at write time. The -//! facade enables the newest frozen `core` edition, [`crate::editions::CORE_2026_08`], and -//! additionally enables the latest unstable edition when the `unstable_encodings` feature is +//! facade enables the newest frozen `core` edition, [`crate::editions::CORE_2026_08_1`], and +//! additionally enables the latest preview edition when the `unstable_encodings` feature is //! selected. pub mod core; +pub mod preview; #[cfg(test)] mod tests; -pub mod unstable; pub use vortex_edition::ComponentKind; pub use vortex_edition::Edition; @@ -38,31 +38,35 @@ use vortex_session::VortexSession; pub use self::core::CORE_2025_05_0; pub use self::core::CORE_2025_06_0; pub use self::core::CORE_2025_10_0; -pub use self::core::CORE_2026_07_0; -pub use self::core::CORE_2026_08; -pub use self::unstable::UNSTABLE_2025_05_0; -pub use self::unstable::UNSTABLE_2026_02_0; -pub use self::unstable::UNSTABLE_2026_04_0; -pub use self::unstable::UNSTABLE_2026_06_0; +pub use self::core::CORE_2026_08_0; +pub use self::core::CORE_2026_08_1; +pub use self::core::CORE_2026_08_2; +pub use self::core::CORE_2026_08_3; +pub use self::preview::PREVIEW_2025_05_0; +pub use self::preview::PREVIEW_2026_02_0; +pub use self::preview::PREVIEW_2026_04_0; +pub use self::preview::PREVIEW_2026_06_0; /// The `core` edition enabled for writing by the default Vortex session. -pub const DEFAULT_CORE_EDITION: EditionId = CORE_2026_08; +pub const DEFAULT_CORE_EDITION: EditionId = CORE_2026_08_1; -/// The `unstable` edition enabled for writing by the default Vortex session when the +/// The `preview` edition enabled for writing by the default Vortex session when the /// `unstable_encodings` feature is selected. -pub const DEFAULT_UNSTABLE_EDITION: EditionId = UNSTABLE_2026_06_0; +pub const DEFAULT_PREVIEW_EDITION: EditionId = PREVIEW_2026_06_0; /// The first-party Vortex edition declarations. pub static EDITION_DECLARATIONS: &[&EditionDeclaration] = &[ &core::v2025_05::DECLARATION, &core::v2025_06::DECLARATION, &core::v2025_10::DECLARATION, - &core::v2026_07::DECLARATION, - &core::v2026_08::DECLARATION, - &unstable::v2025_05::DECLARATION, - &unstable::v2026_02::DECLARATION, - &unstable::v2026_04::DECLARATION, - &unstable::v2026_06::DECLARATION, + &core::v2026_08::DECLARATION_0, + &core::v2026_08::DECLARATION_1, + &core::v2026_08_2::DECLARATION, + &core::v2026_08_3::DECLARATION, + &preview::v2025_05::DECLARATION, + &preview::v2026_02::DECLARATION, + &preview::v2026_04::DECLARATION, + &preview::v2026_06::DECLARATION, ]; /// Register the Vortex edition declarations with the session's [`EditionSession`]. @@ -77,7 +81,7 @@ pub fn register_default_editions(session: &VortexSession) { /// Enable the default Vortex editions for writing. /// -/// This selects the newest frozen `core` edition and, when configured, the newest unstable +/// This selects the newest frozen `core` edition and, when configured, the newest preview /// edition. All declarations must have been registered first with /// [`register_default_editions`]. pub fn enable_default_editions(session: &VortexSession) { @@ -88,7 +92,7 @@ pub fn enable_default_editions(session: &VortexSession) { #[cfg(feature = "unstable_encodings")] session - .enable_edition(DEFAULT_UNSTABLE_EDITION) + .enable_edition(DEFAULT_PREVIEW_EDITION) .map_err(|e| vortex_err!("{e}")) - .vortex_expect("default unstable edition is registered"); + .vortex_expect("default preview edition is registered"); } diff --git a/vortex/src/editions/unstable/mod.rs b/vortex/src/editions/preview/mod.rs similarity index 57% rename from vortex/src/editions/unstable/mod.rs rename to vortex/src/editions/preview/mod.rs index 298e262d727..cba3ba1dc6c 100644 --- a/vortex/src/editions/unstable/mod.rs +++ b/vortex/src/editions/preview/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! The `unstable` edition family: opt-in components without a frozen compatibility guarantee. +//! The `preview` edition family: opt-in components without a frozen compatibility guarantee. //! //! One module per draft edition, each declaring the components that join the family at it. //! Members of earlier editions are inherited and never restated. @@ -11,7 +11,7 @@ pub mod v2026_02; pub mod v2026_04; pub mod v2026_06; -pub use v2025_05::UNSTABLE_2025_05_0; -pub use v2026_02::UNSTABLE_2026_02_0; -pub use v2026_04::UNSTABLE_2026_04_0; -pub use v2026_06::UNSTABLE_2026_06_0; +pub use v2025_05::PREVIEW_2025_05_0; +pub use v2026_02::PREVIEW_2026_02_0; +pub use v2026_04::PREVIEW_2026_04_0; +pub use v2026_06::PREVIEW_2026_06_0; diff --git a/vortex/src/editions/unstable/v2025_05.rs b/vortex/src/editions/preview/v2025_05.rs similarity index 58% rename from vortex/src/editions/unstable/v2025_05.rs rename to vortex/src/editions/preview/v2025_05.rs index 0cbae6cc56b..818ec96b15a 100644 --- a/vortex/src/editions/unstable/v2025_05.rs +++ b/vortex/src/editions/preview/v2025_05.rs @@ -1,20 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! The May 2025 `unstable` encoding cohort. +//! The May 2025 `preview` encoding cohort. use vortex_edition::Edition; use vortex_edition::EditionDeclaration; use vortex_edition::EditionId; use vortex_edition::EditionMember; -/// The May 2025 draft edition of the `unstable` family. -pub const UNSTABLE_2025_05_0: EditionId = EditionId::new("unstable", 2025, 5, 0); +/// The May 2025 draft edition of the `preview` family. +pub const PREVIEW_2025_05_0: EditionId = EditionId::new("preview", 2025, 5, 0); -/// The declaration of [`UNSTABLE_2025_05_0`] and the encodings that join the family at it. +/// The declaration of [`PREVIEW_2025_05_0`] and the encodings that join the family at it. pub static DECLARATION: EditionDeclaration = EditionDeclaration { edition: Edition { - id: UNSTABLE_2025_05_0, + id: PREVIEW_2025_05_0, min_vortex_version: None, }, added: &[EditionMember::array(&"fastlanes.delta")], diff --git a/vortex/src/editions/unstable/v2026_02.rs b/vortex/src/editions/preview/v2026_02.rs similarity index 57% rename from vortex/src/editions/unstable/v2026_02.rs rename to vortex/src/editions/preview/v2026_02.rs index 28930435885..691e96c1850 100644 --- a/vortex/src/editions/unstable/v2026_02.rs +++ b/vortex/src/editions/preview/v2026_02.rs @@ -1,20 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! The February 2026 `unstable` encoding cohort. +//! The February 2026 `preview` encoding cohort. use vortex_edition::Edition; use vortex_edition::EditionDeclaration; use vortex_edition::EditionId; use vortex_edition::EditionMember; -/// The February 2026 draft edition of the `unstable` family. -pub const UNSTABLE_2026_02_0: EditionId = EditionId::new("unstable", 2026, 2, 0); +/// The February 2026 draft edition of the `preview` family. +pub const PREVIEW_2026_02_0: EditionId = EditionId::new("preview", 2026, 2, 0); -/// The declaration of [`UNSTABLE_2026_02_0`] and the encodings that join the family at it. +/// The declaration of [`PREVIEW_2026_02_0`] and the encodings that join the family at it. pub static DECLARATION: EditionDeclaration = EditionDeclaration { edition: Edition { - id: UNSTABLE_2026_02_0, + id: PREVIEW_2026_02_0, min_vortex_version: None, }, added: &[EditionMember::array(&"vortex.zstd_buffers")], diff --git a/vortex/src/editions/unstable/v2026_04.rs b/vortex/src/editions/preview/v2026_04.rs similarity index 68% rename from vortex/src/editions/unstable/v2026_04.rs rename to vortex/src/editions/preview/v2026_04.rs index bbce4a890e3..4d1a5ede4b8 100644 --- a/vortex/src/editions/unstable/v2026_04.rs +++ b/vortex/src/editions/preview/v2026_04.rs @@ -1,24 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! The April 2026 `unstable` component cohort. +//! The April 2026 `preview` component cohort. use vortex_edition::Edition; use vortex_edition::EditionDeclaration; use vortex_edition::EditionId; use vortex_edition::EditionMember; -/// The April 2026 draft edition of the `unstable` family. -pub const UNSTABLE_2026_04_0: EditionId = EditionId::new("unstable", 2026, 4, 0); +/// The April 2026 draft edition of the `preview` family. +pub const PREVIEW_2026_04_0: EditionId = EditionId::new("preview", 2026, 4, 0); -/// The declaration of [`UNSTABLE_2026_04_0`] and the components that join the family at it. +/// The declaration of [`PREVIEW_2026_04_0`] and the components that join the family at it. pub static DECLARATION: EditionDeclaration = EditionDeclaration { edition: Edition { - id: UNSTABLE_2026_04_0, + id: PREVIEW_2026_04_0, min_vortex_version: None, }, added: &[ - EditionMember::array(&"vortex.parquet.variant"), EditionMember::array(&"vortex.patched"), EditionMember::array(&"vortex.tensor.cosine_similarity"), EditionMember::array(&"vortex.tensor.inner_product"), diff --git a/vortex/src/editions/preview/v2026_06.rs b/vortex/src/editions/preview/v2026_06.rs new file mode 100644 index 00000000000..fee5ee4da4d --- /dev/null +++ b/vortex/src/editions/preview/v2026_06.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! The June 2026 `preview` component cohort. + +use vortex_edition::Edition; +use vortex_edition::EditionDeclaration; +use vortex_edition::EditionId; +use vortex_edition::EditionMember; + +/// The June 2026 draft edition of the `preview` family. +pub const PREVIEW_2026_06_0: EditionId = EditionId::new("preview", 2026, 6, 0); + +/// The declaration of [`PREVIEW_2026_06_0`] and the components that join the family at it. +pub static DECLARATION: EditionDeclaration = EditionDeclaration { + edition: Edition { + id: PREVIEW_2026_06_0, + min_vortex_version: None, + }, + added: &[EditionMember::layout(&"vortex.list")], +}; diff --git a/vortex/src/editions/tests.rs b/vortex/src/editions/tests.rs index 9167683ec1b..4ba621baf7a 100644 --- a/vortex/src/editions/tests.rs +++ b/vortex/src/editions/tests.rs @@ -44,12 +44,14 @@ use vortex_session::registry::Id; use vortex_utils::aliases::hash_set::HashSet; use super::CORE_2025_05_0; -use super::CORE_2026_07_0; -use super::CORE_2026_08; +use super::CORE_2026_08_0; +use super::CORE_2026_08_1; +use super::CORE_2026_08_2; +use super::CORE_2026_08_3; use super::DEFAULT_CORE_EDITION; -use super::DEFAULT_UNSTABLE_EDITION; +use super::DEFAULT_PREVIEW_EDITION; use super::EDITION_DECLARATIONS; -use super::UNSTABLE_2026_06_0; +use super::PREVIEW_2026_06_0; fn session() -> Result { let session = EditionSession::empty(); @@ -72,9 +74,9 @@ fn every_declared_edition_validates() -> Result<(), EditionError> { /// way it may change is by declaring a *new* edition, so a failure here means a frozen /// declaration was edited. #[test] -fn core_2026_07_encoding_set_is_pinned() { +fn core_2026_08_1_encoding_set_is_pinned() { let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); - let encodings = session.components_in(&CORE_2026_07_0, ComponentKind::Array); + let encodings = session.components_in(&CORE_2026_08_1, ComponentKind::Array); let ids: Vec<&str> = encodings .iter() .map(|inclusion| inclusion.component_id.as_str()) @@ -102,6 +104,7 @@ fn core_2026_07_encoding_set_is_pinned() { "vortex.listview", "vortex.masked", "vortex.null", + "vortex.onpair", "vortex.pco", "vortex.primitive", "vortex.runend", @@ -110,7 +113,6 @@ fn core_2026_07_encoding_set_is_pinned() { "vortex.struct", "vortex.varbin", "vortex.varbinview", - "vortex.variant", "vortex.zigzag", "vortex.zstd", ] @@ -118,21 +120,71 @@ fn core_2026_07_encoding_set_is_pinned() { } #[test] -fn core_2026_07_dtype_set_is_pinned() { +fn core_2026_08_1_dtype_set_is_pinned() { let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); - let dtypes = session.components_in(&CORE_2026_07_0, ComponentKind::DType); + let dtypes = session.components_in(&CORE_2026_08_1, ComponentKind::DType); let ids: Vec<&str> = dtypes .iter() .map(|inclusion| inclusion.component_id.as_str()) .collect(); - assert_eq!( - ids, - [ - "vortex.date", - "vortex.time", - "vortex.timestamp", - "vortex.uuid", - ] + assert_eq!(ids, ["vortex.date", "vortex.time", "vortex.timestamp"]); +} + +#[test] +fn core_2026_08_2_is_draft() { + let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); + assert!( + session + .find(&CORE_2026_08_2) + .unwrap_or_else(|| panic!("{CORE_2026_08_2} is not registered")) + .is_draft() + ); + assert!( + session + .components_in(&CORE_2026_08_2, ComponentKind::Array) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.map") + ); + assert!( + session + .components_in(&CORE_2026_08_1, ComponentKind::Array) + .iter() + .all(|inclusion| inclusion.component_id.as_str() != "vortex.map") + ); +} + +#[test] +fn core_2026_08_3_adds_variants() { + let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); + assert!( + session + .find(&CORE_2026_08_3) + .unwrap_or_else(|| panic!("{CORE_2026_08_3} is not registered")) + .is_draft() + ); + assert!( + session + .components_in(&CORE_2026_08_3, ComponentKind::Array) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.variant") + ); + assert!( + session + .components_in(&CORE_2026_08_3, ComponentKind::Array) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.parquet.variant") + ); + assert!( + session + .components_in(&CORE_2026_08_2, ComponentKind::Array) + .iter() + .all(|inclusion| inclusion.component_id.as_str() != "vortex.parquet.variant") + ); + assert!( + session + .components_in(&CORE_2026_08_3, ComponentKind::DType) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.uuid") ); } @@ -140,14 +192,14 @@ fn core_2026_07_dtype_set_is_pinned() { fn encodings_in_editions_unions_families() { let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); let core_only: Vec<_> = session - .components_in(&CORE_2026_07_0, ComponentKind::Array) + .components_in(&CORE_2026_08_1, ComponentKind::Array) .into_iter() .map(|inclusion| inclusion.component_id) .collect(); let mut both = core_only.clone(); both.extend( session - .components_in(&UNSTABLE_2026_06_0, ComponentKind::Array) + .components_in(&PREVIEW_2026_06_0, ComponentKind::Array) .into_iter() .map(|inclusion| inclusion.component_id), ); @@ -156,7 +208,7 @@ fn encodings_in_editions_unions_families() { assert!(both.len() > core_only.len()); assert!(both.iter().any(|id| id.as_str() == "fastlanes.delta")); - assert!(both.iter().any(|id| id.as_str() == "vortex.onpair")); + assert!(both.iter().any(|id| id.as_str() == "vortex.zstd_buffers")); assert!(core_only.iter().all(|id| both.contains(id))); } @@ -164,7 +216,7 @@ fn encodings_in_editions_unions_families() { fn earlier_editions_are_subsets() { let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); let first = session.components_in(&CORE_2025_05_0, ComponentKind::Array); - let latest = session.components_in(&CORE_2026_08, ComponentKind::Array); + let latest = session.components_in(&CORE_2026_08_1, ComponentKind::Array); assert!(first.iter().all(|inclusion| { latest .iter() @@ -173,6 +225,48 @@ fn earlier_editions_are_subsets() { assert!(first.len() < latest.len()); } +#[test] +fn core_2026_08_editions_add_onpair_before_map() { + let session = session().unwrap_or_else(|e| panic!("registering editions: {e}")); + assert!( + session + .components_in(&CORE_2026_08_0, ComponentKind::Array) + .iter() + .all(|inclusion| inclusion.component_id.as_str() != "vortex.onpair") + ); + assert!( + session + .components_in(&CORE_2026_08_0, ComponentKind::Layout) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.zoned") + ); + assert!( + session + .components_in(&CORE_2026_08_0, ComponentKind::Aggregate) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.min") + ); + + assert!( + session + .components_in(&CORE_2026_08_1, ComponentKind::Array) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.onpair") + ); + assert!( + session + .components_in(&CORE_2026_08_1, ComponentKind::Array) + .iter() + .all(|inclusion| inclusion.component_id.as_str() != "vortex.map") + ); + assert!( + session + .components_in(&CORE_2026_08_2, ComponentKind::Array) + .iter() + .any(|inclusion| inclusion.component_id.as_str() == "vortex.map") + ); +} + #[test] fn default_session_enables_the_write_editions() { use crate::VortexSessionDefault; @@ -182,9 +276,9 @@ fn default_session_enables_the_write_editions() { assert!(enabled.contains(&DEFAULT_CORE_EDITION)); #[cfg(feature = "unstable_encodings")] - assert!(enabled.contains(&DEFAULT_UNSTABLE_EDITION)); + assert!(enabled.contains(&DEFAULT_PREVIEW_EDITION)); #[cfg(not(feature = "unstable_encodings"))] - assert!(!enabled.contains(&DEFAULT_UNSTABLE_EDITION)); + assert!(!enabled.contains(&DEFAULT_PREVIEW_EDITION)); } #[test] @@ -197,7 +291,7 @@ fn core_edition_ids_are_registered_array_encodings() { let registry = session.arrays().registry().clone(); for inclusion in session .editions() - .components_in(&CORE_2026_08, ComponentKind::Array) + .components_in(&CORE_2026_08_1, ComponentKind::Array) { assert!( registry.contains_key(&inclusion.component_id), @@ -217,7 +311,7 @@ fn core_dtype_ids_are_registered_extension_dtypes() { let registry = session.dtypes().registry().clone(); for inclusion in session .editions() - .components_in(&CORE_2026_08, ComponentKind::DType) + .components_in(&CORE_2026_08_1, ComponentKind::DType) { assert!( registry.contains_key(&inclusion.component_id), @@ -238,7 +332,7 @@ fn core_aggregate_ids_are_registered_aggregate_fns() { let session = VortexSession::default(); let declared = session .editions() - .components_in(&CORE_2026_08, ComponentKind::Aggregate); + .components_in(&CORE_2026_08_1, ComponentKind::Aggregate); assert!( declared .iter() diff --git a/vortex/src/editions/unstable/v2026_06.rs b/vortex/src/editions/unstable/v2026_06.rs deleted file mode 100644 index fe4915dfa70..00000000000 --- a/vortex/src/editions/unstable/v2026_06.rs +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright the Vortex contributors - -//! The June 2026 `unstable` component cohort. - -use vortex_edition::Edition; -use vortex_edition::EditionDeclaration; -use vortex_edition::EditionId; -use vortex_edition::EditionMember; - -/// The June 2026 draft edition of the `unstable` family. -pub const UNSTABLE_2026_06_0: EditionId = EditionId::new("unstable", 2026, 6, 0); - -/// The declaration of [`UNSTABLE_2026_06_0`] and the components that join the family at it. -pub static DECLARATION: EditionDeclaration = EditionDeclaration { - edition: Edition { - id: UNSTABLE_2026_06_0, - min_vortex_version: None, - }, - added: &[ - EditionMember::array(&"vortex.onpair"), - EditionMember::layout(&"vortex.list"), - ], -};