From a755d5519ff0c953d959247ff9ca8deb14bd03af Mon Sep 17 00:00:00 2001 From: kevin9327 <5299031+kevin9327@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:45:35 +0900 Subject: [PATCH] fix(sqlite): honor aliases in RETURNING when naming result columns convertReturning copied each RETURNING expression into a ResTarget but never set ResTarget.Name from the column alias, so field names came from the underlying column or ColumnN. SELECT already passed the alias through convertResultColumns. Fixes #4680 Signed-off-by: kevin9327 <5299031+kevin9327@users.noreply.github.com> --- .../returning_aliases/sqlite/go/db.go | 31 +++ .../returning_aliases/sqlite/go/models.go | 16 ++ .../returning_aliases/sqlite/go/query.sql.go | 205 ++++++++++++++++++ .../returning_aliases/sqlite/query.sql | 36 +++ .../returning_aliases/sqlite/schema.sql | 6 + .../returning_aliases/sqlite/sqlc.json | 12 + internal/engine/sqlite/convert.go | 9 +- 7 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 internal/endtoend/testdata/returning_aliases/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/returning_aliases/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/returning_aliases/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/returning_aliases/sqlite/query.sql create mode 100644 internal/endtoend/testdata/returning_aliases/sqlite/schema.sql create mode 100644 internal/endtoend/testdata/returning_aliases/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/returning_aliases/sqlite/go/db.go b/internal/endtoend/testdata/returning_aliases/sqlite/go/db.go new file mode 100644 index 0000000000..32faa9f2b8 --- /dev/null +++ b/internal/endtoend/testdata/returning_aliases/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...any) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...any) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...any) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/returning_aliases/sqlite/go/models.go b/internal/endtoend/testdata/returning_aliases/sqlite/go/models.go new file mode 100644 index 0000000000..0b10664563 --- /dev/null +++ b/internal/endtoend/testdata/returning_aliases/sqlite/go/models.go @@ -0,0 +1,16 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +import ( + "database/sql" +) + +type T struct { + ID int64 + Name string + Score int64 + Created sql.NullString +} diff --git a/internal/endtoend/testdata/returning_aliases/sqlite/go/query.sql.go b/internal/endtoend/testdata/returning_aliases/sqlite/go/query.sql.go new file mode 100644 index 0000000000..dbd53e322c --- /dev/null +++ b/internal/endtoend/testdata/returning_aliases/sqlite/go/query.sql.go @@ -0,0 +1,205 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" +) + +const deleteReturningAliases = `-- name: DeleteReturningAliases :one +DELETE FROM t WHERE id = ? +RETURNING id AS deleted_id, upper(name) AS shout +` + +type DeleteReturningAliasesRow struct { + DeletedID int64 + Shout string +} + +func (q *Queries) DeleteReturningAliases(ctx context.Context, id int64) (DeleteReturningAliasesRow, error) { + row := q.db.QueryRowContext(ctx, deleteReturningAliases, id) + var i DeleteReturningAliasesRow + err := row.Scan(&i.DeletedID, &i.Shout) + return i, err +} + +const insertOnConflictReturningAliases = `-- name: InsertOnConflictReturningAliases :one +INSERT INTO t (id, name, score) VALUES (?, ?, ?) +ON CONFLICT (id) DO UPDATE SET score = excluded.score +RETURNING id AS new_id, score AS total +` + +type InsertOnConflictReturningAliasesParams struct { + ID int64 + Name string + Score int64 +} + +type InsertOnConflictReturningAliasesRow struct { + NewID int64 + Total int64 +} + +func (q *Queries) InsertOnConflictReturningAliases(ctx context.Context, arg InsertOnConflictReturningAliasesParams) (InsertOnConflictReturningAliasesRow, error) { + row := q.db.QueryRowContext(ctx, insertOnConflictReturningAliases, arg.ID, arg.Name, arg.Score) + var i InsertOnConflictReturningAliasesRow + err := row.Scan(&i.NewID, &i.Total) + return i, err +} + +const insertReturningAliases = `-- name: InsertReturningAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS new_id, score * 2 AS doubled +` + +type InsertReturningAliasesParams struct { + Name string + Score int64 +} + +type InsertReturningAliasesRow struct { + NewID int64 + Doubled int64 +} + +func (q *Queries) InsertReturningAliases(ctx context.Context, arg InsertReturningAliasesParams) (InsertReturningAliasesRow, error) { + row := q.db.QueryRowContext(ctx, insertReturningAliases, arg.Name, arg.Score) + var i InsertReturningAliasesRow + err := row.Scan(&i.NewID, &i.Doubled) + return i, err +} + +const insertReturningDuplicateAliases = `-- name: InsertReturningDuplicateAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS old_id, id AS row_id +` + +type InsertReturningDuplicateAliasesParams struct { + Name string + Score int64 +} + +type InsertReturningDuplicateAliasesRow struct { + OldID int64 + RowID int64 +} + +func (q *Queries) InsertReturningDuplicateAliases(ctx context.Context, arg InsertReturningDuplicateAliasesParams) (InsertReturningDuplicateAliasesRow, error) { + row := q.db.QueryRowContext(ctx, insertReturningDuplicateAliases, arg.Name, arg.Score) + var i InsertReturningDuplicateAliasesRow + err := row.Scan(&i.OldID, &i.RowID) + return i, err +} + +const insertReturningImplicitAliases = `-- name: InsertReturningImplicitAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id new_id, score * 2 doubled +` + +type InsertReturningImplicitAliasesParams struct { + Name string + Score int64 +} + +type InsertReturningImplicitAliasesRow struct { + NewID int64 + Doubled int64 +} + +func (q *Queries) InsertReturningImplicitAliases(ctx context.Context, arg InsertReturningImplicitAliasesParams) (InsertReturningImplicitAliasesRow, error) { + row := q.db.QueryRowContext(ctx, insertReturningImplicitAliases, arg.Name, arg.Score) + var i InsertReturningImplicitAliasesRow + err := row.Scan(&i.NewID, &i.Doubled) + return i, err +} + +const insertReturningQuotedAliases = `-- name: InsertReturningQuotedAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS "newId", name AS [label] +` + +type InsertReturningQuotedAliasesParams struct { + Name string + Score int64 +} + +type InsertReturningQuotedAliasesRow struct { + NewId int64 + Label string +} + +func (q *Queries) InsertReturningQuotedAliases(ctx context.Context, arg InsertReturningQuotedAliasesParams) (InsertReturningQuotedAliasesRow, error) { + row := q.db.QueryRowContext(ctx, insertReturningQuotedAliases, arg.Name, arg.Score) + var i InsertReturningQuotedAliasesRow + err := row.Scan(&i.NewId, &i.Label) + return i, err +} + +const insertReturningSingleAlias = `-- name: InsertReturningSingleAlias :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS new_id +` + +type InsertReturningSingleAliasParams struct { + Name string + Score int64 +} + +func (q *Queries) InsertReturningSingleAlias(ctx context.Context, arg InsertReturningSingleAliasParams) (int64, error) { + row := q.db.QueryRowContext(ctx, insertReturningSingleAlias, arg.Name, arg.Score) + var new_id int64 + err := row.Scan(&new_id) + return new_id, err +} + +const insertReturningStarAndAlias = `-- name: InsertReturningStarAndAlias :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id, name, score, created, score * 2 AS doubled +` + +type InsertReturningStarAndAliasParams struct { + Name string + Score int64 +} + +type InsertReturningStarAndAliasRow struct { + ID int64 + Name string + Score int64 + Created sql.NullString + Doubled int64 +} + +func (q *Queries) InsertReturningStarAndAlias(ctx context.Context, arg InsertReturningStarAndAliasParams) (InsertReturningStarAndAliasRow, error) { + row := q.db.QueryRowContext(ctx, insertReturningStarAndAlias, arg.Name, arg.Score) + var i InsertReturningStarAndAliasRow + err := row.Scan( + &i.ID, + &i.Name, + &i.Score, + &i.Created, + &i.Doubled, + ) + return i, err +} + +const updateReturningAliases = `-- name: UpdateReturningAliases :one +UPDATE t SET score = score + 1 WHERE id = ? +RETURNING id AS updated_id, score - 1 AS previous +` + +type UpdateReturningAliasesRow struct { + UpdatedID int64 + Previous int64 +} + +func (q *Queries) UpdateReturningAliases(ctx context.Context, id int64) (UpdateReturningAliasesRow, error) { + row := q.db.QueryRowContext(ctx, updateReturningAliases, id) + var i UpdateReturningAliasesRow + err := row.Scan(&i.UpdatedID, &i.Previous) + return i, err +} diff --git a/internal/endtoend/testdata/returning_aliases/sqlite/query.sql b/internal/endtoend/testdata/returning_aliases/sqlite/query.sql new file mode 100644 index 0000000000..d15a789c38 --- /dev/null +++ b/internal/endtoend/testdata/returning_aliases/sqlite/query.sql @@ -0,0 +1,36 @@ +-- name: InsertReturningAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS new_id, score * 2 AS doubled; + +-- name: InsertReturningImplicitAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id new_id, score * 2 doubled; + +-- name: InsertReturningQuotedAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS "newId", name AS [label]; + +-- name: InsertReturningSingleAlias :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS new_id; + +-- name: InsertReturningStarAndAlias :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING *, score * 2 AS doubled; + +-- name: InsertReturningDuplicateAliases :one +INSERT INTO t (name, score) VALUES (?, ?) +RETURNING id AS old_id, id AS row_id; + +-- name: InsertOnConflictReturningAliases :one +INSERT INTO t (id, name, score) VALUES (?, ?, ?) +ON CONFLICT (id) DO UPDATE SET score = excluded.score +RETURNING id AS new_id, score AS total; + +-- name: UpdateReturningAliases :one +UPDATE t SET score = score + 1 WHERE id = ? +RETURNING id AS updated_id, score - 1 AS previous; + +-- name: DeleteReturningAliases :one +DELETE FROM t WHERE id = ? +RETURNING id AS deleted_id, upper(name) AS shout; diff --git a/internal/endtoend/testdata/returning_aliases/sqlite/schema.sql b/internal/endtoend/testdata/returning_aliases/sqlite/schema.sql new file mode 100644 index 0000000000..51fa217208 --- /dev/null +++ b/internal/endtoend/testdata/returning_aliases/sqlite/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE t ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + score INTEGER NOT NULL, + created TEXT +); diff --git a/internal/endtoend/testdata/returning_aliases/sqlite/sqlc.json b/internal/endtoend/testdata/returning_aliases/sqlite/sqlc.json new file mode 100644 index 0000000000..cd66df063b --- /dev/null +++ b/internal/endtoend/testdata/returning_aliases/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "sqlite", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/engine/sqlite/convert.go b/internal/engine/sqlite/convert.go index dad36d4d1e..b3865a2aa6 100644 --- a/internal/engine/sqlite/convert.go +++ b/internal/engine/sqlite/convert.go @@ -535,11 +535,16 @@ func (c *cc) convertUpdateStmt(n *meyer.UpdateStmt) ast.Node { func (c *cc) convertReturning(cols []*meyer.ResultColumn) *ast.List { list := &ast.List{Items: []ast.Node{}} for _, col := range cols { - list.Items = append(list.Items, &ast.ResTarget{ + target := &ast.ResTarget{ Indirection: &ast.List{}, Val: c.convertExpr(col.Expr), Location: col.Pos(), - }) + } + if col.Alias != nil { + name := identifier(col.Alias) + target.Name = &name + } + list.Items = append(list.Items, target) } return list }