{"record":{"id":"11003b36210bc4ea","repo":"risingwavelabs/risingwave","slug":"column-was-persisted-with-legacy-encoding-thu","errorCode":null,"errorMessage":"column \"{}\" was persisted with legacy encoding thus cannot be altered, consider dropping and readding the column","messagePattern":"column \"(.+?)\" was persisted with legacy encoding thus cannot be altered, consider dropping and readding the column","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/handler/create_table/col_id_gen.rs","lineNumber":168,"sourceCode":"\n    /// Generate [`ColumnId`]s for the given column and its nested fields (if any) recursively.\n    /// The IDs of nested fields will be reflected in the updated [`DataType`] of the column.\n    ///\n    /// Returns an error if there's incompatible data type change.\n    pub fn generate(&mut self, col: &mut ColumnCatalog) -> Result<()> {\n        let mut path = vec![Segment::Field(col.name().to_owned())];\n\n        if let Some((original_column_id, original_data_type)) = self.existing.get(&path) {\n            if original_data_type == col.data_type() {\n                col.column_desc.column_id = *original_column_id;\n                // Equality above ignores nested field IDs. We need to clone them below.\n                col.column_desc.data_type = original_data_type.clone();\n                return Ok(());\n            } else {\n                // Check if the column can be altered.\n                match original_data_type.can_alter() {\n                    Some(true) => { /* pass */ }\n                    Some(false) => bail!(\n                        \"column \\\"{}\\\" was persisted with legacy encoding thus cannot be altered, \\\n                         consider dropping and readding the column\",\n                        col.name()\n                    ),\n                    None => bail!(\n                        \"column \\\"{}\\\" cannot be altered; only types containing struct can be altered\",\n                        col.name()\n                    ),\n                }\n            }\n        }\n\n        fn handle(\n            this: &mut ColumnIdGenerator,\n            path: &mut Path,\n            data_type: DataType,\n        ) -> Result<(ColumnId, DataType)> {\n            macro_rules! with_segment {","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/handler/create_table/col_id_gen.rs#L150-L186","documentation":"`ColIdGenerator::generate` refuses to ALTER a column whose original data type was persisted with legacy encoding: `DataType::can_alter()` returned `Some(false)`. Changing such a column's type in place would produce data unreadable by the new encoding, so the operation is rejected with guidance to drop and re-add the column.","triggerScenarios":"`ALTER TABLE ... ALTER COLUMN c TYPE ...` where the target type is struct-containing-alterable but the column's original type was persisted before the current encoding (e.g. older struct types), making in-place type alteration unsafe.","commonSituations":"Tables created on an older RisingWave version being altered after an upgrade; migration scripts that alter legacy columns in place.","solutions":["Follow the message: `ALTER TABLE t DROP COLUMN c;` then `ALTER TABLE t ADD COLUMN c <new_type>;`, backfilling data if needed.","Create a new column with the desired type, copy values with a cast via a batch UPDATE/INSERT, then drop the old column.","If legacy encoding is no longer needed, recreate the table and stream data into it."],"exampleFix":"-- before (rejected)\nALTER TABLE t ALTER COLUMN s TYPE struct<a int>;\n-- after\nALTER TABLE t ADD COLUMN s_new struct<a int>;\nUPDATE t SET s_new = s;\nALTER TABLE t DROP COLUMN s;\nALTER TABLE t RENAME COLUMN s_new TO s;","handlingStrategy":"validation","validationCode":"fn check_alterable(original: &DataType, new: &DataType) -> Result<(), String> {\n    match original.can_alter() {\n        Some(true) => Ok(()),\n        Some(false) => Err(\"column uses legacy encoding; drop and re-add instead\".into()),\n        None => Err(\"only struct-containing types can be altered\".into()),\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check `DataType::can_alter()` semantics before planning ALTER COLUMN TYPE","Prefer add-new-column + migrate + drop pattern for legacy columns","Review release notes on encoding changes before altering old tables"],"tags":["rust","schema","alter-table","encoding"],"backgroundTag":"unsupported-operation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}