Skip to content

Commit f1bbc6e

Browse files
kyleconroyclaude
andcommitted
refactor(expander): use forked MySQL driver and fix list formatting
- Update MySQLColumnGetter to use github.com/sqlc-dev/mysql fork with StmtMetadata interface for getting column names via prepare - Add replace directive to go.mod for the forked MySQL driver - Fix list formatting to use ", " separator instead of "," for proper SQL spacing (e.g., "SELECT id, name, bio" instead of "SELECT id,name,bio") - Update test expectations to reflect proper spacing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ffc490b commit f1bbc6e

File tree

4 files changed

+55
-34
lines changed

4 files changed

+55
-34
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,5 @@ require (
6464
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
6565
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
6666
)
67+
68+
replace github.com/go-sql-driver/mysql => github.com/sqlc-dev/mysql v0.0.0-20251129233104-d81e1cac6db2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
2626
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
2727
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
2828
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
29-
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
30-
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
3129
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
3230
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
3331
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -159,6 +157,8 @@ github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4
159157
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
160158
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
161159
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
160+
github.com/sqlc-dev/mysql v0.0.0-20251129233104-d81e1cac6db2 h1:kmCAKKtOgK6EXXQX9oPdEASIhgor7TCpWxD8NtcqVcU=
161+
github.com/sqlc-dev/mysql v0.0.0-20251129233104-d81e1cac6db2/go.mod h1:TrDMWzjNTKvJeK2GC8uspG+PWyPLiY9QKvwdWpAdlZE=
162162
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
163163
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
164164
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

internal/sql/ast/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ func (n *List) Format(buf *TrackedBuffer, d format.Dialect) {
1414
if n == nil {
1515
return
1616
}
17-
buf.join(n, d, ",")
17+
buf.join(n, d, ", ")
1818
}

