Skip to content

feat(ddl): COMMENT ON TABLE / COLUMN with inline MySQL form - #158

Merged
productdevbook merged 1 commit into
mainfrom
feat-comment-on
May 19, 2026
Merged

feat(ddl): COMMENT ON TABLE / COLUMN with inline MySQL form#158
productdevbook merged 1 commit into
mainfrom
feat-comment-on

Conversation

@productdevbook

Copy link
Copy Markdown
Owner

Summary

  • New CommentNode AST + ColumnDefinitionNode.comment field. PG emits standalone COMMENT ON TABLE / COMMENT ON COLUMN; MySQL inlines column comments inside CREATE TABLE and emits ALTER TABLE … COMMENT = '…' for table comments. SQLite and MSSQL refuse via OBJECT_COMMENTS feature flag.
  • Schema DSL surface: .comment("…") on ColumnBuilder and a comment option on defineTable. Threaded through the diff engine so comment edits show up as additive migration steps that never trip the destructive-gate.
  • Single quotes in comment text are escaped through the existing escapeStringLiteral helper.

Test plan

  • New test/migrate/comments.test.ts — 26 cases covering DSL round-trip, diff materialization, PG/MySQL printer SQL shape, quote escaping, and SQLite/MSSQL refusals.
  • pnpm fmt && pnpm lint && pnpm typecheck && pnpm vitest run all green; suite goes from 2150 → 2176 passing tests.

Tradeoff: folded table-comment and column-comment into one CommentNode (discriminated by target: "table" | "column") rather than splitting into two node types — they share the same dialect-routing logic at the printer layer and the same before/after diff path, so splitting would have duplicated code without adding clarity. The downside is the columnName field is conditionally required (only when target === "column"); the printer surfaces this with a clear runtime error if the AST is malformed.

Generated with Claude Code

PG and MySQL both support attaching prose to schema objects, but the
two grammars are different surfaces: PG has two standalone statements
(`COMMENT ON TABLE` / `COMMENT ON COLUMN`), and MySQL prefers an
inline `COMMENT 'text'` clause inside `CREATE TABLE` for columns plus
an `ALTER TABLE … COMMENT = '…'` for the table-level form. Surface
both through the same schema DSL so a single `defineTable(..., {
comment: "…" })` + `.comment("…")` per column compiles to the right
flavor per dialect.

- AST: `ColumnDefinitionNode.comment?: string` and a new top-level
  `CommentNode` (`{ target: "table" | "column", tableName, columnName?,
  comment: string | null }`) added to the `DDLNode` union. A `null`
  comment is the drop form (`IS NULL` on PG; `COMMENT = ''` on MySQL).
  Table and column comments share one node type because they go
  through the same diff path and the same `printCommentOn` dispatcher
  — splitting them would have duplicated the dialect-routing logic
  without adding any clarity.

- Schema DSL: `ColumnBuilder.comment(text)` and a `comment` field on
  `TableOptions` / `TableDefinition` / `NormalizedTable`. Threaded
  through `normalizeTableEntry`, `buildNormalized`, and
  `columnDefinitionFromBuilder` so the value rides the existing
  before/after diff machinery.

- Diff engine: a new `commentNodesForTable` helper emits CommentNodes
  for (a) freshly-created tables and (b) shared tables where the
  before/after table-level or per-column comment differs. Comment
  changes are always additive — metadata edits never trip the
  destructive-gate.

- DDL printer: `printCommentOn` emits the dialect-correct standalone
  statement (PG: `COMMENT ON …`; MySQL: `ALTER TABLE …`); SQLite and
  MSSQL throw via `assertFeature(OBJECT_COMMENTS)`. The
  `ColumnDefinitionNode.comment` field is inlined inside `CREATE
  TABLE` on MySQL and dropped on the other three (PG emits a
  follow-up CommentNode; SQLite/MSSQL have no portable equivalent).
  MySQL standalone column-comment statements are also refused — the
  underlying `ALTER TABLE … MODIFY COLUMN` requires the column's full
  type, which we don't carry through to this layer; callers should
  use the inline `.comment("…")` form on the column instead.

- Features matrix: `OBJECT_COMMENTS` (PG, MySQL).

- Tests: new `test/migrate/comments.test.ts` covers schema DSL
  round-trip, diff materialization (create / add / edit / drop / no-op),
  PG and MySQL printer output (including quote-escaping for `Alice's
  note`), and the SQLite / MSSQL refusals. 26 new test cases; total
  suite goes from 2150 → 2176 passing.

- Docs: a "Schema comments" section in `docs/recipes.md` shows the
  DSL surface, side-by-side PG / MySQL emitted SQL, the
  metadata-only diff semantics, and the SQLite / MSSQL dialect-support
  story.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@productdevbook
productdevbook merged commit 0b46276 into main May 19, 2026
1 check passed
@productdevbook
productdevbook deleted the feat-comment-on branch May 19, 2026 10:01
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.

1 participant