Skip to content

fix(seedling-gen): Keep string and comment text out of SQL and Atlas schema structure - #103

Merged
mhiro2 merged 7 commits into
mainfrom
fix/gen-schema-parsing
Oct 14, 2026
Merged

mhiro2 merged 7 commits into
mainfrom
fix/gen-schema-parsing

Conversation

@mhiro2

@mhiro2 mhiro2 commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Atlas foreign keys are parsed by whole attribute name and unresolvable foreign_key blocks are reported instead of silently dropping the relation
  • SQL and Atlas parsers match structure on an offset-preserving masked copy of the input, so text inside strings, heredocs, and comments never becomes a table, column, or constraint
  • --dialect now selects string/comment syntax (PostgreSQL dollar quotes, E'...', nested comments; MySQL backslash escapes and # comments)
  • A primary key implies NOT NULL (with SQLite's own rule), so a primary-key foreign key yields a required relation

Changes

  • b4a82cb : fix(seedling-gen): match Atlas attributes by whole name and reject unresolved foreign keys
    • columns no longer matches inside ref_columns, so attribute order does not drop the FK
    • A foreign_key block without columns / ref_columns or with an undeclared local column is an error
  • 8fecb84 : fix(seedling-gen): mask Atlas strings and comments before matching structure
    • Escaped quotes such as \" no longer break brace matching, and attribute patterns ignore text inside strings and comments, including block comments
  • b84229c : fix(seedling-gen): lex SQL strings per dialect before matching schema structure
    • One lexer replaces four quote-tracking scanners; a CREATE TABLE inside a string or $$NOT NULL$$ in a DEFAULT is no longer read as schema
  • a0170b7 : fix(seedling-gen): treat primary key columns as NOT NULL by dialect
    • Nullability is settled after all constraints are read; SQLite implies NOT NULL only for the INTEGER PRIMARY KEY rowid alias and WITHOUT ROWID tables
  • 37a413c : fix(seedling-gen): resolve every Atlas reference and mask heredoc strings
    • Each ref_columns entry is resolved, with mismatched counts or multiple tables reported; hyphenated names and <<EOT heredocs are handled
  • 16e39f8 : fix(seedling-gen): follow MySQL hash comments, nested PostgreSQL comments, and descending SQLite keys
    • MySQL # comments, nested PostgreSQL block comments, and SQLite INTEGER PRIMARY KEY DESC follow their dialect's rules
  • ed51831 : docs(seedling-gen): describe dialect-aware lexing and stricter Atlas foreign keys
    • Updates the guide, ARCHITECTURE.md, the CLI skill, and the --dialect flag help

…resolved foreign keys

The columns pattern also matched inside ref_columns, so a foreign_key
block that listed ref_columns first took the referenced column as its
local column, the real FK column was never marked, and the generated
blueprint silently lost the relation. Attribute patterns now match
whole names only, and a foreign_key block without columns or
ref_columns, or with a local column the table does not declare, is
reported as an error instead of being skipped.
…ructure

Brace matching toggled on every double quote, so an escaped quote such
as \" in a comment attribute flipped the string state and a valid
schema failed with "unclosed brace"; attribute patterns could also
match text inside strings, such as a column comment containing
"null = true". The parser now builds one offset-preserving masked copy
of the input in which comments, including block comments, and string
contents are blanked with escapes honored. Structure is matched on the
masked copy while block names are still read from the original text.
… structure

CREATE TABLE statements were searched for in raw text, so a string
such as SELECT 'CREATE TABLE ghosts (...)' produced a table that does
not exist, and constraint detection masked only single-quoted strings,
so DEFAULT $$NOT NULL$$ marked a nullable column NOT NULL. A single
lexer now builds an offset-preserving masked copy of the schema that
blanks comments, string literals, and quoted identifier contents,
following the dialect: dollar quotes and E'...' strings for PostgreSQL
and auto, backslash escapes for MySQL. Table discovery, parenthesis
matching, item splitting, and constraint detection all run on that
copy, replacing four separate quote-tracking scanners.
A column that was both primary key and foreign key, such as user_id
INTEGER PRIMARY KEY REFERENCES users(id), was read as nullable because
only an explicit NOT NULL counted, so the relation became optional and
the shared-key parent was never generated. Nullability is now settled
after every constraint is read: a primary key implies NOT NULL in
standard SQL, while SQLite keeps its own rule and implies it only for
the INTEGER PRIMARY KEY rowid alias and WITHOUT ROWID tables. Each
foreign key's required flag is computed from the final column state.
…ings

The foreign key check accepted ref_columns that named only a table, a
different number of columns than columns, or columns on two tables;
each ref_columns entry is now resolved on its own, a bare column.x
references the current table, and those mismatches are reported.
Attribute names also no longer match after a hyphen, which HCL allows
in identifiers, so allow-null or sql-type are not read as null or
type. Heredoc strings (<<EOT and <<-EOT) are masked up to their
closing marker like quoted strings.
…ents, and descending SQLite keys

The SQL lexer missed comment forms that hide schema text: MySQL reads
# as a line comment, and PostgreSQL block comments nest, so text after
an inner */ was treated as structure. MySQL now masks # comments,
auto masks a # that starts a line, and PostgreSQL and auto nest block
comments. An inline INTEGER PRIMARY KEY DESC is not a rowid alias in
SQLite, so it no longer implies NOT NULL there.
…foreign keys

Explain that --dialect now selects string quoting and primary key
nullability rather than serving only as a validation hint, that text
inside strings and comments never becomes schema structure, and that
the atlas subcommand rejects foreign_key blocks it cannot resolve.
@github-actions

Copy link
Copy Markdown

Code Metrics Report

main (9a2b1f6) #103 (2657649) +/-
Coverage 86.9% 86.6% -0.3%
Code to Test Ratio 1:1.0 1:1.0 +0.0
Test Execution Time 1m11s 1m34s +23s
Details
  |                     | main (9a2b1f6) | #103 (2657649) |  +/-  |
  |---------------------|----------------|----------------|-------|
- | Coverage            |          86.9% |          86.6% | -0.3% |
  |   Files             |             59 |             59 |     0 |
  |   Lines             |           4791 |           4766 |   -25 |
- |   Covered           |           4165 |           4130 |   -35 |
+ | Code to Test Ratio  |          1:1.0 |          1:1.0 |  +0.0 |
  |   Code              |          17787 |          17806 |   +19 |
+ |   Test              |          17916 |          18089 |  +173 |
- | Test Execution Time |          1m11s |          1m34s |  +23s |

Code coverage of files in pull request scope (96.2% → 94.0%, patch 96.1%)

Files Coverage +/- Patch Coverage Status
cmd/seedling-gen/atlas.go 94.4% -1.7% 94.9% modified
cmd/seedling-gen/parser.go 93.7% -2.6% 97.0% modified

Reported by octocov

@mhiro2 mhiro2 self-assigned this Oct 14, 2026
@mhiro2 mhiro2 added the bug Something isn't working label Oct 14, 2026
@mhiro2
mhiro2 merged commit ffbbd87 into main Oct 14, 2026
11 checks passed
@mhiro2
mhiro2 deleted the fix/gen-schema-parsing branch October 14, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant