Skip to content

Commit fcefbdc

Browse files
sgielenclaude
andcommitted
Test :iter with MySQL and SQLite
An :iter query in the wrap_errors, emit_prepared_queries and sqlc_slice cases for MySQL and SQLite, and a runtime test in the SQLite authors example, covering the stdlib template against a live database. queryCodeStdExec no longer starts with a newline when it rewrites a query for sqlc.slice, which left a blank line inside :iter methods. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7f30b96 commit fcefbdc

13 files changed

Lines changed: 342 additions & 1 deletion

File tree

‎examples/authors/sqlite/db_test.go‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package authors
55
import (
66
"context"
77
"database/sql"
8+
"errors"
9+
"slices"
810
"testing"
911

1012
"github.com/sqlc-dev/sqlc/internal/sqltest"
@@ -46,3 +48,66 @@ func TestAuthors(t *testing.T) {
4648
}
4749
t.Log(fetchedAuthor)
4850
}
51+
52+
func TestIterAuthors(t *testing.T) {
53+
sdb, cleanup := sqltest.SQLite(t, []string{"schema.sql"})
54+
defer sdb.Close()
55+
defer cleanup()
56+
57+
ctx := context.Background()
58+
db := New(sdb)
59+
60+
for _, name := range []string{"Rob Pike", "Brian Kernighan", "Ken Thompson"} {
61+
if _, err := db.CreateAuthor(ctx, CreateAuthorParams{Name: name}); err != nil {
62+
t.Fatal(err)
63+
}
64+
}
65+
66+
// the query only runs once the iterator is ranged over
67+
authors, ferr := db.IterAuthors(ctx)
68+
if inUse := sdb.Stats().InUse; inUse != 0 {
69+
t.Fatalf("%d connections in use before ranging, want 0", inUse)
70+
}
71+
var names []string
72+
for author := range authors {
73+
names = append(names, author.Name)
74+
}
75+
if err := ferr(); err != nil {
76+
t.Fatal(err)
77+
}
78+
if want := []string{"Brian Kernighan", "Ken Thompson", "Rob Pike"}; !slices.Equal(names, want) {
79+
t.Fatalf("got %v, want %v", names, want)
80+
}
81+
82+
// the iterator is single-use: ranging over it again yields nothing and
83+
// reports an error
84+
for author := range authors {
85+
t.Errorf("second range yielded %v", author)
86+
}
87+
if err := ferr(); err == nil {
88+
t.Error("ranging twice reported no error")
89+
}
90+
91+
// breaking out of the loop early releases the connection
92+
authors, ferr = db.IterAuthors(ctx)
93+
for range authors {
94+
break
95+
}
96+
if err := ferr(); err != nil {
97+
t.Fatal(err)
98+
}
99+
if inUse := sdb.Stats().InUse; inUse != 0 {
100+
t.Errorf("%d connections in use after breaking out, want 0", inUse)
101+
}
102+
103+
// a query that fails reports its error through the returned func
104+
cctx, cancel := context.WithCancel(ctx)
105+
cancel()
106+
authors, ferr = db.IterAuthors(cctx)
107+
for author := range authors {
108+
t.Errorf("cancelled query yielded %v", author)
109+
}
110+
if err := ferr(); !errors.Is(err, context.Canceled) {
111+
t.Errorf("got error %v, want context.Canceled", err)
112+
}
113+
}

‎examples/authors/sqlite/query.sql‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ INSERT INTO authors (
1616
/* name: DeleteAuthor :exec */
1717
DELETE FROM authors
1818
WHERE id = ?;
19+
20+
/* name: IterAuthors :iter */
21+
SELECT * FROM authors
22+
ORDER BY name;

‎examples/authors/sqlite/query.sql.go‎

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎internal/codegen/golang/templates/stdlib/queryCode.tmpl‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}
164164
{{end}}
165165

166166
{{define "queryCodeStdExec"}}
167-
{{- if .Arg.HasSqlcSlices }}
167+
{{- if .Arg.HasSqlcSlices -}}
168168
query := {{.ConstantName}}
169169
var queryParams []any
170170
{{- if .Arg.Struct }}

‎internal/endtoend/testdata/prepared_queries/mysql/go/db.go‎

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎internal/endtoend/testdata/prepared_queries/mysql/go/query.sql.go‎

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎internal/endtoend/testdata/prepared_queries/mysql/query.sql‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ SELECT first_name, id, last_name FROM users WHERE id = sqlc.arg('target_id');
44
/* name: ListUsers :many */
55
SELECT first_name, last_name FROM users;
66

7+
/* name: IterUsers :iter */
8+
SELECT first_name, last_name FROM users;
9+
710
/* name: InsertNewUser :exec */
811
INSERT INTO users (first_name, last_name) VALUES (?, ?);
912

‎internal/endtoend/testdata/sqlc_slice/mysql/go/query.sql.go‎

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎internal/endtoend/testdata/sqlc_slice/mysql/query.sql‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ SELECT name FROM foo
33
WHERE name = sqlc.arg(slug)
44
AND id IN (sqlc.slice(favourites));
55

6+
/* name: FuncParamIdentIter :iter */
7+
SELECT name FROM foo
8+
WHERE name = sqlc.arg(slug)
9+
AND id IN (sqlc.slice(favourites));
10+
611
/* name: FuncParamString :many */
712
SELECT name FROM foo
813
WHERE name = sqlc.arg('slug')

0 commit comments

Comments
 (0)