Skip to content

Commit 89473b2

Browse files
authored
Merge branch 'main' into feat/linter-expansion
2 parents 8a966f3 + 43ccdf5 commit 89473b2

37 files changed

+3303
-10
lines changed

.github/workflows/integrations.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Integrations
2+
3+
on:
4+
push:
5+
paths:
6+
- 'integrations/**'
7+
- '.github/workflows/integrations.yml'
8+
pull_request:
9+
paths:
10+
- 'integrations/**'
11+
- '.github/workflows/integrations.yml'
12+
13+
jobs:
14+
opentelemetry:
15+
name: OpenTelemetry Integration
16+
runs-on: ubuntu-latest
17+
defaults:
18+
run:
19+
working-directory: integrations/opentelemetry
20+
steps:
21+
- uses: actions/checkout@v4
22+
- uses: actions/setup-go@v5
23+
with:
24+
go-version: '1.26'
25+
- run: go mod tidy
26+
- run: go test -race -timeout 60s ./...
27+
28+
gorm:
29+
name: GORM Integration
30+
runs-on: ubuntu-latest
31+
defaults:
32+
run:
33+
working-directory: integrations/gorm
34+
steps:
35+
- uses: actions/checkout@v4
36+
- uses: actions/setup-go@v5
37+
with:
38+
go-version: '1.26'
39+
- run: go mod tidy
40+
- run: go test -race -timeout 60s ./...

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- **SQL Transpilation** (`pkg/transpiler`): New `Transpile(sql, from, to)` function converts SQL between dialects with a composable rewrite-rule pipeline
12+
- MySQL → PostgreSQL: `AUTO_INCREMENT``SERIAL`/`BIGSERIAL`, `TINYINT(1)``BOOLEAN`
13+
- PostgreSQL → MySQL: `SERIAL``INT AUTO_INCREMENT`, `ILIKE``LOWER() LIKE LOWER()`
14+
- PostgreSQL → SQLite: `SERIAL`/`BIGSERIAL``INTEGER`, array types → `TEXT`
15+
- `gosqlx.Transpile()` top-level convenience wrapper
16+
- `gosqlx transpile --from <dialect> --to <dialect>` CLI subcommand
1117
- **MariaDB dialect** (`--dialect mariadb`): New SQL dialect extending MySQL with support for SEQUENCE DDL (`CREATE/DROP/ALTER SEQUENCE` with full option set), temporal tables (`FOR SYSTEM_TIME`, `WITH SYSTEM VERSIONING`, `PERIOD FOR`), and `CONNECT BY` hierarchical queries with `PRIOR`, `START WITH`, and `NOCYCLE`
18+
- `integrations/opentelemetry/` sub-module: `InstrumentedParse()` wraps `gosqlx.Parse()` with OpenTelemetry spans including `db.system`, `db.statement.type`, `db.sql.tables`, `db.sql.columns` attributes
19+
- `integrations/gorm/` sub-module: GORM plugin that records executed query metadata (tables, columns, statement type) via GoSQLX parsing with GORM SQL normalization (backtick identifiers, `?` placeholders); exposes `Stats()` and `Reset()` APIs
20+
- CI workflow for integration sub-modules (`.github/workflows/integrations.yml`)
1221

1322
## [1.13.0] - 2026-03-20
1423

