Skip to content

Escape reserved Dart keywords in SQL column names - #3838

Merged
simolus3 merged 1 commit into
simolus3:developfrom
mohanedy:fix/escape-dart-keyword-column-names
Jul 22, 2026
Merged

simolus3 merged 1 commit into
simolus3:developfrom
mohanedy:fix/escape-dart-keyword-column-names

Conversation

@mohanedy

Copy link
Copy Markdown
Contributor

Problem

A SQL column whose name is a reserved Dart keyword (e.g. class) generates invalid Dart. The column name is turned into a Dart identifier via ReCase(name).camelCase with no keyword check, so the generator emits code like:

late final GeneratedColumn<int> class = GeneratedColumn<int>('class', ...);

which fails to parse (Can't have modifier 'late' here, etc.).

This is most visible through make-migrations. The serialized schema snapshot embeds a fixed_sql block (the real CREATE TABLE statements). When reading it back, SchemaReader prefers fixed_sql and reconstructs tables directly from SQL (extractDriftElementsFromSql), which bypasses the serialized getter_name. So even a table defined in Dart as IntColumn get productClass => integer().named('class')() — which normally generates fine — crashes when its schema snapshot is turned into a migration test database, because the reconstructed column falls back to the raw SQL name class.

Stack (drift_dev 2.33.0, still present on develop 2.34.4):

Could not format because the source could not be parsed:
line ...: Can't have modifier 'late' here.
  #4 GenerateUtils.generateSchemaCode (.../schema/generate_utils.dart)
  #5 _MigrationTestEmitter.writeTestDatabases (.../make_migrations.dart)

Fix

  • dartNameForSqlColumn now appends $ when the derived name is a reserved Dart keyword (class → class$), using the analyzer's authoritative Keyword list. Built-in identifiers (e.g. mixin) are valid identifiers and left untouched.
  • The drift table resolver (table.dart) now routes column names through dartNameForSqlColumn instead of calling ReCase(...).camelCase directly, so both .drift files and SQL-reconstructed schemas produce valid identifiers. Views already used the shared helper.

The SQL column name is unchanged — only the generated Dart getter/field/parameter identifier is escaped.

Tests

Added a resolver regression test (test/analysis/resolver/drift/table_test.dart) covering a class column (escaped to class$), a built-in identifier (mixin, unchanged), and a normal column. Full test/analysis and test/writer suites pass locally (393 tests).

Also verified end-to-end against a real project: make-migrations previously crashed on a class column and now completes, and the generated schema_vN.dart analyzes clean.

Column names read from SQL were converted to Dart identifiers with
`ReCase(name).camelCase` without checking for reserved keywords. A column
named `class` (or any other reserved word) therefore produced invalid Dart
such as `late final GeneratedColumn<int> class = ...`, which fails to parse.

This surfaces via `make-migrations`: the generated schema snapshot embeds a
`fixed_sql` block, and reading it back reconstructs tables directly from the
`CREATE TABLE` statements, bypassing the serialized `getter_name` that the
element-export path would otherwise use.

`dartNameForSqlColumn` now appends a `$` to reserved words (e.g. `class` ->
`class$`), and the drift table resolver routes column names through it instead
of calling `ReCase` directly, so both `.drift` files and SQL-reconstructed
schemas get valid identifiers.
@mohanedy
mohanedy force-pushed the fix/escape-dart-keyword-column-names branch from 8305941 to d3dfa77 Compare July 21, 2026 13:18

@simolus3 simolus3 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution! This looks good to me.

@mohanedy

Copy link
Copy Markdown
Contributor Author

The CI / Integration Tests pipeline is failing due to an unrelated warning, so please @simolus3 let me know if I can help with fixing that as well.

@ahmeddhus

Copy link
Copy Markdown

I've been waiting for this fix. Great work! 🚀

@simolus3
simolus3 merged commit 719672e into simolus3:develop Jul 22, 2026
10 of 11 checks passed
@simolus3

Copy link
Copy Markdown
Owner

I've fixed that warning, thanks for the ping! I'll release these changes later today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants