Skip to content

feat(transform): dialect-aware SQL formatting#507

Merged
ajitpratap0 merged 1 commit intomainfrom
feat/dialect-aware-transform
Apr 11, 2026
Merged

feat(transform): dialect-aware SQL formatting#507
ajitpratap0 merged 1 commit intomainfrom
feat/dialect-aware-transform

Conversation

@ajitpratap0
Copy link
Copy Markdown
Owner

Summary

  • Adds dialect-aware formatting to the transform and formatter packages so that AST-to-SQL rendering respects dialect-specific row-limiting syntax
  • SQL Server emits TOP N, Oracle emits FETCH FIRST N ROWS ONLY, PostgreSQL/MySQL/etc. keep LIMIT N
  • Also fixes a bug where parsed TopClause was silently dropped during formatting

Changes

New APIs

  • transform.FormatSQLWithDialect(stmt, keywords.SQLDialect) - format with dialect-specific syntax
  • transform.ParseSQLWithDialect(sql, keywords.SQLDialect) - parse with dialect-aware tokenizer/parser

Formatter

  • FormatOptions.Dialect field threads dialect through the rendering pipeline
  • normalizeSelectForDialect() converts LIMIT/OFFSET to TOP or FETCH on a shallow copy (original AST not mutated)
  • fetchSQL() now respects keyword casing via nodeFormatter
  • renderSelect() now renders TopClause (previously silently dropped)

Dialect LIMIT Mapping

Dialect No-Offset With-Offset
PostgreSQL/MySQL/SQLite/Snowflake/CH LIMIT n LIMIT n OFFSET m
SQL Server (no ORDER BY) TOP n TOP n
SQL Server (with ORDER BY) OFFSET 0 ROWS FETCH NEXT n ROWS ONLY OFFSET m ROWS FETCH NEXT n ROWS ONLY
Oracle FETCH FIRST n ROWS ONLY OFFSET m ROWS FETCH FIRST n ROWS ONLY

Example (from issue)

tree, _ := gosqlx.ParseWithDialect("SELECT * FROM users u", keywords.DialectSQLServer)
stmt := tree.Statements[0]
transform.Apply(stmt, transform.SetLimit(100))
fmt.Println(transform.FormatSQLWithDialect(stmt, keywords.DialectSQLServer))
// SELECT TOP 100 * FROM users u

fmt.Println(transform.FormatSQLWithDialect(stmt, keywords.DialectOracle))
// SELECT * FROM users u FETCH FIRST 100 ROWS ONLY

fmt.Println(transform.FormatSQLWithDialect(stmt, keywords.DialectPostgreSQL))
// SELECT * FROM users u LIMIT 100

Test plan

  • 11 formatter dialect rendering tests (TOP, FETCH, LIMIT, keyword casing, AST immutability)
  • 7 transform end-to-end tests (parse -> SetLimit -> FormatSQLWithDialect for each dialect)
  • Full test suite with -race: all packages pass, zero regressions
  • go vet and gofmt clean

Closes #479

🤖 Generated with Claude Code

Add dialect-aware formatting to the transform and formatter packages so
that AST-to-SQL rendering respects dialect-specific row-limiting syntax:
SQL Server emits TOP, Oracle emits FETCH FIRST, and LIMIT-based dialects
remain unchanged.

Key changes:
- Add Dialect field to FormatOptions and formatter.Options
- Render parsed TopClause (previously silently dropped)
- normalizeSelectForDialect converts LIMIT to TOP/FETCH on a shallow copy
- SQL Server with OFFSET uses OFFSET/FETCH NEXT syntax
- Add FormatSQLWithDialect(stmt, keywords.SQLDialect) to transform pkg
- Add ParseSQLWithDialect(sql, keywords.SQLDialect) to transform pkg
- Thread nodeFormatter through fetchSQL for keyword casing

Closes #479

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gosqlx-website Ready Ready Preview, Comment Apr 11, 2026 6:09pm

