Skip to content

Conversation

@mk3008
Copy link
Owner

@mk3008 mk3008 commented Oct 26, 2025

Omit spaces before parentheses in CREATE TABLE statements to enhance SQL formatting consistency. This change applies to table names and constraint keywords.

Summary by CodeRabbit

  • New Features

    • Added a Formatter option to choose constraint rendering style (postgres vs mysql).
  • Style

    • SQL formatter now omits spaces before parentheses in CREATE TABLE constraint definitions (e.g., unique(...), foreign key(...)) for consistent output.
  • Tests

    • Added unit tests covering CREATE TABLE constraint spacing and MySQL-style constraint inlining.
  • Documentation

    • Updated formatting guide with a new section explaining the constraint style option and examples.

@coderabbitai
Copy link

coderabbitai bot commented Oct 26, 2025

Walkthrough

Refactors SQL printing and parsing to add context-aware spacing for CREATE TABLE/constraint parentheses, introduce a constraintStyle option (postgres/mysql), thread it through formatter/parser, adjust SqlPrinter signatures/types, and update tests and docs to reflect new formatting behaviors.

Changes

Cohort / File(s) Summary
SqlPrinter / spacing logic
packages/core/src/transformers/SqlPrinter.ts
Adds context-aware spacing predicates and keyword sets for CREATE TABLE DDL, new private helpers (findPreviousSignificantToken, shouldSkipSpaceBeforeParenthesis, isCreateTableSpacingContext, isCreateTableNameToken, isCreateTableConstraintKeyword, isIdentifierAttachedToConstraint), updates handleSpaceToken signature and token-processing flow, and refines indentChar/newline property types.
Parser: constraint rendering & inlining
packages/core/src/parsers/SqlPrintTokenParser.ts
Introduces exported ConstraintStyle type, threads constraintStyle through parser options and PRESETS, and implements MySQL vs Postgres rendering differences (inline constraint names, unique key vs unique, etc.).
Parser: CreateTable parsing
packages/core/src/parsers/CreateTableParser.ts
Adds optional inline key-name parsing for unique/unique key and foreign key branches and uses inline name when explicit constraintName is absent.
Formatter wiring
packages/core/src/transformers/SqlFormatter.ts
Adds constraintStyle?: ConstraintStyle to SqlFormatterOptions, defaults to 'postgres', and passes it into parser configuration when constructing SqlPrintTokenParser.
Tests: expectations & new coverage
packages/core/tests/parsers/CreateTableParser.test.ts, packages/core/tests/parsers/DDLParsers.test.ts, packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
Updates expectations to omit space before constraint/parenthesis in affected cases; adds unit tests covering CREATE TABLE spacing and mysql-style constraint formatting.
Docs
docs/guide/formatting-recipes.md
Adds documentation for a new DDL constraint style option (constraintStyle) with examples for postgres vs mysql.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Formatter as SqlFormatter
  participant Parser as SqlPrintTokenParser
  participant Printer as SqlPrinter

  Formatter->>Parser: parse AST tokens (with constraintStyle)
  Parser-->>Formatter: token stream (tokens include constraint/context metadata)
  Formatter->>Printer: format tokens (iterates inner tokens)
  loop per token
    Printer->>Printer: findPreviousSignificantToken(prevToken, priorToken)
    Printer->>Printer: shouldSkipSpaceBeforeParenthesis(token, prevSignificant)
    alt skip space
      Printer-->>Formatter: emit token without space then "("
    else default
      Printer-->>Formatter: emit token + space then "("
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • SqlPrinter spacing predicates and edge-case token identification (parenthesis/constraint contexts).
    • Public-facing addition of ConstraintStyle and changes to PRESETS — verify presets consistency.
    • Signature/type changes for indentChar/newline and handleSpaceToken propagation across code paths.
    • Parser changes for inline constraint name parsing and interactions with formatter tokenization.

Possibly related PRs

Poem

🐰
With soft paws I hop through code and light,
I tuck the space before parentheses tight.
Constraints and tables now snugly align,
Postgres or MySQL — formats designed.
Hop, hop, hooray — the printer's feeling right!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Improve SQL formatting for CREATE TABLE statements" is clearly and directly related to the primary objective stated in the PR description: "Omit spaces before parentheses in CREATE TABLE statements to enhance SQL formatting consistency." The changeset focuses on context-aware spacing logic for CREATE TABLE scenarios, constraint style handling, and inline constraint naming—all of which contribute to improving CREATE TABLE formatting. While the title could be more specific by mentioning the particular improvement (removing spaces before parentheses), it accurately captures the main theme and would allow a teammate scanning the history to understand that this change addresses CREATE TABLE formatting enhancements.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ddl_format

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (2)

5-41: Solid coverage for CREATE TABLE spacing.

