diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2011-09-29 16:12:21 -0700 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2011-09-29 16:12:21 -0700 |
| commit | 357f2cb1a385f4d1418e48856f9abe0cced3bcd1 (patch) | |
| tree | 39d65e200886eae369f07d3e2db1509300069418 /src/pkg/exp/sql/driver | |
| parent | 51b09190ac65069b0248bdbc61d50c8026a42efd (diff) | |
| download | go-357f2cb1a385f4d1418e48856f9abe0cced3bcd1.tar.xz | |
exp/sql{,/driver}: new database packages
R=gustavo, rsc, borman, dave, kevlar, nigeltao, dvyukov, kardianos, fw, r, r, david.crawshaw
CC=golang-dev
https://golang.org/cl/4973055
Diffstat (limited to 'src/pkg/exp/sql/driver')
| -rw-r--r-- | src/pkg/exp/sql/driver/Makefile | 12 | ||||
| -rw-r--r-- | src/pkg/exp/sql/driver/driver.go | 169 | ||||
| -rw-r--r-- | src/pkg/exp/sql/driver/types.go | 161 |
3 files changed, 342 insertions, 0 deletions
diff --git a/src/pkg/exp/sql/driver/Makefile b/src/pkg/exp/sql/driver/Makefile new file mode 100644 index 0000000000..fce3f2c27c --- /dev/null +++ b/src/pkg/exp/sql/driver/Makefile @@ -0,0 +1,12 @@ +# 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 new file mode 100644 index 0000000000..7508b19fa1 --- /dev/null +++ b/src/pkg/exp/sql/driver/driver.go @@ -0,0 +1,169 @@ +// 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 db package +// will convert all types into one of the following: +// +// int64 +// float64 +// bool +// nil +// []byte +// string [*] everywhere except from Rows.Next. +// +package driver + +import ( + "os" +) + +// Driver is the interface that must be implemented by a database +// driver. +type Driver interface { + // Open returns a new or cached connection to the database. + // The name is a string in a driver-specific format. + // + // The returned connection is only used by one goroutine at a + // time. + Open(name string) (Conn, os.Error) +} + +// Execer is an optional interface that may be implemented by a Driver +// or a Conn. +// +// If a Driver does not implement Execer, the sql package's DB.Exec +// method first obtains a free connection from its free pool or from +// the driver's Open method. Execer should only be implemented by +// drivers that can provide a more efficient implementation. +// +// 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. +type Execer interface { + Exec(query string, args []interface{}) (Result, os.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, os.Error) + + // Close invalidates and potentially stops any current + // prepared statements and transactions, marking this + // connection as no longer in use. The driver may cache or + // close its underlying connection to its database. + Close() os.Error + + // Begin starts and returns a new transaction. + Begin() (Tx, os.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, os.Error) + + // RowsAffected returns the number of rows affected by the + // query. + RowsAffected() (int64, os.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. + Close() os.Error + + // NumInput returns the number of placeholder parameters. + 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, os.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, os.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() os.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(dest []interface{}) os.Error +} + +// Tx is a transaction. +type Tx interface { + Commit() os.Error + Rollback() os.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, os.Error) { + return 0, os.NewError("no LastInsertId available") +} + +func (v RowsAffected) RowsAffected() (int64, os.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, os.Error) { + return 0, os.NewError("no LastInsertId available after DDL statement") +} + +func (ddlSuccess) RowsAffected() (int64, os.Error) { + return 0, os.NewError("no RowsAffected available after DDL statement") +} diff --git a/src/pkg/exp/sql/driver/types.go b/src/pkg/exp/sql/driver/types.go new file mode 100644 index 0000000000..5521d5389c --- /dev/null +++ b/src/pkg/exp/sql/driver/types.go @@ -0,0 +1,161 @@ +// 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" + "os" + "reflect" + "strconv" +) + +// ValueConverter is the interface providing the ConvertValue method. +type ValueConverter interface { + // ConvertValue converts a value to a restricted subset type. + ConvertValue(v interface{}) (interface{}, os.Error) +} + +// Bool is a ValueConverter that converts input values to bools. +// +// The conversion rules are: +// - .... TODO(bradfitz): TBD +var Bool boolType + +type boolType struct{} + +var _ ValueConverter = boolType{} + +func (boolType) ConvertValue(v interface{}) (interface{}, os.Error) { + return nil, fmt.Errorf("TODO(bradfitz): bool conversions") +} + +// 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{}, os.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{}, os.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 +// string +// +// This is the ame 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 +// +// 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: + 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{}, os.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()) +} |