@github-actions github-actions bot added transform ast AST node changes labels Apr 11, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 11, 2026

Claude finished @ajitpratap0's task —— View job


Code Review Complete

I've completed a comprehensive review of this dialect-aware SQL formatting PR. Overall, this is a well-implemented feature that cleanly adds dialect-specific formatting support to the transform and formatter packages.

Review Tasks

  • Read and analyze all changed files
  • Review transform package changes
  • Review formatter package changes
  • Analyze test coverage and quality
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Check security implications
  • Provide comprehensive feedback

Summary

This PR successfully implements dialect-aware SQL formatting with clean architecture, comprehensive testing, and good performance characteristics. The implementation correctly handles the dialect mapping requirements and maintains AST immutability.

Code Quality ✅

Architecture & Design:

  • Clean separation of concerns with dialect logic in formatter/render.go:336-378
  • Non-mutating approach using shallow copy in normalizeSelectForDialect() at render.go:202-204
  • Well-structured public APIs in transform package

Code Style & Conventions:

  • Consistent with project patterns and existing codebase style
  • Proper use of object pooling for memory management
  • Good function naming and documentation

Performance Considerations ✅

Memory Management:

  • Uses shallow copy for dialect normalization (minimal overhead)
  • Leverages existing object pooling patterns
  • No memory leaks or excessive allocations

Runtime Efficiency:

  • Simple string-based dialect matching
  • Minimal branching in hot paths
  • Dialect conversion only when needed

Security ✅

Input Validation:

  • Uses existing validated dialect constants from keywords package
  • No direct string interpolation or injection risks
  • Safe handling of user-provided dialect values

Test Coverage ✅ (Excellent)

Comprehensive Test Suite:

  • 11 formatter tests covering all dialect mappings and edge cases
  • 7 transform integration tests for end-to-end workflows
  • AST immutability verification at dialect_render_test.go:233-253
  • Keyword casing tests at dialect_render_test.go:209-231

Edge Case Coverage:

  • Mixed TOP/LIMIT scenarios
  • Pagination with OFFSET
  • Keyword casing preservation
  • Empty/generic dialect handling

Issues Found 🔍

Minor Issues:

  1. Documentation Gap - The dialect mapping table in the PR description could be included as code comments in normalizeSelectForDialect() for maintainer reference.

  2. Type Safety - Consider using keywords.SQLDialect type instead of string for the Dialect field in ast.FormatOptions (format.go:62) to leverage type safety.

Recommendations 💡

Code Improvements:

  1. Add inline documentation of dialect mappings in render.go:336
  2. Consider enum/const validation for supported dialects

Future Enhancements:

  1. Could extend to other dialect-specific syntax (identifier quoting, function names)
  2. Consider caching normalized ASTs for repeated formatting with same dialect

Specific Code Review

pkg/transform/transform.go:242-246

  • Clean API design for FormatSQLWithDialect
  • Proper integration with existing FormatSQL

pkg/formatter/render.go:336-378

  • Well-structured dialect normalization logic
  • Correct handling of SQL Server's dual syntax (TOP vs OFFSET/FETCH)
  • Proper Oracle FETCH FIRST mapping

pkg/formatter/render.go:228-242

  • TopClause rendering correctly implemented
  • Fixes the bug where TopClause was silently dropped

Test Coverage

  • Excellent test coverage with both unit and integration tests
  • Good edge case handling and regression prevention

Final Assessment

This is a high-quality implementation that:

  • ✅ Solves the stated problem effectively
  • ✅ Maintains backward compatibility
  • ✅ Follows project conventions
  • ✅ Includes comprehensive testing
  • ✅ Has good performance characteristics
  • ✅ Maintains security standards

Recommendation: APPROVE 🚀

The implementation is production-ready and addresses the issue requirements comprehensively. The minor suggestions above are enhancements rather than blocking issues.


@ajitpratap0 ajitpratap0 merged commit 96137ab into main Apr 11, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ast AST node changes transform

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Dialect aware transform package implementation

1 participant