From 11a7dcab197e89966497c715616f5593f9b3f9a4 Mon Sep 17 00:00:00 2001 From: Marko Tiikkaja Date: Thu, 26 Jun 2014 02:00:51 +0200 Subject: [PATCH 01/47] Warn about deferring the closure of a txn-scoped Stmt --- surprises.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/surprises.md b/surprises.md index f20cc2d..c9f40ef 100644 --- a/surprises.md +++ b/surprises.md @@ -119,5 +119,43 @@ you attempt to perform another statement before the first has released that reso and cleaned up after itself. This also means that each statement in a transaction results in a separate set of network round-trips to the database. +Prepared Statements in Transactions +=================================== + +Caution must be exercised when working with prepared statements in +transactions. Consider the following example: + +
+tx, err := db.Begin()
+if err != nil {
+	log.Fatal(err)
+}
+defer tx.Rollback()
+stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)")
+if err != nil {
+	log.Fatal(err)
+}
+defer stmt.Close() // danger!
+for i := 0; i < 10; i++ {
+	_, err = stmt.Exec(i)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+err = tx.Commit()
+if err != nil {
+	log.Fatal(err)
+}
+// stmt.Close() runs here!
+
+ +Closing a `*sql.Tx` releases the connection associated with it back into the +pool, but the deferred call to Close on the prepared statement is executed +**after** that has happened, which could lead to concurrent access to the +underlying connection, rendering the connection state inconsistent. +`database/sql` does not guard you against this particular behaviour. Instead, +you should make sure the statement is always closed before the transaction is +committed or rolled back. + **Previous: [The Connection Pool](connection-pool.html)** **Next: [Related Reading and Resources](references.html)** From 2aa0fe91744d284cc1105220c5ee3afde881a5ef Mon Sep 17 00:00:00 2001 From: gauravagarwal Date: Fri, 19 Sep 2014 12:23:54 +0530 Subject: [PATCH 02/47] Update modifying.md --- modifying.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modifying.md b/modifying.md index 324a961..5c74d44 100644 --- a/modifying.md +++ b/modifying.md @@ -74,7 +74,8 @@ can call on the database itself, such as `Query()` and so forth. Prepared statements that are created in a transaction are bound exclusively to that transaction, and can't be used outside of it. Likewise, prepared statements -created only on a database handle can't be used within a transaction. +created only on a database handle can't be used within a transaction. In order to +use a Prepared statement prepared outside the transaction, use `Tx.Stmt()`. You should not mingle the use of transaction-related functions such as `Begin()` and `Commit()` with SQL statements such as `BEGIN` and `COMMIT` in your SQL From 445cd734d013910063a6cf96f353e375a1dc9949 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 15:01:07 -0500 Subject: [PATCH 03/47] Minor tweaks --- modifying.md | 6 ++++-- retrieving.md | 3 +++ surprises.md | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modifying.md b/modifying.md index 5c74d44..3a011f9 100644 --- a/modifying.md +++ b/modifying.md @@ -74,8 +74,10 @@ can call on the database itself, such as `Query()` and so forth. Prepared statements that are created in a transaction are bound exclusively to that transaction, and can't be used outside of it. Likewise, prepared statements -created only on a database handle can't be used within a transaction. In order to -use a Prepared statement prepared outside the transaction, use `Tx.Stmt()`. +created only on a database handle can't be used within a transaction. To +use a prepared statement prepared outside the transaction, use `Tx.Stmt()`, +which will create a new transaction-specific statement from the one prepared +outside the transaction. You should not mingle the use of transaction-related functions such as `Begin()` and `Commit()` with SQL statements such as `BEGIN` and `COMMIT` in your SQL diff --git a/retrieving.md b/retrieving.md index d774a7e..e1d184c 100644 --- a/retrieving.md +++ b/retrieving.md @@ -112,6 +112,9 @@ defer rows.Close() for rows.Next() { // ... } +if err = rows.Err(); err != nil { + log.Fatal(err) +} Under the hood, `db.Query()` actually prepares, executes, and closes a prepared diff --git a/surprises.md b/surprises.md index c9f40ef..e332302 100644 --- a/surprises.md +++ b/surprises.md @@ -123,7 +123,7 @@ Prepared Statements in Transactions =================================== Caution must be exercised when working with prepared statements in -transactions. Consider the following example: +transactions. Consider the following example:
 tx, err := db.Begin()
@@ -157,5 +157,8 @@ underlying connection, rendering the connection state inconsistent.
 you should make sure the statement is always closed before the transaction is
 committed or rolled back.
 
+This is a [known issue](https://code.google.com/p/go/issues/detail?id=4459) that
+will probably be fixed in Go 1.4 by [CR 131650043](https://codereview.appspot.com/131650043).
+
 **Previous: [The Connection Pool](connection-pool.html)**
 **Next: [Related Reading and Resources](references.html)**

From efee3d3b7aaadae054c8df322e285acbcb0ae308 Mon Sep 17 00:00:00 2001
From: Baron Schwartz 
Date: Fri, 5 Dec 2014 15:35:05 -0500
Subject: [PATCH 04/47] Reorganize error code; fix #58

---
 errors.md     | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++
 modifying.md  |   2 +-
 nulls.md      |   2 +-
 retrieving.md |  32 ++----------
 4 files changed, 140 insertions(+), 29 deletions(-)
 create mode 100644 errors.md

diff --git a/errors.md b/errors.md
new file mode 100644
index 0000000..37d7eb9
--- /dev/null
+++ b/errors.md
@@ -0,0 +1,133 @@
+---
+layout: article
+title: Handling Errors
+---
+
+Almost all operations with `database/sql` types return an error as the last
+value. You should always check these errors, never ignore them.
+
+There are a few places where error behavior is special-case, or there's
+something additional you might need to know.
+
+Errors From Iterating Resultsets
+================================
+
+Consider the following code:
+
+
+for rows.Next() {
+   // ...
+}
+if err = rows.Err(); err != nil {
+   // handle the error here
+}
+
+ +The error returned by `rows.Close()` is the only exception to the general rule +that it's best to capture and check for errors in all database operations. If +`rows.Close()` throws an error, it's unclear what is the right thing to do. +Logging the error message or panicing might be the only sensible thing to do, +and if that's not sensible, then perhaps you should just ignore the error. + +Errors From QueryRow() +====================== + +Consider the following code to fetch a single row: + +
+var name string
+err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
+if err != nil {
+	log.Fatal(err)
+}
+fmt.Println(name)
+
+ +What if there was no user with `id = 1`? Then there would be no row in the +result, and `.Scan()` would not scan a value into `name`. What happens then? + +Go defines a special error constant, called `sql.ErrNoRows`, which is returned +from `QueryRow()` when the result is empty. This needs to be handled as a +special case in most circumstances. An empty result is often not considered an +error by application code, and if you don't check whether an error is this +special constant, you'll cause application-code errors you didn't expect. + +Errors from the query are deferred until `Scan()` is called, and then are +returned from that. The above code is better written like this instead: + +
+var name string
+err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
+if err != nil {
+	if err != sql.ErrNoRows {
+		// there were no rows, but otherwise no error occurred
+	} else {
+		log.Fatal(err)
+	}
+}
+fmt.Println(name)
+
+ +One might ask why an empty result set is considered an error. There's nothing +erroneous about an empty set. The reason is that the `QueryRow()` method needs +to use this special-case in order to let the caller distinguish whether +`QueryRow()` in fact found a row; without it, `Scan()` wouldn't do anything and +you might not realize that your variable didn't get any value from the database +after all. + +You should not run into this error when you're not using `QueryRow()`. If you +encounter this error elsewhere, you're doing something wrong. + +Identifying Specific Database Errors +==================================== + +It can be tempting to write code like the following: + +
+rows, err := db.Query("SELECT someval FROM sometable")
+// err contains:
+// ERROR 1045 (28000): Access denied for user 'foo'@'::1' (using password: NO)
+if strings.Contains(err.Error(), "Access denied") {
+	// Handle the permission-denied error
+}
+
+ +This is not the best way to do it, though. For example, the string value might +vary depending on what language the server uses to send error messages. It's +much better to compare error numbers to identify what a specific error is. + +The mechanism to do this varies between drivers, however, because this isn't +part of `database/sql` itself. In the MySQL driver that this tutorial focuses +on, you could write the following code: + +
+if driverErr, ok := err.(*mysql.MySQLError); ok { // Now the error number is accessible directly
+	if driverErr.Number == 1045 {
+		// Handle the permission-denied error
+	}
+}
+
+ +Again, the `MySQLError` type here is provided by this specific driver, and the +`.Number` field may differ between drivers. The value of the number, however, +is taken from MySQL's error message, and is therefore database specific, not +driver specific. + +This code is still ugly. Comparing to 1045, a magic number, is a code smell. +Some drivers (though not the MySQL one, for reasons that are off-topic here) +provide a list of error identifiers. The `libpq` driver does, for example, in +[error.go](https://github.com/lib/pq/blob/master/error.go). And there's an +external package of [MySQL error numbers maintained by +VividCortex](https://github.com/VividCortex/mysqlerr). Using such a list, the +above code is better written thus: + +
+if driverErr, ok := err.(*mysql.MySQLError); ok {
+	if driverErr.Number == mysqlerr.ER_ACCESS_DENIED_ERROR {
+		// Handle the permission-denied error
+	}
+}
+
+ +**Previous: [Modifying Data and Using Transactions](modifying.html)** +**Next: [Working with NULLs](nulls.html)** diff --git a/modifying.md b/modifying.md index 3a011f9..6fe57d5 100644 --- a/modifying.md +++ b/modifying.md @@ -88,4 +88,4 @@ code. Bad things might result: * 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. **Previous: [Retrieving Result Sets](retrieving.html)** -**Next: [Working with NULLs](nulls.html)** +**Next: [Handling Errors](errors.html)** diff --git a/nulls.md b/nulls.md index fc18831..baba7d9 100644 --- a/nulls.md +++ b/nulls.md @@ -37,5 +37,5 @@ you need more convincing: If you need to define your own types to handle NULLs, you can copy the design of `sql.NullString` to achieve that. -**Previous: [Modifying Data and Using Transactions](modifying.html)** +**Previous: [Handling Errors](errors.html)** **Next: [Working with Unknown Columns](varcols.html)** diff --git a/retrieving.md b/retrieving.md index e1d184c..01d3fdd 100644 --- a/retrieving.md +++ b/retrieving.md @@ -54,6 +54,11 @@ Here's what's happening in the above code: 4. We read the columns in each row into variables with `rows.Scan()`. 5. We check for errors after we're done iterating over the rows. +This is pretty much the only way to do it in Go. You can't +get a row as a map, for example. That's because everything is strongly typed. +You need to create variables of the correct type and pass pointers to them, as +shown. + A couple parts of this are easy to get wrong, and can have bad consequences. First, as long as there's an open result set (represented by `rows`), the @@ -73,17 +78,6 @@ Second, you should always check for an error at the end of the `for rows.Next()` loop. If there's an error during the loop, you need to know about it. Don't just assume that the loop iterates until you've processed all the rows. -The error returned by `rows.Close()` is the only exception to the general rule -that it's best to capture and check for errors in all database operations. If -`rows.Close()` throws an error, it's unclear what is the right thing to do. -Logging the error message or panicing might be the only sensible thing to do, -and if that's not sensible, then perhaps you should just ignore the error. - -This is pretty much the only way to do it in Go. You can't -get a row as a map, for example. That's because everything is strongly typed. -You need to create variables of the correct type and pass pointers to them, as -shown. - Preparing Queries ================= @@ -157,21 +151,5 @@ if err != nil { fmt.Println(name)
-Go defines a special error constant, called `sql.ErrNoRows`, which is returned -from `QueryRow()` when the result is empty. This needs to be handled as a -special case in most circumstances. An empty result is often not considered an -error by application code, and if you don't check whether an error is this -special constant, you'll cause application-code errors you didn't expect. - -One might ask why an empty result set is considered an error. There's nothing -erroneous about an empty set. The reason is that the `QueryRow()` method needs -to use this special-case in order to let the caller distinguish whether -`QueryRow()` in fact found a row; without it, `Scan()` wouldn't do anything and -you might not realize that your variable didn't get any value from the database -after all. - -You should not run into this error when you're not using `QueryRow()`. If you -encounter this error elsewhere, you're doing something wrong. - **Previous: [Accessing the Database](accessing.html)** **Next: [Modifying Data and Using Transactions](modifying.html)** From 50b2d1e8aa2b2213c9066c37756f8dff4672ad71 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 15:37:10 -0500 Subject: [PATCH 05/47] Reference to transparent encryption blog post --- references.md | 1 + 1 file changed, 1 insertion(+) diff --git a/references.md b/references.md index 86ca6da..ae4de6e 100644 --- a/references.md +++ b/references.md @@ -8,6 +8,7 @@ Here are some external sources of information we've found to be helpful. * [http://golang.org/pkg/database/sql/](http://golang.org/pkg/database/sql/) * [http://jmoiron.net/blog/gos-database-sql/](http://jmoiron.net/blog/gos-database-sql/) * [http://jmoiron.net/blog/built-in-interfaces/](http://jmoiron.net/blog/built-in-interfaces/) +* The VividCortex blog, e.g. [transparent encryption](https://vividcortex.com/blog/2014/11/11/encrypting-data-in-mysql-with-go/) We hope you've found this website to be helpful. If you have any suggestions for improvements, please send pull requests or open an issue report at From aa73780532e7d8441be14e86e5baefaf0ec7eaff Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 15:45:36 -0500 Subject: [PATCH 06/47] note on cxn state; fix #57 --- connection-pool.md | 1 + 1 file changed, 1 insertion(+) diff --git a/connection-pool.md b/connection-pool.md index 3237dd1..ae1dc6f 100644 --- a/connection-pool.md +++ b/connection-pool.md @@ -7,6 +7,7 @@ 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: +* Connection pooling means that executing two consecutive statements on a single database might open two connections and execute them separately. It is fairly common for programmers to be confused as to why their code misbehaves. For example, `LOCK TABLES` followed by an `INSERT` can block because the `INSERT` is on a connection that does not hold the table lock. * Connections are created when needed and there isn't a free connection in the pool. * By default, 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." * In Go 1.1 or newer, you can use `db.SetMaxIdleConns(N)` to limit the number of *idle* connections in the pool. This doesn't limit the pool size, though. From 5cb5fdcbc87fb2003417322ffeee02caa29e8852 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 15:47:57 -0500 Subject: [PATCH 07/47] tweak rows.Close() err handling; fix #46 --- retrieving.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/retrieving.md b/retrieving.md index 01d3fdd..7245a21 100644 --- a/retrieving.md +++ b/retrieving.md @@ -65,9 +65,10 @@ First, as long as there's an open result set (represented by `rows`), the underlying connection is busy and can't be used for any other query. That means it's not available in the connection pool. If you iterate over all of the rows with `rows.Next()`, eventually you'll read the last row, and `rows.Next()` will -encounter an internal EOF error and call `rows.Close()` for you. But if for any -reason you exit that loop -- an error, an early return, or so on -- then the -`rows` doesn't get closed, and the connection remains open. This is an easy way +encounter an internal EOF error and call `rows.Close()` for you. But if for some +reason you exit that loop -- an early return, or so on -- then the +`rows` doesn't get closed, and the connection remains open. (It is auto-closed +if `rows.Next()` returns false due to an error, though). This is an easy way to run out of resources. This is why **you should always `defer rows.Close()`**, even if you also call it explicitly at the end of the loop, which isn't a bad idea. `rows.Close()` is a harmless no-op if it's already closed, so you can call From 36fa3827df663aec4f93cd5e9934bae28e541b3f Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 15:50:21 -0500 Subject: [PATCH 08/47] Upgrade to kramdown; fix #45 --- config.yml => _config.yml | 1 + 1 file changed, 1 insertion(+) rename config.yml => _config.yml (88%) diff --git a/config.yml b/_config.yml similarity index 88% rename from config.yml rename to _config.yml index 3d59ecd..953722e 100644 --- a/config.yml +++ b/_config.yml @@ -4,3 +4,4 @@ safe: true lsi: false pygments: true timezone: UTC +markdown: kramdown From 5be9c38c715ebb2c40add3a79d802a7c8b53195d Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 16:03:43 -0500 Subject: [PATCH 09/47] Add notes about prep stmt support; fix #41 --- retrieving.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/retrieving.md b/retrieving.md index 7245a21..572696f 100644 --- a/retrieving.md +++ b/retrieving.md @@ -121,6 +121,31 @@ Go 1.1, but not all drivers are smart enough to do that. Caveat Emptor. Naturally prepared statements and the management of prepared statements cost resources. You should take care to close statements when they are not used again. +Avoiding Prepared Statements +============================ + +Go creates prepared statements for you under the covers. A simple +`db.Query(sql, param1, param2)`, for example, works by preparing the sql, then +executing it with the parameters and finally closing the statement. + +Sometimes a prepared statement is not what you want, however. There might be +several reasons for this: + +1. The database doesn't support prepared statements. When using the MySQL + driver, for example, you can connect to MemSQL and Sphinx, because they + support the MySQL wire protocol. But they don't support the "binary" protocol + that includes prepared statements, so they can fail in confusing ways. +2. The statements aren't reused enough to make them worthwhile, and security + issues are handled in other ways, so performance overhead is undesired. An + example of this can be seen at the + [VividCortex blog](https://vividcortex.com/blog/2014/11/19/analyzing-prepared-statement-performance-with-vividcortex/). + +If you don't want to use a prepared statement, you need to use `fmt.Sprint()` or +similar to assemble the SQL, and pass this as the only argument to `db.Query()` +or `db.QueryRow()`. And your driver needs to support plaintext query execution, +which is added in Go 1.1 via the `Execer` interface, +[documented here](http://golang.org/pkg/database/sql/driver/#Execer). + Single-Row Queries ================== From 56d6796419efcce327475eef9f568011d1f633de Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 16:28:45 -0500 Subject: [PATCH 10/47] Document type-conversions in Scan(); fix #38 --- retrieving.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/retrieving.md b/retrieving.md index 572696f..f01edc9 100644 --- a/retrieving.md +++ b/retrieving.md @@ -79,6 +79,27 @@ Second, you should always check for an error at the end of the `for rows.Next()` loop. If there's an error during the loop, you need to know about it. Don't just assume that the loop iterates until you've processed all the rows. +How Scan() Works +================ + +When you iterate over rows and scan them into destination variables, Go performs data +type conversions work for you, behind the scenes. It is based on the type of the +destination variable. Being aware of this can clean up your code and help avoid +repetitive work. + +For example, suppose you select some rows from a table that is defined with +string columns, such as `VARCHAR(45)` or similar. You happen to know, however, +that the table always contains numbers. If you pass a pointer to a string, Go +will copy the bytes into the string. Now you can use `strconv.ParseInt()` or +similar to convert the value to a number. You'll have to check for errors in the +SQL operations, as well as errors parsing the integer. This is messy and +tedious. + +Or, you can just pass `Scan()` a pointer to an integer. Go will detect that and +call `strconv.ParseInt()` for you. If there's an error in conversion, the call +to `Scan()` will return it. Your code is neater and smaller now. This is the +recommended way to use `database/sql`. + Preparing Queries ================= From 65d597b6ca4b0e1f20726377bb275d4c962304a4 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 16:38:37 -0500 Subject: [PATCH 11/47] Document txn and cxn state; fix #37 --- modifying.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modifying.md b/modifying.md index 6fe57d5..4cff52f 100644 --- a/modifying.md +++ b/modifying.md @@ -87,5 +87,21 @@ code. Bad things might result: * The state of the database could get out of sync with the state of the Go variables representing it. * 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. +While you are working inside a transaction you should be careful not to make +calls to the `Db` variable. Make all of your calls to the `Tx` variable that you +created with `db.Begin()`. The `Db` is not in a transaction, only the `Tx` is. +If you make further calls to `db.Exec()` or similar, those will happen outside +the scope of your transaction, on other connections. + +If you need to work with multiple statements that modify connection state, you +need a `Tx` even if you don't want a transaction per se. For example: + +* Creating temporary tables, which are only visible to one connection. +* Setting variables, such as MySQL's `SET @var := somevalue` syntax. +* Changing connection options, such as character sets or timeouts. + +If you need to do any of these things, you need to bind your activity to a +single connection, and the only way to do that in Go is to use a `Tx`. + **Previous: [Retrieving Result Sets](retrieving.html)** **Next: [Handling Errors](errors.html)** From 7b2ee04a3edb0a66c4d4bb86d5a83452191fe3c2 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 16:46:46 -0500 Subject: [PATCH 12/47] Add leftnav for new Errors page --- _layouts/article.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_layouts/article.html b/_layouts/article.html index c2940a1..e65a5b9 100644 --- a/_layouts/article.html +++ b/_layouts/article.html @@ -21,6 +21,9 @@ - title: Modifying Data and Using Transactions url: modifying.html + - + title: Handling Errors + url: errors.html - title: Working with NULLs url: nulls.html From 4b385e734f01ed640010e802fbaaffa34ba70555 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Fri, 5 Dec 2014 16:55:35 -0500 Subject: [PATCH 13/47] Increase sidebar, shorten titles; fix #28 --- _layouts/article.html | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/_layouts/article.html b/_layouts/article.html index e65a5b9..d582273 100644 --- a/_layouts/article.html +++ b/_layouts/article.html @@ -7,19 +7,19 @@ url: index.html articles: - - title: Overview of Go's database/sql Package + title: Overview url: overview.html - - title: Importing a Database Driver + title: Importing a Driver url: importing.html - - title: Accessing the Database + title: Accessing a DB url: accessing.html - - title: Retrieving Result Sets + title: Getting Results url: retrieving.html - - title: Modifying Data and Using Transactions + title: Updates and Transactions url: modifying.html - title: Handling Errors @@ -28,16 +28,16 @@ title: Working with NULLs url: nulls.html - - title: Working with Unknown Columns + title: Unknown Columns url: varcols.html - - title: The Connection Pool + title: Connection Pooling url: connection-pool.html - - title: Surprises, Antipatterns and Limitations + title: Surprises and Limitations url: surprises.html - - title: Related Reading and Resources + title: Resources url: references.html --- @@ -51,11 +51,11 @@
-
+
{% include leftnav.html %}
-
+
From 2806884f34be85e0e839a483c1097245be790124 Mon Sep 17 00:00:00 2001 From: Ilja Heitlager Date: Wed, 3 Aug 2016 09:04:56 +0200 Subject: [PATCH 34/47] Update references.md --- references.md | 1 + 1 file changed, 1 insertion(+) diff --git a/references.md b/references.md index ae4de6e..b61fcf6 100644 --- a/references.md +++ b/references.md @@ -9,6 +9,7 @@ Here are some external sources of information we've found to be helpful. * [http://jmoiron.net/blog/gos-database-sql/](http://jmoiron.net/blog/gos-database-sql/) * [http://jmoiron.net/blog/built-in-interfaces/](http://jmoiron.net/blog/built-in-interfaces/) * The VividCortex blog, e.g. [transparent encryption](https://vividcortex.com/blog/2014/11/11/encrypting-data-in-mysql-with-go/) +* Overview of SQL Drivers from [golang github](https://github.com/golang/go/wiki/SQLDrivers) We hope you've found this website to be helpful. If you have any suggestions for improvements, please send pull requests or open an issue report at From c90d644aa2268e5a94136111c0e78e627c50fe56 Mon Sep 17 00:00:00 2001 From: Sartaj Singh Date: Mon, 22 May 2017 20:24:44 +0530 Subject: [PATCH 35/47] Add SetConnMaxLifetime details to connection pool --- connection-pool.md | 1 + 1 file changed, 1 insertion(+) diff --git a/connection-pool.md b/connection-pool.md index ae1dc6f..097945f 100644 --- a/connection-pool.md +++ b/connection-pool.md @@ -14,6 +14,7 @@ useful to know: * In Go 1.2.1 or newer, you can use `db.SetMaxOpenConns(N)` to limit the number of *total* open connections to the database. Unfortunately, a [deadlock bug](https://groups.google.com/d/msg/golang-dev/jOTqHxI09ns/x79ajll-ab4J) ([fix](https://code.google.com/p/go/source/detail?r=8a7ac002f840)) prevents `db.SetMaxOpenConns(N)` from safely being used in 1.2. * Connections are recycled rather fast. Setting a high number of idle connections with `db.SetMaxIdleConns(N)` can reduce this churn, and help keep connections around for reuse. * Keeping a connection idle for a long time can cause problems (like in [this issue](https://github.com/go-sql-driver/mysql/issues/257) with MySQL on Microsoft Azure). Try `db.SetMaxIdleConns(0)` if you get connection timeouts because a connection is idle for too long. +* You can also specify the maximum amount of time a connection may be reused by setting `db.SetConnMaxLifetime(duration)` since reusing long lived connections may cause network issues. This closes the unused connections lazily i.e. closing expired connection may be deferred. **Previous: [Working with Unknown Columns](varcols.html)** **Next: [Surprises, Antipatterns and Limitations](surprises.html)** From ceadad3644eb23bb437029f8873027f0d084a3d6 Mon Sep 17 00:00:00 2001 From: kenny Date: Thu, 15 Feb 2018 13:20:58 -0600 Subject: [PATCH 36/47] Update nulls.md Rename value used for selecting column to `otherField` to reflect comment below --- nulls.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nulls.md b/nulls.md index 274b450..389315c 100644 --- a/nulls.md +++ b/nulls.md @@ -42,7 +42,7 @@ If you can't avoid having NULL values in your database, there is another work ar rows, err := db.Query(` SELECT name, - COALESCE(other_field, '') as other_field + COALESCE(other_field, '') as otherField WHERE id = ? `, 42) From 6b46e2e38dcdf47b7f1cb61ed3625e84ab2c5577 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 8 May 2018 15:04:43 +0800 Subject: [PATCH 37/47] Possible missing stmt.Close Are you missing `derfer stmt.Close` on here or you just did it on purpose? Please explain to us why you don't need to stmt.Close() in this scenario --- retrieving.md | 1 + 1 file changed, 1 insertion(+) diff --git a/retrieving.md b/retrieving.md index 85e14c2..33cee55 100644 --- a/retrieving.md +++ b/retrieving.md @@ -167,6 +167,7 @@ stmt, err := db.Prepare("select name from users where id = ?") if err != nil { log.Fatal(err) } +defer stmt.Close() var name string err = stmt.QueryRow(1).Scan(&name) if err != nil { From 167139ec1528e5c86956329ab427cc015f693963 Mon Sep 17 00:00:00 2001 From: Dimitri Balios Date: Sat, 6 Oct 2018 15:28:10 +0200 Subject: [PATCH 38/47] Update importing.md --- importing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importing.md b/importing.md index 9b6608a..ef1ab3a 100644 --- a/importing.md +++ b/importing.md @@ -30,7 +30,7 @@ import ( Notice that we're loading the driver anonymously, aliasing its package qualifier to `_` so none of its exported names are visible to our code. Under the hood, the driver registers itself as being available to the `database/sql` package, -but in general nothing else happens. +but in general nothing else happens with the exception that the init function is run. Now you're ready to access a database. From d36f9a6ce2d9c498f5c972f23b0a711f54668a60 Mon Sep 17 00:00:00 2001 From: Baron Schwartz Date: Mon, 29 Oct 2018 15:33:53 -0400 Subject: [PATCH 39/47] fix deprecated pygments --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 953722e..9e1bfd3 100644 --- a/_config.yml +++ b/_config.yml @@ -2,6 +2,6 @@ title: Go database/sql tutorial description: Use database/sql effectively! safe: true lsi: false -pygments: true +highlighter: rouge timezone: UTC markdown: kramdown From cb7dc9b43218891d5f7b9ee0edd02edece71cb40 Mon Sep 17 00:00:00 2001 From: "Afriza N. Arief" <68133+afriza@users.noreply.github.com> Date: Fri, 18 Jan 2019 15:46:39 +0700 Subject: [PATCH 40/47] fix typo in overview.md change `db.SQL` to `sql.DB` --- overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overview.md b/overview.md index 92d344a..1a3287a 100644 --- a/overview.md +++ b/overview.md @@ -21,7 +21,7 @@ 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 +to release connections back to the pool, you can cause `sql.DB` 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. From 8d17aaeb98d9a466f19b649de8ff2d1b277e5cb5 Mon Sep 17 00:00:00 2001 From: cglotr Date: Tue, 29 Jan 2019 14:40:03 +0800 Subject: [PATCH 41/47] fix navigation not showing --- _data/nav.yml | 37 +++++++++++++++++++++++++++++++++++++ _includes/leftnav.html | 24 ++++++------------------ 2 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 _data/nav.yml diff --git a/_data/nav.yml b/_data/nav.yml new file mode 100644 index 0000000..1d2d2a1 --- /dev/null +++ b/_data/nav.yml @@ -0,0 +1,37 @@ +docs: + + - title: Go database/sql tutorial + url: index.html + + - title: Overview + url: overview.html + + - title: Accessing the Database + url: accessing.html + + - title: Retrieving Result Sets + url: retrieving.html + + - title: Modifying Data and Using Transactions + url: modifying.html + + - title: Using Prepared Statements + url: prepared.html + + - title: Handling Errors + url: errors.html + + - title: Working with NULLs + url: nulls.html + + - title: Working with Unknown Columns + url: varcols.html + + - title: The Connection Pool + url: connection-pool.html + + - title: Surprises, Antipatterns and Limitations + url: surprises.html + + - title: Related Reading and Resources + url: references.html diff --git a/_includes/leftnav.html b/_includes/leftnav.html index df91360..832c653 100644 --- a/_includes/leftnav.html +++ b/_includes/leftnav.html @@ -1,23 +1,11 @@

From 910626ff2bcd0b772f03948a243b0c812daf35ba Mon Sep 17 00:00:00 2001 From: cglotr Date: Thu, 31 Jan 2019 12:36:11 +0800 Subject: [PATCH 42/47] add missing importing nav --- _data/nav.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_data/nav.yml b/_data/nav.yml index 1d2d2a1..dfab5a9 100644 --- a/_data/nav.yml +++ b/_data/nav.yml @@ -6,6 +6,9 @@ docs: - title: Overview url: overview.html + - title: Importing a Database Driver + url: importing.html + - title: Accessing the Database url: accessing.html From 86f6735f4f287c776e12b8ba1eb2bb1596587ad0 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Wed, 6 Mar 2019 06:26:05 -0800 Subject: [PATCH 43/47] Fix grammar typo --- overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overview.md b/overview.md index 1a3287a..337973b 100644 --- a/overview.md +++ b/overview.md @@ -12,7 +12,7 @@ of a "database" or "schema." It's an abstraction of the interface and existence of a database, which might be as varied as a local file, accessed through a network connection, or in-memory and in-process. -The `sql.DB` performs some important tasks for you behind the scenes: +`sql.DB` performs some important tasks for you behind the scenes: * It opens and closes connections to the actual underlying database, via the driver. * It manages a pool of connections as needed, which may be a variety of things as mentioned. From 9d2e80505f0f4ef12e7c35fab637f9aecd87bf2f Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Wed, 6 Mar 2019 06:39:53 -0800 Subject: [PATCH 44/47] Grammar fix --- modifying.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modifying.md b/modifying.md index 074188a..f649022 100644 --- a/modifying.md +++ b/modifying.md @@ -12,7 +12,7 @@ Statements that Modify Data =========================== Use `Exec()`, preferably with a prepared statement, to accomplish an `INSERT`, -`UPDATE`, `DELETE`, or other statement that doesn't return rows. The following +`UPDATE`, `DELETE`, or another statement that doesn't return rows. The following example shows how to insert a row and inspect metadata about the operation:

From 07f6fad65812bc6db25e748c834da8d634b052e8 Mon Sep 17 00:00:00 2001
From: Eli Bendersky 
Date: Thu, 7 Mar 2019 07:03:00 -0800
Subject: [PATCH 45/47] Fix reference to 'db' var

In the rest of the example `db` is used, not `Db`. Moreover, I think that a simple example here could help clarify the terminology, because it gets a bit murky between `db` (object of type `DB`) and `tx` (object of type `Tx`). Something like:

```
// Create an object of type Tx
tx, err := db.Begin()
...
```
---
 modifying.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modifying.md b/modifying.md
index f649022..487aac3 100644
--- a/modifying.md
+++ b/modifying.md
@@ -84,8 +84,8 @@ code. Bad things might result:
 * 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.
 
 While you are working inside a transaction you should be careful not to make
-calls to the `Db` variable. Make all of your calls to the `Tx` variable that you
-created with `db.Begin()`. The `Db` is not in a transaction, only the `Tx` is.
+calls to the `db` variable. Make all of your calls to the `Tx` variable that you
+created with `db.Begin()`. `db` is not in a transaction, only the `Tx` object is.
 If you make further calls to `db.Exec()` or similar, those will happen outside
 the scope of your transaction, on other connections.
 

From 75b25123d375bc1a86a6b83f4070f0e28c215c38 Mon Sep 17 00:00:00 2001
From: "whitesource-for-github.amrom.workers.dev[bot]"
 <50673670+whitesource-for-github.amrom.workers.dev[bot]@users.noreply.github.com>
Date: Wed, 24 Jun 2020 14:11:58 +0000
Subject: [PATCH 46/47] Add .whitesource configuration file

---
 .whitesource | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 .whitesource

diff --git a/.whitesource b/.whitesource
new file mode 100644
index 0000000..60fc783
--- /dev/null
+++ b/.whitesource
@@ -0,0 +1,13 @@
+{
+  "scanSettings": {
+    "configMode": "AUTO",
+    "configExternalURL": "",
+    "projectToken" : ""
+  },
+  "checkRunSettings": {
+    "vulnerableCheckRunConclusionLevel": "failure"
+  },
+  "issueSettings": {
+    "minSeverityLevel": "LOW"
+  }
+}
\ No newline at end of file

From ae7ad3a8886f5ff92f269fe35debe3021005d336 Mon Sep 17 00:00:00 2001
From: "whitesource-for-github.amrom.workers.dev[bot]"
 <50673670+whitesource-for-github.amrom.workers.dev[bot]@users.noreply.github.com>
Date: Tue, 15 Dec 2020 15:54:46 +0000
Subject: [PATCH 47/47] Migrate .whitesource configuration file to inheritance
 mode

---
 .whitesource | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/.whitesource b/.whitesource
index 60fc783..d7eebc0 100644
--- a/.whitesource
+++ b/.whitesource
@@ -1,13 +1,3 @@
 {
-  "scanSettings": {
-    "configMode": "AUTO",
-    "configExternalURL": "",
-    "projectToken" : ""
-  },
-  "checkRunSettings": {
-    "vulnerableCheckRunConclusionLevel": "failure"
-  },
-  "issueSettings": {
-    "minSeverityLevel": "LOW"
-  }
+  "settingsInheritedFrom": "VividCortex/whitesource-config@master"
 }
\ No newline at end of file