Skip to content

Commit 21e6557

Browse files
kyleconroyclaude
andauthored
feat(expander): Add star expander for SELECT * and RETURNING * (PostgreSQL, MySQL, SQLite) (#4203)
* feat(postgresql): Add star expander for SELECT * and RETURNING * Adds a new expander package that expands * expressions in SQL queries to explicit column names by preparing the query against a PostgreSQL database. Features: - Expands SELECT * to explicit column list - Preserves table prefix for qualified stars (e.g., table.*) - Handles RETURNING * in INSERT/UPDATE/DELETE statements - Recursively expands CTEs, including dependent CTEs - Supports subqueries in FROM clause - Works with both cgo (pganalyze/pg_query_go) and non-cgo (wasilibs/go-pgquery) builds Example: SELECT * FROM authors → SELECT id, name, bio FROM authors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(expander): port expander to use internal AST types Move expander from internal/engine/postgresql/expander to internal/x/expander and port it to use the internal AST types instead of pg_query nodes. Key changes: - Use internal AST types (*ast.SelectStmt, *ast.InsertStmt, etc.) - Use astutils.Search for star detection - Use ast.Format instead of pg_query deparse - Add Parser interface for dependency injection - Add test cases for COUNT(*) (should not be expanded) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(expander): add MySQL support and use ColumnGetter interface - Rename TestExpand to TestExpandPostgreSQL - Add TestExpandMySQL for MySQL database support - Replace pgxpool.Pool with ColumnGetter interface for database-agnostic column resolution - Add PostgreSQLColumnGetter and MySQLColumnGetter implementations - MySQL tests skip edge cases (double star, star in middle) due to intermediate query formatting issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(expander): use valid MySQL syntax for edge case tests MySQL doesn't support unqualified `*` mixed with other columns (e.g., `SELECT *, *` or `SELECT id, *, name`). These are valid PostgreSQL but invalid MySQL syntax. Update MySQL tests to use table-qualified stars which are valid: - `SELECT authors.*, authors.*` instead of `SELECT *, *` - `SELECT id, authors.*, name` instead of `SELECT id, *, name` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(expander): add SQLite support - Add TestExpandSQLite with 8 test cases using in-memory SQLite database - Rename MySQLColumnGetter to SQLColumnGetter since both MySQL and SQLite use the same database/sql-based implementation - SQLite supports the same star syntax as PostgreSQL (including `SELECT *, *` and `SELECT id, *, name`) Test results: - PostgreSQL: 14 tests - MySQL: 8 tests - SQLite: 8 tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(expander): use PrepareContext in SQLColumnGetter Use PrepareContext to validate the query before executing it to get column metadata. While database/sql doesn't expose column names from prepared statements directly (unlike pgx), this at least validates the SQL syntax before execution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor(expander): use native ncruces/go-sqlite3 API for column names Use the native sqlite3.Conn.Prepare and stmt.ColumnName/ColumnCount APIs to get column names without executing the query. This is more efficient and consistent with how PostgreSQL handles it. Changes: - Add SQLiteColumnGetter using native sqlite3.Conn - Rename SQLColumnGetter to MySQLColumnGetter (MySQL still needs to execute) - SQLite test now uses sqlite3.Open instead of sql.Open 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * 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> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ebd32cf commit 21e6557

File tree

5 files changed

+958
-3
lines changed

5 files changed

+958
-3
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
}

0 commit comments

Comments
 (0)