internal/x/expander/expander_test.go

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package expander
33
import (
44
"context"
55
"database/sql"
6+
"database/sql/driver"
67
"fmt"
78
"os"
89
"testing"
910

10-
_ "github.com/go-sql-driver/mysql"
11+
"github.com/go-sql-driver/mysql"
1112
"github.com/jackc/pgx/v5/pgxpool"
1213
"github.com/ncruces/go-sqlite3"
1314
_ "github.com/ncruces/go-sqlite3/embed"
@@ -42,28 +43,46 @@ func (g *PostgreSQLColumnGetter) GetColumnNames(ctx context.Context, query strin
4243
return columns, nil
4344
}
4445

45-
// MySQLColumnGetter implements ColumnGetter for MySQL using database/sql.
46+
// MySQLColumnGetter implements ColumnGetter for MySQL using the forked driver's StmtMetadata.
4647
type MySQLColumnGetter struct {
4748
db *sql.DB
4849
}
4950

5051
func (g *MySQLColumnGetter) GetColumnNames(ctx context.Context, query string) ([]string, error) {
51-
// Prepare the statement to validate the query and get column metadata
52-
stmt, err := g.db.PrepareContext(ctx, query)
52+
conn, err := g.db.Conn(ctx)
5353
if err != nil {
5454
return nil, err
5555
}
56-
defer stmt.Close()
56+
defer conn.Close()
5757

58-
// Execute to get column metadata from database/sql.
59-
// database/sql doesn't expose column names from prepared statements directly.
60-
rows, err := stmt.QueryContext(ctx)
58+
var columns []string
59+
err = conn.Raw(func(driverConn any) error {
60+
preparer, ok := driverConn.(driver.ConnPrepareContext)
61+
if !ok {
62+
return fmt.Errorf("driver connection does not support PrepareContext")
63+
}
64+
65+
stmt, err := preparer.PrepareContext(ctx, query)
66+
if err != nil {
67+
return err
68+
}
69+
defer stmt.Close()
70+
71+
meta, ok := stmt.(mysql.StmtMetadata)
72+
if !ok {
73+
return fmt.Errorf("prepared statement does not implement StmtMetadata")
74+
}
75+
76+
for _, col := range meta.ColumnMetadata() {
77+
columns = append(columns, col.Name)
78+
}
79+
return nil
80+
})
6181
if err != nil {
6282
return nil, err
6383
}
64-
defer rows.Close()
6584

66-
return rows.Columns()
85+
return columns, nil
6786
}
6887

6988
// SQLiteColumnGetter implements ColumnGetter for SQLite using the native ncruces/go-sqlite3 API.
@@ -133,7 +152,7 @@ func TestExpandPostgreSQL(t *testing.T) {
133152
{
134153
name: "simple select star",
135154
query: "SELECT * FROM authors",
136-
expected: "SELECT id,name,bio FROM authors;",
155+
expected: "SELECT id, name, bio FROM authors;",
137156
},
138157
{
139158
name: "select with no star",
@@ -143,52 +162,52 @@ func TestExpandPostgreSQL(t *testing.T) {
143162
{
144163
name: "select star with where clause",
145164
query: "SELECT * FROM authors WHERE id = 1",
146-
expected: "SELECT id,name,bio FROM authors WHERE id = 1;",
165+
expected: "SELECT id, name, bio FROM authors WHERE id = 1;",
147166
},
148167
{
149168
name: "double star",
150169
query: "SELECT *, * FROM authors",
151-
expected: "SELECT id,name,bio,id,name,bio FROM authors;",
170+
expected: "SELECT id, name, bio, id, name, bio FROM authors;",
152171
},
153172
{
154173
name: "table qualified star",
155174
query: "SELECT authors.* FROM authors",
156-
expected: "SELECT authors.id,authors.name,authors.bio FROM authors;",
175+
expected: "SELECT authors.id, authors.name, authors.bio FROM authors;",
157176
},
158177
{
159178
name: "star in middle of columns",
160179
query: "SELECT id, *, name FROM authors",
161-
expected: "SELECT id,id,name,bio,name FROM authors;",
180+
expected: "SELECT id, id, name, bio, name FROM authors;",
162181
},
163182
{
164183
name: "insert returning star",
165184
query: "INSERT INTO authors (name, bio) VALUES ('John', 'A writer') RETURNING *",
166-
expected: "INSERT INTO authors (name,bio) VALUES ('John','A writer') RETURNING id,name,bio;",
185+
expected: "INSERT INTO authors (name, bio) VALUES ('John', 'A writer') RETURNING id, name, bio;",
167186
},
168187
{
169188
name: "insert returning mixed",
170189
query: "INSERT INTO authors (name, bio) VALUES ('John', 'A writer') RETURNING id, *",
171-
expected: "INSERT INTO authors (name,bio) VALUES ('John','A writer') RETURNING id,id,name,bio;",
190+
expected: "INSERT INTO authors (name, bio) VALUES ('John', 'A writer') RETURNING id, id, name, bio;",
172191
},
173192
{
174193
name: "update returning star",
175194
query: "UPDATE authors SET name = 'Jane' WHERE id = 1 RETURNING *",
176-
expected: "UPDATE authors SET name = 'Jane' WHERE id = 1 RETURNING id,name,bio;",
195+
expected: "UPDATE authors SET name = 'Jane' WHERE id = 1 RETURNING id, name, bio;",
177196
},
178197
{
179198
name: "delete returning star",
180199
query: "DELETE FROM authors WHERE id = 1 RETURNING *",
181-
expected: "DELETE FROM authors WHERE id = 1 RETURNING id,name,bio;",
200+
expected: "DELETE FROM authors WHERE id = 1 RETURNING id, name, bio;",
182201
},
183202
{
184203
name: "cte with select star",
185204
query: "WITH a AS (SELECT * FROM authors) SELECT * FROM a",
186-
expected: "WITH a AS (SELECT id,name,bio FROM authors) SELECT id,name,bio FROM a;",
205+
expected: "WITH a AS (SELECT id, name, bio FROM authors) SELECT id, name, bio FROM a;",
187206
},
188207
{
189208
name: "multiple ctes with dependency",
190209
query: "WITH a AS (SELECT * FROM authors), b AS (SELECT * FROM a) SELECT * FROM b",
191-
expected: "WITH a AS (SELECT id,name,bio FROM authors), b AS (SELECT id,name,bio FROM a) SELECT id,name,bio FROM b;",
210+
expected: "WITH a AS (SELECT id, name, bio FROM authors), b AS (SELECT id, name, bio FROM a) SELECT id, name, bio FROM b;",
192211
},
193212
{
194213
name: "count star not expanded",
@@ -285,7 +304,7 @@ func TestExpandMySQL(t *testing.T) {
285304
{
286305
name: "simple select star",
287306
query: "SELECT * FROM authors",
288-
expected: "SELECT id,name,bio FROM authors;",
307+
expected: "SELECT id, name, bio FROM authors;",
289308
},
290309
{
291310
name: "select with no star",
@@ -295,22 +314,22 @@ func TestExpandMySQL(t *testing.T) {
295314
{
296315
name: "select star with where clause",
297316
query: "SELECT * FROM authors WHERE id = 1",
298-
expected: "SELECT id,name,bio FROM authors WHERE id = 1;",
317+
expected: "SELECT id, name, bio FROM authors WHERE id = 1;",
299318
},
300319
{
301320
name: "table qualified star",
302321
query: "SELECT authors.* FROM authors",
303-
expected: "SELECT authors.id,authors.name,authors.bio FROM authors;",
322+
expected: "SELECT authors.id, authors.name, authors.bio FROM authors;",
304323
},
305324
{
306325
name: "double table qualified star",
307326
query: "SELECT authors.*, authors.* FROM authors",
308-
expected: "SELECT authors.id,authors.name,authors.bio,authors.id,authors.name,authors.bio FROM authors;",
327+
expected: "SELECT authors.id, authors.name, authors.bio, authors.id, authors.name, authors.bio FROM authors;",
309328
},
310329
{
311330
name: "star in middle of columns table qualified",
312331
query: "SELECT id, authors.*, name FROM authors",
313-
expected: "SELECT id,authors.id,authors.name,authors.bio,name FROM authors;",
332+
expected: "SELECT id, authors.id, authors.name, authors.bio, name FROM authors;",
314333
},
315334
{
316335
name: "count star not expanded",
@@ -374,7 +393,7 @@ func TestExpandSQLite(t *testing.T) {
374393
{
375394
name: "simple select star",
376395
query: "SELECT * FROM authors",
377-
expected: "SELECT id,name,bio FROM authors;",
396+
expected: "SELECT id, name, bio FROM authors;",
378397
},
379398
{
380399
name: "select with no star",
@@ -384,22 +403,22 @@ func TestExpandSQLite(t *testing.T) {
384403
{
385404
name: "select star with where clause",
386405
query: "SELECT * FROM authors WHERE id = 1",
387-
expected: "SELECT id,name,bio FROM authors WHERE id = 1;",
406+
expected: "SELECT id, name, bio FROM authors WHERE id = 1;",
388407
},
389408
{
390409
name: "double star",
391410
query: "SELECT *, * FROM authors",
392-
expected: "SELECT id,name,bio,id,name,bio FROM authors;",
411+
expected: "SELECT id, name, bio, id, name, bio FROM authors;",
393412
},
394413
{
395414
name: "table qualified star",
396415
query: "SELECT authors.* FROM authors",
397-
expected: "SELECT authors.id,authors.name,authors.bio FROM authors;",
416+
expected: "SELECT authors.id, authors.name, authors.bio FROM authors;",
398417
},
399418
{
400419
name: "star in middle of columns",
401420
query: "SELECT id, *, name FROM authors",
402-
expected: "SELECT id,id,name,bio,name FROM authors;",
421+
expected: "SELECT id, id, name, bio, name FROM authors;",
403422
},
404423
{
405424
name: "count star not expanded",

0 commit comments

Comments
 (0)