Tests exercise table name, CHECK/UNIQUE/PRIMARY KEY spacing. Looks correct and uses SqlFormatter per guidelines. Consider adding a MySQL-style “UNIQUE KEY (col)” case if that dialect is in scope.


42-72: References spacing case is valuable; add one with quoted identifiers.

This asserts removal of the space in REFERENCES t (c). Consider a companion with quoted, multi-part names (e.g., references "s"."t"("c")) to guard tokenizer variations.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec6b51e and 55d229f.

📒 Files selected for processing (4)
  • packages/core/src/transformers/SqlPrinter.ts (5 hunks)
  • packages/core/tests/parsers/CreateTableParser.test.ts (1 hunks)
  • packages/core/tests/parsers/DDLParsers.test.ts (1 hunks)
  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
packages/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

packages/core/**/*.{ts,tsx}: Always compile and fix TypeScript errors before running tests
Source code comments must be written in English only
For model-driven mappings (mapping.typeInfo and mapping.structure), use convertModelDrivenMapping
Use imports from 'rawsql-ts' rather than local relative paths for library usage

Files:

  • packages/core/tests/parsers/DDLParsers.test.ts
  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/tests/parsers/CreateTableParser.test.ts
  • packages/core/src/transformers/SqlPrinter.ts
packages/core/**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Tests are specs — never change expected values without user consultation

Files:

  • packages/core/tests/parsers/DDLParsers.test.ts
  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/tests/parsers/CreateTableParser.test.ts
packages/core/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Remove console.log statements before commit

Files:

  • packages/core/tests/parsers/DDLParsers.test.ts
  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/tests/parsers/CreateTableParser.test.ts
  • packages/core/src/transformers/SqlPrinter.ts
packages/core/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Use SqlFormatter for SQL comparison tests

Files:

  • packages/core/tests/parsers/DDLParsers.test.ts
  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/tests/parsers/CreateTableParser.test.ts
🧠 Learnings (1)
📚 Learning: 2025-10-18T02:29:34.437Z
Learnt from: CR
PR: mk3008/rawsql-ts#0
File: packages/core/AGENTS.md:0-0
Timestamp: 2025-10-18T02:29:34.437Z
Learning: Applies to packages/core/**/*.{test,spec}.{ts,tsx} : Use SqlFormatter for SQL comparison tests

Applied to files:

  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/tests/parsers/CreateTableParser.test.ts
🧬 Code graph analysis (2)
packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (1)
packages/core/src/parsers/CreateTableParser.ts (1)
  • CreateTableParser (48-714)
packages/core/src/transformers/SqlPrinter.ts (1)
packages/core/src/models/SqlPrintToken.ts (1)
  • SqlPrintToken (111-141)
🔇 Additional comments (3)
packages/core/tests/parsers/CreateTableParser.test.ts (1)

94-96: Expectation update aligns with new spacing rule.

The assertion for FK formatting now correctly expects no space before parentheses. Looks good and matches the PR objective. Please confirm this behavior is intended across all supported dialects (e.g., Postgres, MySQL) so we can note it as a formatting change in release notes.

packages/core/tests/parsers/DDLParsers.test.ts (1)

84-86: Unique constraint spacing matches new style.

Expectation updated to unique("email"). This aligns with the PR’s spacing rule. Please confirm we also want this for MySQL’s UNIQUE KEY form, if supported.

packages/core/src/transformers/SqlPrinter.ts (1)

323-336: Propagation of previous significant token for space handling looks good.

Capturing previousChild and threading it into handleSpaceToken enables the targeted skip. This is the right place in the hot path and stays scoped to container contexts. LGTM.
If there are top-level space tokens not covered by the inner loop, consider computing a sibling-aware previous at that callsite in the future, but not required for this PR.

Also applies to: 664-694

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/parsers/SqlPrintTokenParser.ts (1)

2826-2863: Remove 'primary-key' from inlineNameKinds to fix MySQL named PRIMARY KEY syntax

MySQL syntax for named PRIMARY KEY constraints is CONSTRAINT <name> PRIMARY KEY (...), not PRIMARY KEY <name>(...). The current code generates invalid SQL by inlining names for PRIMARY KEY. UNIQUE KEY and FOREIGN KEY do support inline naming in MySQL and should remain in the set.

Apply this fix:

-        const inlineNameKinds = new Set<TableConstraintDefinition['kind']>(['primary-key', 'unique', 'foreign-key']);
+        // MySQL allows inline names for UNIQUE KEY and (index name after) FOREIGN KEY, but not after PRIMARY KEY.
+        const inlineNameKinds = new Set<TableConstraintDefinition['kind']>(['unique', 'foreign-key']);

Also add a unit test covering named PRIMARY KEY with MySQL constraint style, as the test suite only covers UNIQUE KEY and FOREIGN KEY inline forms for MySQL.