cmd/gosqlx/cmd/stats.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2026 GoSQLX Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmd
16+
17+
import (
18+
"encoding/json"
19+
"fmt"
20+
"sort"
21+
22+
"github.com/spf13/cobra"
23+
24+
"github.com/ajitpratap0/GoSQLX/pkg/metrics"
25+
)
26+
27+
// statsCmd shows current object pool utilization counters.
28+
var statsCmd = &cobra.Command{
29+
Use: "stats",
30+
Short: "Show pool utilization statistics",
31+
Long: `Display current object pool utilization counters.
32+
33+
Shows gets (pool retrievals), puts (pool returns), and active (currently borrowed)
34+
counts for each named pool: tokenizer, parser, and ast.
35+
36+
Examples:
37+
gosqlx stats
38+
gosqlx stats --json`,
39+
RunE: func(cmd *cobra.Command, args []string) error {
40+
jsonOutput, _ := cmd.Flags().GetBool("json")
41+
stats := metrics.GetPoolStats()
42+
43+
if jsonOutput {
44+
b, err := json.MarshalIndent(stats, "", " ")
45+
if err != nil {
46+
return err
47+
}
48+
fmt.Fprintln(cmd.OutOrStdout(), string(b))
49+
return nil
50+
}
51+
52+
// Human-readable table
53+
out := cmd.OutOrStdout()
54+
fmt.Fprintf(out, "%-20s %10s %10s %10s\n", "POOL", "GETS", "PUTS", "ACTIVE")
55+
fmt.Fprintf(out, "%-20s %10s %10s %10s\n", "----", "----", "----", "------")
56+
57+
names := make([]string, 0, len(stats))
58+
for k := range stats {
59+
names = append(names, k)
60+
}
61+
sort.Strings(names)
62+
63+
for _, name := range names {
64+
s := stats[name]
65+
fmt.Fprintf(out, "%-20s %10d %10d %10d\n", name, s.Gets, s.Puts, s.Active())
66+
}
67+
return nil
68+
},
69+
}
70+
71+
func init() {
72+
statsCmd.Flags().Bool("json", false, "Output as JSON")
73+
rootCmd.AddCommand(statsCmd)
74+
}

cmd/gosqlx/cmd/transpile.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2026 GoSQLX Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmd
16+
17+
import (
18+
"fmt"
19+
"io"
20+
"os"
21+
"strings"
22+
23+
"github.com/spf13/cobra"
24+
25+
"github.com/ajitpratap0/GoSQLX/pkg/gosqlx"
26+
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
27+
)
28+
29+
var transpileCmd = &cobra.Command{
30+
Use: "transpile [SQL]",
31+
Short: "Convert SQL from one dialect to another",
32+
Long: `Transpile SQL between dialects.
33+
34+
Supported dialect pairs:
35+
mysql → postgres
36+
postgres → mysql
37+
postgres → sqlite
38+
39+
SQL can be provided as a positional argument or piped via stdin.
40+
41+
Examples:
42+
gosqlx transpile --from mysql --to postgres "CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY)"
43+
echo "SELECT * FROM users WHERE name ILIKE '%alice%'" | gosqlx transpile --from postgres --to mysql
44+
gosqlx transpile --from postgres --to sqlite "CREATE TABLE t (id SERIAL PRIMARY KEY, tags TEXT)"`,
45+
Args: cobra.MaximumNArgs(1),
46+
RunE: func(cmd *cobra.Command, args []string) error {
47+
fromStr, _ := cmd.Flags().GetString("from")
48+
toStr, _ := cmd.Flags().GetString("to")
49+
50+
from, err := parseDialectFlag(fromStr)
51+
if err != nil {
52+
return fmt.Errorf("--from: %w", err)
53+
}
54+
to, err := parseDialectFlag(toStr)
55+
if err != nil {
56+
return fmt.Errorf("--to: %w", err)
57+
}
58+
59+
var sql string
60+
if len(args) > 0 {
61+
sql = args[0]
62+
} else {
63+
// Read from stdin.
64+
data, readErr := io.ReadAll(os.Stdin)
65+
if readErr != nil {
66+
return fmt.Errorf("reading stdin: %w", readErr)
67+
}
68+
sql = strings.TrimSpace(string(data))
69+
}
70+
71+
if sql == "" {
72+
return fmt.Errorf("no SQL provided: pass as argument or via stdin")
73+
}
74+
75+
result, err := gosqlx.Transpile(sql, from, to)
76+
if err != nil {
77+
return fmt.Errorf("transpile: %w", err)
78+
}
79+
fmt.Println(result)
80+
return nil
81+
},
82+
}
83+
84+
func init() {
85+
transpileCmd.Flags().String("from", "mysql", "Source dialect (mysql, postgres, sqlite, sqlserver, oracle, snowflake, clickhouse, mariadb)")
86+
transpileCmd.Flags().String("to", "postgres", "Target dialect (mysql, postgres, sqlite, sqlserver, oracle, snowflake, clickhouse, mariadb)")
87+
rootCmd.AddCommand(transpileCmd)
88+
}
89+
90+
// parseDialectFlag converts a dialect name string to a keywords.SQLDialect value.
91+
func parseDialectFlag(s string) (keywords.SQLDialect, error) {
92+
switch strings.ToLower(s) {
93+
case "mysql":
94+
return keywords.DialectMySQL, nil
95+
case "postgres", "postgresql":
96+
return keywords.DialectPostgreSQL, nil
97+
case "sqlite":
98+
return keywords.DialectSQLite, nil
99+
case "sqlserver", "mssql":
100+
return keywords.DialectSQLServer, nil
101+
case "oracle":
102+
return keywords.DialectOracle, nil
103+
case "snowflake":
104+
return keywords.DialectSnowflake, nil
105+
case "clickhouse":
106+
return keywords.DialectClickHouse, nil
107+
case "mariadb":
108+
return keywords.DialectMariaDB, nil
109+
default:
110+
return keywords.DialectGeneric, fmt.Errorf(
111+
"unknown dialect %q; valid: mysql, postgres, sqlite, sqlserver, oracle, snowflake, clickhouse, mariadb", s)
112+
}
113+
}

