Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.19', '1.20', '1.21']
go-version: ['1.24']

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
go-version: '1.24'

- name: golangci-lint
uses: golangci/golangci-lint-action@v3
Expand All @@ -39,5 +39,5 @@ jobs:
- name: Run staticcheck
uses: dominikh/staticcheck-action@v1.3.0
with:
version: "2023.1.6"
version: "latest"
install-go: false
4 changes: 2 additions & 2 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23' # Use latest stable Go for security scanning
go-version: '1.24' # Match project requirements in go.mod
cache: true

- name: Run GoSec Security Scanner
Expand Down Expand Up @@ -176,7 +176,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23' # Use latest stable Go to avoid standard library vulnerabilities
go-version: '1.24' # Match project requirements in go.mod
cache: true

- name: Install govulncheck
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.19', '1.20', '1.21']
go: ['1.24']

steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -54,7 +54,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
go-version: '1.24'

- name: Run benchmarks
run: |
Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ go build -o gosqlx ./cmd/gosqlx
## 🚀 Quick Start

### CLI Usage

**Standard Usage:**
```bash
# Validate SQL syntax
gosqlx validate "SELECT * FROM users WHERE active = true"
Expand All @@ -125,6 +127,46 @@ gosqlx format query.sql | gosqlx validate # Chain commands
cat *.sql | gosqlx format | tee formatted.sql # Pipeline composition
```

**Pipeline/Stdin Support** (New in v1.6.0):
```bash
# Auto-detect piped input
echo "SELECT * FROM users" | gosqlx validate
cat query.sql | gosqlx format
cat complex.sql | gosqlx analyze --security

# Explicit stdin marker
gosqlx validate -
gosqlx format - < query.sql

# Input redirection
gosqlx validate < query.sql
gosqlx parse < complex_query.sql

# Full pipeline chains
cat query.sql | gosqlx format | gosqlx validate
echo "select * from users" | gosqlx format > formatted.sql
find . -name "*.sql" -exec cat {} \; | gosqlx validate

# Works on Windows PowerShell too!
Get-Content query.sql | gosqlx format
"SELECT * FROM users" | gosqlx validate
```

**Cross-Platform Pipeline Examples:**
```bash
# Unix/Linux/macOS
cat query.sql | gosqlx format | tee formatted.sql | gosqlx validate
echo "SELECT 1" | gosqlx validate && echo "Valid!"

# Windows PowerShell
Get-Content query.sql | gosqlx format | Set-Content formatted.sql
"SELECT * FROM users" | gosqlx validate

# Git hooks (pre-commit)
git diff --cached --name-only --diff-filter=ACM "*.sql" | \
xargs cat | gosqlx validate --quiet
```

### Library Usage - Simple API

GoSQLX provides a simple, high-level API that handles all complexity for you:
Expand Down
73 changes: 71 additions & 2 deletions cmd/gosqlx/cmd/analyze.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
"github.com/spf13/pflag"

Expand Down Expand Up @@ -28,20 +30,34 @@ Examples:
gosqlx analyze --all query.sql # Comprehensive analysis
gosqlx analyze "SELECT * FROM users" # Analyze query directly

Pipeline/Stdin Examples:
echo "SELECT * FROM users" | gosqlx analyze # Analyze from stdin (auto-detect)
cat query.sql | gosqlx analyze # Pipe file contents
gosqlx analyze - # Explicit stdin marker
gosqlx analyze < query.sql # Input redirection

Analysis capabilities:
• SQL injection pattern detection
• Performance optimization suggestions
• Query complexity scoring
• Query complexity scoring
• Best practices validation
• Multi-dialect compatibility checks

