From 42f28ab8e374137fe3f5d25424489d879d4724f8 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Wed, 21 Jun 2017 19:06:17 -0700 Subject: Updating server dependancies (#6712) --- vendor/github.com/lib/pq/conn.go | 6 +- vendor/github.com/lib/pq/conn_go18.go | 31 ++++++++-- vendor/github.com/lib/pq/conn_test.go | 2 +- vendor/github.com/lib/pq/go18_test.go | 109 ++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 7 deletions(-) (limited to 'vendor/github.com/lib/pq') diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go index 3e3a5cabc..3b322bc06 100644 --- a/vendor/github.com/lib/pq/conn.go +++ b/vendor/github.com/lib/pq/conn.go @@ -506,13 +506,17 @@ func (cn *conn) checkIsInTransaction(intxn bool) { } func (cn *conn) Begin() (_ driver.Tx, err error) { + return cn.begin("") +} + +func (cn *conn) begin(mode string) (_ driver.Tx, err error) { if cn.bad { return nil, driver.ErrBadConn } defer cn.errRecover(&err) cn.checkIsInTransaction(false) - _, commandTag, err := cn.simpleExec("BEGIN") + _, commandTag, err := cn.simpleExec("BEGIN" + mode) if err != nil { return nil, err } diff --git a/vendor/github.com/lib/pq/conn_go18.go b/vendor/github.com/lib/pq/conn_go18.go index fa3755d99..ab97a104d 100644 --- a/vendor/github.com/lib/pq/conn_go18.go +++ b/vendor/github.com/lib/pq/conn_go18.go @@ -4,8 +4,9 @@ package pq import ( "context" + "database/sql" "database/sql/driver" - "errors" + "fmt" "io" "io/ioutil" ) @@ -19,6 +20,9 @@ func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.Na finish := cn.watchCancel(ctx) r, err := cn.query(query, list) if err != nil { + if finish != nil { + finish() + } return nil, err } r.finish = finish @@ -41,13 +45,30 @@ func (cn *conn) ExecContext(ctx context.Context, query string, args []driver.Nam // Implement the "ConnBeginTx" interface func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { - if opts.Isolation != 0 { - return nil, errors.New("isolation levels not supported") + var mode string + + switch sql.IsolationLevel(opts.Isolation) { + case sql.LevelDefault: + // Don't touch mode: use the server's default + case sql.LevelReadUncommitted: + mode = " ISOLATION LEVEL READ UNCOMMITTED" + case sql.LevelReadCommitted: + mode = " ISOLATION LEVEL READ COMMITTED" + case sql.LevelRepeatableRead: + mode = " ISOLATION LEVEL REPEATABLE READ" + case sql.LevelSerializable: + mode = " ISOLATION LEVEL SERIALIZABLE" + default: + return nil, fmt.Errorf("pq: isolation level not supported: %d", opts.Isolation) } + if opts.ReadOnly { - return nil, errors.New("read-only transactions not supported") + mode += " READ ONLY" + } else { + mode += " READ WRITE" } - tx, err := cn.Begin() + + tx, err := cn.begin(mode) if err != nil { return nil, err } diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go index c9135b727..8c6187fc6 100644 --- a/vendor/github.com/lib/pq/conn_test.go +++ b/vendor/github.com/lib/pq/conn_test.go @@ -160,11 +160,11 @@ func TestPgpass(t *testing.T) { rows, err := txn.Query("SELECT USER") if err != nil { txn.Rollback() - rows.Close() if expected != "fail" { t.Fatalf(reason, err) } } else { + rows.Close() if expected != "ok" { t.Fatalf(reason, err) } diff --git a/vendor/github.com/lib/pq/go18_test.go b/vendor/github.com/lib/pq/go18_test.go index cddbfb6a4..4bf6391ef 100644 --- a/vendor/github.com/lib/pq/go18_test.go +++ b/vendor/github.com/lib/pq/go18_test.go @@ -5,6 +5,8 @@ package pq import ( "context" "database/sql" + "runtime" + "strings" "testing" "time" ) @@ -155,6 +157,36 @@ func TestContextCancelQuery(t *testing.T) { } } +// TestIssue617 tests that a failed query in QueryContext doesn't lead to a +// goroutine leak. +func TestIssue617(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + const N = 10 + + numGoroutineStart := runtime.NumGoroutine() + for i := 0; i < N; i++ { + func() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _, err := db.QueryContext(ctx, `SELECT * FROM DOESNOTEXIST`) + pqErr, _ := err.(*Error) + // Expecting "pq: relation \"doesnotexist\" does not exist" error. + if err == nil || pqErr == nil || pqErr.Code != "42P01" { + t.Fatalf("expected undefined table error, got %v", err) + } + }() + } + numGoroutineFinish := runtime.NumGoroutine() + + // We use N/2 and not N because the GC and other actors may increase or + // decrease the number of goroutines. + if numGoroutineFinish-numGoroutineStart >= N/2 { + t.Errorf("goroutine leak detected, was %d, now %d", numGoroutineStart, numGoroutineFinish) + } +} + func TestContextCancelBegin(t *testing.T) { db := openTestConn(t) defer db.Close() @@ -208,3 +240,80 @@ func TestContextCancelBegin(t *testing.T) { } } } + +func TestTxOptions(t *testing.T) { + db := openTestConn(t) + defer db.Close() + ctx := context.Background() + + tests := []struct { + level sql.IsolationLevel + isolation string + }{ + { + level: sql.LevelDefault, + isolation: "", + }, + { + level: sql.LevelReadUncommitted, + isolation: "read uncommitted", + }, + { + level: sql.LevelReadCommitted, + isolation: "read committed", + }, + { + level: sql.LevelRepeatableRead, + isolation: "repeatable read", + }, + { + level: sql.LevelSerializable, + isolation: "serializable", + }, + } + + for _, test := range tests { + for _, ro := range []bool{true, false} { + tx, err := db.BeginTx(ctx, &sql.TxOptions{ + Isolation: test.level, + ReadOnly: ro, + }) + if err != nil { + t.Fatal(err) + } + + var isolation string + err = tx.QueryRow("select current_setting('transaction_isolation')").Scan(&isolation) + if err != nil { + t.Fatal(err) + } + + if test.isolation != "" && isolation != test.isolation { + t.Errorf("wrong isolation level: %s != %s", isolation, test.isolation) + } + + var isRO string + err = tx.QueryRow("select current_setting('transaction_read_only')").Scan(&isRO) + if err != nil { + t.Fatal(err) + } + + if ro != (isRO == "on") { + t.Errorf("read/[write,only] not set: %t != %s for level %s", + ro, isRO, test.isolation) + } + + tx.Rollback() + } + } + + _, err := db.BeginTx(ctx, &sql.TxOptions{ + Isolation: sql.LevelLinearizable, + }) + if err == nil { + t.Fatal("expected LevelLinearizable to fail") + } + if !strings.Contains(err.Error(), "isolation level not supported") { + t.Errorf("Expected error to mention isolation level, got %q", err) + } +} -- cgit v1.2.3-1-g7c22