Skip to content
This repository was archived by the owner on May 24, 2023. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0e9ffec

Browse files
committedNov 29, 2013
Pretty much a complete rewrite/reorganization
1 parent 00c70c7 commit 0e9ffec

19 files changed

+523
-375
lines changed
 

‎_includes/toc.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
1. [Overview of Go's database/sql Package](/overview/)
2+
1. [Importing a Database Driver](/importing/)
23
1. [Accessing the Database](/accessing/)
3-
1. [Common Database Operations](/operations/)
4-
1. [Fetching Data from the Database](/fetching/)
5-
1. [Assigning Results to Variables](/assigning/)
6-
1. [Preparing Queries](/preparing/)
7-
1. [Single-Row Queries](/single-row-queries/)
8-
1. [Statements that Modify Data](/modifying/)
9-
1. [Working with Transactions](/transactions/)
10-
1. [Error Handling](/errors/)
4+
1. [Retrieving Result Sets](/retrieving/)
5+
1. [Modifying Data and Using Transactions](/modifying/)
6+
1. [Working with NULLs](/nulls/)
7+
1. [Working with Unknown Columns](/varcols/)
118
1. [The Connection Pool](/connection-pool/)
129
1. [Surprises, Antipatterns and Limitations](/surprises/)
1310
1. [Related Reading and Resources](/references/)

‎accessing.md

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,12 @@ title: Accessing the Database
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

11-
1211
Now that you've loaded the driver package, you're ready to create a database
1312
object, a `sql.DB`.
1413

15-
The first thing you should know is that **a `sql.DB` isn't a
16-
database connection**. It also doesn't map to any particular database software's
17-
notion of a "database" or "schema." It's an abstraction of the interface and
18-
existence of a database, which might be a local file, accessed through a network
19-
connection, in-memory and in-process, or what have you.
20-
21-
The `sql.DB` performs some important tasks for you behind the scenes:
22-
23-
* It opens and closes connections to the actual underlying database, via the driver.
24-
* It manages a pool of connections as needed.
25-
26-
These "connections" may be file handles, sockets, network connections, or other ways to access the database. The `sql.DB` abstraction is designed to keep you from worrying about how to manage concurrent access to the underlying datastore. A connection is marked in-use when you use it to perform a task, and then returned to the available pool when it's not in use anymore. One consequence of this is that **if you fail to release connections back to the pool, you can cause `db.SQL` to open a lot of connections**, potentially running out of resources (too many connections, too many open file handles, lack of available network ports, etc). We'll discuss more about this later.
27-
2814
To create a `sql.DB`, you use `sql.Open()`. This returns a `*sql.DB`:
2915

3016
func main() {
@@ -40,18 +26,38 @@ In the example shown, we're illustrating several things:
4026

4127
1. The first argument to `sql.Open` is the driver name. This is the string that the driver used to register itself with `database/sql`, and is conventionally the same as the package name to avoid confusion.
4228
2. The second argument is a driver-specific syntax that tells the driver how to access the underlying datastore. In this example, we're connecting to the "hello" database inside a local MySQL server instance.
43-
3. You should (almost) always check and handle errors returned from all `database/sql` operations.
29+
3. You should (almost) always check and handle errors returned from all `database/sql` operations. There are a few special cases that we'll discuss later where it doesn't make sense to do this.
4430
4. It is idiomatic to `defer db.Close()` if the `sql.DB` should not have a lifetime beyond the scope of the function.
4531

46-
Perhaps counter-intuitively, `sql.Open()` **does not establish any connections to the database**, nor does it validate driver connection parameters. Instead, it simply prepares the database abstraction for later use. The first actual connection to the underlying datastore will be established lazily, when it's needed for the first time. If you want to check right away that the database is available and accessible (for example, check that you can establish a network connection and log in), use `db.Ping()` to do that, and remember to check for errors:
32+
Perhaps counter-intuitively, `sql.Open()` **does not establish any connections
33+
to the database**, nor does it validate driver connection parameters. Instead,
34+
it simply prepares the database abstraction for later use. The first actual
35+
connection to the underlying datastore will be established lazily, when it's
36+
needed for the first time. If you want to check right away that the database is
37+
available and accessible (for example, check that you can establish a network
38+
connection and log in), use `db.Ping()` to do that, and remember to check for
39+
errors:
4740

4841
err = db.Ping()
4942
if err != nil {
5043
// do something here
5144
}
5245

53-
Although it's idiomatic to `Close()` the database when you're finished with it, **the `sql.DB` object is designed to be long-lived.** Don't `Open()` and `Close()` databases frequently. Instead, create **one** `sql.DB` object for each distinct datastore you need to access, and keep it until the program is done accessing that datastore. Pass it around as needed, or make it available somehow globally, but keep it open. And don't `Open()` and `Close()` from a short-lived function. Instead, pass the `sql.DB` into that short-lived function as an argument.
54-
55-
If you don't treat the `sql.DB` as a long-lived object, you could experience problems such as poor reuse and sharing of connections, running out of available network resources, sporadic failures due to a lot of TCP connections remaining in TIME_WAIT status, and so on.
46+
Although it's idiomatic to `Close()` the database when you're finished with it,
47+
**the `sql.DB` object is designed to be long-lived.** Don't `Open()` and
48+
`Close()` databases frequently. Instead, create **one** `sql.DB` object for each
49+
distinct datastore you need to access, and keep it until the program is done
50+
accessing that datastore. Pass it around as needed, or make it available somehow
51+
globally, but keep it open. And don't `Open()` and `Close()` from a short-lived
52+
function. Instead, pass the `sql.DB` into that short-lived function as an
53+
argument.
54+
55+
If you don't treat the `sql.DB` as a long-lived object, you could experience
56+
problems such as poor reuse and sharing of connections, running out of available
57+
network resources, or sporadic failures due to a lot of TCP connections
58+
remaining in `TIME_WAIT` status. Such problems are signs that you're not using
59+
`database/sql` as it was designed.
60+
61+
Now it's time to use your `sql.DB` object.
5662

5763
{% include toc.md %}

‎assigning.md

Lines changed: 0 additions & 51 deletions
This file was deleted.

‎connection-pool.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@ title: The Connection Pool
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

11-
There is a basic connection pool in the `database/sql` package. There isn't a lot of ability to control or inspect it, but here are some things you might find useful to know:
11+
There is a basic connection pool in the `database/sql` package. There isn't a
12+
lot of ability to control or inspect it, but here are some things you might find
13+
useful to know:
1214

1315
* Connections are created when needed and there isn't a free connection in the pool.
1416
* There's no limit on the number of connections. If you try to do a lot of things at once, you can create an arbitrary number of connections. This can cause the database to return an error such as "too many connections."
1517
* In Go 1.1, you can use `db.SetMaxIdleConns(N)` to limit the number of *idle* connections in the pool. This doesn't limit the pool size, though. That will probably come in Go 1.3 or later.
1618
* Connections are recycled rather fast. Setting the number of idle connections with `db.SetMaxIdleConns(N)` can reduce this churn, and help keep connections around for reuse.
1719

20+
In a future version of Go, the `database/sql` package will have the ability to
21+
[limit open connections](https://code.google.com/p/go/issues/detail?id=4805). In
22+
the meantime, it's still rather easy to run out of database connections by
23+
exceeding the limit set in the database itself (e.g. `max_connections` in
24+
MySQL). To avoid this, you'll need to limit your application's concurrent use of
25+
the `sql.DB` object. The idiomatic way to do that is described in the [channels
26+
section of the Effective Go](http://golang.org/doc/effective_go.html#channels)
27+
document.
1828

1929
{% include toc.md %}

‎errors.md

Lines changed: 0 additions & 22 deletions
This file was deleted.

‎fetching.md

Lines changed: 0 additions & 50 deletions
This file was deleted.

‎importing.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
layout: page
3+
permalink: /importing/
4+
title: Importing a Database Driver
5+
tags:
6+
image:
7+
feature: abstract-5.jpg
8+
share: false
9+
---
10+
11+
To use `database/sql` you'll need the package itself, as well as a driver for
12+
the specific database you want to use.
13+
14+
You generally shouldn't use driver packages directly, although some drivers
15+
encourage you to do so. (In our opinion, it's usually a bad idea.) Instead, your
16+
code should only refer to types defined in `database/sql`, if possible. This
17+
helps avoid making your code dependent on the driver, so that you can change the
18+
underlying driver (and thus the database you're accessing) with minimal code
19+
changes. It also forces you to use the Go idioms instead of ad-hoc idioms that a
20+
particular driver author may have provided.
21+
22+
In this documentation, we'll use the excellent [MySQL
23+
drivers](https://github.com/go-sql-driver/mysql) from @arnehormann and
24+
@julienschmidt for examples.
25+
26+
Add the following to the top of your Go source file:
27+
28+
import (
29+
"database/sql"
30+
_ "github.com/go-sql-driver/mysql"
31+
)
32+
33+
Notice that we're loading the driver anonymously, aliasing its package qualifier
34+
to `_` so none of its exported names are visible to our code. Under the hood,
35+
the driver registers itself as being available to the `database/sql` package,
36+
but in general nothing else happens.
37+
38+
Now you're ready to access a database.
39+
40+
{% include toc.md %}

‎index.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ title: Go database/sql tutorial
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

11-
This is a tutorial on [Go's database/sql package](http://golang.org/pkg/database/sql/).
12-
The package's documentation tells you
13-
what everything does, but it doesn't tell you how to use the package. Many of us
14-
find ourselves wishing for a quick-reference and a "getting started"
15-
orientation. This website is an attempt to provide that. Contributions are
16-
welcome; please send pull requests
17-
[here](https://github.com/VividCortex/go-database-sql-tutorial).
11+
The idiomatic way to use a SQL, or SQL-like, database in Go is through the
12+
[database/sql package](http://golang.org/pkg/database/sql/). It provides a
13+
lightweight interface to a row-oriented database. This website is a
14+
reference for the most common aspects of how to use it.
15+
16+
Why is this needed? The package's documentation tells you what everything does,
17+
but it doesn't tell you how to use the package. Many of us find ourselves
18+
wishing for a quick-reference and a "getting started" orientation that tells
19+
stories instead of listing facts. Contributions are welcome; please send pull
20+
requests [here](https://github.com/VividCortex/go-database-sql-tutorial).
1821

1922
{% include toc.md %}

‎modifying.md

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,83 @@
11
---
22
layout: page
33
permalink: /modifying/
4-
title: Statements that Modify Data
4+
title: Modifying Data and Using Transactions
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

11-
As mentioned previously, you should only use `Query` functions to execute queries -- statements that return rows. Use `Exec`, preferably with a prepared statement, to accomplish an INSERT, UPDATE, DELETE, or other statement that doesn't return rows. The following example shows how to insert a row:
12-
13-
```go
14-
stmt, err := db.Prepare("INSERT INTO users(name) VALUES(?)")
15-
if err != nil {
16-
log.Fatal(err)
17-
}
18-
res, err := stmt.Exec("Dolly")
19-
if err != nil {
20-
log.Fatal(err)
21-
}
22-
lastId, err := res.LastInsertId()
23-
if err != nil {
24-
log.Fatal(err)
25-
}
26-
rowCnt, err := res.RowsAffected()
27-
if err != nil {
28-
log.Fatal(err)
29-
}
30-
log.Printf("ID = %d, affected = %d\n", lastId, rowCnt)
31-
```
32-
33-
Executing the statement produces a `sql.Result` that gives access to statement metadata: the last inserted ID and the number of rows affected.
34-
35-
What if you don't care about the result? What if you just want to execute a statement and check if there were any errors, but ignore the result? Wouldn't the following two statements do the same thing?
36-
37-
```go
38-
_, err := db.Exec("DELETE FROM users") // OK
39-
_, err := db.Query("DELETE FROM users") // BAD
40-
```
41-
42-
The answer is no. They do **not** do the same thing, and **you should never use `Query()` like this.** The `Query()` will return a `sql.Rows`, which will not be released until it's garbage collected, which can be a long time. During that time, it will continue to hold open the underlying connection, and this anti-pattern is therefore a good way to run out of resources (too many connections, for example).
11+
Now we're ready to see how to modify data and work with transactions. The
12+
distinction might seem artificial if you're used to programming languages that
13+
use a "statement" object for fetching rows as well as updating data, but in Go,
14+
there's an important reason for the difference.
15+
16+
Statements that Modify Data
17+
===========================
18+
19+
Use `Exec()`, preferably with a prepared statement, to accomplish an `INSERT`,
20+
`UPDATE`, `DELETE`, or other statement that doesn't return rows. The following
21+
example shows how to insert a row and inspect metadata about the operation:
22+
23+
stmt, err := db.Prepare("INSERT INTO users(name) VALUES(?)")
24+
if err != nil {
25+
log.Fatal(err)
26+
}
27+
res, err := stmt.Exec("Dolly")
28+
if err != nil {
29+
log.Fatal(err)
30+
}
31+
lastId, err := res.LastInsertId()
32+
if err != nil {
33+
log.Fatal(err)
34+
}
35+
rowCnt, err := res.RowsAffected()
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
log.Printf("ID = %d, affected = %d\n", lastId, rowCnt)
40+
41+
Executing the statement produces a `sql.Result` that gives access to statement
42+
metadata: the last inserted ID and the number of rows affected.
43+
44+
What if you don't care about the result? What if you just want to execute a
45+
statement and check if there were any errors, but ignore the result? Wouldn't
46+
the following two statements do the same thing?
47+
48+
_, err := db.Exec("DELETE FROM users") // OK
49+
_, err := db.Query("DELETE FROM users") // BAD
50+
51+
The answer is no. They do **not** do the same thing, and **you should never use
52+
`Query()` like this.** The `Query()` will return a `sql.Rows`, which will not be
53+
released until it's garbage collected, which can be a long time. During that
54+
time, it will continue to hold open the underlying connection, and this
55+
anti-pattern is therefore a good way to run out of resources (too many
56+
connections, for example).
57+
58+
Working with Transactions
59+
=========================
60+
61+
In Go, a transaction is essentially an object that reserves a connection to the
62+
datastore. It lets you do all of the operations we've seen thus far, but
63+
guarantees that they'll be executed on the same connection.
64+
65+
You begin a transaction with a call to `db.Begin()`, and close it with a
66+
`Commit()` or `Rollback()` method on the resulting `Tx` variable. Under the
67+
covers, the `Tx` gets a connection from the pool, and reserves it for use only
68+
with that transaction. The methods on the `Tx` map one-for-one to methods you
69+
can call on the database itself, such as `Query()` and so forth.
70+
71+
Prepared statements that are created in a transaction are bound exclusively to
72+
that transaction, and can't be used outside of it. Likewise, prepared statements
73+
created only on a database handle can't be used within a transaction.
74+
75+
You should not mingle the use of transaction-related functions such as `Begin()`
76+
and `Commit()` with SQL statements such as `BEGIN` and `COMMIT` in your SQL
77+
code. Bad things might result:
78+
79+
* The `Tx` objects could remain open, reserving a connection from the pool and not returning it.
80+
* The state of the database could get out of sync with the state of the Go variables representing it.
81+
* You could believe you're executing queries on a single connection, inside of a transaction, when in reality Go has created several connections for you invisibly and some statements aren't part of the transaction.
4382

4483
{% include toc.md %}

‎nulls.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
layout: page
3+
permalink: /nulls/
4+
title: Working with NULLs
5+
tags:
6+
image:
7+
feature: abstract-5.jpg
8+
share: false
9+
---
10+
11+
Nullable columns are annoying and lead to a lot of ugly code. If you can, avoid
12+
them. If not, then you'll need to use special types from the `database/sql`
13+
package to handle them, or define your own.
14+
15+
There are types for nullable booleans, strings, integers, and floats. Here's how
16+
you use them:
17+
18+
for rows.Next() {
19+
var s sql.NullString
20+
err := rows.Scan(&s)
21+
// check err
22+
if s.Valid {
23+
// use s.String
24+
} else {
25+
// NULL value
26+
}
27+
}
28+
29+
Limitations of the nullable types, and reasons to avoid nullable columns in case
30+
you need more convincing:
31+
32+
1. There's no `sql.NullUint64` or `sql.NullYourFavoriteType`. You'd need to
33+
define your own for this.
34+
1. Nullability can be tricky, and not future-proof. If you think something won't
35+
be null, but you're wrong, your program will crash, perhaps rarely enough
36+
that you won't catch errors before you ship them.
37+
1. One of the nice things about Go is having a useful default zero-value for
38+
every variable. This isn't the way nullable things work.
39+
40+
If you need to define your own types to handle NULLs, you can copy the design of
41+
`sql.NullString` to achieve that.
42+
43+
{% include toc.md %}

‎operations.md

Lines changed: 0 additions & 24 deletions
This file was deleted.

‎overview.md

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,33 @@ title: Overview
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

11-
The idiomatic way to use a SQL, or SQL-like, database in Go is through the
12-
`database/sql` package. It provides a lightweight interface to a row-oriented
13-
database. This documentation is a reference for the most common aspects of how
14-
to use it.
11+
To access databases in Go, you use a `sql.DB`. You use this type to create
12+
statements and transactions, execute queries, and fetch results.
1513

16-
The first thing to do is import the `database/sql` package, and a driver
17-
package. You generally shouldn't use the driver package directly, although some
18-
drivers encourage you to do so. (In our opinion, it's usually a bad idea.)
19-
Instead, your code should only refer to `database/sql`. This helps avoid making
20-
your code dependent on the driver, so that you can change the underlying driver
21-
(and thus the database you're accessing) without changing your code. It also
22-
forces you to use the Go idioms instead of ad-hoc idioms that a particular
23-
driver author may have provided.
14+
The first thing you should know is that **a `sql.DB` isn't a database
15+
connection**. It also doesn't map to any particular database software's notion
16+
of a "database" or "schema." It's an abstraction of the interface and existence
17+
of a database, which might be as varied as a local file, accessed through a network
18+
connection, or in-memory and in-process.
2419

25-
Keep in mind that only the `database/sql` API provides this abstraction.
26-
Specific databases and drivers can differ in behavior and/or syntax. One
27-
example is the syntax for placeholder parameters in prepared statements. For
28-
example, comparing MySQL, PostgreSQL, and Oracle:
20+
The `sql.DB` performs some important tasks for you behind the scenes:
2921

30-
MySQL PostgreSQL Oracle
31-
===== ========== ======
32-
WHERE col = ? WHERE col = $1 WHERE col = :col
33-
VALUES(?, ?, ?) VALUES($1, $2, $3) VALUES(:val1, :val2, :val3)
22+
* It opens and closes connections to the actual underlying database, via the driver.
23+
* It manages a pool of connections as needed, which may be a variety of things as mentioned.
3424

35-
In this documentation, we'll use the excellent
36-
[MySQL drivers](https://github.com/go-sql-driver/mysql) from @arnehormann and @julienschmidt for examples.
25+
The `sql.DB` abstraction is designed to keep you from worrying about how to
26+
manage concurrent access to the underlying datastore. A connection is marked
27+
in-use when you use it to perform a task, and then returned to the available
28+
pool when it's not in use anymore. One consequence of this is that **if you fail
29+
to release connections back to the pool, you can cause `db.SQL` to open a lot of
30+
connections**, potentially running out of resources (too many connections, too
31+
many open file handles, lack of available network ports, etc). We'll discuss
32+
more about this later.
3733

38-
Add the following to the top of your Go source file:
39-
40-
import (
41-
"database/sql"
42-
_ "github.com/go-sql-driver/mysql"
43-
)
44-
45-
Notice that we're loading the driver anonymously, aliasing its package qualifier
46-
to `_` so none of its exported names are visible to our code. Under the hood,
47-
the driver registers itself as being available to the `database/sql` package,
48-
but in general nothing else happens.
49-
50-
Now you're ready to access a database.
34+
After creating a `sql.DB`, you can use it to query the database that it
35+
represents, as well as creating statements and transactions.
5136

5237
{% include toc.md %}

‎preparing.md

Lines changed: 0 additions & 33 deletions
This file was deleted.

‎references.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ title: Related Reading and Resources
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

1111
Here are some external sources of information we've found to be helpful.
1212

1313
* http://golang.org/pkg/database/sql/
1414
* http://jmoiron.net/blog/gos-database-sql/
15+
* http://jmoiron.net/blog/built-in-interfaces/
16+
17+
We hope you've found this website to be helpful. If you have any suggestions for
18+
improvements, please send pull requests or open an issue report at
19+
https://github.com/VividCortex/go-database-sql-tutorial.
1520

1621
{% include toc.md %}

‎retrieving.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
---
2+
layout: page
3+
permalink: /retrieving/
4+
title: Retrieving Result Sets
5+
tags:
6+
image:
7+
feature: abstract-5.jpg
8+
share: false
9+
---
10+
11+
There are several idiomatic operations to retrieve results from the datastore.
12+
13+
1. Execute a query that returns rows.
14+
1. Prepare a statement for repeated use, execute it multiple times, and destroy it.
15+
1. Execute a statement in a once-off fashion, without preparing it for repeated use.
16+
1. Execute a query that returns a single row. There is a shortcut for this special case.
17+
18+
Go's `database/sql` function names are significant. **If a function name
19+
includes `Query`, it is designed to ask a question of the database, and will
20+
return a set of rows**, even if it's empty. Statements that don't return rows
21+
should not use `Query` functions; they should use `Exec()`.
22+
23+
Fetching Data from the Database
24+
===============================
25+
26+
Let's take a look at an example of how to query the database, working with
27+
results. We'll query the `users` table for a user whose `id` is 1, and print out
28+
the user's `id` and `name`. We will assign results to variables, a row at a
29+
time, with `rows.Scan()`.
30+
31+
var (
32+
id int
33+
name string
34+
)
35+
rows, err := db.Query("select id, name from users where id = ?", 1)
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
defer rows.Close()
40+
for rows.Next() {
41+
err := rows.Scan(&id, &name)
42+
if err != nil {
43+
log.Fatal(err)
44+
}
45+
log.Println(id, name)
46+
}
47+
err = rows.Err()
48+
if err != nil {
49+
log.Fatal(err)
50+
}
51+
52+
Here's what's happening in the above code:
53+
54+
1. We're using `db.Query()` to send the query to the database. We check the error, as usual.
55+
2. We defer `rows.Close()`. This is very important.
56+
3. We iterate over the rows with `rows.Next()`.
57+
4. We read the columns in each row into variables with `rows.Scan()`.
58+
5. We check for errors after we're done iterating over the rows.
59+
60+
A couple parts of this are easy to get wrong, and can have bad consequences.
61+
62+
First, as long as there's an open result set (represented by `rows`), the
63+
underlying connection is busy and can't be used for any other query. That means
64+
it's not available in the connection pool. If you iterate over all of the rows
65+
with `rows.Next()`, eventually you'll read the last row, and `rows.Next()` will
66+
encounter an internal EOF error and call `rows.Close()` for you. But if for any
67+
reason you exit that loop -- an error, an early return, or so on -- then the
68+
`rows` doesn't get closed, and the connection remains open. This is an easy way
69+
to run out of resources. This is why **you should always `defer rows.Close()`**,
70+
even if you also call it explicitly at the end of the loop, which isn't a bad
71+
idea. `rows.Close()` is a harmless no-op if it's already closed, so you can call
72+
it multiple times. Notice, however, that we check the error first, and only do
73+
`rows.Close()` if there isn't an error, in order to avoid a runtime panic.
74+
75+
Second, you should always check for an error at the end of the `for rows.Next()`
76+
loop. If there's an error during the loop, you need to know about it. Don't just
77+
assume that the loop iterates until you've processed all the rows.
78+
79+
The error returned by `rows.Close()` is the only exception to the general rule
80+
that it's best to capture and check for errors in all database operations. If
81+
`rows.Close()` throws an error, it's unclear what is the right thing to do.
82+
Logging the error message or panicing might be the only sensible thing to do,
83+
and if that's not sensible, then perhaps you should just ignore the error.
84+
85+
This is pretty much the only way to do it in Go. You can't
86+
get a row as a map, for example. That's because everything is strongly typed.
87+
You need to create variables of the correct type and pass pointers to them, as
88+
shown.
89+
90+
Preparing Queries
91+
=================
92+
93+
You should, in general, always prepare queries to be used multiple times. The
94+
result of preparing the query is a prepared statement, which can have
95+
placeholders (a.k.a. bind values) for parameters that you'll provide when you
96+
execute the statement. This is much better than concatenating strings, for all
97+
the usual reasons (avoiding SQL injection attacks, for example).
98+
99+
In MySQL, the parameter placeholder is `?`, and in PostgreSQL it is `$N`, where
100+
N is a number. In Oracle placeholders begin with a colon and are named, like
101+
`:param1`. We'll use `?` because we're using MySQL as our example.
102+
103+
stmt, err := db.Prepare("select id, name from users where id = ?")
104+
if err != nil {
105+
log.Fatal(err)
106+
}
107+
defer stmt.Close()
108+
rows, err := stmt.Query(1)
109+
if err != nil {
110+
log.Fatal(err)
111+
}
112+
defer rows.Close()
113+
for rows.Next() {
114+
// ...
115+
}
116+
117+
Under the hood, `db.Query()` actually prepares, executes, and closes a prepared
118+
statement. That's three round-trips to the database. If you're not careful, you
119+
can triple the number of database interactions your application makes! Some
120+
drivers can avoid this in specific cases with an addition to `database/sql` in
121+
Go 1.1, but not all drivers are smart enough to do that. Caveat Emptor.
122+
123+
Statements are like results: they claim a connection and should be closed. It's
124+
idiomatic to `defer stmt.Close()` if the prepared statement `stmt` should not
125+
have a lifetime beyond the scope of the function. If you don't, it'll reserve a
126+
connection from the pool, and can cause resource exhaustion. (Note that for
127+
efficiency you should always close statements, rows, transactions, etc as soon
128+
as you can.)
129+
130+
Single-Row Queries
131+
==================
132+
133+
If a query returns at most one row, you can use a shortcut around some of the
134+
lengthy boilerplate code:
135+
136+
var name string
137+
err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
138+
if err != nil {
139+
log.Fatal(err)
140+
}
141+
fmt.Println(name)
142+
143+
Errors from the query are deferred until `Scan()` is called, and then are
144+
returned from that. You can also call `QueryRow()` on a prepared statement:
145+
146+
stmt, err := db.Prepare("select id, name from users where id = ?")
147+
if err != nil {
148+
log.Fatal(err)
149+
}
150+
var name string
151+
err = stmt.QueryRow(1).Scan(&name)
152+
if err != nil {
153+
log.Fatal(err)
154+
}
155+
fmt.Println(name)
156+
157+
Go defines a special error constant, called `sql.ErrNoRows`, which is returned
158+
from `QueryRow()` when the result is empty. This needs to be handled as a
159+
special case in most circumstances. An empty result is often not considered an
160+
error by application code, and if you don't check whether an error is this
161+
special constant, you'll cause application-code errors you didn't expect.
162+
163+
One might ask why an empty result set is considered an error. There's nothing
164+
erroneous about an empty set. The reason is that the `QueryRow()` method needs
165+
to use this special-case in order to let the caller distinguish whether
166+
`QueryRow()` in fact found a row; without it, `Scan()` wouldn't do anything and
167+
you might not realize that your variable didn't get any value from the database
168+
after all.
169+
170+
You should not run into this error when you're not using `QueryRow()`. If you
171+
encounter this error elsewhere, you're doing something wrong.
172+
173+
{% include toc.md %}

‎single-row-queries.md

Lines changed: 0 additions & 37 deletions
This file was deleted.

‎surprises.md

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,70 @@ title: Surprises, Antipatterns and Limitations
55
tags:
66
image:
77
feature: abstract-5.jpg
8-
share: true
8+
share: false
99
---
1010

11-
We've documented several surprises and antipatterns throughout this tutorial, so please refer back to them if you didn't read them already:
11+
Although `database/sql` is simple once you're accustomed to it, you might be
12+
surprised by the subtlety of use cases it supports. This is common to Go's core
13+
libraries.
14+
15+
Resource Exhaustion
16+
===================
17+
18+
As mentioned throughtout this site, if you don't use `database/sql` as intended,
19+
you can certainly cause trouble for yourself, usually by consuming some
20+
resources or preventing them from being reused effectively:
1221

1322
* Opening and closing databases can cause exhaustion of resources.
14-
* Failing to use `rows.Close()` or `stmt.Close()` can cause exhaustion of resources.
15-
* Mixing transactional methods and transactional SQL commands can cause exhaustion of resources.
16-
* Using `Query()` for a statement that doesn't return rows is a bad idea.
23+
* Failing to use `rows.Close()` or `stmt.Close()` reserves connections from the pool.
24+
* Using `Query()` for a statement that doesn't return rows will reserve a connection from the pool.
1725
* Failing to use prepared statements can lead to a lot of extra database activity.
18-
* Nulls cause annoying problems, which may show up only in production.
19-
* There's a special error when there's an empty result set, which should be checked to avoid application bugs.
2026

21-
There are also a couple of limitations in the `database/sql` package. The interface doesn't give you all-encompassing access to what's happening under the hood. For example, you don't have much control over the pool of connections.
27+
Large uint64 Values
28+
===================
29+
30+
Here's a surprising error. You can't pass big unsigned integers as parameters to
31+
statements if their high bit is set:
32+
33+
_, err := db.Exec("INSERT INTO users(id) VALUES", math.MaxUint64)
34+
35+
This will throw an error. Be careful if you use `uint64` values, as they may
36+
start out small and work without error, but increment over time and start
37+
throwing errors.
38+
39+
Connection State Mismatch
40+
=========================
41+
42+
Some things can change connection state, and that can cause problems for two
43+
reasons:
44+
45+
1. Some connection state, such as whether you're in a transaction, should be
46+
handled through the Go types instead.
47+
2. You might be assuming that your queries run on a single connection when they
48+
don't.
49+
50+
For example, setting the current database with a `USE` statement is a typical
51+
thing for many people to do. But in Go, it will affect only the connection that
52+
you run it in. Unless you are in a transaction, other statements that you think
53+
are executed on that connection may actually run on different connections gotten
54+
from the pool, so they won't see the effects of such changes.
2255

23-
Another limitation, which can be a surprise, is that you can't pass big unsigned integers as parameters to statements if their high bit is set:
56+
Additionally, after you've changed the connection, it'll return to the pool and
57+
potentially pollute the state for some other code. This is one of the reasons
58+
why you should never issue BEGIN or COMMIT statements as SQL commands directly,
59+
too.
2460

25-
```go
26-
_, err := db.Exec("INSERT INTO users(id) VALUES", math.MaxUint64)
27-
```
61+
Database-Specific Syntax
62+
========================
2863

29-
This will throw an error. Be careful if you use `uint64` values, as they may start out small and work without error, but increment over time and start throwing errors.
64+
The `database/sql` API provides an abstraction of a row-oriented database, but
65+
specific databases and drivers can differ in behavior and/or syntax. One
66+
example is the syntax for placeholder parameters in prepared statements. For
67+
example, comparing MySQL, PostgreSQL, and Oracle:
3068

31-
Another possible surprise is the effects of doing things that change connection state, such as
32-
setting the current database with a `USE` statement. This will affect only the connection
33-
that you run it in. Unless you are in a transaction, other statements that you think are
34-
executed on that connection may actually run on different connections gotten from the pool, so they won't see
35-
the effects of such changes. Additionally, after you've changed the connection, it'll return
36-
to the pool and potentially pollute the state for some other code. This is one of the reasons
37-
why you should never issue BEGIN or COMMIT statements as SQL commands directly, too.
69+
MySQL PostgreSQL Oracle
70+
===== ========== ======
71+
WHERE col = ? WHERE col = $1 WHERE col = :col
72+
VALUES(?, ?, ?) VALUES($1, $2, $3) VALUES(:val1, :val2, :val3)
3873

3974
{% include toc.md %}

‎transactions.md

Lines changed: 0 additions & 29 deletions
This file was deleted.

‎varcols.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
layout: page
3+
permalink: /varcols/
4+
title: Working with Unknown Columns
5+
tags:
6+
image:
7+
feature: abstract-5.jpg
8+
share: false
9+
---
10+
11+
The `Scan()` function requires you to pass exactly the right number of
12+
destination variables. What if you don't know what the query will return?
13+
14+
If you don't know how many columns the query will return, you can use
15+
`Columns()` to find a list of column names. You can examine the length of this
16+
list to see how many columns there are, and you can pass a slice into `Scan()`
17+
with the correct number of values. For example, some forks of MySQL return
18+
different columns for the `SHOW PROCESSLIST` command, so you have to be prepared
19+
for that or you'll cause an error. Here's one way to do it; there are others:
20+
21+
cols, err := rows.Columns()
22+
if err != nil {
23+
// handle the error
24+
} else {
25+
dest := []interface{}{ // Standard MySQL columns
26+
new(uint64), // id
27+
new(string), // host
28+
new(string), // user
29+
new(string), // db
30+
new(string), // command
31+
new(uint32), // time
32+
new(string), // state
33+
new(string), // info
34+
}
35+
if len(cols) == 11 {
36+
// Percona Server
37+
} else if len(cols) > 8 {
38+
// Handle this case
39+
}
40+
err = rows.Scan(dest...)
41+
// Work with the values in dest
42+
}
43+
44+
If you don't know the columns or their types, you should use `sql.RawBytes`.
45+
46+
cols, err := rows.Columns() // Remember to check err afterwards
47+
vals := make([]interface{}, len(cols))
48+
for i, _ := range cols {
49+
ints[i] = new(sql.RawBytes)
50+
}
51+
for rows.Next() {
52+
err = rows.Scan(vals...)
53+
// Now you can check each element of vals for nil-ness,
54+
// and you can use type introspection and type assertions
55+
// to fetch the column into a typed variable.
56+
}
57+
58+
{% include toc.md %}

0 commit comments

Comments
 (0)
This repository has been archived.