Repository: mattn/go-sqlite3
Pull request #879 (2020-11-15 15:42:25)

TestExecContextCancel: Reduce timeout to make less flaky
This test fails fairly often. On my system, I can trigger it with:

    go test . -run=TestExecContextCancel -count=10 -failfast -v

This makes the test less flaky by timing out the context after a
consistent 50 millisecond delay. This was enough time for the query
to start, then get cancelled with sqlite3_interrupt() in my tests.
This now passes the above check.

This is a modified version of the change suggested in:
https://github.com/mattn/go-sqlite3/pull/865

codecov-io: # [Codecov](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=h1) Report
> Merging [#879](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=desc) (0d9f18b) into [master](https://codecov.io/gh/mattn/go-sqlite3/commit/92f580b350d88e068bfbe223ea51f64044c0f280?el=desc) (92f580b) will **decrease** coverage by `0.73%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/mattn/go-sqlite3/pull/879/graphs/tree.svg?width=650&height=150&src=pr&token=iuTYvLwNp6)](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #879      +/-   ##
==========================================
- Coverage   47.26%   46.52%   -0.74%     
==========================================
  Files          12       11       -1     
  Lines        1917     1440     -477     
==========================================
- Hits          906      670     -236     
+ Misses        876      636     -240     
+ Partials      135      134       -1     
```


| [Impacted Files](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [sqlite3\_load\_extension.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-c3FsaXRlM19sb2FkX2V4dGVuc2lvbi5nbw==) | `59.37% <0.00%> (-3.67%)` | :arrow_down: |
| [backup.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-YmFja3VwLmdv) | `83.33% <0.00%> (-1.29%)` | :arrow_down: |
| [callback.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-Y2FsbGJhY2suZ28=) | `58.16% <0.00%> (-0.81%)` | :arrow_down: |
| [error.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-ZXJyb3IuZ28=) | `81.81% <0.00%> (-0.54%)` | :arrow_down: |
| [convert.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-Y29udmVydC5nbw==) | `0.00% <0.00%> (ø)` | |
| [sqlite3\_go18.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-c3FsaXRlM19nbzE4Lmdv) | `88.23% <0.00%> (ø)` | |
| [sqlite3\_type.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-c3FsaXRlM190eXBlLmdv) | `0.00% <0.00%> (ø)` | |
| [sqlite3\_context.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-c3FsaXRlM19jb250ZXh0Lmdv) | `0.00% <0.00%> (ø)` | |
| [sqlite3\_func\_crypt.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-c3FsaXRlM19mdW5jX2NyeXB0Lmdv) | `100.00% <0.00%> (ø)` | |
| [sqlite3\_opt\_userauth\_omit.go](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree#diff-c3FsaXRlM19vcHRfdXNlcmF1dGhfb21pdC5nbw==) | `10.00% <0.00%> (ø)` | |
| ... and [2 more](https://codecov.io/gh/mattn/go-sqlite3/pull/879/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=footer). Last update [92f580b...0d9f18b](https://codecov.io/gh/mattn/go-sqlite3/pull/879?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/doc.go b/doc.go
index c721f77..ac27633 100644
--- a/doc.go
+++ b/doc.go
@@ -79,9 +79,8 @@ Then, you can use this extension.
 
 Connection Hook
 
-You can hook and inject your code when the connection is established. database/sql
-doesn't provide a way to get native go-sqlite3 interfaces. So if you want,
-you need to set ConnectHook and get the SQLiteConn.
+You can hook and inject your code when the connection is established by setting
+ConnectHook to get the SQLiteConn.
 
 	sql.Register("sqlite3_with_hook_example",
 			&sqlite3.SQLiteDriver{
@@ -91,21 +90,45 @@ you need to set ConnectHook and get the SQLiteConn.
 					},
 			})
 
+You can also use database/sql.Conn.Raw (Go >= 1.13):
+
+	conn, err := db.Conn(context.Background())
+	// if err != nil { ... }
+	defer conn.Close()
+	err = conn.Raw(func (driverConn interface{}) error {
+		sqliteConn := driverConn.(*sqlite3.SQLiteConn)
+		// ... use sqliteConn
+	})
+	// if err != nil { ... }
+
 Go SQlite3 Extensions
 
-If you want to register Go functions as SQLite extension functions,
-call RegisterFunction from ConnectHook.
+If you want to register Go functions as SQLite extension functions
+you can make a custom driver by calling RegisterFunction from
+ConnectHook.
 
 	regex = func(re, s string) (bool, error) {
 		return regexp.MatchString(re, s)
 	}
-	sql.Register("sqlite3_with_go_func",
+	sql.Register("sqlite3_extended",
 			&sqlite3.SQLiteDriver{
 					ConnectHook: func(conn *sqlite3.SQLiteConn) error {
 						return conn.RegisterFunc("regexp", regex, true)
 					},
 			})
 
+You can then use the custom driver by passing its name to sql.Open.
+
+	var i int
+	conn, err := sql.Open("sqlite3_extended", "./foo.db")
+	if err != nil {
+		panic(err)
+	}
+	err = db.QueryRow(`SELECT regexp("foo.*", "seafood")`).Scan(&i)
+	if err != nil {
+		panic(err)
+	}
+
 See the documentation of RegisterFunc for more details.
 
 */


Test patch:
diff --git a/sqlite3_go113_test.go b/sqlite3_go113_test.go
index 74036f8..6f74e6b 100644
--- a/sqlite3_go113_test.go
+++ b/sqlite3_go113_test.go
@@ -49,6 +49,10 @@ func TestBeginTxCancel(t *testing.T) {
 				if !ok {
 					t.Fatal("unexpected: wrong type")
 				}
+				// checks that conn.Raw can be used to get *SQLiteConn
+				if _, ok = driverConn.(*SQLiteConn); !ok {
+					t.Fatalf("conn.Raw() driverConn type=%T, expected *SQLiteConn", driverConn)
+				}
 
 				go cancel() // make it cancel concurrently with exec("BEGIN");
 				tx, err := d.BeginTx(ctx, driver.TxOptions{})
diff --git a/sqlite3_go18_test.go b/sqlite3_go18_test.go
index 5ee3d81..8c8c451 100644
--- a/sqlite3_go18_test.go
+++ b/sqlite3_go18_test.go
@@ -150,8 +150,11 @@ func TestExecContextCancel(t *testing.T) {
 	ts := time.Now()
 	initDatabase(t, db, 1000)
 	spent := time.Since(ts)
-	if spent < 100*time.Millisecond {
-		t.Skip("test will be too racy, as ExecContext below will be too fast.")
+	const minTestTime = 100 * time.Millisecond
+	if spent < minTestTime {
+		t.Skipf("test will be too racy (spent=%s < min=%s) as ExecContext below will be too fast.",
+			spent.String(), minTestTime.String(),
+		)
 	}
 
 	// expected to be extremely slow query
@@ -160,14 +163,17 @@ INSERT INTO test_table (key1, key_id, key2, key3, key4, key5, key6, data)
 SELECT t1.key1 || t2.key1, t1.key_id || t2.key_id, t1.key2 || t2.key2, t1.key3 || t2.key3, t1.key4 || t2.key4, t1.key5 || t2.key5, t1.key6 || t2.key6, t1.data || t2.data
 FROM test_table t1 LEFT OUTER JOIN test_table t2`
 	// expect query above take ~ same time as setup above
-	ctx, cancel := context.WithTimeout(context.Background(), spent/2)
+	// This is racy: the context must be valid so sql/db.ExecContext calls the sqlite3 driver.
+	// It starts the query, the context expires, then calls sqlite3_interrupt
+	ctx, cancel := context.WithTimeout(context.Background(), minTestTime/2)
 	defer cancel()
 	ts = time.Now()
 	r, err := db.ExecContext(ctx, q)
 	// racy check
 	if r != nil {
 		n, err := r.RowsAffected()
-		t.Log(n, err, time.Since(ts))
+		t.Logf("query should not have succeeded: rows=%d; err=%v; duration=%s",
+			n, err, time.Since(ts).String())
 	}
 	if err != context.DeadlineExceeded {
 		t.Fatal(err, ctx.Err())