Skip to content

Commit 6f08b9f

Browse files
kevinburkekardianos
authored andcommitted
database/sql: add more examples
This aims to expand the coverage of examples showing how the sql package works, as well as to address a number of issues I've observed while explaining how the database package works: - The best way to issue UPDATE or INSERT queries, that don't need to scan anything in return. (Previously, we had no examples for any Execute statement). - How to use prepared statements and transactions. - How to aggregate arguments from a Query/QueryContext query into a slice. Furthermore just having examples in more places should help, as users click on e.g. the "Rows" return parameter and are treated with the lack of any example about how Rows is used. Switch package examples to use QueryContext/QueryRowContext; I think it is a good practice to prepare users to issue queries with a timeout attached, even if they are not using it immediately. Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3 Reviewed-on: https://go-review.googlesource.com/91015 Reviewed-by: Daniel Theophanes <[email protected]> Run-TryBot: Daniel Theophanes <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 782f9ce commit 6f08b9f

File tree

2 files changed

+181
-25
lines changed

2 files changed

+181
-25
lines changed

src/database/sql/example_test.go

Lines changed: 180 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,65 @@
55
package sql_test
66

77
import (
8+
"context"
89
"database/sql"
910
"fmt"
1011
"log"
12+
"strings"
13+
"time"
1114
)
1215

16+
var ctx = context.Background()
1317
var db *sql.DB
1418

15-
func ExampleDB_Query() {
19+
func ExampleDB_QueryContext() {
1620
age := 27
17-
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
21+
rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age)
1822
if err != nil {
1923
log.Fatal(err)
2024
}
2125
defer rows.Close()
26+
names := make([]string, 0)
2227
for rows.Next() {
2328
var name string
2429
if err := rows.Scan(&name); err != nil {
2530
log.Fatal(err)
2631
}
27-
fmt.Printf("%s is %d\n", name, age)
32+
names = append(names, name)
2833
}
2934
if err := rows.Err(); err != nil {
3035
log.Fatal(err)
3136
}
37+
fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
3238
}
3339

34-
func ExampleDB_QueryRow() {
40+
func ExampleDB_QueryRowContext() {
3541
id := 123
3642
var username string
37-
err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username)
43+
var created time.Time
44+
err := db.QueryRowContext(ctx, "SELECT username, created_at FROM users WHERE id=?", id).Scan(&username, &created)
3845
switch {
3946
case err == sql.ErrNoRows:
40-
log.Printf("No user with that ID.")
47+
log.Printf("No user with id %d", id)
4148
case err != nil:
4249
log.Fatal(err)
4350
default:
44-
fmt.Printf("Username is %s\n", username)
51+
fmt.Printf("Username is %s, account created on %s\n", username, created)
52+
}
53+
}
54+
55+
func ExampleDB_ExecContext() {
56+
id := 47
57+
result, err := db.ExecContext(ctx, "UPDATE balances SET balance = balance + 10 WHERE user_id = ?", id)
58+
if err != nil {
59+
log.Fatal(err)
60+
}
61+
rows, err := result.RowsAffected()
62+
if err != nil {
63+
log.Fatal(err)
64+
}
65+
if rows != 1 {
66+
panic(err)
4567
}
4668
}
4769

@@ -106,3 +128,154 @@ from
106128
log.Fatal(err)
107129
}
108130
}
131+
132+
func ExampleDB_PingContext() {
133+
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
134+
defer cancel()
135+
if err := db.PingContext(ctx); err != nil {
136+
log.Fatal(err)
137+
}
138+
}
139+
140+
func ExampleConn_BeginTx() {
141+
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
142+
if err != nil {
143+
log.Fatal(err)
144+
}
145+
id := 37
146+
_, execErr := tx.Exec(`UPDATE users SET status = ? WHERE id = ?`, "paid", id)
147+
if execErr != nil {
148+
_ = tx.Rollback()
149+
log.Fatal(execErr)
150+
}
151+
if err := tx.Commit(); err != nil {
152+
log.Fatal(err)
153+
}
154+
}
155+
156+
func ExampleConn_ExecContext() {
157+
// A *DB is a pool of connections. Call Conn to reserve a connection for
158+
// exclusive use.
159+
conn, err := db.Conn(ctx)
160+
if err != nil {
161+
log.Fatal(err)
162+
}
163+
defer conn.Close() // Return the connection to the pool.
164+
id := 41
165+
result, err := conn.ExecContext(ctx, `UPDATE balances SET balance = balance + 10 WHERE user_id = ?`, id)
166+
if err != nil {
167+
log.Fatal(err)
168+
}
169+
rows, err := result.RowsAffected()
170+
if err != nil {
171+
log.Fatal(err)
172+
}
173+
if rows != 1 {
174+
panic(err)
175+
}
176+
}
177+
178+
func ExampleTx_ExecContext() {
179+
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
180+
if err != nil {
181+
log.Fatal(err)
182+
}
183+
id := 37
184+
_, execErr := tx.ExecContext(ctx, "UPDATE users SET status = ? WHERE id = ?", "paid", id)
185+
if execErr != nil {
186+
if rollbackErr := tx.Rollback(); rollbackErr != nil {
187+
log.Printf("Could not roll back: %v\n", rollbackErr)
188+
}
189+
log.Fatal(execErr)
190+
}
191+
if err := tx.Commit(); err != nil {
192+
log.Fatal(err)
193+
}
194+
}
195+
196+
func ExampleTx_Rollback() {
197+
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
198+
if err != nil {
199+
log.Fatal(err)
200+
}
201+
id := 53
202+
_, err = tx.ExecContext(ctx, "UPDATE drivers SET status = ? WHERE id = ?", "assigned", id)
203+
if err != nil {
204+
if rollbackErr := tx.Rollback(); rollbackErr != nil {
205+
log.Printf("Could not roll back: %v\n", rollbackErr)
206+
}
207+
log.Fatal(err)
208+
}
209+
_, err = tx.ExecContext(ctx, "UPDATE pickups SET driver_id = $1", id)
210+
if err != nil {
211+
if rollbackErr := tx.Rollback(); rollbackErr != nil {
212+
log.Printf("Could not roll back: %v\n", rollbackErr)
213+
}
214+
log.Fatal(err)
215+
}
216+
if err := tx.Commit(); err != nil {
217+
log.Fatal(err)
218+
}
219+
}
220+
221+
func ExampleStmt() {
222+
// In normal use, create one Stmt when your process starts.
223+
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
224+
if err != nil {
225+
log.Fatal(err)
226+
}
227+
defer stmt.Close()
228+
// Then reuse it each time you need to issue the query.
229+
id := 43
230+
var username string
231+
err = stmt.QueryRowContext(ctx, id).Scan(&username)
232+
switch {
233+
case err == sql.ErrNoRows:
234+
log.Printf("No user with that ID.")
235+
case err != nil:
236+
log.Fatal(err)
237+
default:
238+
fmt.Printf("Username is %s\n", username)
239+
}
240+
}
241+
242+
func ExampleStmt_QueryRowContext() {
243+
// In normal use, create one Stmt when your process starts.
244+
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
245+
if err != nil {
246+
log.Fatal(err)
247+
}
248+
// Then reuse it each time you need to issue the query.
249+
id := 43
250+
var username string
251+
err = stmt.QueryRowContext(ctx, id).Scan(&username)
252+
switch {
253+
case err == sql.ErrNoRows:
254+
log.Printf("No user with that ID.")
255+
case err != nil:
256+
log.Fatal(err)
257+
default:
258+
fmt.Printf("Username is %s\n", username)
259+
}
260+
}
261+
262+
func ExampleRows() {
263+
age := 27
264+
rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age)
265+
if err != nil {
266+
log.Fatal(err)
267+
}
268+
defer rows.Close()
269+
names := make([]string, 0)
270+
for rows.Next() {
271+
var name string
272+
if err := rows.Scan(&name); err != nil {
273+
log.Fatal(err)
274+
}
275+
names = append(names, name)
276+
}
277+
if err := rows.Err(); err != nil {
278+
log.Fatal(err)
279+
}
280+
fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
281+
}

src/database/sql/sql.go

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,11 +2472,6 @@ func rowsiFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, arg
24722472
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
24732473
// Otherwise, the *Row's Scan scans the first selected row and discards
24742474
// the rest.
2475-
//
2476-
// Example usage:
2477-
//
2478-
// var name string
2479-
// err := nameByUseridStmt.QueryRowContext(ctx, id).Scan(&name)
24802475
func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row {
24812476
rows, err := s.QueryContext(ctx, args...)
24822477
if err != nil {
@@ -2545,19 +2540,7 @@ func (s *Stmt) finalClose() error {
25452540
}
25462541

25472542
// Rows is the result of a query. Its cursor starts before the first row
2548-
// of the result set. Use Next to advance through the rows:
2549-
//
2550-
// rows, err := db.Query("SELECT ...")
2551-
// ...
2552-
// defer rows.Close()
2553-
// for rows.Next() {
2554-
// var id int
2555-
// var name string
2556-
// err = rows.Scan(&id, &name)
2557-
// ...
2558-
// }
2559-
// err = rows.Err() // get any error encountered during iteration
2560-
// ...
2543+
// of the result set. Use Next to advance from row to row.
25612544
type Rows struct {
25622545
dc *driverConn // owned; must call releaseConn when closed to release
25632546
releaseConn func(error)

0 commit comments

Comments
 (0)