Purpose
Make leaf column-option tokens a consistent read-only snapshot, with an explicit way to edit raw options. Currently getTokens().set(...) can mutate a raw option's SQL, silently edit a detached typed-option snapshot, or throw depending on the option kind.
Current design (As-is)
ColumnDefinition.getColumnSpecs() already provides an unmodifiable snapshot, but ColumnOption.getTokens() mixes mutable backing lists, mutable snapshots and unmodifiable lists. Raw factories also retain the caller's list or array. Generated-column token lists are mutable snapshots whose edits do not affect rendering.
Target design (To-be)
- Return unmodifiable detached snapshots from raw and typed option tokens, including generated definitions.
- Add
setTokens(List<String>) for opaque Kind.OTHER options, copying the input. Raw factories also copy inputs. Preserve null (unset) versus empty raw tokens.
- Reject raw token replacement on typed options, including SERIAL DEFAULT VALUE, with an explicit exception. Typed setters and editable expression/constraint children remain the way to edit structured options.
- Document migration and preserve custom deparser and traversal behavior.
Migration
Direct token-list mutation now throws consistently. Use an explicit replacement for opaque options:
List<String> replacement = new ArrayList<>(rawOption.getTokens());
replacement.set(1, "FIXED");
rawOption.setTokens(replacement);
For typed options, use e.g. option.setDefaultExpression(...) or option.getGeneratedDefinition().setExpression(...). To replace a typed option with raw text, replace the node through column.getColumnOptions().set(i, ColumnOption.raw(tokens)).
Validation
- Tests cover read-only tokens across typed kinds; parsed CREATE/ALTER raw-option editing; input ownership; null/empty semantics; explicit rejection without mutation; live expression edits; stable snapshots; custom expression rendering; traversal and reparsing.
- Java 17 offline Gradle
spotlessApply check: 10,065 tests, 10,040 passed, 25 skipped, zero failures/errors. Grammar, Checkstyle, PMD, SpotBugs and coverage checks pass.
- Default PMD comparison finds no newly affected symbols; pre-existing complexity and field-placement findings remain.
Integration validation
Applied #2770, #2771 and all four follow-ups (#2775–#2778) together on master (066f581e), resolving overlapping validator edits while preserving their respective behavior. The integrated build passes Java 17 offline Gradle spotlessApply check: 10,141 tests, 10,116 passed, 25 skipped, zero failures/errors. The combined ALTER validator has zero findings under the default PMD complexity and field-placement rules.
Replayed 882 previously DB-validated MySQL/PostgreSQL DDL fixtures: 875 pass and the same seven existing parse gaps remain. SQL output, reparsing and AST node-type sets match the preceding integration baseline, with no new differences. This replay did not rerun the databases.
The published head passes grammar checks, Gradle and Maven on Ubuntu/Windows/macOS, and Codacy. Deployment-only jobs are skipped as expected for a PR.