|
| 1 | +# SQL Package Development Guide |
| 2 | + |
| 3 | +## Schema Changers: Legacy vs Declarative |
| 4 | + |
| 5 | +CockroachDB has two schema change systems: the legacy schema changer and the newer declarative schema changer. Understanding their differences is crucial for DDL development work. |
| 6 | + |
| 7 | +### Architectural Overview |
| 8 | + |
| 9 | +**Legacy Schema Changer** (`/pkg/sql/schema_changer.go`): |
| 10 | +- **Hard-coded state transitions**: Uses fixed sequences of descriptor mutations stored in the `mutations` slice |
| 11 | +- **Imperative approach**: Logic written procedurally with specific code paths for each DDL operation |
| 12 | +- **Limited state model**: Uses states like `DELETE_ONLY`, `WRITE_ONLY`, `BACKFILL_ONLY` from `DescriptorMutation` |
| 13 | +- **Job type**: Uses `SCHEMA_CHANGE` and `TYPEDESC_SCHEMA_CHANGE` job types |
| 14 | + |
| 15 | +**Declarative Schema Changer** (`/pkg/sql/schemachanger/`): |
| 16 | +- **Element-based modeling**: Schema changes modeled as elements (columns, indexes, constraints) with target statuses |
| 17 | +- **Declarative planning**: Uses rules and dependency graphs to generate execution plans |
| 18 | +- **State stored in descriptors**: Uses `declarative_schema_changer_state` field instead of `mutations` slice |
| 19 | +- **Job type**: Uses `NEW_SCHEMA_CHANGE` job type |
| 20 | + |
| 21 | +### State Management Differences |
| 22 | + |
| 23 | +**Legacy Schema Changer:** |
| 24 | +```go |
| 25 | +// Uses DescriptorMutation in mutations slice |
| 26 | +type DescriptorMutation struct { |
| 27 | + State DescriptorMutation_State // DELETE_ONLY, WRITE_ONLY, etc. |
| 28 | + Direction DescriptorMutation_Direction // ADD, DROP |
| 29 | + // ... specific column/index descriptors |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +**Declarative Schema Changer:** |
| 34 | +```proto |
| 35 | +// Uses element model with target statuses |
| 36 | +message Target { |
| 37 | + ElementProto element_proto = 1; |
| 38 | + Status target_status = 3; // PUBLIC, ABSENT, TRANSIENT_ABSENT |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +### Element Model (Declarative) |
| 43 | + |
| 44 | +Elements are defined in `/pkg/sql/schemachanger/scpb/elements.proto`: |
| 45 | +- **Table-level**: `Table`, `TableComment`, `TableData` |
| 46 | +- **Column elements**: `Column`, `ColumnName`, `ColumnType`, `ColumnDefaultExpression` |
| 47 | +- **Index elements**: `PrimaryIndex`, `SecondaryIndex`, `IndexColumn` |
| 48 | +- **Constraint elements**: `CheckConstraint`, `ForeignKeyConstraint` |
| 49 | + |
| 50 | +Status transitions: `ABSENT` -> `DELETE_ONLY` -> `WRITE_ONLY` -> `BACKFILL_ONLY` -> `VALIDATED` -> `PUBLIC` |
| 51 | + |
| 52 | +### Planning and Execution |
| 53 | + |
| 54 | +**Legacy Schema Changer:** |
| 55 | +- Planning and execution tightly coupled |
| 56 | +- Each DDL statement has custom imperative logic |
| 57 | +- State transitions are hard-coded sequences |
| 58 | +- Difficult to handle complex multi-statement transactions |
| 59 | + |
| 60 | +**Declarative Schema Changer:** |
| 61 | +- Clear separation between planning and execution phases |
| 62 | +- Uses dependency graphs and rules (`/pkg/sql/schemachanger/scplan/internal/rules/`) |
| 63 | +- Can handle complex multi-statement transactions declaratively |
| 64 | +- Better composition of multiple DDL operations |
| 65 | + |
| 66 | +### Error Handling and Rollback |
| 67 | + |
| 68 | +**Legacy Schema Changer:** |
| 69 | +- Known rollback issues, especially with data loss during failed multi-operation schema changes |
| 70 | +- Example: dropping columns alongside adding indexes can permanently lose data on rollback |
| 71 | + |
| 72 | +**Declarative Schema Changer:** |
| 73 | +- Designed for correct rollbacks by reversing target statuses |
| 74 | +- Uses `OnFailOrCancel` method to revert by flipping directions (ADD becomes DROP) |
| 75 | +- Better handling of complex failure scenarios |
| 76 | + |
| 77 | +### Configuration |
| 78 | + |
| 79 | +Enable declarative schema changer via: |
| 80 | +```sql |
| 81 | +-- Session level |
| 82 | +SET use_declarative_schema_changer = 'on'; -- Options: 'off', 'on', 'unsafe', 'unsafe_always' |
| 83 | + |
| 84 | +-- Cluster-wide default |
| 85 | +SET CLUSTER SETTING sql.defaults.use_declarative_schema_changer = 'on'; |
| 86 | +``` |
| 87 | + |
| 88 | +### Development Patterns |
| 89 | + |
| 90 | +**Adding New DDL Operations:** |
| 91 | + |
| 92 | +*Legacy Schema Changer:* |
| 93 | +- Add case statements in main schema changer loop |
| 94 | +- Implement specific state transition logic |
| 95 | +- Hard-code mutation sequences |
| 96 | + |
| 97 | +*Declarative Schema Changer:* |
| 98 | +- Define new elements in `elements.proto` |
| 99 | +- Add rules for state transitions in `scplan/internal/rules/` |
| 100 | +- Implement operations in `scop/` and execution in `scexec/` |
| 101 | +- More modular and composable approach |
| 102 | + |
| 103 | +### Code Organization |
| 104 | + |
| 105 | +**Legacy Schema Changer:** |
| 106 | +- `/pkg/sql/schema_changer.go` - Main implementation |
| 107 | +- `/pkg/sql/schema_changer_state.go` - State management |
| 108 | +- `/pkg/sql/type_change.go` - Type changes |
| 109 | + |
| 110 | +**Declarative Schema Changer:** |
| 111 | +- `scpb/` - Protocol buffer definitions for elements and state |
| 112 | +- `scbuild/` - Building targets from DDL ASTs |
| 113 | +- `scplan/` - Planning state transitions and dependencies |
| 114 | +- `scexec/` - Executing operations |
| 115 | +- `scop/` - Operation definitions |
| 116 | +- `screl/` - Relational model for elements |
| 117 | + |
| 118 | +### When to Use Each |
| 119 | + |
| 120 | +**Legacy Schema Changer:** |
| 121 | +- Still required for some DDL operations not yet implemented in declarative changer |
| 122 | +- May be faster for simple, single-operation schema changes |
| 123 | +- More predictable performance due to hard-coded paths |
| 124 | + |
| 125 | +**Declarative Schema Changer:** |
| 126 | +- Better for complex multi-statement transactions |
| 127 | +- More overhead in planning phase but better execution for complex scenarios |
| 128 | +- Required for future transactional schema changes |
| 129 | +- Better correctness guarantees and rollback handling |
| 130 | + |
| 131 | +### Migration Path |
| 132 | + |
| 133 | +CockroachDB is gradually migrating from legacy to declarative: |
| 134 | +1. **Coexistence period**: Both schema changers run simultaneously |
| 135 | +2. **Feature-by-feature migration**: DDL operations migrated individually |
| 136 | +3. **Version compatibility**: Old jobs must continue during upgrades |
| 137 | +4. **Extensive testing**: Test coverage in `schemachanger_test.go` and related files |
| 138 | + |
| 139 | +### Key Benefits of Declarative Approach |
| 140 | + |
| 141 | +1. **Correctness**: Better handling of complex scenarios and rollbacks |
| 142 | +2. **Extensibility**: Easier to add new DDL operations |
| 143 | +3. **Composability**: Can handle multiple DDL operations in single transaction |
| 144 | +4. **Testing**: Better separation of concerns allows more targeted testing |
| 145 | +5. **Future-ready**: Foundation for transactional schema changes |
| 146 | + |
| 147 | +The declarative schema changer represents a significant architectural improvement addressing fundamental limitations of the legacy approach, particularly around correctness, extensibility, and complex transaction handling. |
0 commit comments