diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-25 21:54:22 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-25 21:54:22 +0000 |
commit | af92e385667da3fc91ac7f9f0867a56c111110b8 (patch) | |
tree | c8e8990a2197e33f6fe50a28a16714aafe982102 /libgo/go/exp | |
parent | df1304ee03f41aed179545d1e8b4684cfd22bbdf (diff) | |
download | gcc-af92e385667da3fc91ac7f9f0867a56c111110b8.zip gcc-af92e385667da3fc91ac7f9f0867a56c111110b8.tar.gz gcc-af92e385667da3fc91ac7f9f0867a56c111110b8.tar.bz2 |
libgo: Update to weekly.2012-01-20.
From-SVN: r183540
Diffstat (limited to 'libgo/go/exp')
-rw-r--r-- | libgo/go/exp/gotype/testdata/test1.go | 4 | ||||
-rw-r--r-- | libgo/go/exp/inotify/inotify_linux_test.go | 9 | ||||
-rw-r--r-- | libgo/go/exp/norm/trie_test.go | 4 | ||||
-rw-r--r-- | libgo/go/exp/proxy/proxy.go | 12 | ||||
-rw-r--r-- | libgo/go/exp/sql/convert.go | 133 | ||||
-rw-r--r-- | libgo/go/exp/sql/convert_test.go | 173 | ||||
-rw-r--r-- | libgo/go/exp/sql/driver/driver.go | 207 | ||||
-rw-r--r-- | libgo/go/exp/sql/driver/types.go | 219 | ||||
-rw-r--r-- | libgo/go/exp/sql/driver/types_test.go | 61 | ||||
-rw-r--r-- | libgo/go/exp/sql/fakedb_test.go | 597 | ||||
-rw-r--r-- | libgo/go/exp/sql/sql.go | 860 | ||||
-rw-r--r-- | libgo/go/exp/sql/sql_test.go | 305 | ||||
-rw-r--r-- | libgo/go/exp/ssh/client.go | 5 | ||||
-rw-r--r-- | libgo/go/exp/ssh/messages.go | 20 | ||||
-rw-r--r-- | libgo/go/exp/ssh/session.go | 8 | ||||
-rw-r--r-- | libgo/go/exp/ssh/tcpip.go | 31 | ||||
-rw-r--r-- | libgo/go/exp/ssh/transport.go | 36 | ||||
-rw-r--r-- | libgo/go/exp/ssh/transport_test.go | 18 | ||||
-rw-r--r-- | libgo/go/exp/types/check_test.go | 18 | ||||
-rw-r--r-- | libgo/go/exp/types/gcimporter.go | 31 | ||||
-rw-r--r-- | libgo/go/exp/utf8string/string.go | 203 | ||||
-rw-r--r-- | libgo/go/exp/utf8string/string_test.go | 123 |
22 files changed, 441 insertions, 2636 deletions
diff --git a/libgo/go/exp/gotype/testdata/test1.go b/libgo/go/exp/gotype/testdata/test1.go index a3298e6..ba8a51f 100644 --- a/libgo/go/exp/gotype/testdata/test1.go +++ b/libgo/go/exp/gotype/testdata/test1.go @@ -1,3 +1,7 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package p func _() { diff --git a/libgo/go/exp/inotify/inotify_linux_test.go b/libgo/go/exp/inotify/inotify_linux_test.go index d035ec1..c2160fc 100644 --- a/libgo/go/exp/inotify/inotify_linux_test.go +++ b/libgo/go/exp/inotify/inotify_linux_test.go @@ -83,14 +83,15 @@ func TestInotifyClose(t *testing.T) { watcher, _ := NewWatcher() watcher.Close() - done := false + done := make(chan bool) go func() { watcher.Close() - done = true + done <- true }() - time.Sleep(50 * time.Millisecond) - if !done { + select { + case <-done: + case <-time.After(50 * time.Millisecond): t.Fatal("double Close() test failed: second Close() call didn't return") } diff --git a/libgo/go/exp/norm/trie_test.go b/libgo/go/exp/norm/trie_test.go index 7308d28..c457c9d 100644 --- a/libgo/go/exp/norm/trie_test.go +++ b/libgo/go/exp/norm/trie_test.go @@ -1,3 +1,7 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package norm import ( diff --git a/libgo/go/exp/proxy/proxy.go b/libgo/go/exp/proxy/proxy.go index ccd3d1d..b6cfd45 100644 --- a/libgo/go/exp/proxy/proxy.go +++ b/libgo/go/exp/proxy/proxy.go @@ -11,7 +11,6 @@ import ( "net" "net/url" "os" - "strings" ) // A Dialer is a means to establish a connection. @@ -70,14 +69,11 @@ func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) // Dialer for it to make network requests. func FromURL(u *url.URL, forward Dialer) (Dialer, error) { var auth *Auth - if len(u.RawUserinfo) > 0 { + if u.User != nil { auth = new(Auth) - parts := strings.SplitN(u.RawUserinfo, ":", 1) - if len(parts) == 1 { - auth.User = parts[0] - } else if len(parts) >= 2 { - auth.User = parts[0] - auth.Password = parts[1] + auth.User = u.User.Username() + if p, ok := u.User.Password(); ok { + auth.Password = p } } diff --git a/libgo/go/exp/sql/convert.go b/libgo/go/exp/sql/convert.go deleted file mode 100644 index feb79ae..0000000 --- a/libgo/go/exp/sql/convert.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Type conversions for Scan. - -package sql - -import ( - "errors" - "exp/sql/driver" - "fmt" - "reflect" - "strconv" -) - -// subsetTypeArgs takes a slice of arguments from callers of the sql -// package and converts them into a slice of the driver package's -// "subset types". -func subsetTypeArgs(args []interface{}) ([]interface{}, error) { - out := make([]interface{}, len(args)) - for n, arg := range args { - var err error - out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) - if err != nil { - return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) - } - } - return out, nil -} - -// convertAssign copies to dest the value in src, converting it if possible. -// An error is returned if the copy would result in loss of information. -// dest should be a pointer type. -func convertAssign(dest, src interface{}) error { - // Common cases, without reflect. Fall through. - switch s := src.(type) { - case string: - switch d := dest.(type) { - case *string: - *d = s - return nil - } - case []byte: - switch d := dest.(type) { - case *string: - *d = string(s) - return nil - case *[]byte: - *d = s - return nil - } - } - - var sv reflect.Value - - switch d := dest.(type) { - case *string: - sv = reflect.ValueOf(src) - switch sv.Kind() { - case reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64: - *d = fmt.Sprintf("%v", src) - return nil - } - case *bool: - bv, err := driver.Bool.ConvertValue(src) - if err == nil { - *d = bv.(bool) - } - return err - } - - if scanner, ok := dest.(ScannerInto); ok { - return scanner.ScanInto(src) - } - - dpv := reflect.ValueOf(dest) - if dpv.Kind() != reflect.Ptr { - return errors.New("destination not a pointer") - } - - if !sv.IsValid() { - sv = reflect.ValueOf(src) - } - - dv := reflect.Indirect(dpv) - if dv.Kind() == sv.Kind() { - dv.Set(sv) - return nil - } - - switch dv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - s := asString(src) - i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) - if err != nil { - return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) - } - dv.SetInt(i64) - return nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - s := asString(src) - u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) - if err != nil { - return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) - } - dv.SetUint(u64) - return nil - case reflect.Float32, reflect.Float64: - s := asString(src) - f64, err := strconv.ParseFloat(s, dv.Type().Bits()) - if err != nil { - return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) - } - dv.SetFloat(f64) - return nil - } - - return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) -} - -func asString(src interface{}) string { - switch v := src.(type) { - case string: - return v - case []byte: - return string(v) - } - return fmt.Sprintf("%v", src) -} diff --git a/libgo/go/exp/sql/convert_test.go b/libgo/go/exp/sql/convert_test.go deleted file mode 100644 index 702ba43..0000000 --- a/libgo/go/exp/sql/convert_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sql - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -var someTime = time.Unix(123, 0) - -type conversionTest struct { - s, d interface{} // source and destination - - // following are used if they're non-zero - wantint int64 - wantuint uint64 - wantstr string - wantf32 float32 - wantf64 float64 - wanttime time.Time - wantbool bool // used if d is of type *bool - wanterr string -} - -// Target variables for scanning into. -var ( - scanstr string - scanint int - scanint8 int8 - scanint16 int16 - scanint32 int32 - scanuint8 uint8 - scanuint16 uint16 - scanbool bool - scanf32 float32 - scanf64 float64 - scantime time.Time -) - -var conversionTests = []conversionTest{ - // Exact conversions (destination pointer type matches source type) - {s: "foo", d: &scanstr, wantstr: "foo"}, - {s: 123, d: &scanint, wantint: 123}, - {s: someTime, d: &scantime, wanttime: someTime}, - - // To strings - {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, - {s: 123, d: &scanstr, wantstr: "123"}, - {s: int8(123), d: &scanstr, wantstr: "123"}, - {s: int64(123), d: &scanstr, wantstr: "123"}, - {s: uint8(123), d: &scanstr, wantstr: "123"}, - {s: uint16(123), d: &scanstr, wantstr: "123"}, - {s: uint32(123), d: &scanstr, wantstr: "123"}, - {s: uint64(123), d: &scanstr, wantstr: "123"}, - {s: 1.5, d: &scanstr, wantstr: "1.5"}, - - // Strings to integers - {s: "255", d: &scanuint8, wantuint: 255}, - {s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`}, - {s: "256", d: &scanuint16, wantuint: 256}, - {s: "-1", d: &scanint, wantint: -1}, - {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`}, - - // True bools - {s: true, d: &scanbool, wantbool: true}, - {s: "True", d: &scanbool, wantbool: true}, - {s: "TRUE", d: &scanbool, wantbool: true}, - {s: "1", d: &scanbool, wantbool: true}, - {s: 1, d: &scanbool, wantbool: true}, - {s: int64(1), d: &scanbool, wantbool: true}, - {s: uint16(1), d: &scanbool, wantbool: true}, - - // False bools - {s: false, d: &scanbool, wantbool: false}, - {s: "false", d: &scanbool, wantbool: false}, - {s: "FALSE", d: &scanbool, wantbool: false}, - {s: "0", d: &scanbool, wantbool: false}, - {s: 0, d: &scanbool, wantbool: false}, - {s: int64(0), d: &scanbool, wantbool: false}, - {s: uint16(0), d: &scanbool, wantbool: false}, - - // Not bools - {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`}, - {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`}, - - // Floats - {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)}, - {s: int64(1), d: &scanf64, wantf64: float64(1)}, - {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, - {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, - {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, -} - -func intValue(intptr interface{}) int64 { - return reflect.Indirect(reflect.ValueOf(intptr)).Int() -} - -func uintValue(intptr interface{}) uint64 { - return reflect.Indirect(reflect.ValueOf(intptr)).Uint() -} - -func float64Value(ptr interface{}) float64 { - return *(ptr.(*float64)) -} - -func float32Value(ptr interface{}) float32 { - return *(ptr.(*float32)) -} - -func timeValue(ptr interface{}) time.Time { - return *(ptr.(*time.Time)) -} - -func TestConversions(t *testing.T) { - for n, ct := range conversionTests { - err := convertAssign(ct.d, ct.s) - errstr := "" - if err != nil { - errstr = err.Error() - } - errf := func(format string, args ...interface{}) { - base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) - t.Errorf(base+format, args...) - } - if errstr != ct.wanterr { - errf("got error %q, want error %q", errstr, ct.wanterr) - } - if ct.wantstr != "" && ct.wantstr != scanstr { - errf("want string %q, got %q", ct.wantstr, scanstr) - } - if ct.wantint != 0 && ct.wantint != intValue(ct.d) { - errf("want int %d, got %d", ct.wantint, intValue(ct.d)) - } - if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { - errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) - } - if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) { - errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d)) - } - if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) { - errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d)) - } - if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { - errf("want bool %v, got %v", ct.wantbool, *bp) - } - if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) { - errf("want time %v, got %v", ct.wanttime, timeValue(ct.d)) - } - } -} - -func TestNullableString(t *testing.T) { - var ns NullableString - convertAssign(&ns, []byte("foo")) - if !ns.Valid { - t.Errorf("expecting not null") - } - if ns.String != "foo" { - t.Errorf("expecting foo; got %q", ns.String) - } - convertAssign(&ns, nil) - if ns.Valid { - t.Errorf("expecting null on nil") - } - if ns.String != "" { - t.Errorf("expecting blank on nil; got %q", ns.String) - } -} diff --git a/libgo/go/exp/sql/driver/driver.go b/libgo/go/exp/sql/driver/driver.go deleted file mode 100644 index 0cd2562..0000000 --- a/libgo/go/exp/sql/driver/driver.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package driver defines interfaces to be implemented by database -// drivers as used by package sql. -// -// Code simply using databases should use package sql. -// -// Drivers only need to be aware of a subset of Go's types. The sql package -// will convert all types into one of the following: -// -// int64 -// float64 -// bool -// nil -// []byte -// string [*] everywhere except from Rows.Next. -// time.Time -// -package driver - -import "errors" - -// Driver is the interface that must be implemented by a database -// driver. -type Driver interface { - // Open returns a new connection to the database. - // The name is a string in a driver-specific format. - // - // Open may return a cached connection (one previously - // closed), but doing so is unnecessary; the sql package - // maintains a pool of idle connections for efficient re-use. - // - // The returned connection is only used by one goroutine at a - // time. - Open(name string) (Conn, error) -} - -// ErrSkip may be returned by some optional interfaces' methods to -// indicate at runtime that the fast path is unavailable and the sql -// package should continue as if the optional interface was not -// implemented. ErrSkip is only supported where explicitly -// documented. -var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") - -// Execer is an optional interface that may be implemented by a Conn. -// -// If a Conn does not implement Execer, the db package's DB.Exec will -// first prepare a query, execute the statement, and then close the -// statement. -// -// All arguments are of a subset type as defined in the package docs. -// -// Exec may return ErrSkip. -type Execer interface { - Exec(query string, args []interface{}) (Result, error) -} - -// Conn is a connection to a database. It is not used concurrently -// by multiple goroutines. -// -// Conn is assumed to be stateful. -type Conn interface { - // Prepare returns a prepared statement, bound to this connection. - Prepare(query string) (Stmt, error) - - // Close invalidates and potentially stops any current - // prepared statements and transactions, marking this - // connection as no longer in use. - // - // Because the sql package maintains a free pool of - // connections and only calls Close when there's a surplus of - // idle connections, it shouldn't be necessary for drivers to - // do their own connection caching. - Close() error - - // Begin starts and returns a new transaction. - Begin() (Tx, error) -} - -// Result is the result of a query execution. -type Result interface { - // LastInsertId returns the database's auto-generated ID - // after, for example, an INSERT into a table with primary - // key. - LastInsertId() (int64, error) - - // RowsAffected returns the number of rows affected by the - // query. - RowsAffected() (int64, error) -} - -// Stmt is a prepared statement. It is bound to a Conn and not -// used by multiple goroutines concurrently. -type Stmt interface { - // Close closes the statement. - // - // Closing a statement should not interrupt any outstanding - // query created from that statement. That is, the following - // order of operations is valid: - // - // * create a driver statement - // * call Query on statement, returning Rows - // * close the statement - // * read from Rows - // - // If closing a statement invalidates currently-running - // queries, the final step above will incorrectly fail. - // - // TODO(bradfitz): possibly remove the restriction above, if - // enough driver authors object and find it complicates their - // code too much. The sql package could be smarter about - // refcounting the statement and closing it at the appropriate - // time. - Close() error - - // NumInput returns the number of placeholder parameters. - // - // If NumInput returns >= 0, the sql package will sanity check - // argument counts from callers and return errors to the caller - // before the statement's Exec or Query methods are called. - // - // NumInput may also return -1, if the driver doesn't know - // its number of placeholders. In that case, the sql package - // will not sanity check Exec or Query argument counts. - NumInput() int - - // Exec executes a query that doesn't return rows, such - // as an INSERT or UPDATE. The args are all of a subset - // type as defined above. - Exec(args []interface{}) (Result, error) - - // Exec executes a query that may return rows, such as a - // SELECT. The args of all of a subset type as defined above. - Query(args []interface{}) (Rows, error) -} - -// ColumnConverter may be optionally implemented by Stmt if the -// the statement is aware of its own columns' types and can -// convert from any type to a driver subset type. -type ColumnConverter interface { - // ColumnConverter returns a ValueConverter for the provided - // column index. If the type of a specific column isn't known - // or shouldn't be handled specially, DefaultValueConverter - // can be returned. - ColumnConverter(idx int) ValueConverter -} - -// Rows is an iterator over an executed query's results. -type Rows interface { - // Columns returns the names of the columns. The number of - // columns of the result is inferred from the length of the - // slice. If a particular column name isn't known, an empty - // string should be returned for that entry. - Columns() []string - - // Close closes the rows iterator. - Close() error - - // Next is called to populate the next row of data into - // the provided slice. The provided slice will be the same - // size as the Columns() are wide. - // - // The dest slice may be populated with only with values - // of subset types defined above, but excluding string. - // All string values must be converted to []byte. - // - // Next should return io.EOF when there are no more rows. - Next(dest []interface{}) error -} - -// Tx is a transaction. -type Tx interface { - Commit() error - Rollback() error -} - -// RowsAffected implements Result for an INSERT or UPDATE operation -// which mutates a number of rows. -type RowsAffected int64 - -var _ Result = RowsAffected(0) - -func (RowsAffected) LastInsertId() (int64, error) { - return 0, errors.New("no LastInsertId available") -} - -func (v RowsAffected) RowsAffected() (int64, error) { - return int64(v), nil -} - -// DDLSuccess is a pre-defined Result for drivers to return when a DDL -// command succeeds. -var DDLSuccess ddlSuccess - -type ddlSuccess struct{} - -var _ Result = ddlSuccess{} - -func (ddlSuccess) LastInsertId() (int64, error) { - return 0, errors.New("no LastInsertId available after DDL statement") -} - -func (ddlSuccess) RowsAffected() (int64, error) { - return 0, errors.New("no RowsAffected available after DDL statement") -} diff --git a/libgo/go/exp/sql/driver/types.go b/libgo/go/exp/sql/driver/types.go deleted file mode 100644 index d6ba641..0000000 --- a/libgo/go/exp/sql/driver/types.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package driver - -import ( - "fmt" - "reflect" - "strconv" - "time" -) - -// ValueConverter is the interface providing the ConvertValue method. -// -// Various implementations of ValueConverter are provided by the -// driver package to provide consistent implementations of conversions -// between drivers. The ValueConverters have several uses: -// -// * converting from the subset types as provided by the sql package -// into a database table's specific column type and making sure it -// fits, such as making sure a particular int64 fits in a -// table's uint16 column. -// -// * converting a value as given from the database into one of the -// subset types. -// -// * by the sql package, for converting from a driver's subset type -// to a user's type in a scan. -type ValueConverter interface { - // ConvertValue converts a value to a restricted subset type. - ConvertValue(v interface{}) (interface{}, error) -} - -// Bool is a ValueConverter that converts input values to bools. -// -// The conversion rules are: -// - booleans are returned unchanged -// - for integer types, -// 1 is true -// 0 is false, -// other integers are an error -// - for strings and []byte, same rules as strconv.ParseBool -// - all other types are an error -var Bool boolType - -type boolType struct{} - -var _ ValueConverter = boolType{} - -func (boolType) String() string { return "Bool" } - -func (boolType) ConvertValue(src interface{}) (interface{}, error) { - switch s := src.(type) { - case bool: - return s, nil - case string: - b, err := strconv.ParseBool(s) - if err != nil { - return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) - } - return b, nil - case []byte: - b, err := strconv.ParseBool(string(s)) - if err != nil { - return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) - } - return b, nil - } - - sv := reflect.ValueOf(src) - switch sv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - iv := sv.Int() - if iv == 1 || iv == 0 { - return iv == 1, nil - } - return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - uv := sv.Uint() - if uv == 1 || uv == 0 { - return uv == 1, nil - } - return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv) - } - - return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src) -} - -// Int32 is a ValueConverter that converts input values to int64, -// respecting the limits of an int32 value. -var Int32 int32Type - -type int32Type struct{} - -var _ ValueConverter = int32Type{} - -func (int32Type) ConvertValue(v interface{}) (interface{}, error) { - rv := reflect.ValueOf(v) - switch rv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - i64 := rv.Int() - if i64 > (1<<31)-1 || i64 < -(1<<31) { - return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) - } - return i64, nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - u64 := rv.Uint() - if u64 > (1<<31)-1 { - return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) - } - return int64(u64), nil - case reflect.String: - i, err := strconv.Atoi(rv.String()) - if err != nil { - return nil, fmt.Errorf("sql/driver: value %q can't be converted to int32", v) - } - return int64(i), nil - } - return nil, fmt.Errorf("sql/driver: unsupported value %v (type %T) converting to int32", v, v) -} - -// String is a ValueConverter that converts its input to a string. -// If the value is already a string or []byte, it's unchanged. -// If the value is of another type, conversion to string is done -// with fmt.Sprintf("%v", v). -var String stringType - -type stringType struct{} - -func (stringType) ConvertValue(v interface{}) (interface{}, error) { - switch v.(type) { - case string, []byte: - return v, nil - } - return fmt.Sprintf("%v", v), nil -} - -// IsParameterSubsetType reports whether v is of a valid type for a -// parameter. These types are: -// -// int64 -// float64 -// bool -// nil -// []byte -// time.Time -// string -// -// This is the same list as IsScanSubsetType, with the addition of -// string. -func IsParameterSubsetType(v interface{}) bool { - if IsScanSubsetType(v) { - return true - } - if _, ok := v.(string); ok { - return true - } - return false -} - -// IsScanSubsetType reports whether v is of a valid type for a -// value populated by Rows.Next. These types are: -// -// int64 -// float64 -// bool -// nil -// []byte -// time.Time -// -// This is the same list as IsParameterSubsetType, without string. -func IsScanSubsetType(v interface{}) bool { - if v == nil { - return true - } - switch v.(type) { - case int64, float64, []byte, bool, time.Time: - return true - } - return false -} - -// DefaultParameterConverter is the default implementation of -// ValueConverter that's used when a Stmt doesn't implement -// ColumnConverter. -// -// DefaultParameterConverter returns the given value directly if -// IsSubsetType(value). Otherwise integer type are converted to -// int64, floats to float64, and strings to []byte. Other types are -// an error. -var DefaultParameterConverter defaultConverter - -type defaultConverter struct{} - -var _ ValueConverter = defaultConverter{} - -func (defaultConverter) ConvertValue(v interface{}) (interface{}, error) { - if IsParameterSubsetType(v) { - return v, nil - } - - rv := reflect.ValueOf(v) - switch rv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return rv.Int(), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: - return int64(rv.Uint()), nil - case reflect.Uint64: - u64 := rv.Uint() - if u64 >= 1<<63 { - return nil, fmt.Errorf("uint64 values with high bit set are not supported") - } - return int64(u64), nil - case reflect.Float32, reflect.Float64: - return rv.Float(), nil - } - return nil, fmt.Errorf("unsupported type %s", rv.Kind()) -} diff --git a/libgo/go/exp/sql/driver/types_test.go b/libgo/go/exp/sql/driver/types_test.go deleted file mode 100644 index 966bc6b..0000000 --- a/libgo/go/exp/sql/driver/types_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package driver - -import ( - "reflect" - "testing" - "time" -) - -type valueConverterTest struct { - c ValueConverter - in interface{} - out interface{} - err string -} - -var now = time.Now() - -var valueConverterTests = []valueConverterTest{ - {Bool, "true", true, ""}, - {Bool, "True", true, ""}, - {Bool, []byte("t"), true, ""}, - {Bool, true, true, ""}, - {Bool, "1", true, ""}, - {Bool, 1, true, ""}, - {Bool, int64(1), true, ""}, - {Bool, uint16(1), true, ""}, - {Bool, "false", false, ""}, - {Bool, false, false, ""}, - {Bool, "0", false, ""}, - {Bool, 0, false, ""}, - {Bool, int64(0), false, ""}, - {Bool, uint16(0), false, ""}, - {c: Bool, in: "foo", err: "sql/driver: couldn't convert \"foo\" into type bool"}, - {c: Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"}, - {DefaultParameterConverter, now, now, ""}, -} - -func TestValueConverters(t *testing.T) { - for i, tt := range valueConverterTests { - out, err := tt.c.ConvertValue(tt.in) - goterr := "" - if err != nil { - goterr = err.Error() - } - if goterr != tt.err { - t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", - i, tt.c, tt.in, tt.in, goterr, tt.err) - } - if tt.err != "" { - continue - } - if !reflect.DeepEqual(out, tt.out) { - t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", - i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) - } - } -} diff --git a/libgo/go/exp/sql/fakedb_test.go b/libgo/go/exp/sql/fakedb_test.go deleted file mode 100644 index 70aa68c..0000000 --- a/libgo/go/exp/sql/fakedb_test.go +++ /dev/null @@ -1,597 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sql - -import ( - "errors" - "fmt" - "io" - "log" - "strconv" - "strings" - "sync" - "time" - - "exp/sql/driver" -) - -var _ = log.Printf - -// fakeDriver is a fake database that implements Go's driver.Driver -// 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 -// follows: -// -// WIPE -// CREATE|<tablename>|<col>=<type>,<col>=<type>,... -// where types are: "string", [u]int{8,16,32,64}, "bool" -// INSERT|<tablename>|col=val,col2=val2,col3=? -// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=? -// -// When opening a a fakeDriver's database, it starts empty with no -// tables. All tables and data are stored in memory only. -type fakeDriver struct { - mu sync.Mutex - openCount int - dbs map[string]*fakeDB -} - -type fakeDB struct { - name string - - mu sync.Mutex - free []*fakeConn - tables map[string]*table -} - -type table struct { - mu sync.Mutex - colname []string - coltype []string - rows []*row -} - -func (t *table) columnIndex(name string) int { - for n, nname := range t.colname { - if name == nname { - return n - } - } - return -1 -} - -type row struct { - cols []interface{} // must be same size as its table colname + coltype -} - -func (r *row) clone() *row { - nrow := &row{cols: make([]interface{}, len(r.cols))} - copy(nrow.cols, r.cols) - return nrow -} - -type fakeConn struct { - db *fakeDB // where to return ourselves to - - currTx *fakeTx - - // Stats for tests: - mu sync.Mutex - stmtsMade int - stmtsClosed int -} - -func (c *fakeConn) incrStat(v *int) { - c.mu.Lock() - *v++ - c.mu.Unlock() -} - -type fakeTx struct { - c *fakeConn -} - -type fakeStmt struct { - c *fakeConn - q string // just for debugging - - cmd string - table string - - closed bool - - colName []string // used by CREATE, INSERT, SELECT (selected columns) - colType []string // used by CREATE - colValue []interface{} // used by INSERT (mix of strings and "?" for bound params) - placeholders int // used by INSERT/SELECT: number of ? params - - whereCol []string // used by SELECT (all placeholders) - - placeholderConverter []driver.ValueConverter // used by INSERT -} - -var fdriver driver.Driver = &fakeDriver{} - -func init() { - Register("test", fdriver) -} - -// Supports dsn forms: -// <dbname> -// <dbname>;<opts> (no currently supported options) -func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { - parts := strings.Split(dsn, ";") - if len(parts) < 1 { - return nil, errors.New("fakedb: no database name") - } - name := parts[0] - - db := d.getDB(name) - - d.mu.Lock() - d.openCount++ - d.mu.Unlock() - return &fakeConn{db: db}, nil -} - -func (d *fakeDriver) getDB(name string) *fakeDB { - d.mu.Lock() - defer d.mu.Unlock() - if d.dbs == nil { - d.dbs = make(map[string]*fakeDB) - } - db, ok := d.dbs[name] - if !ok { - db = &fakeDB{name: name} - d.dbs[name] = db - } - return db -} - -func (db *fakeDB) wipe() { - db.mu.Lock() - defer db.mu.Unlock() - db.tables = nil -} - -func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error { - db.mu.Lock() - defer db.mu.Unlock() - if db.tables == nil { - db.tables = make(map[string]*table) - } - if _, exist := db.tables[name]; exist { - return fmt.Errorf("table %q already exists", name) - } - if len(columnNames) != len(columnTypes) { - return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d", - name, len(columnNames), len(columnTypes)) - } - db.tables[name] = &table{colname: columnNames, coltype: columnTypes} - return nil -} - -// must be called with db.mu lock held -func (db *fakeDB) table(table string) (*table, bool) { - if db.tables == nil { - return nil, false - } - t, ok := db.tables[table] - return t, ok -} - -func (db *fakeDB) columnType(table, column string) (typ string, ok bool) { - db.mu.Lock() - defer db.mu.Unlock() - t, ok := db.table(table) - if !ok { - return - } - for n, cname := range t.colname { - if cname == column { - return t.coltype[n], true - } - } - return "", false -} - -func (c *fakeConn) Begin() (driver.Tx, error) { - if c.currTx != nil { - return nil, errors.New("already in a transaction") - } - c.currTx = &fakeTx{c: c} - return c.currTx, nil -} - -func (c *fakeConn) Close() error { - if c.currTx != nil { - return errors.New("can't close; in a Transaction") - } - if c.db == nil { - return errors.New("can't close; already closed") - } - c.db = nil - return nil -} - -func checkSubsetTypes(args []interface{}) error { - for n, arg := range args { - switch arg.(type) { - case int64, float64, bool, nil, []byte, string, time.Time: - default: - return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg) - } - } - return nil -} - -func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) { - // This is an optional interface, but it's implemented here - // just to check that all the args of of the proper types. - // ErrSkip is returned so the caller acts as if we didn't - // implement this at all. - err := checkSubsetTypes(args) - if err != nil { - return nil, err - } - return nil, driver.ErrSkip -} - -func errf(msg string, args ...interface{}) error { - return errors.New("fakedb: " + fmt.Sprintf(msg, args...)) -} - -// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? -// (note that where where columns must always contain ? marks, -// just a limitation for fakedb) -func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) { - if len(parts) != 3 { - return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts)) - } - stmt.table = parts[0] - stmt.colName = strings.Split(parts[1], ",") - for n, colspec := range strings.Split(parts[2], ",") { - if colspec == "" { - continue - } - nameVal := strings.Split(colspec, "=") - if len(nameVal) != 2 { - return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) - } - column, value := nameVal[0], nameVal[1] - _, ok := c.db.columnType(stmt.table, column) - if !ok { - return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column) - } - if value != "?" { - return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark", - stmt.table, column) - } - stmt.whereCol = append(stmt.whereCol, column) - stmt.placeholders++ - } - return stmt, nil -} - -// parts are table|col=type,col2=type2 -func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) { - if len(parts) != 2 { - return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts)) - } - stmt.table = parts[0] - for n, colspec := range strings.Split(parts[1], ",") { - nameType := strings.Split(colspec, "=") - if len(nameType) != 2 { - return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) - } - stmt.colName = append(stmt.colName, nameType[0]) - stmt.colType = append(stmt.colType, nameType[1]) - } - return stmt, nil -} - -// parts are table|col=?,col2=val -func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) { - if len(parts) != 2 { - return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts)) - } - stmt.table = parts[0] - for n, colspec := range strings.Split(parts[1], ",") { - nameVal := strings.Split(colspec, "=") - if len(nameVal) != 2 { - return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) - } - column, value := nameVal[0], nameVal[1] - ctype, ok := c.db.columnType(stmt.table, column) - if !ok { - return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column) - } - stmt.colName = append(stmt.colName, column) - - if value != "?" { - var subsetVal interface{} - // Convert to driver subset type - switch ctype { - case "string": - subsetVal = []byte(value) - case "blob": - subsetVal = []byte(value) - case "int32": - i, err := strconv.Atoi(value) - if err != nil { - return nil, errf("invalid conversion to int32 from %q", value) - } - subsetVal = int64(i) // int64 is a subset type, but not int32 - default: - return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype) - } - stmt.colValue = append(stmt.colValue, subsetVal) - } else { - stmt.placeholders++ - stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype)) - stmt.colValue = append(stmt.colValue, "?") - } - } - return stmt, nil -} - -func (c *fakeConn) Prepare(query string) (driver.Stmt, error) { - if c.db == nil { - panic("nil c.db; conn = " + fmt.Sprintf("%#v", c)) - } - parts := strings.Split(query, "|") - if len(parts) < 1 { - return nil, errf("empty query") - } - cmd := parts[0] - parts = parts[1:] - stmt := &fakeStmt{q: query, c: c, cmd: cmd} - c.incrStat(&c.stmtsMade) - switch cmd { - case "WIPE": - // Nothing - case "SELECT": - return c.prepareSelect(stmt, parts) - case "CREATE": - return c.prepareCreate(stmt, parts) - case "INSERT": - return c.prepareInsert(stmt, parts) - default: - return nil, errf("unsupported command type %q", cmd) - } - return stmt, nil -} - -func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { - return s.placeholderConverter[idx] -} - -func (s *fakeStmt) Close() error { - if !s.closed { - s.c.incrStat(&s.c.stmtsClosed) - s.closed = true - } - return nil -} - -var errClosed = errors.New("fakedb: statement has been closed") - -func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) { - if s.closed { - return nil, errClosed - } - err := checkSubsetTypes(args) - if err != nil { - return nil, err - } - - db := s.c.db - switch s.cmd { - case "WIPE": - db.wipe() - return driver.DDLSuccess, nil - case "CREATE": - if err := db.createTable(s.table, s.colName, s.colType); err != nil { - return nil, err - } - return driver.DDLSuccess, nil - case "INSERT": - return s.execInsert(args) - } - fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) - return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) -} - -func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) { - db := s.c.db - if len(args) != s.placeholders { - panic("error in pkg db; should only get here if size is correct") - } - db.mu.Lock() - t, ok := db.table(s.table) - db.mu.Unlock() - if !ok { - return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) - } - - t.mu.Lock() - defer t.mu.Unlock() - - cols := make([]interface{}, len(t.colname)) - argPos := 0 - for n, colname := range s.colName { - colidx := t.columnIndex(colname) - if colidx == -1 { - return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname) - } - var val interface{} - if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" { - val = args[argPos] - argPos++ - } else { - val = s.colValue[n] - } - cols[colidx] = val - } - - t.rows = append(t.rows, &row{cols: cols}) - return driver.RowsAffected(1), nil -} - -func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) { - if s.closed { - return nil, errClosed - } - err := checkSubsetTypes(args) - if err != nil { - return nil, err - } - - db := s.c.db - if len(args) != s.placeholders { - panic("error in pkg db; should only get here if size is correct") - } - - db.mu.Lock() - t, ok := db.table(s.table) - db.mu.Unlock() - if !ok { - return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) - } - t.mu.Lock() - defer t.mu.Unlock() - - colIdx := make(map[string]int) // select column name -> column index in table - for _, name := range s.colName { - idx := t.columnIndex(name) - if idx == -1 { - return nil, fmt.Errorf("fakedb: unknown column name %q", name) - } - colIdx[name] = idx - } - - mrows := []*row{} -rows: - for _, trow := range t.rows { - // Process the where clause, skipping non-match rows. This is lazy - // and just uses fmt.Sprintf("%v") to test equality. Good enough - // for test code. - for widx, wcol := range s.whereCol { - idx := t.columnIndex(wcol) - if idx == -1 { - return nil, fmt.Errorf("db: invalid where clause column %q", wcol) - } - tcol := trow.cols[idx] - if bs, ok := tcol.([]byte); ok { - // lazy hack to avoid sprintf %v on a []byte - tcol = string(bs) - } - if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) { - continue rows - } - } - mrow := &row{cols: make([]interface{}, len(s.colName))} - for seli, name := range s.colName { - mrow.cols[seli] = trow.cols[colIdx[name]] - } - mrows = append(mrows, mrow) - } - - cursor := &rowsCursor{ - pos: -1, - rows: mrows, - cols: s.colName, - } - return cursor, nil -} - -func (s *fakeStmt) NumInput() int { - return s.placeholders -} - -func (tx *fakeTx) Commit() error { - tx.c.currTx = nil - return nil -} - -func (tx *fakeTx) Rollback() error { - tx.c.currTx = nil - return nil -} - -type rowsCursor struct { - cols []string - pos int - rows []*row - closed bool - - // a clone of slices to give out to clients, indexed by the - // the original slice's first byte address. we clone them - // just so we're able to corrupt them on close. - bytesClone map[*byte][]byte -} - -func (rc *rowsCursor) Close() error { - if !rc.closed { - for _, bs := range rc.bytesClone { - bs[0] = 255 // first byte corrupted - } - } - rc.closed = true - return nil -} - -func (rc *rowsCursor) Columns() []string { - return rc.cols -} - -func (rc *rowsCursor) Next(dest []interface{}) error { - if rc.closed { - return errors.New("fakedb: cursor is closed") - } - rc.pos++ - if rc.pos >= len(rc.rows) { - return io.EOF // per interface spec - } - for i, v := range rc.rows[rc.pos].cols { - // TODO(bradfitz): convert to subset types? naah, I - // think the subset types should only be input to - // driver, but the sql package should be able to handle - // a wider range of types coming out of drivers. all - // for ease of drivers, and to prevent drivers from - // messing up conversions or doing them differently. - dest[i] = v - - if bs, ok := v.([]byte); ok { - if rc.bytesClone == nil { - rc.bytesClone = make(map[*byte][]byte) - } - clone, ok := rc.bytesClone[&bs[0]] - if !ok { - clone = make([]byte, len(bs)) - copy(clone, bs) - rc.bytesClone[&bs[0]] = clone - } - dest[i] = clone - } - } - return nil -} - -func converterForType(typ string) driver.ValueConverter { - switch typ { - case "bool": - return driver.Bool - case "int32": - return driver.Int32 - case "string": - return driver.String - case "datetime": - return driver.DefaultParameterConverter - } - panic("invalid fakedb column type of " + typ) -} diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go deleted file mode 100644 index 4e68c3e..0000000 --- a/libgo/go/exp/sql/sql.go +++ /dev/null @@ -1,860 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package sql provides a generic interface around SQL (or SQL-like) -// databases. -package sql - -import ( - "errors" - "fmt" - "io" - "sync" - - "exp/sql/driver" -) - -var drivers = make(map[string]driver.Driver) - -// Register makes a database driver available by the provided name. -// If Register is called twice with the same name or if driver is nil, -// it panics. -func Register(name string, driver driver.Driver) { - if driver == nil { - panic("sql: Register driver is nil") - } - if _, dup := drivers[name]; dup { - panic("sql: Register called twice for driver " + name) - } - drivers[name] = driver -} - -// NullableString represents a string that may be null. -// NullableString implements the ScannerInto interface so -// it can be used as a scan destination: -// -// var s NullableString -// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s) -// ... -// if s.Valid { -// // use s.String -// } else { -// // NULL value -// } -// -// TODO(bradfitz): add other types. -type NullableString struct { - String string - Valid bool // Valid is true if String is not NULL -} - -// ScanInto implements the ScannerInto interface. -func (ms *NullableString) ScanInto(value interface{}) error { - if value == nil { - ms.String, ms.Valid = "", false - return nil - } - ms.Valid = true - return convertAssign(&ms.String, value) -} - -// ScannerInto is an interface used by Scan. -type ScannerInto interface { - // ScanInto assigns a value from a database driver. - // - // The value will be of one of the following restricted - // set of types: - // - // int64 - // float64 - // bool - // []byte - // nil - for NULL values - // - // An error should be returned if the value can not be stored - // without loss of information. - ScanInto(value interface{}) error -} - -// ErrNoRows is returned by Scan when QueryRow doesn't return a -// row. In such a case, QueryRow returns a placeholder *Row value that -// defers this error until a Scan. -var ErrNoRows = errors.New("sql: no rows in result set") - -// DB is a database handle. It's safe for concurrent use by multiple -// goroutines. -type DB struct { - driver driver.Driver - dsn string - - mu sync.Mutex // protects freeConn and closed - freeConn []driver.Conn - closed bool -} - -// Open opens a database specified by its database driver name and a -// driver-specific data source name, usually consisting of at least a -// database name and connection information. -// -// Most users will open a database via a driver-specific connection -// helper function that returns a *DB. -func Open(driverName, dataSourceName string) (*DB, error) { - driver, ok := drivers[driverName] - if !ok { - return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) - } - return &DB{driver: driver, dsn: dataSourceName}, nil -} - -// Close closes the database, releasing any open resources. -func (db *DB) Close() error { - db.mu.Lock() - defer db.mu.Unlock() - var err error - for _, c := range db.freeConn { - err1 := c.Close() - if err1 != nil { - err = err1 - } - } - db.freeConn = nil - db.closed = true - return err -} - -func (db *DB) maxIdleConns() int { - const defaultMaxIdleConns = 2 - // TODO(bradfitz): ask driver, if supported, for its default preference - // TODO(bradfitz): let users override? - return defaultMaxIdleConns -} - -// conn returns a newly-opened or cached driver.Conn -func (db *DB) conn() (driver.Conn, error) { - db.mu.Lock() - if db.closed { - db.mu.Unlock() - return nil, errors.New("sql: database is closed") - } - if n := len(db.freeConn); n > 0 { - conn := db.freeConn[n-1] - db.freeConn = db.freeConn[:n-1] - db.mu.Unlock() - return conn, nil - } - db.mu.Unlock() - return db.driver.Open(db.dsn) -} - -func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { - db.mu.Lock() - defer db.mu.Unlock() - for n, conn := range db.freeConn { - if conn == wanted { - db.freeConn[n] = db.freeConn[len(db.freeConn)-1] - db.freeConn = db.freeConn[:len(db.freeConn)-1] - return wanted, true - } - } - return nil, false -} - -func (db *DB) putConn(c driver.Conn) { - db.mu.Lock() - defer db.mu.Unlock() - if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() { - db.freeConn = append(db.freeConn, c) - return - } - db.closeConn(c) // TODO(bradfitz): release lock before calling this? -} - -func (db *DB) closeConn(c driver.Conn) { - // TODO: check to see if we need this Conn for any prepared statements - // that are active. - c.Close() -} - -// Prepare creates a prepared statement for later execution. -func (db *DB) Prepare(query string) (*Stmt, error) { - // TODO: check if db.driver supports an optional - // driver.Preparer interface and call that instead, if so, - // otherwise we make a prepared statement that's bound - // to a connection, and to execute this prepared statement - // we either need to use this connection (if it's free), else - // get a new connection + re-prepare + execute on that one. - ci, err := db.conn() - if err != nil { - return nil, err - } - defer db.putConn(ci) - si, err := ci.Prepare(query) - if err != nil { - return nil, err - } - stmt := &Stmt{ - db: db, - query: query, - css: []connStmt{{ci, si}}, - } - return stmt, nil -} - -// Exec executes a query without returning any rows. -func (db *DB) Exec(query string, args ...interface{}) (Result, error) { - sargs, err := subsetTypeArgs(args) - if err != nil { - return nil, err - } - - ci, err := db.conn() - if err != nil { - return nil, err - } - defer db.putConn(ci) - - if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, sargs) - if err != driver.ErrSkip { - if err != nil { - return nil, err - } - return result{resi}, nil - } - } - - sti, err := ci.Prepare(query) - if err != nil { - return nil, err - } - defer sti.Close() - - resi, err := sti.Exec(sargs) - if err != nil { - return nil, err - } - return result{resi}, nil -} - -// Query executes a query that returns rows, typically a SELECT. -func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { - stmt, err := db.Prepare(query) - if err != nil { - return nil, err - } - rows, err := stmt.Query(args...) - if err != nil { - stmt.Close() - return nil, err - } - rows.closeStmt = stmt - return rows, nil -} - -// QueryRow executes a query that is expected to return at most one row. -// QueryRow always return a non-nil value. Errors are deferred until -// Row's Scan method is called. -func (db *DB) QueryRow(query string, args ...interface{}) *Row { - rows, err := db.Query(query, args...) - return &Row{rows: rows, err: err} -} - -// Begin starts a transaction. The isolation level is dependent on -// the driver. -func (db *DB) Begin() (*Tx, error) { - ci, err := db.conn() - if err != nil { - return nil, err - } - txi, err := ci.Begin() - if err != nil { - db.putConn(ci) - return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err) - } - return &Tx{ - db: db, - ci: ci, - txi: txi, - }, nil -} - -// DriverDatabase returns the database's underlying driver. -func (db *DB) Driver() driver.Driver { - return db.driver -} - -// Tx is an in-progress database transaction. -// -// A transaction must end with a call to Commit or Rollback. -// -// After a call to Commit or Rollback, all operations on the -// transaction fail with ErrTransactionFinished. -type Tx struct { - db *DB - - // ci is owned exclusively until Commit or Rollback, at which point - // it's returned with putConn. - ci driver.Conn - txi driver.Tx - - // cimu is held while somebody is using ci (between grabConn - // and releaseConn) - cimu sync.Mutex - - // done transitions from false to true exactly once, on Commit - // or Rollback. once done, all operations fail with - // ErrTransactionFinished. - done bool -} - -var ErrTransactionFinished = errors.New("sql: Transaction has already been committed or rolled back") - -func (tx *Tx) close() { - if tx.done { - panic("double close") // internal error - } - tx.done = true - tx.db.putConn(tx.ci) - tx.ci = nil - tx.txi = nil -} - -func (tx *Tx) grabConn() (driver.Conn, error) { - if tx.done { - return nil, ErrTransactionFinished - } - tx.cimu.Lock() - return tx.ci, nil -} - -func (tx *Tx) releaseConn() { - tx.cimu.Unlock() -} - -// Commit commits the transaction. -func (tx *Tx) Commit() error { - if tx.done { - return ErrTransactionFinished - } - defer tx.close() - return tx.txi.Commit() -} - -// Rollback aborts the transaction. -func (tx *Tx) Rollback() error { - if tx.done { - return ErrTransactionFinished - } - defer tx.close() - return tx.txi.Rollback() -} - -// Prepare creates a prepared statement for use within a transaction. -// -// The returned statement operates within the transaction and can no longer -// be used once the transaction has been committed or rolled back. -// -// To use an existing prepared statement on this transaction, see Tx.Stmt. -func (tx *Tx) Prepare(query string) (*Stmt, error) { - // TODO(bradfitz): We could be more efficient here and either - // provide a method to take an existing Stmt (created on - // perhaps a different Conn), and re-create it on this Conn if - // necessary. Or, better: keep a map in DB of query string to - // Stmts, and have Stmt.Execute do the right thing and - // re-prepare if the Conn in use doesn't have that prepared - // statement. But we'll want to avoid caching the statement - // in the case where we only call conn.Prepare implicitly - // (such as in db.Exec or tx.Exec), but the caller package - // can't be holding a reference to the returned statement. - // Perhaps just looking at the reference count (by noting - // Stmt.Close) would be enough. We might also want a finalizer - // on Stmt to drop the reference count. - ci, err := tx.grabConn() - if err != nil { - return nil, err - } - defer tx.releaseConn() - - si, err := ci.Prepare(query) - if err != nil { - return nil, err - } - - stmt := &Stmt{ - db: tx.db, - tx: tx, - txsi: si, - query: query, - } - return stmt, nil -} - -// Stmt returns a transaction-specific prepared statement from -// an existing statement. -// -// Example: -// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?") -// ... -// tx, err := db.Begin() -// ... -// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203) -func (tx *Tx) Stmt(stmt *Stmt) *Stmt { - // TODO(bradfitz): optimize this. Currently this re-prepares - // each time. This is fine for now to illustrate the API but - // we should really cache already-prepared statements - // per-Conn. See also the big comment in Tx.Prepare. - - if tx.db != stmt.db { - return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")} - } - ci, err := tx.grabConn() - if err != nil { - return &Stmt{stickyErr: err} - } - defer tx.releaseConn() - si, err := ci.Prepare(stmt.query) - return &Stmt{ - db: tx.db, - tx: tx, - txsi: si, - query: stmt.query, - stickyErr: err, - } -} - -// Exec executes a query that doesn't return rows. -// For example: an INSERT and UPDATE. -func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { - ci, err := tx.grabConn() - if err != nil { - return nil, err - } - defer tx.releaseConn() - - if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, args) - if err != nil { - return nil, err - } - return result{resi}, nil - } - - sti, err := ci.Prepare(query) - if err != nil { - return nil, err - } - defer sti.Close() - - sargs, err := subsetTypeArgs(args) - if err != nil { - return nil, err - } - - resi, err := sti.Exec(sargs) - if err != nil { - return nil, err - } - return result{resi}, nil -} - -// Query executes a query that returns rows, typically a SELECT. -func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { - if tx.done { - return nil, ErrTransactionFinished - } - stmt, err := tx.Prepare(query) - if err != nil { - return nil, err - } - defer stmt.Close() - return stmt.Query(args...) -} - -// QueryRow executes a query that is expected to return at most one row. -// QueryRow always return a non-nil value. Errors are deferred until -// Row's Scan method is called. -func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { - rows, err := tx.Query(query, args...) - return &Row{rows: rows, err: err} -} - -// connStmt is a prepared statement on a particular connection. -type connStmt struct { - ci driver.Conn - si driver.Stmt -} - -// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines. -type Stmt struct { - // Immutable: - db *DB // where we came from - query string // that created the Stmt - stickyErr error // if non-nil, this error is returned for all operations - - // If in a transaction, else both nil: - tx *Tx - txsi driver.Stmt - - mu sync.Mutex // protects the rest of the fields - closed bool - - // css is a list of underlying driver statement interfaces - // that are valid on particular connections. This is only - // used if tx == nil and one is found that has idle - // connections. If tx != nil, txsi is always used. - css []connStmt -} - -// Exec executes a prepared statement with the given arguments and -// returns a Result summarizing the effect of the statement. -func (s *Stmt) Exec(args ...interface{}) (Result, error) { - _, releaseConn, si, err := s.connStmt() - if err != nil { - return nil, err - } - defer releaseConn() - - // -1 means the driver doesn't know how to count the number of - // placeholders, so we won't sanity check input here and instead let the - // driver deal with errors. - if want := si.NumInput(); want != -1 && len(args) != want { - return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args)) - } - - // Convert args to subset types. - if cc, ok := si.(driver.ColumnConverter); ok { - for n, arg := range args { - args[n], err = cc.ColumnConverter(n).ConvertValue(arg) - if err != nil { - return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) - } - if !driver.IsParameterSubsetType(args[n]) { - return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", - arg, args[n]) - } - } - } else { - for n, arg := range args { - args[n], err = driver.DefaultParameterConverter.ConvertValue(arg) - if err != nil { - return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) - } - } - } - - resi, err := si.Exec(args) - if err != nil { - return nil, err - } - return result{resi}, nil -} - -// connStmt returns a free driver connection on which to execute the -// statement, a function to call to release the connection, and a -// statement bound to that connection. -func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, err error) { - if err = s.stickyErr; err != nil { - return - } - s.mu.Lock() - if s.closed { - s.mu.Unlock() - err = errors.New("sql: statement is closed") - return - } - - // In a transaction, we always use the connection that the - // transaction was created on. - if s.tx != nil { - s.mu.Unlock() - ci, err = s.tx.grabConn() // blocks, waiting for the connection. - if err != nil { - return - } - releaseConn = func() { s.tx.releaseConn() } - return ci, releaseConn, s.txsi, nil - } - - var cs connStmt - match := false - for _, v := range s.css { - // TODO(bradfitz): lazily clean up entries in this - // list with dead conns while enumerating - if _, match = s.db.connIfFree(cs.ci); match { - cs = v - break - } - } - s.mu.Unlock() - - // Make a new conn if all are busy. - // TODO(bradfitz): or wait for one? make configurable later? - if !match { - ci, err := s.db.conn() - if err != nil { - return nil, nil, nil, err - } - si, err := ci.Prepare(s.query) - if err != nil { - return nil, nil, nil, err - } - s.mu.Lock() - cs = connStmt{ci, si} - s.css = append(s.css, cs) - s.mu.Unlock() - } - - conn := cs.ci - releaseConn = func() { s.db.putConn(conn) } - return conn, releaseConn, cs.si, nil -} - -// Query executes a prepared query statement with the given arguments -// and returns the query results as a *Rows. -func (s *Stmt) Query(args ...interface{}) (*Rows, error) { - ci, releaseConn, si, err := s.connStmt() - if err != nil { - return nil, err - } - - // -1 means the driver doesn't know how to count the number of - // placeholders, so we won't sanity check input here and instead let the - // driver deal with errors. - if want := si.NumInput(); want != -1 && len(args) != want { - return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args)) - } - sargs, err := subsetTypeArgs(args) - if err != nil { - return nil, err - } - rowsi, err := si.Query(sargs) - if err != nil { - s.db.putConn(ci) - return nil, err - } - // Note: ownership of ci passes to the *Rows, to be freed - // with releaseConn. - rows := &Rows{ - db: s.db, - ci: ci, - releaseConn: releaseConn, - rowsi: rowsi, - } - return rows, nil -} - -// QueryRow executes a prepared query statement with the given arguments. -// If an error occurs during the execution of the statement, that error will -// be returned by a call to Scan on the returned *Row, which is always non-nil. -// If the query selects no rows, the *Row's Scan will return ErrNoRows. -// Otherwise, the *Row's Scan scans the first selected row and discards -// the rest. -// -// Example usage: -// -// var name string -// err := nameByUseridStmt.QueryRow(id).Scan(&s) -func (s *Stmt) QueryRow(args ...interface{}) *Row { - rows, err := s.Query(args...) - if err != nil { - return &Row{err: err} - } - return &Row{rows: rows} -} - -// Close closes the statement. -func (s *Stmt) Close() error { - if s.stickyErr != nil { - return s.stickyErr - } - s.mu.Lock() - defer s.mu.Unlock() - if s.closed { - return nil - } - s.closed = true - - if s.tx != nil { - s.txsi.Close() - } else { - for _, v := range s.css { - if ci, match := s.db.connIfFree(v.ci); match { - v.si.Close() - s.db.putConn(ci) - } else { - // TODO(bradfitz): care that we can't close - // this statement because the statement's - // connection is in use? - } - } - } - return nil -} - -// Rows is the result of a query. Its cursor starts before the first row -// of the result set. Use Next to advance through the rows: -// -// rows, err := db.Query("SELECT ...") -// ... -// for rows.Next() { -// var id int -// var name string -// err = rows.Scan(&id, &name) -// ... -// } -// err = rows.Err() // get any error encountered during iteration -// ... -type Rows struct { - db *DB - ci driver.Conn // owned; must call putconn when closed to release - releaseConn func() - rowsi driver.Rows - - closed bool - lastcols []interface{} - lasterr error - closeStmt *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. -func (rs *Rows) Next() bool { - if rs.closed { - return false - } - if rs.lasterr != nil { - return false - } - if rs.lastcols == nil { - rs.lastcols = make([]interface{}, len(rs.rowsi.Columns())) - } - rs.lasterr = rs.rowsi.Next(rs.lastcols) - if rs.lasterr == io.EOF { - rs.Close() - } - return rs.lasterr == nil -} - -// Err returns the error, if any, that was encountered during iteration. -func (rs *Rows) Err() error { - if rs.lasterr == io.EOF { - return nil - } - return rs.lasterr -} - -// Columns returns the column names. -// Columns returns an error if the rows are closed, or if the rows -// are from QueryRow and there was a deferred error. -func (rs *Rows) Columns() ([]string, error) { - if rs.closed { - return nil, errors.New("sql: Rows are closed") - } - if rs.rowsi == nil { - return nil, errors.New("sql: no Rows available") - } - return rs.rowsi.Columns(), nil -} - -// Scan copies the columns in the current row into the values pointed -// at by dest. If dest contains pointers to []byte, the slices should -// not be modified and should only be considered valid until the next -// call to Next or Scan. -func (rs *Rows) Scan(dest ...interface{}) error { - if rs.closed { - return errors.New("sql: Rows closed") - } - if rs.lasterr != nil { - return rs.lasterr - } - if rs.lastcols == nil { - return errors.New("sql: Scan called without calling Next") - } - if len(dest) != len(rs.lastcols) { - return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) - } - for i, sv := range rs.lastcols { - err := convertAssign(dest[i], sv) - if err != nil { - return fmt.Errorf("sql: Scan error on column index %d: %v", i, err) - } - } - return nil -} - -// Close closes the Rows, preventing further enumeration. If the -// end is encountered, the Rows are closed automatically. Close -// is idempotent. -func (rs *Rows) Close() error { - if rs.closed { - return nil - } - rs.closed = true - err := rs.rowsi.Close() - rs.releaseConn() - if rs.closeStmt != nil { - rs.closeStmt.Close() - } - return err -} - -// Row is the result of calling QueryRow to select a single row. -type Row struct { - // One of these two will be non-nil: - err error // deferred error for easy chaining - rows *Rows -} - -// Scan copies the columns from the matched row into the values -// pointed at by dest. If more than one row matches the query, -// Scan uses the first row and discards the rest. If no row matches -// the query, Scan returns ErrNoRows. -func (r *Row) Scan(dest ...interface{}) error { - if r.err != nil { - return r.err - } - defer r.rows.Close() - if !r.rows.Next() { - return ErrNoRows - } - err := r.rows.Scan(dest...) - if err != nil { - return err - } - - // TODO(bradfitz): for now we need to defensively clone all - // []byte that the driver returned, since we're about to close - // the Rows in our defer, when we return from this function. - // the contract with the driver.Next(...) interface is that it - // can return slices into read-only temporary memory that's - // only valid until the next Scan/Close. But the TODO is that - // for a lot of drivers, this copy will be unnecessary. We - // should provide an optional interface for drivers to - // implement to say, "don't worry, the []bytes that I return - // from Next will not be modified again." (for instance, if - // they were obtained from the network anyway) But for now we - // don't care. - for _, dp := range dest { - b, ok := dp.(*[]byte) - if !ok { - continue - } - clone := make([]byte, len(*b)) - copy(clone, *b) - *b = clone - } - return nil -} - -// A Result summarizes an executed SQL command. -type Result interface { - LastInsertId() (int64, error) - RowsAffected() (int64, error) -} - -type result struct { - driver.Result -} diff --git a/libgo/go/exp/sql/sql_test.go b/libgo/go/exp/sql/sql_test.go deleted file mode 100644 index 3f98a8c..0000000 --- a/libgo/go/exp/sql/sql_test.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sql - -import ( - "reflect" - "strings" - "testing" - "time" -) - -const fakeDBName = "foo" - -var chrisBirthday = time.Unix(123456789, 0) - -func newTestDB(t *testing.T, name string) *DB { - db, err := Open("test", fakeDBName) - if err != nil { - t.Fatalf("Open: %v", err) - } - if _, err := db.Exec("WIPE"); err != nil { - t.Fatalf("exec wipe: %v", err) - } - if name == "people" { - exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime") - exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1) - exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2) - exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) - } - return db -} - -func exec(t *testing.T, db *DB, query string, args ...interface{}) { - _, err := db.Exec(query, args...) - if err != nil { - t.Fatalf("Exec of %q: %v", query, err) - } -} - -func closeDB(t *testing.T, db *DB) { - err := db.Close() - if err != nil { - t.Fatalf("error closing DB: %v", err) - } -} - -func TestQuery(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - rows, err := db.Query("SELECT|people|age,name|") - if err != nil { - t.Fatalf("Query: %v", err) - } - type row struct { - age int - name string - } - got := []row{} - for rows.Next() { - var r row - err = rows.Scan(&r.age, &r.name) - if err != nil { - t.Fatalf("Scan: %v", err) - } - got = append(got, r) - } - err = rows.Err() - if err != nil { - t.Fatalf("Err: %v", err) - } - want := []row{ - {age: 1, name: "Alice"}, - {age: 2, name: "Bob"}, - {age: 3, name: "Chris"}, - } - if !reflect.DeepEqual(got, want) { - t.Logf(" got: %#v\nwant: %#v", got, want) - } - - // And verify that the final rows.Next() call, which hit EOF, - // also closed the rows connection. - if n := len(db.freeConn); n != 1 { - t.Errorf("free conns after query hitting EOF = %d; want 1", n) - } -} - -func TestRowsColumns(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - rows, err := db.Query("SELECT|people|age,name|") - if err != nil { - t.Fatalf("Query: %v", err) - } - cols, err := rows.Columns() - if err != nil { - t.Fatalf("Columns: %v", err) - } - want := []string{"age", "name"} - if !reflect.DeepEqual(cols, want) { - t.Errorf("got %#v; want %#v", cols, want) - } -} - -func TestQueryRow(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - var name string - var age int - var birthday time.Time - - err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) - if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") { - t.Errorf("expected error from wrong number of arguments; actually got: %v", err) - } - - err = db.QueryRow("SELECT|people|bdate|age=?", 3).Scan(&birthday) - if err != nil || !birthday.Equal(chrisBirthday) { - t.Errorf("chris birthday = %v, err = %v; want %v", birthday, err, chrisBirthday) - } - - err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name) - if err != nil { - t.Fatalf("age QueryRow+Scan: %v", err) - } - if name != "Bob" { - t.Errorf("expected name Bob, got %q", name) - } - if age != 2 { - t.Errorf("expected age 2, got %d", age) - } - - err = db.QueryRow("SELECT|people|age,name|name=?", "Alice").Scan(&age, &name) - if err != nil { - t.Fatalf("name QueryRow+Scan: %v", err) - } - if name != "Alice" { - t.Errorf("expected name Alice, got %q", name) - } - if age != 1 { - t.Errorf("expected age 1, got %d", age) - } - - var photo []byte - err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) - if err != nil { - t.Fatalf("photo QueryRow+Scan: %v", err) - } - want := []byte("APHOTO") - if !reflect.DeepEqual(photo, want) { - t.Errorf("photo = %q; want %q", photo, want) - } -} - -func TestStatementErrorAfterClose(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - stmt, err := db.Prepare("SELECT|people|age|name=?") - if err != nil { - t.Fatalf("Prepare: %v", err) - } - err = stmt.Close() - if err != nil { - t.Fatalf("Close: %v", err) - } - var name string - err = stmt.QueryRow("foo").Scan(&name) - if err == nil { - t.Errorf("expected error from QueryRow.Scan after Stmt.Close") - } -} - -func TestStatementQueryRow(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - stmt, err := db.Prepare("SELECT|people|age|name=?") - if err != nil { - t.Fatalf("Prepare: %v", err) - } - var age int - for n, tt := range []struct { - name string - want int - }{ - {"Alice", 1}, - {"Bob", 2}, - {"Chris", 3}, - } { - if err := stmt.QueryRow(tt.name).Scan(&age); err != nil { - t.Errorf("%d: on %q, QueryRow/Scan: %v", n, tt.name, err) - } else if age != tt.want { - t.Errorf("%d: age=%d, want %d", n, age, tt.want) - } - } - -} - -// just a test of fakedb itself -func TestBogusPreboundParameters(t *testing.T) { - db := newTestDB(t, "foo") - defer closeDB(t, db) - exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") - _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") - if err == nil { - t.Fatalf("expected error") - } - if err.Error() != `fakedb: invalid conversion to int32 from "bogusconversion"` { - t.Errorf("unexpected error: %v", err) - } -} - -func TestExec(t *testing.T) { - db := newTestDB(t, "foo") - defer closeDB(t, db) - exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") - stmt, err := db.Prepare("INSERT|t1|name=?,age=?") - if err != nil { - t.Errorf("Stmt, err = %v, %v", stmt, err) - } - - type execTest struct { - args []interface{} - wantErr string - } - execTests := []execTest{ - // Okay: - {[]interface{}{"Brad", 31}, ""}, - {[]interface{}{"Brad", int64(31)}, ""}, - {[]interface{}{"Bob", "32"}, ""}, - {[]interface{}{7, 9}, ""}, - - // Invalid conversions: - {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"}, - {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"}, - - // Wrong number of args: - {[]interface{}{}, "sql: expected 2 arguments, got 0"}, - {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"}, - } - for n, et := range execTests { - _, err := stmt.Exec(et.args...) - errStr := "" - if err != nil { - errStr = err.Error() - } - if errStr != et.wantErr { - t.Errorf("stmt.Execute #%d: for %v, got error %q, want error %q", - n, et.args, errStr, et.wantErr) - } - } -} - -func TestTxStmt(t *testing.T) { - db := newTestDB(t, "") - defer closeDB(t, db) - exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") - stmt, err := db.Prepare("INSERT|t1|name=?,age=?") - if err != nil { - t.Fatalf("Stmt, err = %v, %v", stmt, err) - } - tx, err := db.Begin() - if err != nil { - t.Fatalf("Begin = %v", err) - } - _, err = tx.Stmt(stmt).Exec("Bobby", 7) - if err != nil { - t.Fatalf("Exec = %v", err) - } - err = tx.Commit() - if err != nil { - t.Fatalf("Commit = %v", err) - } -} - -// Tests fix for issue 2542, that we release a lock when querying on -// a closed connection. -func TestIssue2542Deadlock(t *testing.T) { - db := newTestDB(t, "people") - closeDB(t, db) - for i := 0; i < 2; i++ { - _, err := db.Query("SELECT|people|age,name|") - if err == nil { - t.Fatalf("expected error") - } - } -} - -func TestQueryRowClosingStmt(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - var name string - var age int - err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age, &name) - if err != nil { - t.Fatal(err) - } - if len(db.freeConn) != 1 { - t.Fatalf("expected 1 free conn") - } - fakeConn := db.freeConn[0].(*fakeConn) - if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed { - t.Logf("statement close mismatch: made %d, closed %d", made, closed) - } -} diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go index 8df8145..eb6c035 100644 --- a/libgo/go/exp/ssh/client.go +++ b/libgo/go/exp/ssh/client.go @@ -306,9 +306,8 @@ type clientChan struct { stdout *chanReader // receives the payload of channelData messages stderr *chanReader // receives the payload of channelExtendedData messages msg chan interface{} // incoming messages - - theyClosed bool // indicates the close msg has been received from the remote side - weClosed bool // incidates the close msg has been sent from our side + theyClosed bool // indicates the close msg has been received from the remote side + weClosed bool // incidates the close msg has been sent from our side } // newClientChan returns a partially constructed *clientChan diff --git a/libgo/go/exp/ssh/messages.go b/libgo/go/exp/ssh/messages.go index cebb560..34ad131 100644 --- a/libgo/go/exp/ssh/messages.go +++ b/libgo/go/exp/ssh/messages.go @@ -484,6 +484,26 @@ func intLength(n *big.Int) int { return length } +func marshalUint32(to []byte, n uint32) []byte { + to[0] = byte(n >> 24) + to[1] = byte(n >> 16) + to[2] = byte(n >> 8) + to[3] = byte(n) + return to[4:] +} + +func marshalUint64(to []byte, n uint64) []byte { + to[0] = byte(n >> 56) + to[1] = byte(n >> 48) + to[2] = byte(n >> 40) + to[3] = byte(n >> 32) + to[4] = byte(n >> 24) + to[5] = byte(n >> 16) + to[6] = byte(n >> 8) + to[7] = byte(n) + return to[8:] +} + func marshalInt(to []byte, n *big.Int) []byte { lengthBytes := to to = to[4:] diff --git a/libgo/go/exp/ssh/session.go b/libgo/go/exp/ssh/session.go index 807dd87..ea4addb 100644 --- a/libgo/go/exp/ssh/session.go +++ b/libgo/go/exp/ssh/session.go @@ -70,7 +70,7 @@ type Session struct { started bool // true once Start, Run or Shell is invoked. copyFuncs []func() error - errch chan error // one send per copyFunc + errors chan error // one send per copyFunc // true if pipe method is active stdinpipe, stdoutpipe, stderrpipe bool @@ -244,10 +244,10 @@ func (s *Session) start() error { setupFd(s) } - s.errch = make(chan error, len(s.copyFuncs)) + s.errors = make(chan error, len(s.copyFuncs)) for _, fn := range s.copyFuncs { go func(fn func() error) { - s.errch <- fn() + s.errors <- fn() }(fn) } return nil @@ -270,7 +270,7 @@ func (s *Session) Wait() error { var copyError error for _ = range s.copyFuncs { - if err := <-s.errch; err != nil && copyError == nil { + if err := <-s.errors; err != nil && copyError == nil { copyError = err } } diff --git a/libgo/go/exp/ssh/tcpip.go b/libgo/go/exp/ssh/tcpip.go index bee41ee..e0c47bc 100644 --- a/libgo/go/exp/ssh/tcpip.go +++ b/libgo/go/exp/ssh/tcpip.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net" + "time" ) // Dial initiates a connection to the addr from the remote host. @@ -107,27 +108,25 @@ func (t *tcpchanconn) RemoteAddr() net.Addr { return t.raddr } -// SetTimeout sets the read and write deadlines associated +// SetDeadline sets the read and write deadlines associated // with the connection. -func (t *tcpchanconn) SetTimeout(nsec int64) error { - if err := t.SetReadTimeout(nsec); err != nil { +func (t *tcpchanconn) SetDeadline(deadline time.Time) error { + if err := t.SetReadDeadline(deadline); err != nil { return err } - return t.SetWriteTimeout(nsec) + return t.SetWriteDeadline(deadline) } -// SetReadTimeout sets the time (in nanoseconds) that -// Read will wait for data before returning an error with Timeout() == true. -// Setting nsec == 0 (the default) disables the deadline. -func (t *tcpchanconn) SetReadTimeout(nsec int64) error { - return errors.New("ssh: tcpchan: timeout not supported") +// SetReadDeadline sets the read deadline. +// A zero value for t means Read will not time out. +// After the deadline, the error from Read will implement net.Error +// with Timeout() == true. +func (t *tcpchanconn) SetReadDeadline(deadline time.Time) error { + return errors.New("ssh: tcpchan: deadline not supported") } -// SetWriteTimeout sets the time (in nanoseconds) that -// Write will wait to send its data before returning an error with Timeout() == true. -// Setting nsec == 0 (the default) disables the deadline. -// Even if write times out, it may return n > 0, indicating that -// some of the data was successfully written. -func (t *tcpchanconn) SetWriteTimeout(nsec int64) error { - return errors.New("ssh: tcpchan: timeout not supported") +// SetWriteDeadline exists to satisfy the net.Conn interface +// but is not implemented by this type. It always returns an error. +func (t *tcpchanconn) SetWriteDeadline(deadline time.Time) error { + return errors.New("ssh: tcpchan: deadline not supported") } diff --git a/libgo/go/exp/ssh/transport.go b/libgo/go/exp/ssh/transport.go index 2e7c955..e21bc4b 100644 --- a/libgo/go/exp/ssh/transport.go +++ b/libgo/go/exp/ssh/transport.go @@ -9,6 +9,7 @@ import ( "crypto" "crypto/cipher" "crypto/hmac" + "crypto/sha1" "crypto/subtle" "errors" "hash" @@ -266,7 +267,7 @@ func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto. generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) - c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} + c.mac = truncatingMAC{12, hmac.New(sha1.New, macKey)} cipher, err := cipherMode.createCipher(key, iv) if err != nil { @@ -328,6 +329,8 @@ func (t truncatingMAC) Size() int { return t.length } +func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } + // maxVersionStringBytes is the maximum number of bytes that we'll accept as a // version string. In the event that the client is talking a different protocol // we need to set a limit otherwise we will keep using more and more memory @@ -337,7 +340,7 @@ const maxVersionStringBytes = 1024 // Read version string as specified by RFC 4253, section 4.2. func readVersion(r io.Reader) ([]byte, error) { versionString := make([]byte, 0, 64) - var ok, seenCR bool + var ok bool var buf [1]byte forEachByte: for len(versionString) < maxVersionStringBytes { @@ -345,27 +348,22 @@ forEachByte: if err != nil { return nil, err } - b := buf[0] - - if !seenCR { - if b == '\r' { - seenCR = true - } - } else { - if b == '\n' { - ok = true - break forEachByte - } else { - seenCR = false - } + // The RFC says that the version should be terminated with \r\n + // but several SSH servers actually only send a \n. + if buf[0] == '\n' { + ok = true + break forEachByte } - versionString = append(versionString, b) + versionString = append(versionString, buf[0]) } if !ok { - return nil, errors.New("failed to read version string") + return nil, errors.New("ssh: failed to read version string") } - // We need to remove the CR from versionString - return versionString[:len(versionString)-1], nil + // There might be a '\r' on the end which we should remove. + if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { + versionString = versionString[:len(versionString)-1] + } + return versionString, nil } diff --git a/libgo/go/exp/ssh/transport_test.go b/libgo/go/exp/ssh/transport_test.go index b2e2a7f..ab9177f 100644 --- a/libgo/go/exp/ssh/transport_test.go +++ b/libgo/go/exp/ssh/transport_test.go @@ -11,7 +11,7 @@ import ( ) func TestReadVersion(t *testing.T) { - buf := []byte(serverVersion) + buf := serverVersion result, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))) if err != nil { t.Errorf("readVersion didn't read version correctly: %s", err) @@ -21,6 +21,20 @@ func TestReadVersion(t *testing.T) { } } +func TestReadVersionWithJustLF(t *testing.T) { + var buf []byte + buf = append(buf, serverVersion...) + buf = buf[:len(buf)-1] + buf[len(buf)-1] = '\n' + result, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))) + if err != nil { + t.Error("readVersion failed to handle just a \n") + } + if !bytes.Equal(buf[:len(buf)-1], result) { + t.Errorf("version read did not match expected: got %x, want %x", result, buf[:len(buf)-1]) + } +} + func TestReadVersionTooLong(t *testing.T) { buf := make([]byte, maxVersionStringBytes+1) if _, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); err == nil { @@ -29,7 +43,7 @@ func TestReadVersionTooLong(t *testing.T) { } func TestReadVersionWithoutCRLF(t *testing.T) { - buf := []byte(serverVersion) + buf := serverVersion buf = buf[:len(buf)-1] if _, err := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); err == nil { t.Error("readVersion did not notice \\n was missing") diff --git a/libgo/go/exp/types/check_test.go b/libgo/go/exp/types/check_test.go index ea9218f..0e20646 100644 --- a/libgo/go/exp/types/check_test.go +++ b/libgo/go/exp/types/check_test.go @@ -47,17 +47,17 @@ var tests = []struct { var fset = token.NewFileSet() -// TODO(gri) This functionality should be in token.Fileset. -func getFile(filename string) *token.File { - for f := range fset.Files() { +func getFile(filename string) (file *token.File) { + fset.Iterate(func(f *token.File) bool { if f.Name() == filename { - return f + file = f + return false // end iteration } - } - return nil + return true + }) + return file } -// TODO(gri) This functionality should be in token.Fileset. func getPos(filename string, offset int) token.Pos { if f := getFile(filename); f != nil { return f.Pos(offset) @@ -65,8 +65,6 @@ func getPos(filename string, offset int) token.Pos { return token.NoPos } -// TODO(gri) Need to revisit parser interface. We should be able to use parser.ParseFiles -// or a similar function instead. func parseFiles(t *testing.T, testname string, filenames []string) (map[string]*ast.File, error) { files := make(map[string]*ast.File) var errors scanner.ErrorList @@ -145,8 +143,6 @@ func eliminate(t *testing.T, expected map[token.Pos]string, errors error) { for _, error := range errors.(scanner.ErrorList) { // error.Pos is a token.Position, but we want // a token.Pos so we can do a map lookup - // TODO(gri) Need to move scanner.Errors over - // to use token.Pos and file set info. pos := getPos(error.Pos.Filename, error.Pos.Offset) if msg, found := expected[pos]; found { // we expect a message at pos; check if it matches diff --git a/libgo/go/exp/types/gcimporter.go b/libgo/go/exp/types/gcimporter.go index 10c56db..a573fbb 100644 --- a/libgo/go/exp/types/gcimporter.go +++ b/libgo/go/exp/types/gcimporter.go @@ -460,29 +460,32 @@ func (p *gcParser) parseSignature() *Func { return &Func{Params: params, Results: results, IsVariadic: isVariadic} } -// MethodSpec = ( identifier | ExportedName ) Signature . +// MethodOrEmbedSpec = Name [ Signature ] . // -func (p *gcParser) parseMethodSpec() *ast.Object { - if p.tok == scanner.Ident { - p.expect(scanner.Ident) - } else { - p.parseExportedName() +func (p *gcParser) parseMethodOrEmbedSpec() *ast.Object { + p.parseName() + if p.tok == '(' { + p.parseSignature() + // TODO(gri) compute method object + return ast.NewObj(ast.Fun, "_") } - p.parseSignature() - - // TODO(gri) compute method object - return ast.NewObj(ast.Fun, "_") + // TODO lookup name and return that type + return ast.NewObj(ast.Typ, "_") } -// InterfaceType = "interface" "{" [ MethodList ] "}" . -// MethodList = MethodSpec { ";" MethodSpec } . +// InterfaceType = "interface" "{" [ MethodOrEmbedList ] "}" . +// MethodOrEmbedList = MethodOrEmbedSpec { ";" MethodOrEmbedSpec } . // func (p *gcParser) parseInterfaceType() Type { var methods ObjList parseMethod := func() { - meth := p.parseMethodSpec() - methods = append(methods, meth) + switch m := p.parseMethodOrEmbedSpec(); m.Kind { + case ast.Typ: + // TODO expand embedded methods + case ast.Fun: + methods = append(methods, m) + } } p.expectKeyword("interface") diff --git a/libgo/go/exp/utf8string/string.go b/libgo/go/exp/utf8string/string.go new file mode 100644 index 0000000..da1e2de --- /dev/null +++ b/libgo/go/exp/utf8string/string.go @@ -0,0 +1,203 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package utf8string provides an efficient way to index strings by rune rather than by byte. +package utf8string + +import ( + "errors" + "unicode/utf8" +) + +// String wraps a regular string with a small structure that provides more +// efficient indexing by code point index, as opposed to byte index. +// Scanning incrementally forwards or backwards is O(1) per index operation +// (although not as fast a range clause going forwards). Random access is +// O(N) in the length of the string, but the overhead is less than always +// scanning from the beginning. +// If the string is ASCII, random access is O(1). +// Unlike the built-in string type, String has internal mutable state and +// is not thread-safe. +type String struct { + str string + numRunes int + // If width > 0, the rune at runePos starts at bytePos and has the specified width. + width int + bytePos int + runePos int + nonASCII int // byte index of the first non-ASCII rune. +} + +// NewString returns a new UTF-8 string with the provided contents. +func NewString(contents string) *String { + return new(String).Init(contents) +} + +// Init initializes an existing String to hold the provided contents. +// It returns a pointer to the initialized String. +func (s *String) Init(contents string) *String { + s.str = contents + s.bytePos = 0 + s.runePos = 0 + for i := 0; i < len(contents); i++ { + if contents[i] >= utf8.RuneSelf { + // Not ASCII. + s.numRunes = utf8.RuneCountInString(contents) + _, s.width = utf8.DecodeRuneInString(contents) + s.nonASCII = i + return s + } + } + // ASCII is simple. Also, the empty string is ASCII. + s.numRunes = len(contents) + s.width = 0 + s.nonASCII = len(contents) + return s +} + +// String returns the contents of the String. This method also means the +// String is directly printable by fmt.Print. +func (s *String) String() string { + return s.str +} + +// RuneCount returns the number of runes (Unicode code points) in the String. +func (s *String) RuneCount() int { + return s.numRunes +} + +// IsASCII returns a boolean indicating whether the String contains only ASCII bytes. +func (s *String) IsASCII() bool { + return s.width == 0 +} + +// Slice returns the string sliced at rune positions [i:j]. +func (s *String) Slice(i, j int) string { + // ASCII is easy. Let the compiler catch the indexing error if there is one. + if j < s.nonASCII { + return s.str[i:j] + } + if i < 0 || j > s.numRunes || i > j { + panic(sliceOutOfRange) + } + if i == j { + return "" + } + // For non-ASCII, after At(i), bytePos is always the position of the indexed character. + var low, high int + switch { + case i < s.nonASCII: + low = i + case i == s.numRunes: + low = len(s.str) + default: + s.At(i) + low = s.bytePos + } + switch { + case j == s.numRunes: + high = len(s.str) + default: + s.At(j) + high = s.bytePos + } + return s.str[low:high] +} + +// At returns the rune with index i in the String. The sequence of runes is the same +// as iterating over the contents with a "for range" clause. +func (s *String) At(i int) rune { + // ASCII is easy. Let the compiler catch the indexing error if there is one. + if i < s.nonASCII { + return rune(s.str[i]) + } + + // Now we do need to know the index is valid. + if i < 0 || i >= s.numRunes { + panic(outOfRange) + } + + var r rune + + // Five easy common cases: within 1 spot of bytePos/runePos, or the beginning, or the end. + // With these cases, all scans from beginning or end work in O(1) time per rune. + switch { + + case i == s.runePos-1: // backing up one rune + r, s.width = utf8.DecodeLastRuneInString(s.str[0:s.bytePos]) + s.runePos = i + s.bytePos -= s.width + return r + case i == s.runePos+1: // moving ahead one rune + s.runePos = i + s.bytePos += s.width + fallthrough + case i == s.runePos: + r, s.width = utf8.DecodeRuneInString(s.str[s.bytePos:]) + return r + case i == 0: // start of string + r, s.width = utf8.DecodeRuneInString(s.str) + s.runePos = 0 + s.bytePos = 0 + return r + + case i == s.numRunes-1: // last rune in string + r, s.width = utf8.DecodeLastRuneInString(s.str) + s.runePos = i + s.bytePos = len(s.str) - s.width + return r + } + + // We need to do a linear scan. There are three places to start from: + // 1) The beginning + // 2) bytePos/runePos. + // 3) The end + // Choose the closest in rune count, scanning backwards if necessary. + forward := true + if i < s.runePos { + // Between beginning and pos. Which is closer? + // Since both i and runePos are guaranteed >= nonASCII, that's the + // lowest location we need to start from. + if i < (s.runePos-s.nonASCII)/2 { + // Scan forward from beginning + s.bytePos, s.runePos = s.nonASCII, s.nonASCII + } else { + // Scan backwards from where we are + forward = false + } + } else { + // Between pos and end. Which is closer? + if i-s.runePos < (s.numRunes-s.runePos)/2 { + // Scan forward from pos + } else { + // Scan backwards from end + s.bytePos, s.runePos = len(s.str), s.numRunes + forward = false + } + } + if forward { + // TODO: Is it much faster to use a range loop for this scan? + for { + r, s.width = utf8.DecodeRuneInString(s.str[s.bytePos:]) + if s.runePos == i { + break + } + s.runePos++ + s.bytePos += s.width + } + } else { + for { + r, s.width = utf8.DecodeLastRuneInString(s.str[0:s.bytePos]) + s.runePos-- + s.bytePos -= s.width + if s.runePos == i { + break + } + } + } + return r +} + +var outOfRange = errors.New("utf8.String: index out of range") +var sliceOutOfRange = errors.New("utf8.String: slice index out of range") diff --git a/libgo/go/exp/utf8string/string_test.go b/libgo/go/exp/utf8string/string_test.go new file mode 100644 index 0000000..28511b2 --- /dev/null +++ b/libgo/go/exp/utf8string/string_test.go @@ -0,0 +1,123 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package utf8string + +import ( + "math/rand" + "testing" + "unicode/utf8" +) + +var testStrings = []string{ + "", + "abcd", + "☺☻☹", + "日a本b語ç日ð本Ê語þ日¥本¼語i日©", + "日a本b語ç日ð本Ê語þ日¥本¼語i日©日a本b語ç日ð本Ê語þ日¥本¼語i日©日a本b語ç日ð本Ê語þ日¥本¼語i日©", + "\x80\x80\x80\x80", +} + +func TestScanForwards(t *testing.T) { + for _, s := range testStrings { + runes := []rune(s) + str := NewString(s) + if str.RuneCount() != len(runes) { + t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount()) + break + } + for i, expect := range runes { + got := str.At(i) + if got != expect { + t.Errorf("%s[%d]: expected %c (%U); got %c (%U)", s, i, expect, expect, got, got) + } + } + } +} + +func TestScanBackwards(t *testing.T) { + for _, s := range testStrings { + runes := []rune(s) + str := NewString(s) + if str.RuneCount() != len(runes) { + t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount()) + break + } + for i := len(runes) - 1; i >= 0; i-- { + expect := runes[i] + got := str.At(i) + if got != expect { + t.Errorf("%s[%d]: expected %c (%U); got %c (%U)", s, i, expect, expect, got, got) + } + } + } +} + +func randCount() int { + if testing.Short() { + return 100 + } + return 100000 +} + +func TestRandomAccess(t *testing.T) { + for _, s := range testStrings { + if len(s) == 0 { + continue + } + runes := []rune(s) + str := NewString(s) + if str.RuneCount() != len(runes) { + t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount()) + break + } + for j := 0; j < randCount(); j++ { + i := rand.Intn(len(runes)) + expect := runes[i] + got := str.At(i) + if got != expect { + t.Errorf("%s[%d]: expected %c (%U); got %c (%U)", s, i, expect, expect, got, got) + } + } + } +} + +func TestRandomSliceAccess(t *testing.T) { + for _, s := range testStrings { + if len(s) == 0 || s[0] == '\x80' { // the bad-UTF-8 string fools this simple test + continue + } + runes := []rune(s) + str := NewString(s) + if str.RuneCount() != len(runes) { + t.Errorf("%s: expected %d runes; got %d", s, len(runes), str.RuneCount()) + break + } + for k := 0; k < randCount(); k++ { + i := rand.Intn(len(runes)) + j := rand.Intn(len(runes) + 1) + if i > j { // include empty strings + continue + } + expect := string(runes[i:j]) + got := str.Slice(i, j) + if got != expect { + t.Errorf("%s[%d:%d]: expected %q got %q", s, i, j, expect, got) + } + } + } +} + +func TestLimitSliceAccess(t *testing.T) { + for _, s := range testStrings { + str := NewString(s) + if str.Slice(0, 0) != "" { + t.Error("failure with empty slice at beginning") + } + nr := utf8.RuneCountInString(s) + if str.Slice(nr, nr) != "" { + t.Error("failure with empty slice at end") + } + } +} |