Skip to content

Commit 628af3c

Browse files
feat: add rename_column support for ALTER TABLE
Adds RenameColumn as a terminal operation in the ALTER TABLE framework: - Renaming type state ensures rename cannot be combined with other operations - Buildable trait + sealed module for shared build() between Modifying and Renaming - Case-insensitive sibling conflict check via sibling_names_at_path - Physical identity (column ID + physical name) preserved via with_name - Runtime defense-in-depth: RenameColumn must be the sole operation Full integration test suite covering add/drop/rename/set_nullable, chaining, sequential alters, checkpoints, time travel, and append-only tables.
1 parent 40e3a1f commit 628af3c

4 files changed

Lines changed: 365 additions & 32 deletions

File tree

kernel/src/transaction/alter_table.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//! [`try_new_alter_table`](AlterTableTransaction::try_new_alter_table) constructor.
55
//! The builder logic lives in [`builder::alter_table`](super::builder::alter_table).
66
7+
// Allow `pub` items -- module visibility controls external access; items are `pub` for
8+
// use within the crate and for tests.
79
#![allow(unreachable_pub)]
810

911
use std::marker::PhantomData;

kernel/src/transaction/builder/alter_table.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
//!
88
//! - [`Ready`]: Initial state. Operations are available, but `build()` is not (at least one
99
//! operation is required).
10-
//! - [`Modifying`]: After at least one operation (`add_column`, `set_nullable`). Can chain more
11-
//! operations, and `build()` is available.
10+
//! - [`Modifying`]: After `add_column`, `drop_column`, or `set_nullable`. These can be chained,
11+
//! and `build()` is available.
12+
//! - [`Renaming`]: After `rename_column`. Only `build()` is available (rename is terminal).
1213
1314
use std::marker::PhantomData;
1415
use std::sync::Arc;
@@ -31,6 +32,9 @@ pub struct Ready;
3132
/// State after at least one operation has been added. `add_column` and `build()` are available.
3233
pub struct Modifying;
3334

35+
/// State after rename_column. Only `build()` is available (rename is terminal).
36+
pub struct Renaming;
37+
3438
/// Builder for constructing an [`AlterTableTransaction`] with schema evolution operations.
3539
///
3640
/// Uses a type-state pattern to enforce valid operation chaining at compile time.
@@ -107,6 +111,30 @@ impl AlterTableTransactionBuilder<Ready> {
107111
self.operations.push(SchemaOperation::SetNullable { path });
108112
self.transition()
109113
}
114+
115+
/// Rename a column in the table schema. Supports nested columns via [`ColumnName`] paths.
116+
///
117+
/// Requires column mapping mode = name or id. Only the logical name changes; the physical
118+
/// name and column ID remain the same.
119+
///
120+
/// Rename is a terminal operation -- no further operations can be chained.
121+
///
122+
/// # Errors (at build time)
123+
///
124+
/// - Column does not exist
125+
/// - Column mapping mode is not enabled
126+
/// - New name conflicts with an existing column at the same level
127+
pub fn rename_column(
128+
mut self,
129+
path: ColumnName,
130+
new_name: impl Into<String>,
131+
) -> AlterTableTransactionBuilder<Renaming> {
132+
self.operations.push(SchemaOperation::RenameColumn {
133+
path,
134+
new_name: new_name.into(),
135+
});
136+
self.transition()
137+
}
110138
}
111139

112140
impl AlterTableTransactionBuilder<Modifying> {
@@ -127,7 +155,21 @@ impl AlterTableTransactionBuilder<Modifying> {
127155
self.operations.push(SchemaOperation::SetNullable { path });
128156
self
129157
}
158+
}
159+
160+
/// Marker trait for builder states that support `build()`. Sealed to prevent external impls.
161+
pub trait Buildable: sealed::Sealed {}
162+
impl Buildable for Modifying {}
163+
impl Buildable for Renaming {}
164+
165+
mod sealed {
166+
/// Prevents external implementations of [`Buildable`](super::Buildable).
167+
pub trait Sealed {}
168+
impl Sealed for super::Modifying {}
169+
impl Sealed for super::Renaming {}
170+
}
130171

172+
impl<S: Buildable> AlterTableTransactionBuilder<S> {
131173
/// Validate and apply schema operations, then build the [`AlterTableTransaction`].
132174
///
133175
/// This method:

kernel/src/transaction/mod.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,10 +445,9 @@ impl<S> Transaction<S> {
445445
let remove_actions =
446446
self.generate_remove_actions(engine, self.remove_files_metadata.iter(), &[])?;
447447

448-
// Build the action chain
449-
// For create-table: CommitInfo -> Protocol -> Metadata -> adds -> txns -> domain_metadata
450-
// -> removes For existing table: CommitInfo -> adds -> txns -> domain_metadata ->
451-
// removes
448+
// Build the action chain:
449+
// CommitInfo -> Protocol (if emitted) -> Metadata (if emitted) -> adds -> txns ->
450+
// domain_metadata -> removes
452451
let actions = iter::once(commit_info_action)
453452
.chain(protocol_action.map(Ok))
454453
.chain(metadata_action.map(Ok))
@@ -740,6 +739,11 @@ impl<S> Transaction<S> {
740739
// must be the larger of:
741740
// - The time at which the writer attempted the commit
742741
// - One millisecond later than the previous commit's inCommitTimestamp
742+
//
743+
// TODO: When ALTER TABLE supports enabling ICT for the first time, the read snapshot
744+
// won't have a prior ICT. In that case we should fall back to self.commit_timestamp
745+
// (same as CREATE TABLE). Currently protocol evolution is out of scope so this path
746+
// is not reachable.
743747
Ok(self
744748
.read_snapshot()?
745749
.get_in_commit_timestamp(engine)?

0 commit comments

Comments
 (0)