mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
database/sql: allow using a single connection from the database
Databases have the following concepts: Statement, Batch, and Session. A statement is often a single line like: SELECT Amount from Account where ID = 50; A batch is one or more statements submitted together for the query to process. It may be a DELETE, INSERT, two UPDATES and a SELECT in a single query text. A session is usually represented by a single database connection. This often is an issue when dealing with scopes in databases. Temporary tables and variables can have batch, session, or global scope depending on the syntax, database, and use. Furthermore, some databases (sybase and derivatives in perticular) that prevent certain statements from being in the same batch and may necessitate being in the same session. By allowing users to extract a Conn from the database they can manage session on their own without hacking around it by making connection pools of single connections (a real workaround presented in issue). It is tempting to just use a transaction, but this isn't always desirable or an option if running an interactive session or alter script set that itself starts transactions. Fixes #18081 Change-Id: I9bdf0796632c48d4bcaef3624c629641984ffaf2 Reviewed-on: https://go-review.googlesource.com/40694 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
42c5f3993b
commit
d234f9a754
2 changed files with 283 additions and 5 deletions
|
|
@ -139,6 +139,7 @@ func closeDB(t testing.TB, db *DB) {
|
|||
t.Errorf("Error closing fakeConn: %v", err)
|
||||
}
|
||||
})
|
||||
db.mu.Lock()
|
||||
for i, dc := range db.freeConn {
|
||||
if n := len(dc.openStmt); n > 0 {
|
||||
// Just a sanity check. This is legal in
|
||||
|
|
@ -149,6 +150,8 @@ func closeDB(t testing.TB, db *DB) {
|
|||
t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, len(db.freeConn), n)
|
||||
}
|
||||
}
|
||||
db.mu.Unlock()
|
||||
|
||||
err := db.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("error closing DB: %v", err)
|
||||
|
|
@ -1298,6 +1301,69 @@ func TestTxErrBadConn(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConnQuery(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
conn, err := db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var name string
|
||||
err = conn.QueryRowContext(ctx, "SELECT|people|name|age=?", 3).Scan(&name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if name != "Chris" {
|
||||
t.Fatalf("unexpected result, got %q want Chris", name)
|
||||
}
|
||||
|
||||
err = conn.PingContext(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnTx(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
conn, err := db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
tx, err := conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
insertName, insertAge := "Nancy", 33
|
||||
_, err = tx.ExecContext(ctx, "INSERT|people|name=?,age=?,photo=APHOTO", insertName, insertAge)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var selectName string
|
||||
err = conn.QueryRowContext(ctx, "SELECT|people|name|age=?", insertAge).Scan(&selectName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if selectName != insertName {
|
||||
t.Fatalf("got %q want %q", selectName, insertName)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests fix for issue 2542, that we release a lock when querying on
|
||||
// a closed connection.
|
||||
func TestIssue2542Deadlock(t *testing.T) {
|
||||
|
|
@ -2338,6 +2404,28 @@ func TestManyErrBadConn(t *testing.T) {
|
|||
if err = stmt.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Conn
|
||||
db = manyErrBadConnSetup()
|
||||
defer closeDB(t, db)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
conn, err := db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ping
|
||||
db = manyErrBadConnSetup()
|
||||
defer closeDB(t, db)
|
||||
err = db.PingContext(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// golang.org/issue/5718
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue