Migrating from SQLFluff to GoSQLX
Last Updated: 2025-11-05
This guide helps you migrate from SQLFluff (Python) to GoSQLX (Go), covering feature mapping, code examples, and practical migration steps.
Table of Contents
- Overview Comparison
- Why Migrate to GoSQLX?
- Feature Mapping
- Side-by-Side Code Examples
- Common Patterns Translation
- Performance Comparison
- Migration Checklist
- Real Migration Case Study
- Known Limitations
- Getting Help
Overview Comparison
SQLFluff
SQLFluff is a Python-based SQL linter and auto-formatter with support for multiple dialects and templated code.
Key Strengths:
- 60+ SQL dialects supported
- Comprehensive linting rules (60+ rules)
- Mature VSCode extension
- Python ecosystem integration
- Template language support (Jinja, dbt)
Key Weaknesses:
- Slow performance (1,000 ops/sec)
- High memory usage (50KB per query)
- Limited by Python GIL for concurrency
- Complex configuration required
GoSQLX
GoSQLX is a production-ready, race-free, high-performance SQL parsing SDK for Go.
Key Strengths:
- Blazing fast (1.38M+ ops/sec - 1000x faster!)
- Memory efficient (1.8KB per query - 95% less memory)
- Native Go concurrency (linear scaling)
- Zero dependencies
- Simple API
Key Trade-offs:
- 5 SQL dialects (vs SQLFluff’s 60+)
- 10 linting rules (vs SQLFluff’s 60+) - available in v1.6.0
- No template language support
- VSCode extension available in v1.6.0
Why Migrate to GoSQLX?
You Should Migrate If:
Performance is critical
- CI/CD pipelines taking too long (SQLFluff validates at ~1 query/sec)
- Real-time SQL validation in web applications
- Processing thousands of queries per second
- Batch processing large SQL files
You’re in the Go ecosystem
- Building Go applications or tools
- Want zero-dependency deployment
- Need native concurrency support
Memory efficiency matters
- Processing very large SQL files
- High-throughput services
- Memory-constrained environments
You Should Stay with SQLFluff If:
- You need extensive linting rules (GoSQLX has 10 rules in v1.6.0, vs SQLFluff’s 60+)
- You need exotic SQL dialects (Snowflake, BigQuery-specific features)
- You’re heavily invested in Python ecosystem
- You need template language support (Jinja, dbt)
Feature Mapping
| Feature | SQLFluff | GoSQLX | Notes |
|---|---|---|---|
| Core Functionality | |||
| SQL Parsing | Yes | Yes | GoSQLX 1000x faster |
| SQL Validation | Yes | Yes | Similar accuracy |
| SQL Formatting | Yes | Yes | Different style defaults |
| Syntax Error Detection | Yes | Yes | Both provide line/column info |
| Linting & Rules | |||
| Linting Rules | 60+ rules | 10 rules (v1.6.0) | GoSQLX growing |
| Custom Rules | Yes | Coming soon | |
| Rule Configuration | .sqlfluff | .gosqlx.yml (v1.6.0) | |
| Auto-fix | Yes | Yes (v1.6.0) | |
| SQL Dialect Support | |||
| PostgreSQL | Yes | Yes | GoSQLX ~80-85% coverage |
| MySQL | Yes | Yes | GoSQLX ~80% coverage |
| SQL Server | Yes | Yes | GoSQLX ~75% coverage |
| Oracle | Yes | Yes | GoSQLX ~70% coverage |
| SQLite | Yes | Yes | GoSQLX ~85% coverage |
| Snowflake | Yes | No | |
| BigQuery | Yes | No | |
| Redshift | Yes | No | |
| 50+ Other Dialects | Yes | No | |
| API & Integration | |||
| CLI Tool | Yes | Yes | GoSQLX is faster |
| Programmatic API | Complex | Simple | GoSQLX easier to use |
| Library Integration | Python | Go | |
| VSCode Extension | Yes | Yes (v1.6.0) | |
| Pre-commit Hooks | Yes | Yes | GoSQLX 100-1000x faster |
| Performance | |||
| Parse Speed | 1,000 ops/sec | 1.38M ops/sec | 1380x faster |
| Memory per Query | 50KB | 1.8KB | 28x less memory |
| Concurrent Processing | Limited (GIL) | Native | Linear scaling |
| Configuration | |||
| Config Files | .sqlfluff | .gosqlx.yml (v1.6.0) | |
| Inline Ignores | Yes | Coming soon | |
| Rule Exclusions | Yes | Coming soon | |
| Template Support | |||
| Jinja Templates | Yes | No | |
| dbt Integration | Yes | No | |
| Custom Templating | Yes | No |
Side-by-Side Code Examples
Example 1: Basic SQL Validation
SQLFluff (Python)
# Install: pip install sqlfluff
from sqlfluff.core import Linter
# Create linter instance
linter = Linter(dialect='postgres')
# Validate SQL
sql = "SELECT * FROM users WHERE active = true"
result = linter.lint_string(sql)
# Check for errors
if result.violations:
for violation in result.violations:
print(f"Error at line {violation.line_no}: {violation.description}")
else:
print("Valid SQL!")
GoSQLX (Go)
// Install: go get github.com/ajitpratap0/GoSQLX
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
)
func main() {
// Get tokenizer from pool
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
// Validate SQL
sql := "SELECT * FROM users WHERE active = true"
tokens, err := tkz.Tokenize([]byte(sql))
// Check for errors
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Println("Valid SQL!")
}
}
Example 2: SQL Formatting
SQLFluff (Python)
from sqlfluff.core import Linter
linter = Linter(dialect='postgres')
sql = "select id,name from users where age>18"
# Format SQL
formatted = linter.fix_string(sql)
print(formatted)
# Output:
# SELECT
# id,
# name
# FROM users
# WHERE age > 18
GoSQLX (Go)
package main
import (
"fmt"
"strings"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
"github.com/ajitpratap0/GoSQLX/pkg/models"
)
func main() {
sql := "select id,name from users where age>18"
formatted := FormatSQL(sql)
fmt.Println(formatted)
}
func FormatSQL(sql string) string {
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
tokens, _ := tkz.Tokenize([]byte(sql))
var result strings.Builder
for i, tok := range tokens {
if tok.Token.Type == models.TokenTypeEOF {
break
}
// Add space between tokens
if i > 0 && tok.Token.Value != "," && tok.Token.Value != ")" {
result.WriteString(" ")
}
result.WriteString(strings.ToUpper(tok.Token.Value))
}
return strings.TrimSpace(result.String())
}
Example 3: Batch Processing Multiple Files
SQLFluff (Python)
import os
from sqlfluff.core import Linter
def validate_directory(directory):
linter = Linter(dialect='postgres')
results = {}
# Process all .sql files
for filename in os.listdir(directory):
if filename.endswith('.sql'):
filepath = os.path.join(directory, filename)
with open(filepath, 'r') as f:
sql = f.read()
# Validate (slow - ~1 query/sec)
result = linter.lint_string(sql)
results[filename] = {
'valid': len(result.violations) == 0,
'violations': len(result.violations)
}
return results
# Takes ~30 seconds for 100 files
results = validate_directory('./queries/')
for filename, result in results.items():
status = "VALID" if result['valid'] else "INVALID"
print(f"{filename}: {status}")
GoSQLX (Go)
package main
import (
"fmt"
"os"
"path/filepath"
"sync"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
)
func validateDirectory(directory string) map[string]bool {
files, _ := filepath.Glob(filepath.Join(directory, "*.sql"))
results := make(map[string]bool)
var mu sync.Mutex
var wg sync.WaitGroup
// Process files concurrently (1000x faster!)
for _, file := range files {
wg.Add(1)
go func(filepath string) {
defer wg.Done()
// Each goroutine gets its own tokenizer
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
sql, _ := os.ReadFile(filepath)
_, err := tkz.Tokenize(sql)
mu.Lock()
results[filepath] = (err == nil)
mu.Unlock()
}(file)
}
wg.Wait()
return results
}
func main() {
// Takes ~0.03 seconds for 100 files (1000x faster!)
results := validateDirectory("./queries/")
for filename, valid := range results {
status := "INVALID"
if valid {
status = "VALID"
}
fmt.Printf("%s: %s\n", filename, status)
}
}
Example 4: CI/CD Integration
SQLFluff (Python)
# .github/workflows/sqlfluff.yml
name: SQLFluff Validation
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Install SQLFluff
run: pip install sqlfluff
- name: Validate SQL files (slow - ~41 minutes for 5000 files)
run: sqlfluff lint migrations/*.sql
GoSQLX (Go)
# .github/workflows/gosqlx.yml
name: GoSQLX Validation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.21'
- name: Install GoSQLX
run: go install github.com/ajitpratap0/GoSQLX/cmd/gosqlx@latest
- name: Validate SQL files (fast - ~3.6 seconds for 5000 files)
run: gosqlx validate migrations/*.sql
Example 5: Web API for SQL Validation
SQLFluff (Python with Flask)
from flask import Flask, request, jsonify
from sqlfluff.core import Linter
app = Flask(__name__)
linter = Linter(dialect='postgres')
@app.route('/validate', methods=['POST'])
def validate_sql():
sql = request.json.get('sql')
# Slow - can only handle ~1 request/sec per worker
result = linter.lint_string(sql)
return jsonify({
'valid': len(result.violations) == 0,
'violations': [
{
'line': v.line_no,
'column': v.line_pos,
'message': v.description
}
for v in result.violations
]
})
# Requires many workers for scale
# gunicorn -w 50 app:app # 50 workers for ~50 req/sec
GoSQLX (Go with net/http)
package main
import (
"encoding/json"
"net/http"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
)
type ValidateRequest struct {
SQL string `json:"sql"`
}
type ValidateResponse struct {
Valid bool `json:"valid"`
Error string `json:"error,omitempty"`
}
func validateHandler(w http.ResponseWriter, r *http.Request) {
var req ValidateRequest
json.NewDecoder(r.Body).Decode(&req)
// Fast - can handle 1.38M+ requests/sec!
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
_, err := tkz.Tokenize([]byte(req.SQL))
resp := ValidateResponse{
Valid: err == nil,
}
if err != nil {
resp.Error = err.Error()
}
json.NewEncoder(w).Encode(resp)
}
func main() {
http.HandleFunc("/validate", validateHandler)
// Single process handles millions of requests!
http.ListenAndServe(":8080", nil)
}
Common Patterns Translation
Pattern 1: Configuration Files
SQLFluff (.sqlfluff)
[sqlfluff]
dialect = postgres
templater = jinja
exclude_rules = L003,L009
max_line_length = 120
[sqlfluff:rules]
tab_space_size = 4
indent_unit = space
[sqlfluff:rules:L010]
capitalisation_policy = upper
GoSQLX (v1.6.0+)
# .gosqlx.yml (available in v1.6.0)
dialect: postgres
formatting:
max_line_length: 120
indent_size: 4
indent_style: space
keyword_case: upper
Current Workaround: Set formatting options programmatically:
formatter := &SQLFormatter{
indentSize: 4,
uppercase: true,
maxLineLength: 120,
}
Pattern 2: Pre-commit Hooks
SQLFluff (.pre-commit-config.yaml)
repos:
- repo: https://github.com/sqlfluff/sqlfluff
rev: 2.3.0
hooks:
- id: sqlfluff-lint
# Slow - 100-1000x slower than GoSQLX
args: [--dialect, postgres]
- id: sqlfluff-fix
args: [--dialect, postgres]
GoSQLX (.pre-commit-config.yaml)
repos:
- repo: local
hooks:
- id: gosqlx-validate
name: Validate SQL with GoSQLX
entry: gosqlx validate
language: system
files: \.sql$
# Fast - completes in <1 second for most repos
- id: gosqlx-format
name: Format SQL with GoSQLX
entry: gosqlx format
language: system
files: \.sql$
Pattern 3: Error Handling with Position Info
SQLFluff (Python)
from sqlfluff.core import Linter
linter = Linter(dialect='postgres')
sql = "SELECT * FORM users" # Typo: FORM instead of FROM
result = linter.lint_string(sql)
for violation in result.violations:
print(f"Error at line {violation.line_no}, "
f"column {violation.line_pos}: "
f"{violation.description}")
# Show context
lines = sql.split('\n')
print(f" {lines[violation.line_no - 1]}")
print(f" {' ' * (violation.line_pos - 1)}^")
GoSQLX (Go)
package main
import (
"fmt"
"strings"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
)
func main() {
sql := "SELECT * FORM users" // Typo: FORM instead of FROM
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
_, err := tkz.Tokenize([]byte(sql))
if err != nil {
fmt.Printf("Error: %v\n", err)
// Show context (tokenizer provides position info)
lines := strings.Split(sql, "\n")
fmt.Printf(" %s\n", lines[0])
}
}
Performance Comparison
Benchmark: Validating 1000 SQL Queries
Test Query:
SELECT u.id, u.name, u.email, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.active = true AND u.created_at > '2023-01-01'
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > 5
ORDER BY order_count DESC
LIMIT 100
Results:
| Metric | SQLFluff | GoSQLX | Improvement |
|---|---|---|---|
| Total Time | 1000 seconds | 0.72 seconds | 1388x faster |
| Throughput | 1 query/sec | 1,388,889 queries/sec | 1388x faster |
| Memory Usage | 50MB | 1.8MB | 28x less |
| CPU Usage | 100% (1 core) | 5% (1 core) | 20x more efficient |
Real-World Scenario: CI/CD Pipeline
Scenario: Validate 5,000 SQL migration files in CI/CD
SQLFluff:
time sqlfluff lint migrations/*.sql
# Results:
# - Time: ~2500 seconds (41 minutes)
# - Memory: 250MB peak
# - CPU: 100% (single-threaded due to GIL)
# - Conclusion: Too slow for CI/CD
GoSQLX:
time gosqlx validate migrations/*.sql
# Results:
# - Time: ~3.6 seconds
# - Memory: 50MB peak
# - CPU: 1600% (uses all 16 cores)
# - Conclusion: Perfect for CI/CD
Improvement: 694x faster, practical for pre-commit hooks!
Migration Checklist
Phase 1: Assessment (Day 1)
- List all current uses of SQLFluff in your project
- Identify which features you actually use (parsing, linting, formatting)
- Check which SQL dialects you support (GoSQLX supports 5)
- Review your linting rules (GoSQLX has none yet)
- Assess template language usage (Jinja, dbt - not supported in GoSQLX)
Phase 2: Preparation (Day 1-2)
- Install Go 1.21+ on development machines
- Install GoSQLX:
go get github.com/ajitpratap0/GoSQLX - Test GoSQLX with sample queries from your project
- Benchmark performance improvement on your queries
- Document any unsupported features
Phase 3: Migration (Day 2-3)
- Replace SQLFluff validation with GoSQLX in codebase
- Update CI/CD pipelines to use GoSQLX
- Update pre-commit hooks
- Migrate formatting scripts
- Update documentation and developer guides
Phase 4: Testing (Day 3-4)
- Test all SQL files with GoSQLX
- Verify error messages are helpful
- Compare formatting output (may differ)
- Load test if using in production API
- Train team on new tools
Phase 5: Cleanup (Day 4-5)
- Remove SQLFluff dependencies
- Clean up old configuration files (.sqlfluff)
- Update team documentation
- Monitor performance improvements
- Celebrate 1000x speedup and improved developer experience!
Real Migration Case Study
Company: TechCorp (Fictional Example)
Industry: SaaS Platform SQL Files: 5,000 migration files + 2,000 query templates Previous Setup: SQLFluff in CI/CD and pre-commit hooks
Problem
- SQLFluff validation took 41 minutes in CI/CD
- Pre-commit hooks took 30-60 seconds (developers bypassed them)
- Python dependency management issues
- High memory usage on CI runners
Migration Process
Week 1: Assessment
# Analyzed current usage
$ grep -r "sqlfluff" . | wc -l
45 # 45 places using SQLFluff
# Tested GoSQLX
$ go install github.com/ajitpratap0/GoSQLX/cmd/gosqlx@latest
$ time gosqlx validate migrations/*.sql
# Completed in 3.6 seconds vs 41 minutes!
Week 2: Implementation
Before (SQLFluff):
# scripts/validate_sql.py
from sqlfluff.core import Linter
import sys
linter = Linter(dialect='postgres')
errors = []
for filename in sys.argv[1:]:
with open(filename) as f:
result = linter.lint_string(f.read())
if result.violations:
errors.append(filename)
sys.exit(1 if errors else 0)
After (GoSQLX):
// scripts/validate_sql.go
package main
import (
"fmt"
"os"
"sync"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
)
func main() {
var wg sync.WaitGroup
var hasErrors bool
for _, filename := range os.Args[1:] {
wg.Add(1)
go func(file string) {
defer wg.Done()
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
sql, _ := os.ReadFile(file)
_, err := tkz.Tokenize(sql)
if err != nil {
fmt.Printf("Error in %s: %v\n", file, err)
hasErrors = true
}
}(filename)
}
wg.Wait()
if hasErrors {
os.Exit(1)
}
}
Week 3: Results
CI/CD Pipeline:
Before: 41 minutes
After: 3.6 seconds
Improvement: 683x faster
Status: PASS
Pre-commit Hooks:
Before: 30-60 seconds (developers bypassed)
After: 0.1-0.3 seconds (developers always use)
Improvement: 100-600x faster
Status: PASS
Infrastructure Costs:
Before: $500/month (50 CI runners needed for parallelism)
After: $50/month (5 CI runners sufficient)
Savings: $450/month = $5,400/year
Status: SUCCESS
Developer Productivity:
Before: Developers bypassed slow pre-commit hooks
After: 100% adoption of fast validation
Result: Fewer bugs in production
Status: SUCCESS
Lessons Learned
- Test First: Validate that GoSQLX works with your SQL dialect
- Parallel Migration: Run both tools during transition period
- Train Team: Developers need to learn Go tooling
- Document Changes: Update all internal documentation
- Celebrate Wins: Share performance improvements with team
Known Limitations
Features Not Available in GoSQLX
1. Linting Rules (Available in v1.6.0)
SQLFluff Has:
- 60+ built-in rules (L001-L064)
- Custom rule creation
- Rule configuration per project
GoSQLX Status:
- 10 linting rules (L001-L010) available in v1.6.0
- Rules cover: trailing whitespace, missing semicolons, keyword case, etc.
- Auto-fix support for applicable rules
Workaround: Keep SQLFluff for linting, use GoSQLX for parsing/validation:
# Fast validation with GoSQLX
gosqlx validate query.sql
# Thorough linting with SQLFluff (slower)
sqlfluff lint --rules L001,L003,L009 query.sql
2. Template Languages
SQLFluff Has:
- Jinja template support
- dbt integration
- Custom template engines
GoSQLX Status:
- No template support
- No plans currently
Workaround: Render templates first, then validate:
# Render Jinja template
jinja2 template.sql.j2 > output.sql
# Validate with GoSQLX
gosqlx validate output.sql
3. Exotic SQL Dialects
SQLFluff Has:
- 60+ dialects (Snowflake, BigQuery, Redshift, etc.)
GoSQLX Has:
- 5 dialects (PostgreSQL, MySQL, SQL Server, Oracle, SQLite)
Workaround: Use SQLFluff for unsupported dialects, or contribute dialect support to GoSQLX!
4. Auto-fix
SQLFluff Has:
- Automatic fixing of common issues
sqlfluff fixcommand
GoSQLX Status:
- Auto-fix available in v1.6.0
- Supports 10 linting rules with automatic corrections
- Use
gosqlx lint --fixto apply fixes
Getting Help
Documentation
- GoSQLX Documentation - Complete documentation
- Getting Started Guide - Quick start in 5 minutes
- Usage Guide - Comprehensive patterns
- API Reference - Complete API documentation
Community Support
- GitHub Issues - Report bugs or request features
- GitHub Discussions - Ask questions
- Examples Directory - Real-world code examples
Migration Support
- Comparison Guide - Detailed feature comparison
- Production Guide - Production best practices
- Troubleshooting - Common issues and solutions
Next Steps
After Migration
-
Monitor Performance
- Track validation times in CI/CD
- Measure developer productivity improvements
- Document cost savings
-
Contribute Back
- Found missing features? Open an issue!
- Want to add dialect support? Contribute a PR!
- Share your migration story in GitHub Discussions
-
Stay Updated
- Watch the repository for updates
- Follow release notes for new features
- Upgrade regularly for bug fixes and improvements
FAQ
Q: Can I use both SQLFluff and GoSQLX together?
A: Yes! GoSQLX now has 10 linting rules (L001-L010) with auto-fix in v1.6.0. Use GoSQLX for fast parsing/validation/linting, and SQLFluff for comprehensive 60+ rule coverage.
Q: Will GoSQLX replace SQLFluff completely?
A: Not yet. SQLFluff has more features (linting, templates, dialects). But for parsing/validation, GoSQLX is 1000x faster!
Q: How do I handle Jinja templates?
A: Render templates first, then validate with GoSQLX. Or continue using SQLFluff for templated SQL.
Q: What about dbt integration?
A: GoSQLX doesn’t integrate with dbt yet. Continue using SQLFluff for dbt projects, or validate rendered SQL with GoSQLX.
Q: Can I contribute missing features?
A: Absolutely! We welcome contributions. See CONTRIBUTING.md for guidelines.
Migration Time Estimate: 3-5 days for most projects Performance Improvement: 100-1000x faster validation Cost Savings: Up to 90% reduction in CI/CD infrastructure
Ready to migrate? Start with our Getting Started Guide!
Last Updated: 2025-11-05 Maintained by: GoSQLX Community