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

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

FeatureSQLFluffGoSQLXNotes
Core Functionality
SQL ParsingYesYesGoSQLX 1000x faster
SQL ValidationYesYesSimilar accuracy
SQL FormattingYesYesDifferent style defaults
Syntax Error DetectionYesYesBoth provide line/column info
Linting & Rules
Linting Rules60+ rules10 rules (v1.6.0)GoSQLX growing
Custom RulesYesComing soon
Rule Configuration.sqlfluff.gosqlx.yml (v1.6.0)
Auto-fixYesYes (v1.6.0)
SQL Dialect Support
PostgreSQLYesYesGoSQLX ~80-85% coverage
MySQLYesYesGoSQLX ~80% coverage
SQL ServerYesYesGoSQLX ~75% coverage
OracleYesYesGoSQLX ~70% coverage
SQLiteYesYesGoSQLX ~85% coverage
SnowflakeYesNo
BigQueryYesNo
RedshiftYesNo
50+ Other DialectsYesNo
API & Integration
CLI ToolYesYesGoSQLX is faster
Programmatic APIComplexSimpleGoSQLX easier to use
Library IntegrationPythonGo
VSCode ExtensionYesYes (v1.6.0)
Pre-commit HooksYesYesGoSQLX 100-1000x faster
Performance
Parse Speed1,000 ops/sec1.38M ops/sec1380x faster
Memory per Query50KB1.8KB28x less memory
Concurrent ProcessingLimited (GIL)NativeLinear scaling
Configuration
Config Files.sqlfluff.gosqlx.yml (v1.6.0)
Inline IgnoresYesComing soon
Rule ExclusionsYesComing soon
Template Support
Jinja TemplatesYesNo
dbt IntegrationYesNo
Custom TemplatingYesNo

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:

MetricSQLFluffGoSQLXImprovement
Total Time1000 seconds0.72 seconds1388x faster
Throughput1 query/sec1,388,889 queries/sec1388x faster
Memory Usage50MB1.8MB28x less
CPU Usage100% (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

  1. Test First: Validate that GoSQLX works with your SQL dialect
  2. Parallel Migration: Run both tools during transition period
  3. Train Team: Developers need to learn Go tooling
  4. Document Changes: Update all internal documentation
  5. 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 fix command

GoSQLX Status:

  • Auto-fix available in v1.6.0
  • Supports 10 linting rules with automatic corrections
  • Use gosqlx lint --fix to apply fixes

Getting Help

Documentation

Community Support

Migration Support


Next Steps

After Migration

  1. Monitor Performance

    • Track validation times in CI/CD
    • Measure developer productivity improvements
    • Document cost savings
  2. Contribute Back

    • Found missing features? Open an issue!
    • Want to add dialect support? Contribute a PR!
    • Share your migration story in GitHub Discussions
  3. 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