libgo: Merge from revision 18783:00cce3a34d7e of master library.
This revision was committed January 7, 2014. The next revision deleted runtime/mfinal.c. That will be done in a subsequent merge. This merge changes type descriptors to add a zero field, pointing to a zero value for that type. This is implemented as a common variable. * go-gcc.cc (Gcc_backend::implicit_variable): Add is_common and alignment parameters. Permit init parameter to be NULL. From-SVN: r211249
This commit is contained in:
parent
82b3da6a71
commit
bae90c989c
230 changed files with 9570 additions and 7953 deletions
|
@ -23,7 +23,7 @@ var _ = log.Printf
|
|||
// interface, just for testing.
|
||||
//
|
||||
// It speaks a query language that's semantically similar to but
|
||||
// syntantically different and simpler than SQL. The syntax is as
|
||||
// syntactically different and simpler than SQL. The syntax is as
|
||||
// follows:
|
||||
//
|
||||
// WIPE
|
||||
|
@ -433,11 +433,19 @@ func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, e
|
|||
return stmt, nil
|
||||
}
|
||||
|
||||
// hook to simulate broken connections
|
||||
var hookPrepareBadConn func() bool
|
||||
|
||||
func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
|
||||
c.numPrepare++
|
||||
if c.db == nil {
|
||||
panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
|
||||
}
|
||||
|
||||
if hookPrepareBadConn != nil && hookPrepareBadConn() {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
parts := strings.Split(query, "|")
|
||||
if len(parts) < 1 {
|
||||
return nil, errf("empty query")
|
||||
|
@ -489,10 +497,18 @@ func (s *fakeStmt) Close() error {
|
|||
|
||||
var errClosed = errors.New("fakedb: statement has been closed")
|
||||
|
||||
// hook to simulate broken connections
|
||||
var hookExecBadConn func() bool
|
||||
|
||||
func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if s.closed {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
if hookExecBadConn != nil && hookExecBadConn() {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
err := checkSubsetTypes(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -565,10 +581,18 @@ func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result
|
|||
return driver.RowsAffected(1), nil
|
||||
}
|
||||
|
||||
// hook to simulate broken connections
|
||||
var hookQueryBadConn func() bool
|
||||
|
||||
func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
if s.closed {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
if hookQueryBadConn != nil && hookQueryBadConn() {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
err := checkSubsetTypes(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -686,7 +710,13 @@ func (rc *rowsCursor) Columns() []string {
|
|||
return rc.cols
|
||||
}
|
||||
|
||||
var rowsCursorNextHook func(dest []driver.Value) error
|
||||
|
||||
func (rc *rowsCursor) Next(dest []driver.Value) error {
|
||||
if rowsCursorNextHook != nil {
|
||||
return rowsCursorNextHook(dest)
|
||||
}
|
||||
|
||||
if rc.closed {
|
||||
return errors.New("fakedb: cursor is closed")
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@ func (dc *driverConn) prepareLocked(query string) (driver.Stmt, error) {
|
|||
// stmt closes if the conn is about to close anyway? For now
|
||||
// do the safe thing, in case stmts need to be closed.
|
||||
//
|
||||
// TODO(bradfitz): after Go 1.1, closing driver.Stmts
|
||||
// TODO(bradfitz): after Go 1.2, closing driver.Stmts
|
||||
// should be moved to driverStmt, using unique
|
||||
// *driverStmts everywhere (including from
|
||||
// *Stmt.connStmt, instead of returning a
|
||||
|
@ -569,7 +569,7 @@ func (db *DB) maybeOpenNewConnections() {
|
|||
}
|
||||
}
|
||||
|
||||
// Runs in a seperate goroutine, opens new connections when requested.
|
||||
// Runs in a separate goroutine, opens new connections when requested.
|
||||
func (db *DB) connectionOpener() {
|
||||
for _ = range db.openerCh {
|
||||
db.openNewConnection()
|
||||
|
@ -774,8 +774,8 @@ func (db *DB) putConn(dc *driverConn, err error) {
|
|||
// Satisfy a connRequest or put the driverConn in the idle pool and return true
|
||||
// or return false.
|
||||
// putConnDBLocked will satisfy a connRequest if there is one, or it will
|
||||
// return the *driverConn to the freeConn list if err != nil and the idle
|
||||
// connection limit would not be reached.
|
||||
// return the *driverConn to the freeConn list if err == nil and the idle
|
||||
// connection limit will not be exceeded.
|
||||
// If err != nil, the value of dc is ignored.
|
||||
// If err == nil, then dc must not equal nil.
|
||||
// If a connRequest was fullfilled or the *driverConn was placed in the
|
||||
|
@ -791,20 +791,24 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
|
|||
req <- dc
|
||||
}
|
||||
return true
|
||||
} else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() {
|
||||
} else if err == nil && !db.closed && db.maxIdleConnsLocked() > db.freeConn.Len() {
|
||||
dc.listElem = db.freeConn.PushFront(dc)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// maxBadConnRetries is the number of maximum retries if the driver returns
|
||||
// driver.ErrBadConn to signal a broken connection.
|
||||
const maxBadConnRetries = 10
|
||||
|
||||
// Prepare creates a prepared statement for later queries or executions.
|
||||
// Multiple queries or executions may be run concurrently from the
|
||||
// returned statement.
|
||||
func (db *DB) Prepare(query string) (*Stmt, error) {
|
||||
var stmt *Stmt
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < maxBadConnRetries; i++ {
|
||||
stmt, err = db.prepare(query)
|
||||
if err != driver.ErrBadConn {
|
||||
break
|
||||
|
@ -846,7 +850,7 @@ func (db *DB) prepare(query string) (*Stmt, error) {
|
|||
func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
|
||||
var res Result
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < maxBadConnRetries; i++ {
|
||||
res, err = db.exec(query, args)
|
||||
if err != driver.ErrBadConn {
|
||||
break
|
||||
|
@ -895,7 +899,7 @@ func (db *DB) exec(query string, args []interface{}) (res Result, err error) {
|
|||
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
|
||||
var rows *Rows
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < maxBadConnRetries; i++ {
|
||||
rows, err = db.query(query, args)
|
||||
if err != driver.ErrBadConn {
|
||||
break
|
||||
|
@ -983,7 +987,7 @@ func (db *DB) QueryRow(query string, args ...interface{}) *Row {
|
|||
func (db *DB) Begin() (*Tx, error) {
|
||||
var tx *Tx
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < maxBadConnRetries; i++ {
|
||||
tx, err = db.begin()
|
||||
if err != driver.ErrBadConn {
|
||||
break
|
||||
|
@ -1245,13 +1249,24 @@ type Stmt struct {
|
|||
func (s *Stmt) Exec(args ...interface{}) (Result, error) {
|
||||
s.closemu.RLock()
|
||||
defer s.closemu.RUnlock()
|
||||
dc, releaseConn, si, err := s.connStmt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer releaseConn(nil)
|
||||
|
||||
return resultFromStatement(driverStmt{dc, si}, args...)
|
||||
var res Result
|
||||
for i := 0; i < maxBadConnRetries; i++ {
|
||||
dc, releaseConn, si, err := s.connStmt()
|
||||
if err != nil {
|
||||
if err == driver.ErrBadConn {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err = resultFromStatement(driverStmt{dc, si}, args...)
|
||||
releaseConn(err)
|
||||
if err != driver.ErrBadConn {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) {
|
||||
|
@ -1329,26 +1344,21 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
|
|||
// Make a new conn if all are busy.
|
||||
// TODO(bradfitz): or wait for one? make configurable later?
|
||||
if !match {
|
||||
for i := 0; ; i++ {
|
||||
dc, err := s.db.conn()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
dc.Lock()
|
||||
si, err := dc.prepareLocked(s.query)
|
||||
dc.Unlock()
|
||||
if err == driver.ErrBadConn && i < 10 {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.mu.Lock()
|
||||
cs = connStmt{dc, si}
|
||||
s.css = append(s.css, cs)
|
||||
s.mu.Unlock()
|
||||
break
|
||||
dc, err := s.db.conn()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
dc.Lock()
|
||||
si, err := dc.prepareLocked(s.query)
|
||||
dc.Unlock()
|
||||
if err != nil {
|
||||
s.db.putConn(dc, err)
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
s.mu.Lock()
|
||||
cs = connStmt{dc, si}
|
||||
s.css = append(s.css, cs)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
conn := cs.dc
|
||||
|
@ -1361,31 +1371,39 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
|
|||
s.closemu.RLock()
|
||||
defer s.closemu.RUnlock()
|
||||
|
||||
dc, releaseConn, si, err := s.connStmt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var rowsi driver.Rows
|
||||
for i := 0; i < maxBadConnRetries; i++ {
|
||||
dc, releaseConn, si, err := s.connStmt()
|
||||
if err != nil {
|
||||
if err == driver.ErrBadConn {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ds := driverStmt{dc, si}
|
||||
rowsi, err := rowsiFromStatement(ds, args...)
|
||||
if err != nil {
|
||||
releaseConn(err)
|
||||
return nil, err
|
||||
}
|
||||
rowsi, err = rowsiFromStatement(driverStmt{dc, si}, args...)
|
||||
if err == nil {
|
||||
// Note: ownership of ci passes to the *Rows, to be freed
|
||||
// with releaseConn.
|
||||
rows := &Rows{
|
||||
dc: dc,
|
||||
rowsi: rowsi,
|
||||
// releaseConn set below
|
||||
}
|
||||
s.db.addDep(s, rows)
|
||||
rows.releaseConn = func(err error) {
|
||||
releaseConn(err)
|
||||
s.db.removeDep(s, rows)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// Note: ownership of ci passes to the *Rows, to be freed
|
||||
// with releaseConn.
|
||||
rows := &Rows{
|
||||
dc: dc,
|
||||
rowsi: rowsi,
|
||||
// releaseConn set below
|
||||
}
|
||||
s.db.addDep(s, rows)
|
||||
rows.releaseConn = func(err error) {
|
||||
releaseConn(err)
|
||||
s.db.removeDep(s, rows)
|
||||
if err != driver.ErrBadConn {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return rows, nil
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
func rowsiFromStatement(ds driverStmt, args ...interface{}) (driver.Rows, error) {
|
||||
|
@ -1495,10 +1513,12 @@ type Rows struct {
|
|||
closeStmt driver.Stmt // if non-nil, statement to Close on close
|
||||
}
|
||||
|
||||
// Next prepares the next result row for reading with the Scan method.
|
||||
// It returns true on success, false if there is no next result row.
|
||||
// Every call to Scan, even the first one, must be preceded by a call
|
||||
// to Next.
|
||||
// Next prepares the next result row for reading with the Scan method. It
|
||||
// returns true on success, or false if there is no next result row or an error
|
||||
// happened while preparing it. Err should be consulted to distinguish between
|
||||
// the two cases.
|
||||
//
|
||||
// Every call to Scan, even the first one, must be preceded by a call to Next.
|
||||
func (rs *Rows) Next() bool {
|
||||
if rs.closed {
|
||||
return false
|
||||
|
@ -1625,12 +1645,19 @@ func (r *Row) Scan(dest ...interface{}) error {
|
|||
}
|
||||
|
||||
if !r.rows.Next() {
|
||||
if err := r.rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrNoRows
|
||||
}
|
||||
err := r.rows.Scan(dest...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Make sure the query can be processed to completion with no errors.
|
||||
if err := r.rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -348,7 +348,6 @@ func TestStatementQueryRow(t *testing.T) {
|
|||
t.Errorf("%d: age=%d, want %d", n, age, tt.want)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// golang.org/issue/3734
|
||||
|
@ -660,6 +659,35 @@ func TestQueryRowClosingStmt(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test issue 6651
|
||||
func TestIssue6651(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
var v string
|
||||
|
||||
want := "error in rows.Next"
|
||||
rowsCursorNextHook = func(dest []driver.Value) error {
|
||||
return fmt.Errorf(want)
|
||||
}
|
||||
defer func() { rowsCursorNextHook = nil }()
|
||||
err := db.QueryRow("SELECT|people|name|").Scan(&v)
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error = %q; want %q", err, want)
|
||||
}
|
||||
rowsCursorNextHook = nil
|
||||
|
||||
want = "error in rows.Close"
|
||||
rowsCloseHook = func(rows *Rows, err *error) {
|
||||
*err = fmt.Errorf(want)
|
||||
}
|
||||
defer func() { rowsCloseHook = nil }()
|
||||
err = db.QueryRow("SELECT|people|name|").Scan(&v)
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error = %q; want %q", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
type nullTestRow struct {
|
||||
nullParam interface{}
|
||||
notNullParam interface{}
|
||||
|
@ -1249,6 +1277,111 @@ func TestStmtCloseOrder(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// golang.org/issue/5781
|
||||
func TestErrBadConnReconnect(t *testing.T) {
|
||||
db := newTestDB(t, "foo")
|
||||
defer closeDB(t, db)
|
||||
exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
|
||||
|
||||
simulateBadConn := func(name string, hook *func() bool, op func() error) {
|
||||
broken, retried := false, false
|
||||
numOpen := db.numOpen
|
||||
|
||||
// simulate a broken connection on the first try
|
||||
*hook = func() bool {
|
||||
if !broken {
|
||||
broken = true
|
||||
return true
|
||||
}
|
||||
retried = true
|
||||
return false
|
||||
}
|
||||
|
||||
if err := op(); err != nil {
|
||||
t.Errorf(name+": %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !broken || !retried {
|
||||
t.Error(name + ": Failed to simulate broken connection")
|
||||
}
|
||||
*hook = nil
|
||||
|
||||
if numOpen != db.numOpen {
|
||||
t.Errorf(name+": leaked %d connection(s)!", db.numOpen-numOpen)
|
||||
numOpen = db.numOpen
|
||||
}
|
||||
}
|
||||
|
||||
// db.Exec
|
||||
dbExec := func() error {
|
||||
_, err := db.Exec("INSERT|t1|name=?,age=?,dead=?", "Gordon", 3, true)
|
||||
return err
|
||||
}
|
||||
simulateBadConn("db.Exec prepare", &hookPrepareBadConn, dbExec)
|
||||
simulateBadConn("db.Exec exec", &hookExecBadConn, dbExec)
|
||||
|
||||
// db.Query
|
||||
dbQuery := func() error {
|
||||
rows, err := db.Query("SELECT|t1|age,name|")
|
||||
if err == nil {
|
||||
err = rows.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
simulateBadConn("db.Query prepare", &hookPrepareBadConn, dbQuery)
|
||||
simulateBadConn("db.Query query", &hookQueryBadConn, dbQuery)
|
||||
|
||||
// db.Prepare
|
||||
simulateBadConn("db.Prepare", &hookPrepareBadConn, func() error {
|
||||
stmt, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stmt.Close()
|
||||
return nil
|
||||
})
|
||||
|
||||
// stmt.Exec
|
||||
stmt1, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?")
|
||||
if err != nil {
|
||||
t.Fatalf("prepare: %v", err)
|
||||
}
|
||||
defer stmt1.Close()
|
||||
// make sure we must prepare the stmt first
|
||||
for _, cs := range stmt1.css {
|
||||
cs.dc.inUse = true
|
||||
}
|
||||
|
||||
stmtExec := func() error {
|
||||
_, err := stmt1.Exec("Gopher", 3, false)
|
||||
return err
|
||||
}
|
||||
simulateBadConn("stmt.Exec prepare", &hookPrepareBadConn, stmtExec)
|
||||
simulateBadConn("stmt.Exec exec", &hookExecBadConn, stmtExec)
|
||||
|
||||
// stmt.Query
|
||||
stmt2, err := db.Prepare("SELECT|t1|age,name|")
|
||||
if err != nil {
|
||||
t.Fatalf("prepare: %v", err)
|
||||
}
|
||||
defer stmt2.Close()
|
||||
// make sure we must prepare the stmt first
|
||||
for _, cs := range stmt2.css {
|
||||
cs.dc.inUse = true
|
||||
}
|
||||
|
||||
stmtQuery := func() error {
|
||||
rows, err := stmt2.Query()
|
||||
if err == nil {
|
||||
err = rows.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
simulateBadConn("stmt.Query prepare", &hookPrepareBadConn, stmtQuery)
|
||||
simulateBadConn("stmt.Query exec", &hookQueryBadConn, stmtQuery)
|
||||
}
|
||||
|
||||
type concurrentTest interface {
|
||||
init(t testing.TB, db *DB)
|
||||
finish(t testing.TB)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue