Kevin Burke has uploaded this change for review.
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
---
M src/database/sql/example_test.go
M src/database/sql/sql.go
2 files changed, 179 insertions(+), 25 deletions(-)
diff --git a/src/database/sql/example_test.go b/src/database/sql/example_test.go
index ce56ca4..13a3ae7 100644
--- a/src/database/sql/example_test.go
+++ b/src/database/sql/example_test.go
@@ -5,43 +5,65 @@
package sql_test
import (
+ "context"
"database/sql"
"fmt"
"log"
+ "strings"
+ "time"
)
var db *sql.DB
-func ExampleDB_Query() {
+func ExampleDB_QueryContext() {
age := 27
- rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
+ rows, err := db.QueryContext(context.Background(), "SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
+ names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
- fmt.Printf("%s is %d\n", name, age)
+ names = append(names, name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
+ fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
}
-func ExampleDB_QueryRow() {
+func ExampleDB_QueryRowContext() {
id := 123
var username string
- err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username)
+ var created time.Time
+ err := db.QueryRowContext(context.Background(), "SELECT username, created_at FROM users WHERE id=?", id).Scan(&username, &created)
switch {
case err == sql.ErrNoRows:
- log.Printf("No user with that ID.")
+ log.Printf("No user with id %d", id)
case err != nil:
log.Fatal(err)
default:
- fmt.Printf("Username is %s\n", username)
+ fmt.Printf("Username is %s, account created on %s\n", username, created)
+ }
+}
+
+func ExampleDB_ExecContext() {
+ ctx := context.Background()
+ id := 47
+ result, err := db.ExecContext(ctx, "UPDATE balances SET balance = balance + 10 WHERE user_id = ?", id)
+ if err != nil {
+ log.Fatal(err)
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if rows != 1 {
+ panic(err)
}
}
@@ -106,3 +128,152 @@
log.Fatal(err)
}
}
+
+func ExampleDB_PingContext() {
+ ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+ defer cancel()
+ if err := db.PingContext(ctx); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleDB_Stats() {
+ stats := db.Stats()
+ fmt.Println(stats.OpenConnections)
+}
+
+func ExampleConn_BeginTx() {
+ tx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelSerializable})
+ if err != nil {
+ log.Fatal(err)
+ }
+ id := 37
+ _, execErr := tx.Exec(`UPDATE users SET status = 'paid' WHERE id = ?`, id)
+ if execErr != nil {
+ log.Fatal(execErr)
+ }
+ if err := tx.Commit(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleConn_ExecContext() {
+ ctx := context.Background()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer conn.Close()
+ id := 41
+ result, err := conn.ExecContext(ctx, `UPDATE balances SET balance = balance + 10 WHERE user_id = ?`, id)
+ if err != nil {
+ log.Fatal(err)
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if rows != 1 {
+ panic(err)
+ }
+}
+
+func ExampleTx_ExecContext() {
+ ctx := context.Background()
+ tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
+ if err != nil {
+ log.Fatal(err)
+ }
+ id := 37
+ _, execErr := tx.ExecContext(ctx, "UPDATE users SET status = 'paid' WHERE id = ?", id)
+ if execErr != nil {
+ log.Fatal(execErr)
+ }
+ if err := tx.Commit(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleTx_Rollback() {
+ ctx := context.Background()
+ tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
+ if err != nil {
+ log.Fatal(err)
+ }
+ id := 53
+ _, err = tx.ExecContext(ctx, "UPDATE drivers SET status = 'assigned' WHERE id = ?", id)
+ if err != nil {
+ log.Fatal(err)
+ }
+ _, err = tx.ExecContext(ctx, "UPDATE pickups SET driver_id = $1", id)
+ if err != nil {
+ if rollbackErr := tx.Rollback(); rollbackErr != nil {
+ log.Printf("Could not roll back: %v\n", rollbackErr)
+ }
+ log.Fatal(err)
+ }
+ if err := tx.Commit(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleStmt() {
+ // In normal use, create one Stmt when your process starts
+ stmt, err := db.PrepareContext(context.Background(), "SELECT username FROM users WHERE id = ?")
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Then reuse it each time you need to issue the query.
+ id := 43
+ var username string
+ err = stmt.QueryRowContext(context.Background(), id).Scan(&username)
+ switch {
+ case err == sql.ErrNoRows:
+ log.Printf("No user with that ID.")
+ case err != nil:
+ log.Fatal(err)
+ default:
+ fmt.Printf("Username is %s\n", username)
+ }
+}
+
+func ExampleStmt_QueryRowContext() {
+ // In normal use, create one Stmt when your process starts
+ stmt, err := db.PrepareContext(context.Background(), "SELECT username FROM users WHERE id = ?")
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Then reuse it each time you need to issue the query.
+ id := 43
+ var username string
+ err = stmt.QueryRowContext(context.Background(), id).Scan(&username)
+ switch {
+ case err == sql.ErrNoRows:
+ log.Printf("No user with that ID.")
+ case err != nil:
+ log.Fatal(err)
+ default:
+ fmt.Printf("Username is %s\n", username)
+ }
+}
+
+func ExampleRows() {
+ age := 27
+ rows, err := db.QueryContext(context.Background(), "SELECT name FROM users WHERE age=?", age)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer rows.Close()
+ names := make([]string, 0)
+ for rows.Next() {
+ var name string
+ if err := rows.Scan(&name); err != nil {
+ log.Fatal(err)
+ }
+ names = append(names, name)
+ }
+ if err := rows.Err(); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
+}
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index 8f5588e..7f7ea51 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -2460,11 +2460,6 @@
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
-//
-// Example usage:
-//
-// var name string
-// err := nameByUseridStmt.QueryRowContext(ctx, id).Scan(&name)
func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row {
rows, err := s.QueryContext(ctx, args...)
if err != nil {
@@ -2533,19 +2528,7 @@
}
// Rows is the result of a query. Its cursor starts before the first row
-// of the result set. Use Next to advance through the rows:
-//
-// rows, err := db.Query("SELECT ...")
-// ...
-// defer rows.Close()
-// for rows.Next() {
-// var id int
-// var name string
-// err = rows.Scan(&id, &name)
-// ...
-// }
-// err = rows.Err() // get any error encountered during iteration
-// ...
+// of the result set. Use Next to advance from row to row.
type Rows struct {
dc *driverConn // owned; must call releaseConn when closed to release
releaseConn func(error)
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
R=go1.11
I would also suggest demonstrating named parameters and OUT variables.
1 comment:
File src/database/sql/example_test.go:
Patch Set #1, Line 20: context.Background()
I would be inclined to make this context.TODO() in all the examples.
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
Patch Set #1, Line 20: context.Background()
I would be inclined to make this context.TODO() in all the examples.
I don't think we want to see context.TODO in examples. If anything, we should deemphasize any specific ctx and just write "ctx" here. We could make a global variable ctx that is:
var ctx = context.Background() // arbitrary context for examples
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
Kevin Burke uploaded patch set #2 to this change.
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
---
M src/database/sql/example_test.go
M src/database/sql/sql.go
2 files changed, 180 insertions(+), 25 deletions(-)
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
Patch Set 1:
(1 comment)
I would also suggest demonstrating named parameters and OUT variables.
TBH I haven't ever used those, so I'd need more time to figure out how they work and write an example that demonstrates their use.
1 comment:
I don't think we want to see context.TODO in examples. […]
Done
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
17 comments:
names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
names = append(names, name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
Don't wrap up into an array, just print within the loop.
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
You'll want to add a comment about why you may choose to add a shorter timeout here.
Patch Set #2, Line 137: log.Fatal(err)
Probably add a discussion of what a real application might do in this case (like update the health endpoint status rather then just exiting).
func ExampleDB_Stats() {
stats := db.Stats()
fmt.Println(stats.OpenConnections)
}
This doesn't really do anything for me. Remove?
Patch Set #2, Line 162: ctx := ctx
What's this?
Patch Set #2, Line 163: conn, err := db.Conn(ctx)
Perhaps you could add a comment on how a *DB is a pool of connections. If we want to work on a single connection outside of a transaction, we can call db.Conn.
Patch Set #2, Line 167: defer conn.Close()
Add comment // Return the connection to the connection pool.
Patch Set #2, Line 183: ctx := ctx
What's this?
_, execErr := tx.ExecContext(ctx, "UPDATE users SET status = 'paid' WHERE id = ?", id)
if execErr != nil {
log.Fatal(execErr)
}
What happens to the Tx if ExecContext fails?
Patch Set #2, Line 199: ctx := ctx
Why this?
Patch Set #2, Line 205: 'assigned'
This is safe SQL. But for example SQL, let's put the status in as a parameter as well.
_, err = tx.ExecContext(ctx, "UPDATE drivers SET status = 'assigned' WHERE id = ?", id)
if err != nil {
log.Fatal(err)
}
What happens to the Tx if ExecContext fails?
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
if err != nil {
log.Fatal(err)
}
You need "defer stmt.Close()" after checking for error or your DB will run out of prepared stmt slots.
func ExampleStmt_QueryRowContext() {
// In normal use, create one Stmt when your process starts
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
if err != nil {
log.Fatal(err)
}
// Then reuse it each time you need to issue the query.
id := 43
var username string
err = stmt.QueryRowContext(ctx, id).Scan(&username)
switch {
case err == sql.ErrNoRows:
log.Printf("No user with that ID.")
case err != nil:
log.Fatal(err)
default:
fmt.Printf("Username is %s\n", username)
}
}
I could be missing something, but this looks the same as ExampleStmt.
func ExampleRows() {
age := 27
rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
names = append(names, name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
}
This looks the same as ExampleDB_QueryContext.
Please keep.
Please keep.
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
Kevin Burke uploaded patch set #3 to this change.
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
---
M src/database/sql/example_test.go
M src/database/sql/sql.go
2 files changed, 185 insertions(+), 25 deletions(-)
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
6 comments:
names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
names = append(names, name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
Don't wrap up into an array, just print within the loop.
This is part of the goal of an example, I guess, is to demonstrate how to gather rows into an array. (It's something I was struggling to show to someone learning Go).
func ExampleDB_Stats() {
stats := db.Stats()
fmt.Println(stats.OpenConnections)
}
This doesn't really do anything for me. […]
What's the harm?
Patch Set #2, Line 183: tx, err :=
What's this?
Done
Why this?
Copy paste fail, I've removed this here and the other places.
default:
fmt.Printf("Username is %s\n", username)
}
}
func ExampleStmt_QueryRowContext() {
// In normal use, create one Stmt when your process starts
stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
if err != nil {
log.Fatal(err)
}
// Then reuse it each time you need to issue the query.
id := 43
var username string
err = stmt.QueryRowContext(ctx, id).Scan(&username)
switch {
case err == sql.ErrNoRows:
log.Printf("No user with that ID.")
I could be missing something, but this looks the same as ExampleStmt.
It is, but I think it's important to present the information where you expect users to run across it. Most users drill down pretty quickly to the place they expect to see the information and don't actually browse an entire page or read summaries. In some cases this may mean duplicating the same information.
I gave a talk about this if you're interested: https://www.youtube.com/watch?v=sQP_hUNCrcE
default:
fmt.Printf("Username is %s\n", username)
}
}
func ExampleRows() {
age := 27
rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
names = append(names, name)
This looks the same as ExampleDB_QueryContext.
It is the same.
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
Please keep.
I'm a little confused by this. It seems like the Example() is the correct place to put sample code.
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
7 comments:
names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
names = append(names, name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
This is part of the goal of an example, I guess, is to demonstrate how to gather rows into an array. […]
I suppose that's fine.
func ExampleDB_Stats() {
stats := db.Stats()
fmt.Println(stats.OpenConnections)
}
I really don't think this is needed. The harm is it makes the doc page even larger (even if only a little bit) then it already is, which is rather large. Please remove. I can see no use in it.
Patch Set #3, Line 153: log.Fatal(execErr)
Open Tx on Exec error. Need to commit or rollback.
Patch Set #3, Line 188: 'paid'
Make status a parameter. Agree this is safe, but want to guard against someone modifying an example with by using string concat.
Patch Set #3, Line 226: process starts
Full stop at end of sentence.
Patch Set #3, Line 247: starts
Full stop at end of sentence.
I'm a little confused by this. It seems like the Example() is the correct place to put sample code.
Okay, I looked into how godoc is displaying examples now, removing these will be fine. Thanks.
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
Kevin, do you intend to finish this CL?
Hey, apologies. Working on it now.
Kevin Burke uploaded patch set #4 to this change.
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
---
M src/database/sql/example_test.go
M src/database/sql/sql.go
2 files changed, 181 insertions(+), 25 deletions(-)
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
5 comments:
File src/database/sql/example_test.go:
func ExampleConn_BeginTx() {
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
if err != nil {
I really don't think this is needed. […]
Done
Open Tx on Exec error. Need to commit or rollback.
Done
Make status a parameter. […]
Done
Full stop at end of sentence.
Done
Full stop at end of sentence.
Done
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.
This looks good. I've recently looked at some details regarding this package. Could you review my comment on the linked issue? It may influence the examples we write, but let me know what you think.
https://github.com/golang/go/issues/24431#comment-373908823
Lastly, I think we should reconsider using log.fatal, or log at all. I would prefer using a return err, as that would be more useful to the majority of people. Again feedback appreciated.
"return err" with a non-nil err will fail the Examples in question so we'd need to add some dummy driver implementation that never returns errors, and load it from the examples, to avoid failures like the one on your CL.
Patch set 4:Run-TryBot +1Code-Review +2
TryBots are happy.
Patch set 4:TryBot-Result +1
Daniel Theophanes merged this change.
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 <kard...@gmail.com>
Run-TryBot: Daniel Theophanes <kard...@gmail.com>
TryBot-Result: Gobot Gobot <go...@golang.org>
---
M src/database/sql/example_test.go
M src/database/sql/sql.go
2 files changed, 181 insertions(+), 25 deletions(-)
diff --git a/src/database/sql/example_test.go b/src/database/sql/example_test.go
index ce56ca4..da938b0 100644
--- a/src/database/sql/example_test.go
+++ b/src/database/sql/example_test.go
@@ -5,43 +5,65 @@
package sql_test
import (
+ "context"
"database/sql"
"fmt"
"log"
+ "strings"
+ "time"
)
+var ctx = context.Background()
var db *sql.DB
-func ExampleDB_Query() {
+func ExampleDB_QueryContext() {
age := 27
- rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
+ rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
+ names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
- fmt.Printf("%s is %d\n", name, age)
+ names = append(names, name)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
+ fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
}
-func ExampleDB_QueryRow() {
+func ExampleDB_QueryRowContext() {
id := 123
var username string
- err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username)
+ var created time.Time
+ err := db.QueryRowContext(ctx, "SELECT username, created_at FROM users WHERE id=?", id).Scan(&username, &created)
switch {
case err == sql.ErrNoRows:
- log.Printf("No user with that ID.")
+ log.Printf("No user with id %d", id)
case err != nil:
log.Fatal(err)
default:
- fmt.Printf("Username is %s\n", username)
+ fmt.Printf("Username is %s, account created on %s\n", username, created)
+ }
+}
+
+func ExampleDB_ExecContext() {
+ id := 47
+ result, err := db.ExecContext(ctx, "UPDATE balances SET balance = balance + 10 WHERE user_id = ?", id)
+ if err != nil {
+ log.Fatal(err)
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if rows != 1 {
+ panic(err)
}
}
@@ -106,3 +128,154 @@
log.Fatal(err)
}
}
+
+func ExampleDB_PingContext() {
+ ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
+ defer cancel()
+ if err := db.PingContext(ctx); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleConn_BeginTx() {
+ tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
+ if err != nil {
+ log.Fatal(err)
+ }
+ id := 37
+ _, execErr := tx.Exec(`UPDATE users SET status = ? WHERE id = ?`, "paid", id)
+ if execErr != nil {
+ _ = tx.Rollback()
+ log.Fatal(execErr)
+ }
+ if err := tx.Commit(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleConn_ExecContext() {
+ // A *DB is a pool of connections. Call Conn to reserve a connection for
+ // exclusive use.
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer conn.Close() // Return the connection to the pool.
+ id := 41
+ result, err := conn.ExecContext(ctx, `UPDATE balances SET balance = balance + 10 WHERE user_id = ?`, id)
+ if err != nil {
+ log.Fatal(err)
+ }
+ rows, err := result.RowsAffected()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if rows != 1 {
+ panic(err)
+ }
+}
+
+func ExampleTx_ExecContext() {
+ tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
+ if err != nil {
+ log.Fatal(err)
+ }
+ id := 37
+ _, execErr := tx.ExecContext(ctx, "UPDATE users SET status = ? WHERE id = ?", "paid", id)
+ if execErr != nil {
+ if rollbackErr := tx.Rollback(); rollbackErr != nil {
+ log.Printf("Could not roll back: %v\n", rollbackErr)
+ }
+ log.Fatal(execErr)
+ }
+ if err := tx.Commit(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleTx_Rollback() {
+ tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
+ if err != nil {
+ log.Fatal(err)
+ }
+ id := 53
+ _, err = tx.ExecContext(ctx, "UPDATE drivers SET status = ? WHERE id = ?", "assigned", id)
+ if err != nil {
+ if rollbackErr := tx.Rollback(); rollbackErr != nil {
+ log.Printf("Could not roll back: %v\n", rollbackErr)
+ }
+ log.Fatal(err)
+ }
+ _, err = tx.ExecContext(ctx, "UPDATE pickups SET driver_id = $1", id)
+ if err != nil {
+ if rollbackErr := tx.Rollback(); rollbackErr != nil {
+ log.Printf("Could not roll back: %v\n", rollbackErr)
+ }
+ log.Fatal(err)
+ }
+ if err := tx.Commit(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleStmt() {
+ // In normal use, create one Stmt when your process starts.
+ stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer stmt.Close()
+ // Then reuse it each time you need to issue the query.
+ id := 43
+ var username string
+ err = stmt.QueryRowContext(ctx, id).Scan(&username)
+ switch {
+ case err == sql.ErrNoRows:
+ log.Printf("No user with that ID.")
+ case err != nil:
+ log.Fatal(err)
+ default:
+ fmt.Printf("Username is %s\n", username)
+ }
+}
+
+func ExampleStmt_QueryRowContext() {
+ // In normal use, create one Stmt when your process starts.
+ stmt, err := db.PrepareContext(ctx, "SELECT username FROM users WHERE id = ?")
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Then reuse it each time you need to issue the query.
+ id := 43
+ var username string
+ err = stmt.QueryRowContext(ctx, id).Scan(&username)
+ switch {
+ case err == sql.ErrNoRows:
+ log.Printf("No user with that ID.")
+ case err != nil:
+ log.Fatal(err)
+ default:
+ fmt.Printf("Username is %s\n", username)
+ }
+}
+
+func ExampleRows() {
+ age := 27
+ rows, err := db.QueryContext(ctx, "SELECT name FROM users WHERE age=?", age)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer rows.Close()
+ names := make([]string, 0)
+ for rows.Next() {
+ var name string
+ if err := rows.Scan(&name); err != nil {
+ log.Fatal(err)
+ }
+ names = append(names, name)
+ }
+ if err := rows.Err(); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s are %d years old", strings.Join(names, ", "), age)
+}
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index 05d1545..784ffac 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -2472,11 +2472,6 @@
// If the query selects no rows, the *Row's Scan will return ErrNoRows.
// Otherwise, the *Row's Scan scans the first selected row and discards
// the rest.
-//
-// Example usage:
-//
-// var name string
-// err := nameByUseridStmt.QueryRowContext(ctx, id).Scan(&name)
func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row {
rows, err := s.QueryContext(ctx, args...)
if err != nil {
@@ -2545,19 +2540,7 @@
}
// Rows is the result of a query. Its cursor starts before the first row
-// of the result set. Use Next to advance through the rows:
-//
-// rows, err := db.Query("SELECT ...")
-// ...
-// defer rows.Close()
-// for rows.Next() {
-// var id int
-// var name string
-// err = rows.Scan(&id, &name)
-// ...
-// }
-// err = rows.Err() // get any error encountered during iteration
-// ...
+// of the result set. Use Next to advance from row to row.
type Rows struct {
dc *driverConn // owned; must call releaseConn when closed to release
releaseConn func(error)
To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.