Note: Advanced analysis features are implemented in Phase 4 of the roadmap.
This is a basic implementation for CLI foundation.`,
Args: cobra.ExactArgs(1),
Args: cobra.MaximumNArgs(1), // Changed to allow stdin with no args
RunE: analyzeRun,
}

func analyzeRun(cmd *cobra.Command, args []string) error {
// Handle stdin input
if len(args) == 0 || (len(args) == 1 && args[0] == "-") {
if ShouldReadFromStdin(args) {
return analyzeFromStdin(cmd)
}
return fmt.Errorf("no input provided: specify file path, SQL query, or pipe via stdin")
}

// Load configuration with CLI flag overrides
cfg, err := config.LoadDefault()
if err != nil {
Expand Down Expand Up @@ -83,6 +99,59 @@ func analyzeRun(cmd *cobra.Command, args []string) error {
return analyzer.DisplayReport(result.Report)
}

// analyzeFromStdin handles analysis from stdin input
func analyzeFromStdin(cmd *cobra.Command) error {
// Read from stdin
content, err := ReadFromStdin()
if err != nil {
return fmt.Errorf("failed to read from stdin: %w", err)
}

// Validate stdin content
if err := ValidateStdinInput(content); err != nil {
return fmt.Errorf("stdin validation failed: %w", err)
}

// Load configuration
cfg, err := config.LoadDefault()
if err != nil {
cfg = config.DefaultConfig()
}

// Track which flags were explicitly set
flagsChanged := make(map[string]bool)
cmd.Flags().Visit(func(f *pflag.Flag) {
flagsChanged[f.Name] = true
})
if cmd.Parent() != nil && cmd.Parent().PersistentFlags() != nil {
cmd.Parent().PersistentFlags().Visit(func(f *pflag.Flag) {
flagsChanged[f.Name] = true
})
}

// Create analyzer options
opts := AnalyzerOptionsFromConfig(cfg, flagsChanged, AnalyzerFlags{
Security: analyzeSecurity,
Performance: analyzePerformance,
Complexity: analyzeComplexity,
All: analyzeAll,
Format: format,
Verbose: verbose,
})

// Create analyzer
analyzer := NewAnalyzer(cmd.OutOrStdout(), cmd.ErrOrStderr(), opts)

// Analyze the stdin content (Analyze accepts string input directly)
result, err := analyzer.Analyze(string(content))
if err != nil {
return err
}

// Display the report
return analyzer.DisplayReport(result.Report)
}

func init() {
rootCmd.AddCommand(analyzeCmd)

Expand Down
97 changes: 96 additions & 1 deletion cmd/gosqlx/cmd/format.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -34,12 +35,29 @@ Examples:
gosqlx format "*.sql" # Format all SQL files
gosqlx format -o formatted.sql query.sql # Save to specific file

Pipeline/Stdin Examples:
echo "SELECT * FROM users" | gosqlx format # Format from stdin (auto-detect)
cat query.sql | gosqlx format # Pipe file contents
gosqlx format - # Explicit stdin marker
gosqlx format < query.sql # Input redirection
cat query.sql | gosqlx format > formatted.sql # Full pipeline

Performance: 100x faster than SQLFluff for equivalent operations`,
Args: cobra.MinimumNArgs(1),
Args: cobra.MinimumNArgs(0), // Changed to allow stdin with no args
RunE: formatRun,
}

func formatRun(cmd *cobra.Command, args []string) error {
// Handle stdin input
if ShouldReadFromStdin(args) {
return formatFromStdin(cmd)
}

// Validate that we have file arguments if not using stdin
if len(args) == 0 {
return fmt.Errorf("no input provided: specify file paths or pipe SQL via stdin")
}

// Load configuration with CLI flag overrides
cfg, err := config.LoadDefault()
if err != nil {
Expand Down Expand Up @@ -87,6 +105,83 @@ func formatRun(cmd *cobra.Command, args []string) error {
return nil
}

// formatFromStdin handles formatting from stdin input
func formatFromStdin(cmd *cobra.Command) error {
// Read from stdin
content, err := ReadFromStdin()
if err != nil {
return fmt.Errorf("failed to read from stdin: %w", err)
}

// Validate stdin content
if err := ValidateStdinInput(content); err != nil {
return fmt.Errorf("stdin validation failed: %w", err)
}

// Note: in-place mode is not supported for stdin (would be no-op)
if formatInPlace {
return fmt.Errorf("in-place mode (-i) is not supported with stdin input")
}

// Load configuration
cfg, err := config.LoadDefault()
if err != nil {
cfg = config.DefaultConfig()
}

// Track which flags were explicitly set
flagsChanged := make(map[string]bool)
cmd.Flags().Visit(func(f *pflag.Flag) {
flagsChanged[f.Name] = true
})
if cmd.Parent() != nil && cmd.Parent().PersistentFlags() != nil {
cmd.Parent().PersistentFlags().Visit(func(f *pflag.Flag) {
flagsChanged[f.Name] = true
})
}

// Create formatter options
opts := FormatterOptionsFromConfig(cfg, flagsChanged, FormatterFlags{
InPlace: false, // always false for stdin
IndentSize: formatIndentSize,
Uppercase: formatUppercase,
Compact: formatCompact,
Check: formatCheck,
MaxLine: formatMaxLine,
Verbose: verbose,
Output: outputFile,
})

// Create formatter
formatter := NewFormatter(cmd.OutOrStdout(), cmd.ErrOrStderr(), opts)

// Format the SQL content using the internal formatSQL method
formattedSQL, err := formatter.formatSQL(string(content))
if err != nil {
return fmt.Errorf("formatting failed: %w", err)
}

// In check mode, compare original and formatted
if formatCheck {
if string(content) != formattedSQL {
fmt.Fprintf(cmd.ErrOrStderr(), "stdin needs formatting\n")
os.Exit(1)
}

if verbose {
fmt.Fprintf(cmd.OutOrStdout(), "stdin is properly formatted\n")
}
return nil
}

// Write formatted output
if err := WriteOutput([]byte(formattedSQL), outputFile, cmd.OutOrStdout()); err != nil {
return err
}

return nil
}

func init() {
rootCmd.AddCommand(formatCmd)

Expand Down
Loading
Loading