Skip to content

Commit e6358c7

Browse files
james-lawrencekardianos
authored andcommitted
database/sql: add OpenDB to directly create a *DB without a DSN.
The current Open method limits the ability for driver maintainers to expose options for their drivers by forcing all the configuration to pass through the DSN in order to create a *DB. This CL allows driver maintainers to write their own initialization functions that return a *DB making configuration of the underlying drivers easier. Fixes #20268 Change-Id: Ib10b794f36a201bbb92c23999c8351815d38eedb Reviewed-on: https://go-review.googlesource.com/53430 Reviewed-by: Daniel Theophanes <kardianos@gmail.com> Run-TryBot: Daniel Theophanes <kardianos@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
1 parent 6936671 commit e6358c7

File tree

3 files changed

+84
-20
lines changed

3 files changed

+84
-20
lines changed

src/database/sql/driver/driver.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,29 @@ type Driver interface {
5555
Open(name string) (Conn, error)
5656
}
5757

58+
// Connector is an optional interface that drivers can implement.
59+
// It allows drivers to provide more flexible methods to open
60+
// database connections without requiring the use of a DSN string.
61+
type Connector interface {
62+
// Connect returns a connection to the database.
63+
// Connect may return a cached connection (one previously
64+
// closed), but doing so is unnecessary; the sql package
65+
// maintains a pool of idle connections for efficient re-use.
66+
//
67+
// The provided context.Context is for dialing purposes only
68+
// (see net.DialContext) and should not be stored or used for
69+
// other purposes.
70+
//
71+
// The returned connection is only used by one goroutine at a
72+
// time.
73+
Connect(context.Context) (Conn, error)
74+
75+
// Driver returns the underlying Driver of the Connector,
76+
// mainly to maintain compatibility with the Driver method
77+
// on sql.DB.
78+
Driver() Driver
79+
}
80+
5881
// ErrSkip may be returned by some optional interfaces' methods to
5982
// indicate at runtime that the fast path is unavailable and the sql
6083
// package should continue as if the optional interface was not