🧹 Nitpick comments (6)
packages/core/src/parsers/SqlPrintTokenParser.ts (1)

87-101: Preset consistency: consider making constraintStyle explicit for all presets

You rely on a global default ('postgres') when a preset omits constraintStyle. Explicitly setting constraintStyle for every PRESETS entry improves predictability and avoids regressions if the default changes later.

Also applies to: 108-131

docs/guide/formatting-recipes.md (1)

136-151: Clarify MySQL naming nuances for PRIMARY/FOREIGN KEY

Add a short note:

  • PRIMARY KEY: name via “CONSTRAINT PRIMARY KEY (...)” (not “PRIMARY KEY (...)”).
  • FOREIGN KEY: inline name after “FOREIGN KEY” is the index name; the constraint symbol uses the CONSTRAINT prefix.

This prevents users from assuming all three kinds accept the same inline form.

packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (1)

74-106: Add guard test: named PRIMARY KEY under MySQL style (no inline name)

To prevent regressions, add a test where input has a named PK (via CONSTRAINT), and verify output remains “constraint primary key(...)” under constraintStyle: 'mysql' (i.e., no “primary key (...)”).

packages/core/src/parsers/CreateTableParser.ts (2)

489-493: Prefer explicit comment: inlineKeyName precedence

Minor: add a comment that inlineKeyName is used only when no prefixed CONSTRAINT name is present to make intent obvious.


496-505: Foreign key inline token can be index name, not constraint symbol

In MySQL, the identifier after “FOREIGN KEY” is the index name; the constraint symbol is provided via “CONSTRAINT ...”. Since you store inlineKeyName into constraintName, please ensure downstream consumers don’t assume it’s always the constraint symbol (e.g., for DROP operations). If necessary, carry a separate indexName field in the model.

packages/core/src/transformers/SqlFormatter.ts (1)

8-11: Avoid preset name drift — derive PresetName from PRESETS

Hard-coding VALID_PRESETS risks diverging from PRESETS keys (you already have many more). Derive the type instead.

Apply this diff:

-export const VALID_PRESETS = ['mysql', 'postgres', 'sqlserver', 'sqlite'] as const;
-export type PresetName = (typeof VALID_PRESETS)[number];
+export type PresetName = keyof typeof PRESETS;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55d229f and 9721aff.

📒 Files selected for processing (6)
  • docs/guide/formatting-recipes.md (2 hunks)
  • packages/core/src/parsers/CreateTableParser.ts (2 hunks)
  • packages/core/src/parsers/SqlPrintTokenParser.ts (6 hunks)
  • packages/core/src/transformers/SqlFormatter.ts (3 hunks)
  • packages/core/src/transformers/SqlPrinter.ts (5 hunks)
  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
packages/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

packages/core/**/*.{ts,tsx}: Always compile and fix TypeScript errors before running tests
Source code comments must be written in English only
For model-driven mappings (mapping.typeInfo and mapping.structure), use convertModelDrivenMapping
Use imports from 'rawsql-ts' rather than local relative paths for library usage

Files:

  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/src/transformers/SqlFormatter.ts
  • packages/core/src/parsers/CreateTableParser.ts
  • packages/core/src/transformers/SqlPrinter.ts
  • packages/core/src/parsers/SqlPrintTokenParser.ts
packages/core/**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Tests are specs — never change expected values without user consultation

Files:

  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
packages/core/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Remove console.log statements before commit

Files:

  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
  • packages/core/src/transformers/SqlFormatter.ts
  • packages/core/src/parsers/CreateTableParser.ts
  • packages/core/src/transformers/SqlPrinter.ts
  • packages/core/src/parsers/SqlPrintTokenParser.ts
packages/core/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (packages/core/AGENTS.md)

Use SqlFormatter for SQL comparison tests

Files:

  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
🧠 Learnings (1)
📚 Learning: 2025-10-18T02:29:34.437Z
Learnt from: CR
PR: mk3008/rawsql-ts#0
File: packages/core/AGENTS.md:0-0
Timestamp: 2025-10-18T02:29:34.437Z
Learning: Applies to packages/core/**/*.{test,spec}.{ts,tsx} : Use SqlFormatter for SQL comparison tests

Applied to files:

  • packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts
🧬 Code graph analysis (5)
packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (2)
packages/core/src/parsers/CreateTableParser.ts (1)
  • CreateTableParser (48-730)
packages/core/src/transformers/SqlFormatter.ts (1)
  • SqlFormatter (118-178)
packages/core/src/transformers/SqlFormatter.ts (1)
packages/core/src/parsers/SqlPrintTokenParser.ts (2)
  • ConstraintStyle (69-69)
  • SqlPrintTokenParser (201-3175)
packages/core/src/parsers/CreateTableParser.ts (2)
packages/core/src/models/ValueComponent.ts (1)
  • IdentifierString (277-284)
