[go] database/sql: add more examples

948 views
Skip to first unread message

Kevin Burke (Gerrit)

unread,
Jan 30, 2018, 10:45:47 PM1/30/18
to Ian Lance Taylor, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Kevin Burke has uploaded this change for review.

View 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, 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
Gerrit-Change-Number: 91015
Gerrit-PatchSet: 1
Gerrit-Owner: Kevin Burke <k...@inburke.com>

Kevin Burke (Gerrit)

unread,
Jan 30, 2018, 10:51:27 PM1/30/18
to goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

R=go1.11

View Change

    To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 1
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Wed, 31 Jan 2018 03:51:25 +0000
    Gerrit-HasComments: No
    Gerrit-HasLabels: No

    Daniel Theophanes (Gerrit)

    unread,
    Jan 31, 2018, 3:17:30 PM1/31/18
    to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, golang-co...@googlegroups.com

    I would also suggest demonstrating named parameters and OUT variables.

    View Change

    1 comment:

    To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 1
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Wed, 31 Jan 2018 20:17:27 +0000
    Gerrit-HasComments: Yes
    Gerrit-HasLabels: No

    Brad Fitzpatrick (Gerrit)

    unread,
    Jan 31, 2018, 3:32:29 PM1/31/18
    to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, Daniel Theophanes, golang-co...@googlegroups.com

    View Change

    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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 1
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Wed, 31 Jan 2018 20:32:26 +0000
    Gerrit-HasComments: Yes
    Gerrit-HasLabels: No

    Kevin Burke (Gerrit)

    unread,
    Feb 1, 2018, 10:15:33 PM2/1/18
    to Daniel Theophanes, Brad Fitzpatrick, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

    Kevin Burke uploaded patch set #2 to this change.

    View 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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: newpatchset
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 2

    Kevin Burke (Gerrit)

    unread,
    Feb 1, 2018, 10:16:57 PM2/1/18
    to goph...@pubsubhelper.golang.org, Brad Fitzpatrick, Daniel Theophanes, golang-co...@googlegroups.com

    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.

    View Change

    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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 2
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Fri, 02 Feb 2018 03:16:54 +0000
    Gerrit-HasComments: Yes
    Gerrit-HasLabels: No

    Daniel Theophanes (Gerrit)

    unread,
    Feb 12, 2018, 9:31:10 AM2/12/18
    to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, golang-co...@googlegroups.com

    View Change

    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.

      • Patch Set #2, Line 134:

        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).

      • Patch Set #2, Line 140:


        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?

      • Patch Set #2, Line 189:

      • 	_, execErr := tx.ExecContext(ctx, "UPDATE users SET status = 'paid' WHERE id = ?", id)

      • 	_, 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?

      • Patch Set #2, Line 223:

        	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.

      • Patch Set #2, Line 241:

        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.

      • Patch Set #2, Line 261:

        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)
        }

    To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 2
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Mon, 12 Feb 2018 14:31:07 +0000
    Gerrit-HasComments: Yes
    Gerrit-HasLabels: No

    Kevin Burke (Gerrit)

    unread,
    Feb 13, 2018, 2:09:23 PM2/13/18
    to Daniel Theophanes, Brad Fitzpatrick, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

    Kevin Burke uploaded patch set #3 to this change.

    View 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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: newpatchset
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 3

    Kevin Burke (Gerrit)

    unread,
    Feb 13, 2018, 2:12:54 PM2/13/18
    to goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

    View Change

    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).

      • 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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 3
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Tue, 13 Feb 2018 19:12:51 +0000
    Gerrit-HasComments: Yes
    Gerrit-HasLabels: No
    Gerrit-Comment-In-Reply-To: Daniel Theophanes <kard...@gmail.com>

    Kevin Burke (Gerrit)

    unread,
    Feb 13, 2018, 2:13:31 PM2/13/18
    to goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

    View Change

    1 comment:

      • 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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 3
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Tue, 13 Feb 2018 19:13:29 +0000

    Daniel Theophanes (Gerrit)

    unread,
    Feb 13, 2018, 4:23:26 PM2/13/18
    to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, golang-co...@googlegroups.com

    View Change

    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)

      • func ExampleDB_Stats() {
        stats := db.Stats()
        fmt.Println(stats.OpenConnections)
        }

      • 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.

    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-MessageType: comment
    Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
    Gerrit-Change-Number: 91015
    Gerrit-PatchSet: 3
    Gerrit-Owner: Kevin Burke <k...@inburke.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
    Gerrit-Comment-Date: Tue, 13 Feb 2018 21:23:21 +0000
    Gerrit-HasComments: Yes
    Gerrit-HasLabels: No
    Gerrit-Comment-In-Reply-To: Daniel Theophanes <kard...@gmail.com>
    Gerrit-Comment-In-Reply-To: Kevin Burke <k...@inburke.com>

    Daniel Theophanes (Gerrit)

    unread,
    Mar 16, 2018, 12:29:02 PM3/16/18
    to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, golang-co...@googlegroups.com

    Kevin, do you intend to finish this CL?

    View Change

      To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
      Gerrit-Change-Number: 91015
      Gerrit-PatchSet: 3
      Gerrit-Owner: Kevin Burke <k...@inburke.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
      Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
      Gerrit-Comment-Date: Fri, 16 Mar 2018 16:29:00 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: No
      Gerrit-MessageType: comment

      Kevin Burke (Gerrit)

      unread,
      Mar 17, 2018, 5:33:26 PM3/17/18
      to goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

      Hey, apologies. Working on it now.

      View Change

        To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
        Gerrit-Change-Number: 91015
        Gerrit-PatchSet: 3
        Gerrit-Owner: Kevin Burke <k...@inburke.com>
        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
        Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
        Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
        Gerrit-Comment-Date: Sat, 17 Mar 2018 21:33:23 +0000

        Kevin Burke (Gerrit)

        unread,
        Mar 17, 2018, 5:42:40 PM3/17/18
        to Daniel Theophanes, Brad Fitzpatrick, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

        Kevin Burke uploaded patch set #4 to this change.

        View 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.

        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
        Gerrit-Change-Number: 91015
        Gerrit-PatchSet: 4
        Gerrit-Owner: Kevin Burke <k...@inburke.com>
        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
        Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
        Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
        Gerrit-MessageType: newpatchset

        Kevin Burke (Gerrit)

        unread,
        Mar 17, 2018, 5:43:24 PM3/17/18
        to goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

        View Change

        5 comments:


          • 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

          • Done

        To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
        Gerrit-Change-Number: 91015
        Gerrit-PatchSet: 4
        Gerrit-Owner: Kevin Burke <k...@inburke.com>
        Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
        Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
        Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
        Gerrit-Comment-Date: Sat, 17 Mar 2018 21:43:22 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Daniel Theophanes <kard...@gmail.com>
        Gerrit-MessageType: comment

        Daniel Theophanes (Gerrit)

        unread,
        Mar 17, 2018, 5:52:35 PM3/17/18
        to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, golang-co...@googlegroups.com

        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.

        View Change

          To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
          Gerrit-Change-Number: 91015
          Gerrit-PatchSet: 4
          Gerrit-Owner: Kevin Burke <k...@inburke.com>
          Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
          Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
          Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
          Gerrit-Comment-Date: Sat, 17 Mar 2018 21:52:32 +0000

          Kevin Burke (Gerrit)

          unread,
          Mar 17, 2018, 6:27:11 PM3/17/18
          to goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

          "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.

          View Change

            To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

            Gerrit-Project: go
            Gerrit-Branch: master
            Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
            Gerrit-Change-Number: 91015
            Gerrit-PatchSet: 4
            Gerrit-Owner: Kevin Burke <k...@inburke.com>
            Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
            Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
            Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
            Gerrit-Comment-Date: Sat, 17 Mar 2018 22:27:08 +0000

            Daniel Theophanes (Gerrit)

            unread,
            Mar 25, 2018, 12:54:12 AM3/25/18
            to Kevin Burke, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, golang-co...@googlegroups.com

            Patch set 4:Run-TryBot +1Code-Review +2

            View Change

              To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

              Gerrit-Project: go
              Gerrit-Branch: master
              Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
              Gerrit-Change-Number: 91015
              Gerrit-PatchSet: 4
              Gerrit-Owner: Kevin Burke <k...@inburke.com>
              Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
              Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
              Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
              Gerrit-Comment-Date: Sun, 25 Mar 2018 04:54:10 +0000
              Gerrit-HasComments: No
              Gerrit-Has-Labels: Yes
              Gerrit-MessageType: comment

              Gobot Gobot (Gerrit)

              unread,
              Mar 25, 2018, 12:54:24 AM3/25/18
              to Kevin Burke, goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

              TryBots beginning. Status page: https://farmer.golang.org/try?commit=1d41c672

              View Change

                To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

                Gerrit-Project: go
                Gerrit-Branch: master
                Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
                Gerrit-Change-Number: 91015
                Gerrit-PatchSet: 4
                Gerrit-Owner: Kevin Burke <k...@inburke.com>
                Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
                Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
                Gerrit-CC: Gobot Gobot <go...@golang.org>
                Gerrit-Comment-Date: Sun, 25 Mar 2018 04:54:22 +0000

                Gobot Gobot (Gerrit)

                unread,
                Mar 25, 2018, 1:04:06 AM3/25/18
                to Kevin Burke, goph...@pubsubhelper.golang.org, Daniel Theophanes, Brad Fitzpatrick, golang-co...@googlegroups.com

                TryBots are happy.

                Patch set 4:TryBot-Result +1

                View Change

                  To view, visit change 91015. To unsubscribe, or for help writing mail filters, visit settings.

                  Gerrit-Project: go
                  Gerrit-Branch: master
                  Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
                  Gerrit-Change-Number: 91015
                  Gerrit-PatchSet: 4
                  Gerrit-Owner: Kevin Burke <k...@inburke.com>
                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                  Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                  Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
                  Gerrit-Comment-Date: Sun, 25 Mar 2018 05:04:04 +0000

                  Daniel Theophanes (Gerrit)

                  unread,
                  Mar 25, 2018, 1:10:00 AM3/25/18
                  to Kevin Burke, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Gobot Gobot, Brad Fitzpatrick, golang-co...@googlegroups.com

                  Daniel Theophanes merged this change.

                  View Change

                  Approvals: Daniel Theophanes: Looks good to me, approved; Run TryBots Gobot Gobot: TryBots succeeded
                  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.

                  Gerrit-Project: go
                  Gerrit-Branch: master
                  Gerrit-Change-Id: I4e63af91c7e4fff88b25f820906104ecefde4cc3
                  Gerrit-Change-Number: 91015
                  Gerrit-PatchSet: 5
                  Gerrit-Owner: Kevin Burke <k...@inburke.com>
                  Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
                  Gerrit-Reviewer: Daniel Theophanes <kard...@gmail.com>
                  Gerrit-Reviewer: Gobot Gobot <go...@golang.org>
                  Gerrit-Reviewer: Kevin Burke <k...@inburke.com>
                  Gerrit-MessageType: merged
                  Reply all
                  Reply to author
                  Forward
                  0 new messages