From 04bfcb1d1ea45b3deac92db22a6a5baffa1fb714 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Wed, 30 Sep 2020 12:31:01 +0300 Subject: [PATCH] database/sql: close driver.Connector if it implements an io.Closer This change allows driver implementations to manage resources in driver.Connector, e.g. to share the same underlying database handle between multiple connections. That is, it allows embedded databases with in-memory backends like SQLite and Genji to safely release the resources once the sql.DB is closed. This makes it possible to address oddities with in-memory stores in SQLite and Genji drivers without introducing too much complexity in the driver implementations. See also: - https://github.com/mattn/go-sqlite3/issues/204 - https://github.com/mattn/go-sqlite3/issues/511 - https://github.com/genjidb/genji/issues/210 --- src/database/sql/driver/driver.go | 3 +++ src/database/sql/fakedb_test.go | 9 +++++++++ src/database/sql/sql.go | 3 +++ src/database/sql/sql_test.go | 11 ++++++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index 5bbcf20db2f07d..d1702a624e066e 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -115,6 +115,9 @@ type DriverContext interface { // DriverContext's OpenConnector method, to allow drivers // access to context and to avoid repeated parsing of driver // configuration. +// +// A Connector may optionally implement io.Closer interface +// to release the resources when sql.DB is closed. type Connector interface { // Connect returns a connection to the database. // Connect may return a cached connection (one previously diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go index 7605a2a6d23e03..1bfd1118aad68b 100644 --- a/src/database/sql/fakedb_test.go +++ b/src/database/sql/fakedb_test.go @@ -56,6 +56,7 @@ type fakeConnector struct { name string waiter func(context.Context) + closed bool } func (c *fakeConnector) Connect(context.Context) (driver.Conn, error) { @@ -68,6 +69,14 @@ func (c *fakeConnector) Driver() driver.Driver { return fdriver } +func (c *fakeConnector) Close() error { + if c.closed { + return errors.New("fakedb: connector is closed") + } + c.closed = true + return nil +} + type fakeDriverCtx struct { fakeDriver } diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index e3580698fd6fa4..3c2870caceed53 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -850,6 +850,9 @@ func (db *DB) Close() error { } } db.stop() + if c, ok := db.connector.(io.Closer); ok { + err = c.Close() + } return err } diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 762d42f54b4e06..d82b1ba2328d17 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -4022,9 +4022,18 @@ func TestOpenConnector(t *testing.T) { } defer db.Close() - if _, is := db.connector.(*fakeConnector); !is { + c, ok := db.connector.(*fakeConnector) + if !ok { t.Fatal("not using *fakeConnector") } + + if err := db.Close(); err != nil { + t.Fatal(err) + } + + if !c.closed { + t.Fatal("connector is not closed") + } } type ctxOnlyDriver struct {