data-structures: a publish keeps a column its table no longer declares, drops it only when listed as dropped, and renames it in place when declared renamedFrom (#7635) - #7674
Merged
Conversation
…s, drops it only when listed as dropped, and renames it in place when declared renamedFrom (#7635) TableAlterProcessor reconciled a published .table / .schema against the live table by ADDing missing columns and then DROPping every live column the model no longer named - in every tenant schema, with nothing in the log beyond the INFO "Processing Alter Table". A renamed intent field emits a new column name, so its data was gone on the first boot of the new image, and a rollback that only swaps the image could not bring it back. The alter now follows an expand/contract contract: - An undeclared column is KEPT with its data, logged at WARN with the table, the column and the artefact that owns the table, and counted as the orphanColumns detail of the artefacts health component (PlatformReadiness.recordOrphanColumns, keyed per catalog.schema.table so one tenant's copy does not overwrite another's). A kept NOT NULL column is relaxed to nullable, because the application no longer writes it and every insert would be refused otherwise; a dialect that cannot express that without the column's whole type (MySQL, MariaDB, MSSQL, HANA) leaves it and says so. - A column leaves only when the table lists it under `dropped`, or on an instance with DIRIGIBLE_DATABASE_DROP_UNDECLARED_COLUMNS=true (dev opt-in). - A column declaring `renamedFrom` renames the live one in place before the ADD pass (new ISqlDialect.renameColumn: ANSI by default, HANA's own form). Where a dialect has no in-place rename (MSSQL, MongoDB) the ADD pass adds the column nullable, copies the values and keeps the old one. - The incompatible-change refusals are unchanged. The intent carries both: a field's `renamedFrom: <old name>` and an entity's `dropped: [names]` become the .model's dataRenamedFrom / dataDropped (the former COLUMN names, derived like dataName; comma-separated so they survive the scalar-only .edm), which the schema template emits as the column's renamedFrom and the table's dropped. The parser refuses either one naming a column the entity still declares (compared by column, so a relation counts), a rename from its own name or from a dropped name, and two renames from one name. Verified: TableAlterProcessorTest (keep + count + relaxed NOT NULL, declared drop, operator opt-in, in-place rename with a no-op second run), ColumnChangeStatementTest (default / MySQL / HANA statement shapes), PlatformReadinessTest, SchemaEvolutionValidationTest, EdmIntentGeneratorTest; the unit suites of every changed module; TableUndeclaredColumnIT on H2 and on a local PostgreSQL 16 (publish A,B + a row, republish A only: B and its data survive, the WARN is logged, an insert without B passes; republish with dropped: [B]: B is gone; the schema the template renders from a .model with dataRenamedFrom / dataDropped moves the data and drops the column), plus SchemaRepublishTypeToleranceIT, SchemaTemplateUniqueConstraintIT, SchemaTwoUniqueConstraintsIT and IntentEngineIT on H2; formatter:validate with the cache wiped; the release-profile javadoc build of the touched modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cause
TableAlterProcessor, the alter path of every.tableand.schema(so of every intent-generated table), reconciled the live table in two passes. First it ADDed the columns the model declares, then it DROPped every live column the model no longer names.TablesSynchronizer/SchemasSynchronizerare multitenant, so the DROP ran in every tenant schema, and the only log line was the INFOProcessing Alter Table. A renamed intent field emits a new column name, so the publish added the new column and dropped the old one with its data, on the first boot of the new image. An image-only rollback cannot undo that.Change: expand/contract for undeclared columns
orphanColumnsdetail of theartefactshealth component (PlatformReadiness.recordOrphanColumns, keyed percatalog.schema.tableso one tenant's copy does not overwrite another's). A keptNOT NULLcolumn is relaxed to nullable, because the app no longer writes it and every insert would otherwise be refused (newISqlDialect.dropNotNull). MySQL, MariaDB, MSSQL and HANA returnnullthere, because they need the whole column type restated; they get a WARN saying inserts that omit the column will be refused.dropped: [...]removes those columns.DIRIGIBLE_DATABASE_DROP_UNDECLARED_COLUMNS=trueis an operator opt-in for dev instances.renamedFromrenames the live one in place before the ADD pass (newISqlDialect.renameColumn: ANSI by default, HANA'sRENAME COLUMN t.c TO n). On MSSQL and MongoDB it returnsnull, and the ADD pass then adds the column nullable, copies the values and keeps the old one. A rename runs only while the old name is live and the new one is not, so it is harmless to leave in the definition.INCOMPATIBLE_CHANGE_OF_TABLErefusals are unchanged.Intent DSL. A field's
renamedFrom: <old name>and an entity'sdropped: [names]become the.model'sdataRenamedFrom/dataDropped. These are the former column names, derived likedataName, and comma-separated so they survive the scalar-only.edm.template-application-schemaemits them as the column'srenamedFromand the table'sdropped. The parser refuses:The assistant guide,
intent-dsl-features.mdandsynchronizer-model.mddocument it.Verification
Run:
TableAlterProcessorTest(keep + count + relaxed NOT NULL with a later insert, declared drop, operator opt-in, in-place rename with a no-op second run),ColumnChangeStatementTest(default / MySQL / HANA),PlatformReadinessTest,SchemaEvolutionValidationTest,EdmIntentGeneratorTest. Every changed module's unit suite is green.TableUndeclaredColumnIT(new, HTTP-level, untagged so it is in the PR smoke set) on H2 and on a local PostgreSQL 16.pg_stat_databaseconfirmed PostgreSQL was written to. The run:A, B NOT NULLwith a row, then republish withAonly:Band its value survive, the WARN is logged, the orphan is counted, and an insert withoutBpasses;dropped: [B]:Bis gone;.modelwithdataRenamedFrom/dataDropped, published over the old table: the values moved to the new name, and the old and the dropped columns are gone.SchemaRepublishTypeToleranceIT(H2 + PostgreSQL),SchemaTemplateUniqueConstraintIT,SchemaTwoUniqueConstraintsITandIntentEngineIT(81 tests) on H2.formatter:validatewith the cache wiped, and the release-profile javadoc build of the touched modules.Not run:
Not in this PR: the intentfile.org spec/site and dirigible.io
/help/intent/pages forrenamedFrom:/dropped:follow as separate PRs.Fixes #7635
🤖 Generated with Claude Code