diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2012-01-19 16:04:26 -0800 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2012-01-19 16:04:26 -0800 |
| commit | 7fc4c07172b7b6feb2da345511ad439fedaf876b (patch) | |
| tree | 88637df85d0f813cbb80045811a2c341edb4a242 /src/pkg/exp/sql | |
| parent | 369454d7b2f3b03ee5bdfde1eae6092bace14951 (diff) | |
| download | go-7fc4c07172b7b6feb2da345511ad439fedaf876b.tar.xz | |
database/sql: move from exp/sql
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5536076
Diffstat (limited to 'src/pkg/exp/sql')
| -rw-r--r-- | src/pkg/exp/sql/Makefile | 12 | ||||
| -rw-r--r-- | src/pkg/exp/sql/convert.go | 133 | ||||
| -rw-r--r-- | src/pkg/exp/sql/convert_test.go | 206 | ||||
| -rw-r--r-- | src/pkg/exp/sql/doc.txt | 46 | ||||
| -rw-r--r-- | src/pkg/exp/sql/driver/Makefile | 12 | ||||
| -rw-r--r-- | src/pkg/exp/sql/driver/driver.go | 207 | ||||
| -rw-r--r-- | src/pkg/exp/sql/driver/types.go | 265 | ||||
| -rw-r--r-- | src/pkg/exp/sql/driver/types_test.go | 61 | ||||
| -rw-r--r-- | src/pkg/exp/sql/fakedb_test.go | 599 | ||||
| -rw-r--r-- | src/pkg/exp/sql/sql.go | 913 | ||||
| -rw-r--r-- | src/pkg/exp/sql/sql_test.go | 404 |
11 files changed, 0 insertions, 2858 deletions
diff --git a/src/pkg/exp/sql/Makefile b/src/pkg/exp/sql/Makefile deleted file mode 100644 index 1e4f74c821..0000000000 --- a/src/pkg/exp/sql/Makefile +++ /dev/null @@ -1,12 +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. - -include ../../../Make.inc - -TARG=exp/sql -GOFILES=\ - convert.go\ - sql.go\ - -include ../../../Make.pkg diff --git a/src/pkg/exp/sql/convert.go b/src/pkg/exp/sql/convert.go deleted file mode 100644 index feb79aeafe..0000000000 --- a/src/pkg/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/src/pkg/exp/sql/convert_test.go b/src/pkg/exp/sql/convert_test.go deleted file mode 100644 index 8c0cafc150..0000000000 --- a/src/pkg/exp/sql/convert_test.go +++ /dev/null @@ -1,206 +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 ( - "exp/sql/driver" - "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 TestNullString(t *testing.T) { - var ns NullString - 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) - } -} - -type valueConverterTest struct { - c driver.ValueConverter - in, out interface{} - err string -} - -var valueConverterTests = []valueConverterTest{ - {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""}, - {driver.DefaultParameterConverter, NullString{"", false}, nil, ""}, -} - -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/src/pkg/exp/sql/doc.txt b/src/pkg/exp/sql/doc.txt deleted file mode 100644 index fb16595487..0000000000 --- a/src/pkg/exp/sql/doc.txt +++ /dev/null @@ -1,46 +0,0 @@ -Goals of the sql and sql/driver packages: - -* Provide a generic database API for a variety of SQL or SQL-like - databases. There currently exist Go libraries for SQLite, MySQL, - and Postgres, but all with a very different feel, and often - a non-Go-like feel. - -* Feel like Go. - -* Care mostly about the common cases. Common SQL should be portable. - SQL edge cases or db-specific extensions can be detected and - conditionally used by the application. It is a non-goal to care - about every particular db's extension or quirk. - -* Separate out the basic implementation of a database driver - (implementing the sql/driver interfaces) vs the implementation - of all the user-level types and convenience methods. - In a nutshell: - - User Code ---> sql package (concrete types) ---> sql/driver (interfaces) - Database Driver -> sql (to register) + sql/driver (implement interfaces) - -* Make type casting/conversions consistent between all drivers. To - achieve this, most of the conversions are done in the db package, - not in each driver. The drivers then only have to deal with a - smaller set of types. - -* Be flexible with type conversions, but be paranoid about silent - truncation or other loss of precision. - -* Handle concurrency well. Users shouldn't need to care about the - database's per-connection thread safety issues (or lack thereof), - and shouldn't have to maintain their own free pools of connections. - The 'db' package should deal with that bookkeeping as needed. Given - an *sql.DB, it should be possible to share that instance between - multiple goroutines, without any extra synchronization. - -* Push complexity, where necessary, down into the sql+driver packages, - rather than exposing it to users. Said otherwise, the sql package - should expose an ideal database that's not finnicky about how it's - accessed, even if that's not true. - -* Provide optional interfaces in sql/driver for drivers to implement - for special cases or fastpaths. But the only party that knows about - those is the sql package. To user code, some stuff just might start - working or start working slightly faster. diff --git a/src/pkg/exp/sql/driver/Makefile b/src/pkg/exp/sql/driver/Makefile deleted file mode 100644 index fce3f2c27c..0000000000 --- a/src/pkg/exp/sql/driver/Makefile +++ /dev/null @@ -1,12 +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. - -include ../../../../Make.inc - -TARG=exp/sql/driver -GOFILES=\ - driver.go\ - types.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/exp/sql/driver/driver.go b/src/pkg/exp/sql/driver/driver.go deleted file mode 100644 index 0cd2562d68..0000000000 --- a/src/pkg/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/src/pkg/exp/sql/driver/types.go b/src/pkg/exp/sql/driver/types.go deleted file mode 100644 index f383885231..0000000000 --- a/src/pkg/exp/sql/driver/types.go +++ /dev/null @@ -1,265 +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) -} - -// SubsetValuer is the interface providing the SubsetValue method. -// -// Types implementing SubsetValuer interface are able to convert -// themselves to one of the driver's allowed subset values. -type SubsetValuer interface { - // SubsetValue returns a driver parameter subset value. - SubsetValue() (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 -} - -// Null is a type that implements ValueConverter by allowing nil -// values but otherwise delegating to another ValueConverter. -type Null struct { - Converter ValueConverter -} - -func (n Null) ConvertValue(v interface{}) (interface{}, error) { - if v == nil { - return nil, nil - } - return n.Converter.ConvertValue(v) -} - -// NotNull is a type that implements ValueConverter by disallowing nil -// values but otherwise delegating to another ValueConverter. -type NotNull struct { - Converter ValueConverter -} - -func (n NotNull) ConvertValue(v interface{}) (interface{}, error) { - if v == nil { - return nil, fmt.Errorf("nil value not allowed") - } - return n.Converter.ConvertValue(v) -} - -// 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 - } - - if svi, ok := v.(SubsetValuer); ok { - sv, err := svi.SubsetValue() - if err != nil { - return nil, err - } - if !IsParameterSubsetType(sv) { - return nil, fmt.Errorf("non-subset type %T returned from SubsetValue", sv) - } - return sv, 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 %T, a %s", v, rv.Kind()) -} diff --git a/src/pkg/exp/sql/driver/types_test.go b/src/pkg/exp/sql/driver/types_test.go deleted file mode 100644 index 966bc6b458..0000000000 --- a/src/pkg/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/src/pkg/exp/sql/fakedb_test.go b/src/pkg/exp/sql/fakedb_test.go deleted file mode 100644 index 0376583b68..0000000000 --- a/src/pkg/exp/sql/fakedb_test.go +++ /dev/null @@ -1,599 +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.NotNull{driver.String} - case "nullstring": - return driver.Null{driver.String} - case "datetime": - return driver.DefaultParameterConverter - } - panic("invalid fakedb column type of " + typ) -} diff --git a/src/pkg/exp/sql/sql.go b/src/pkg/exp/sql/sql.go deleted file mode 100644 index 3201e76674..0000000000 --- a/src/pkg/exp/sql/sql.go +++ /dev/null @@ -1,913 +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 -} - -// RawBytes is a byte slice that holds a reference to memory owned by -// the database itself. After a Scan into a RawBytes, the slice is only -// valid until the next call to Next, Scan, or Close. -type RawBytes []byte - -// NullString represents a string that may be null. -// NullString implements the ScannerInto interface so -// it can be used as a scan destination: -// -// var s NullString -// 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 NullString struct { - String string - Valid bool // Valid is true if String is not NULL -} - -// ScanInto implements the ScannerInto interface. -func (ns *NullString) ScanInto(value interface{}) error { - if value == nil { - ns.String, ns.Valid = "", false - return nil - } - ns.Valid = true - return convertAssign(&ns.String, value) -} - -// SubsetValue implements the driver SubsetValuer interface. -func (ns NullString) SubsetValue() (interface{}, error) { - if !ns.Valid { - return nil, nil - } - return ns.String, nil -} - -// 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 { - // First, see if the value itself knows how to convert - // itself to a driver type. For example, a NullString - // struct changing into a string or nil. - if svi, ok := arg.(driver.SubsetValuer); ok { - sv, err := svi.SubsetValue() - if err != nil { - return nil, fmt.Errorf("sql: argument index %d from SubsetValue: %v", n, err) - } - if !driver.IsParameterSubsetType(sv) { - return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from SubsetValue", n, sv) - } - arg = sv - } - - // Second, ask the column to sanity check itself. For - // example, drivers might use this to make sure that - // an int64 values being inserted into a 16-bit - // integer field is in range (before getting - // truncated), or that a nil can't go into a NOT NULL - // column before going across the network to get the - // same error. - 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 an argument has type *[]byte, Scan saves in that argument a copy -// of the corresponding data. The copy is owned by the caller and can -// be modified and held indefinitely. The copy can be avoided by using -// an argument of type *RawBytes instead; see the documentation for -// RawBytes for restrictions on its use. -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) - } - } - for _, dp := range dest { - b, ok := dp.(*[]byte) - if !ok { - continue - } - if _, ok = dp.(*RawBytes); ok { - continue - } - clone := make([]byte, len(*b)) - copy(clone, *b) - *b = clone - } - 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 { - if _, ok := dp.(*RawBytes); ok { - return errors.New("sql: RawBytes isn't allowed on Row.Scan") - } - 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/src/pkg/exp/sql/sql_test.go b/src/pkg/exp/sql/sql_test.go deleted file mode 100644 index 3fe93986fa..0000000000 --- a/src/pkg/exp/sql/sql_test.go +++ /dev/null @@ -1,404 +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.Errorf("mismatch.\n 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 TestByteOwnership(t *testing.T) { - db := newTestDB(t, "people") - defer closeDB(t, db) - rows, err := db.Query("SELECT|people|name,photo|") - if err != nil { - t.Fatalf("Query: %v", err) - } - type row struct { - name []byte - photo RawBytes - } - got := []row{} - for rows.Next() { - var r row - err = rows.Scan(&r.name, &r.photo) - if err != nil { - t.Fatalf("Scan: %v", err) - } - got = append(got, r) - } - corruptMemory := []byte("\xffPHOTO") - want := []row{ - {name: []byte("Alice"), photo: corruptMemory}, - {name: []byte("Bob"), photo: corruptMemory}, - {name: []byte("Chris"), photo: corruptMemory}, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) - } - - var photo RawBytes - err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) - if err == nil { - t.Error("want error scanning into RawBytes from QueryRow") - } -} - -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.Errorf("statement close mismatch: made %d, closed %d", made, closed) - } -} - -func TestNullStringParam(t *testing.T) { - db := newTestDB(t, "") - defer closeDB(t, db) - exec(t, db, "CREATE|t|id=int32,name=string,favcolor=nullstring") - - // Inserts with db.Exec: - exec(t, db, "INSERT|t|id=?,name=?,favcolor=?", 1, "alice", NullString{"aqua", true}) - exec(t, db, "INSERT|t|id=?,name=?,favcolor=?", 2, "bob", NullString{"brown", false}) - - _, err := db.Exec("INSERT|t|id=?,name=?,favcolor=?", 999, nil, nil) - if err == nil { - // TODO: this test fails, but it's just because - // fakeConn implements the optional Execer interface, - // so arguably this is the correct behavior. But - // maybe I should flesh out the fakeConn.Exec - // implementation so this properly fails. - // t.Errorf("expected error inserting nil name with Exec") - } - - // Inserts with a prepared statement: - stmt, err := db.Prepare("INSERT|t|id=?,name=?,favcolor=?") - if err != nil { - t.Fatalf("prepare: %v", err) - } - if _, err := stmt.Exec(3, "chris", "chartreuse"); err != nil { - t.Errorf("exec insert chris: %v", err) - } - if _, err := stmt.Exec(4, "dave", NullString{"darkred", true}); err != nil { - t.Errorf("exec insert dave: %v", err) - } - if _, err := stmt.Exec(5, "eleanor", NullString{"eel", false}); err != nil { - t.Errorf("exec insert dave: %v", err) - } - - // Can't put null name into non-nullstring column, - if _, err := stmt.Exec(5, NullString{"", false}, nil); err == nil { - t.Errorf("expected error inserting nil name with prepared statement Exec") - } - - type nameColor struct { - name string - favColor NullString - } - - wantMap := map[int]nameColor{ - 1: nameColor{"alice", NullString{"aqua", true}}, - 2: nameColor{"bob", NullString{"", false}}, - 3: nameColor{"chris", NullString{"chartreuse", true}}, - 4: nameColor{"dave", NullString{"darkred", true}}, - 5: nameColor{"eleanor", NullString{"", false}}, - } - for id, want := range wantMap { - var got nameColor - if err := db.QueryRow("SELECT|t|name,favcolor|id=?", id).Scan(&got.name, &got.favColor); err != nil { - t.Errorf("id=%d Scan: %v", id, err) - } - if got != want { - t.Errorf("id=%d got %#v, want %#v", id, got, want) - } - } -} |