src/database/sql/sql.go

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,7 @@ var ErrNoRows = errors.New("sql: no rows in result set")
317317
// connection is returned to DB's idle connection pool. The pool size
318318
// can be controlled with SetMaxIdleConns.
319319
type DB struct {
320-
driver driver.Driver
321-
dsn string
320+
connector driver.Connector
322321
// numClosed is an atomic counter which represents a total number of
323322
// closed connections. Stmt.openStmt checks it before cleaning closed
324323
// connections in Stmt.css.
@@ -575,6 +574,48 @@ func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error {
575574
// to block until the connectionOpener can satisfy the backlog of requests.
576575
var connectionRequestQueueSize = 1000000
577576

577+
type dsnConnector struct {
578+
dsn string
579+
driver driver.Driver
580+
}
581+
582+
func (t dsnConnector) Connect(_ context.Context) (driver.Conn, error) {
583+
return t.driver.Open(t.dsn)
584+
}
585+
586+
func (t dsnConnector) Driver() driver.Driver {
587+
return t.driver
588+
}
589+
590+
// OpenDB opens a database using a Connector, allowing drivers to
591+
// bypass a string based data source name.
592+
//
593+
// Most users will open a database via a driver-specific connection
594+
// helper function that returns a *DB. No database drivers are included
595+
// in the Go standard library. See https://golang.org/s/sqldrivers for
596+
// a list of third-party drivers.
597+
//
598+
// OpenDB may just validate its arguments without creating a connection
599+
// to the database. To verify that the data source name is valid, call
600+
// Ping.
601+
//
602+
// The returned DB is safe for concurrent use by multiple goroutines
603+
// and maintains its own pool of idle connections. Thus, the OpenDB
604+
// function should be called just once. It is rarely necessary to
605+
// close a DB.
606+
func OpenDB(c driver.Connector) *DB {
607+
db := &DB{
608+
connector: c,
609+
openerCh: make(chan struct{}, connectionRequestQueueSize),
610+
lastPut: make(map[*driverConn]string),
611+
connRequests: make(map[uint64]chan connRequest),
612+
}
613+
614+
go db.connectionOpener()
615+
616+
return db
617+
}
618+
578619
// Open opens a database specified by its database driver name and a
579620
// driver-specific data source name, usually consisting of at least a
580621
// database name and connection information.
@@ -599,15 +640,8 @@ func Open(driverName, dataSourceName string) (*DB, error) {
599640
if !ok {
600641
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
601642
}
602-
db := &DB{
603-
driver: driveri,
604-
dsn: dataSourceName,
605-
openerCh: make(chan struct{}, connectionRequestQueueSize),
606-
lastPut: make(map[*driverConn]string),
607-
connRequests: make(map[uint64]chan connRequest),
608-
}
609-
go db.connectionOpener()
610-
return db, nil
643+
644+
return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
611645
}
612646

613647
func (db *DB) pingDC(ctx context.Context, dc *driverConn, release func(error)) error {
@@ -878,7 +912,7 @@ func (db *DB) openNewConnection() {
878912
// maybeOpenNewConnctions has already executed db.numOpen++ before it sent
879913
// on db.openerCh. This function must execute db.numOpen-- if the
880914
// connection fails or is closed before returning.
881-
ci, err := db.driver.Open(db.dsn)
915+
ci, err := db.connector.Connect(context.Background())
882916
db.mu.Lock()
883917
defer db.mu.Unlock()
884918
if db.closed {
@@ -996,7 +1030,7 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn
9961030

9971031
db.numOpen++ // optimistically
9981032
db.mu.Unlock()
999-
ci, err := db.driver.Open(db.dsn)
1033+
ci, err := db.connector.Connect(ctx)
10001034
if err != nil {
10011035
db.mu.Lock()
10021036
db.numOpen-- // correct for earlier optimism
@@ -1454,7 +1488,7 @@ func (db *DB) beginDC(ctx context.Context, dc *driverConn, release func(error),
14541488

14551489
// Driver returns the database's underlying driver.
14561490
func (db *DB) Driver() driver.Driver {
1457-
return db.driver
1491+
return db.connector.Driver()
14581492
}
14591493

14601494
// ErrConnDone is returned by any operation that is performed on a connection

src/database/sql/sql_test.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ func newTestDB(t testing.TB, name string) *DB {
8181
return db
8282
}
8383

84+
func TestOpenDB(t *testing.T) {
85+
db := OpenDB(dsnConnector{dsn: fakeDBName, driver: fdriver})
86+
if db.Driver() != fdriver {
87+
t.Fatalf("OpenDB should return the driver of the Connector")
88+
}
89+
}
90+
8491
func TestDriverPanic(t *testing.T) {
8592
// Test that if driver panics, database/sql does not deadlock.
8693
db, err := Open("test", fakeDBName)
@@ -1672,7 +1679,7 @@ func TestIssue4902(t *testing.T) {
16721679
db := newTestDB(t, "people")
16731680
defer closeDB(t, db)
16741681

1675-
driver := db.driver.(*fakeDriver)
1682+
driver := db.Driver().(*fakeDriver)
16761683
opens0 := driver.openCount
16771684

16781685
var stmt *Stmt
@@ -1765,7 +1772,7 @@ func TestMaxOpenConns(t *testing.T) {
17651772
db := newTestDB(t, "magicquery")
17661773
defer closeDB(t, db)
17671774

1768-
driver := db.driver.(*fakeDriver)
1775+
driver := db.Driver().(*fakeDriver)
17691776

17701777
// Force the number of open connections to 0 so we can get an accurate
17711778
// count for the test
@@ -2057,7 +2064,7 @@ func TestConnMaxLifetime(t *testing.T) {
20572064
db := newTestDB(t, "magicquery")
20582065
defer closeDB(t, db)
20592066

2060-
driver := db.driver.(*fakeDriver)
2067+
driver := db.Driver().(*fakeDriver)
20612068

20622069
// Force the number of open connections to 0 so we can get an accurate
20632070
// count for the test
@@ -2146,7 +2153,7 @@ func TestStmtCloseDeps(t *testing.T) {
21462153
db := newTestDB(t, "magicquery")
21472154
defer closeDB(t, db)
21482155

2149-
driver := db.driver.(*fakeDriver)
2156+
driver := db.Driver().(*fakeDriver)
21502157

21512158
driver.mu.Lock()
21522159
opens0 := driver.openCount
@@ -3071,7 +3078,7 @@ func TestIssue6081(t *testing.T) {
30713078
db := newTestDB(t, "people")
30723079
defer closeDB(t, db)
30733080

3074-
drv := db.driver.(*fakeDriver)
3081+
drv := db.Driver().(*fakeDriver)
30753082
drv.mu.Lock()
30763083
opens0 := drv.openCount
30773084
closes0 := drv.closeCount
@@ -3326,7 +3333,7 @@ func TestConnectionLeak(t *testing.T) {
33263333
// Now we have defaultMaxIdleConns busy connections. Open
33273334
// a new one, but wait until the busy connections are released
33283335
// before returning control to DB.
3329-
drv := db.driver.(*fakeDriver)
3336+
drv := db.Driver().(*fakeDriver)
33303337
drv.waitCh = make(chan struct{}, 1)
33313338
drv.waitingCh = make(chan struct{}, 1)
33323339
var wg sync.WaitGroup

0 commit comments

Comments
 (0)