packages/core/src/models/CreateTableQuery.ts (1)
  • TableConstraintDefinition (101-132)
packages/core/src/transformers/SqlPrinter.ts (1)
packages/core/src/models/SqlPrintToken.ts (1)
  • SqlPrintToken (111-141)
packages/core/src/parsers/SqlPrintTokenParser.ts (1)
packages/core/src/models/CreateTableQuery.ts (1)
  • TableConstraintDefinition (101-132)
🔇 Additional comments (12)
packages/core/src/parsers/SqlPrintTokenParser.ts (1)

69-85: Good public-surface addition

Adding ConstraintStyle and threading it into FormatterConfig is clean and backwards compatible. LGTM.

docs/guide/formatting-recipes.md (1)

41-41: Docs option row LGTM

Clear description and defaults for constraintStyle.

packages/core/tests/transformers/SqlFormatter.create-table-spacing.test.ts (2)

5-40: Solid coverage for no-space-before '('

Good end-to-end assertion using SqlFormatter; expectations match the PR goal.


42-72: FK spacing case reads well

The REFERENCES branch formatting looks correct and readable.

packages/core/src/parsers/CreateTableParser.ts (1)

474-483: Inline name capture for UNIQUE KEY is sensible

Parsing an inline name between the keyword and '(' aligns with MySQL syntax and helps formatting.

packages/core/src/transformers/SqlFormatter.ts (2)

1-1: Plumbing constraintStyle through the formatter: LGTM

Import and option wiring are correct and default to 'postgres' safely.


141-153: Default resolution for constraintStyle is clear

Merging option, preset, and fallback is straightforward. No changes needed.

packages/core/src/transformers/SqlPrinter.ts (5)

326-338: LGTM: Token tracking logic is correct.

The new logic correctly finds the previous and prior significant tokens by skipping whitespace and comments, then passes them to handleSpaceToken for context-aware spacing decisions. This enables the formatter to make informed decisions about CREATE TABLE spacing.


671-695: LGTM: Space handling correctly implements CREATE TABLE spacing logic.

The updated handleSpaceToken signature accepts additional context tokens to enable precise spacing decisions. The early return at lines 692-695 correctly skips spaces before structural parentheses in CREATE TABLE DDL, as intended by the PR objectives.


699-711: LGTM: Token filtering logic is sound.

The findPreviousSignificantToken method correctly skips whitespace and comments to find semantically significant tokens. Returning both the token and its index enables efficient multi-step lookback for prior context.


713-811: LGTM: CREATE TABLE spacing logic correctly implements context-aware formatting.

The new helper methods implement comprehensive context-aware spacing for CREATE TABLE statements:

  1. shouldSkipSpaceBeforeParenthesis (713-744): Correctly identifies three cases where spaces should be omitted before '(':

    • After table name: CREATE TABLE name(...)
    • After constraint keywords: PRIMARY KEY(...)
    • After identifiers following constraint keywords: REFERENCES table(...)
  2. isCreateTableSpacingContext (746-757): Appropriately scopes the logic to relevant container types (CreateTableQuery, CreateTableDefinition, constraint definitions, and ReferenceDefinition).

  3. isCreateTableConstraintKeyword (767-785): Special handling for ReferenceDefinition (lines 773-775) ensures 'references' keyword is recognized, addressing the past review comment about REFERENCES spacing.

  4. isIdentifierAttachedToConstraint (787-811): Correctly handles both qualified table names after REFERENCES (lines 796-799) and inline constraint names.

The implementation addresses both past review comments:

  • "unique key" is now in CREATE_TABLE_MULTI_PAREN_KEYWORDS (line 12)
  • REFERENCES table name spacing is handled via ReferenceDefinition context checking (lines 773-775, 796-799)

11-13: Great! I've confirmed that KEY is a synonym for INDEX in MySQL, and the syntax KEY idx_name (column) is valid in CREATE TABLE. This validates adding both 'key' and 'index' to CREATE_TABLE_SINGLE_PAREN_KEYWORDS.

Now let me verify test coverage comprehensively:

Based on my verification through web searches and test file analysis:

The 'key' and 'index' keywords are valid and correctly implemented.

In MySQL, KEY is normally a synonym for INDEX and can be used interchangeably. The test file contains explicit test coverage for these cases—specifically the MySQL constraint test (lines 73–102) which validates UNIQUE KEY uk(id) formatting, confirming that the keyword handling is correct.

The standalone 'key' keyword is documented in MySQL as valid syntax for defining indexes in CREATE TABLE (e.g., KEY idx_name (col)), and the constants on lines 11–13 appropriately capture this. The context-limiting logic at line 722 (isCreateTableSpacingContext) ensures these keywords only match in CREATE TABLE DDL, preventing false positives.

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.

2 participants