cmd/gosqlx/cmd/watch_cmd.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2026 GoSQLX Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmd
16+
17+
import (
18+
"github.com/spf13/cobra"
19+
)
20+
21+
// watchCmd is the cobra command that registers the watch subcommand.
22+
// The FileWatcher implementation lives in watch.go.
23+
var watchCmd = &cobra.Command{
24+
Use: "watch [file|dir...]",
25+
Short: "Watch SQL files and re-validate or re-format on change",
26+
Long: `Watch SQL files or directories and automatically re-process them when changes are detected.
27+
28+
Supports two modes:
29+
• validate (default): Re-run SQL validation on every file change
30+
• format: Re-format SQL files in-place on every file change
31+
32+
Examples:
33+
gosqlx watch *.sql
34+
gosqlx watch --mode format queries/
35+
gosqlx watch --debounce 500 schema.sql migrations/`,
36+
RunE: func(cmd *cobra.Command, args []string) error {
37+
if len(args) == 0 {
38+
return cmd.Help()
39+
}
40+
41+
modeStr, _ := cmd.Flags().GetString("mode")
42+
debounce, _ := cmd.Flags().GetInt("debounce")
43+
clear, _ := cmd.Flags().GetBool("clear")
44+
watchVerbose, _ := cmd.Flags().GetBool("watch-verbose")
45+
46+
mode := WatchModeValidate
47+
if modeStr == "format" {
48+
mode = WatchModeFormat
49+
}
50+
51+
opts := WatchOptions{
52+
Mode: mode,
53+
DebounceMs: debounce,
54+
ClearScreen: clear,
55+
Verbose: watchVerbose,
56+
Out: cmd.OutOrStdout(),
57+
Err: cmd.ErrOrStderr(),
58+
}
59+
60+
fw, err := NewFileWatcher(opts)
61+
if err != nil {
62+
return err
63+
}
64+
return fw.Watch(args)
65+
},
66+
}
67+
68+
func init() {
69+
watchCmd.Flags().String("mode", "validate", "watch mode: validate or format")
70+
watchCmd.Flags().Int("debounce", 300, "debounce delay in milliseconds")
71+
watchCmd.Flags().Bool("clear", false, "clear screen before each re-run")
72+
watchCmd.Flags().Bool("watch-verbose", false, "verbose output from watcher")
73+
rootCmd.AddCommand(watchCmd)
74+
}

integrations/gorm/go.mod

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module github.com/ajitpratap0/GoSQLX/integrations/gorm
2+
3+
go 1.26.1
4+
5+
require (
6+
github.com/ajitpratap0/GoSQLX v1.13.0
7+
gorm.io/driver/sqlite v1.5.6
8+
gorm.io/gorm v1.25.10
9+
)
10+
11+
require (
12+
github.com/jinzhu/inflection v1.0.0 // indirect
13+
github.com/jinzhu/now v1.1.5 // indirect
14+
github.com/mattn/go-sqlite3 v1.14.22 // indirect
15+
)
16+
17+
replace github.com/ajitpratap0/GoSQLX => ../../

integrations/gorm/go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
2+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
3+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
4+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
5+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
6+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
7+
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
8+
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
9+
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
10+
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

0 commit comments

Comments
 (0)