205 lines
7.6 KiB
Markdown
205 lines
7.6 KiB
Markdown
# 5.1 database/sql interface
|
|
|
|
Go doesn't provide any official database driver which PHP does, but it does have some database driver interface standards for developers develop database drivers. There is an advantage that if your code is developed according to these interface standards, you will not change any code when your database changes. Let's see what these database interface standards are.
|
|
|
|
## sql.Register
|
|
|
|
This function is in package `database/sql` for registering database drivers when you use third-party database drivers. In all those drivers, they should call function `Register(name string, driver driver.Driver)` in `init()` function in order to register their drivers.
|
|
|
|
Let's take a look at corresponding code in drivers of mymysql and sqlite3:
|
|
|
|
//https://github.com/mattn/go-sqlite3 driver
|
|
func init() {
|
|
sql.Register("sqlite3", &SQLiteDriver{})
|
|
}
|
|
|
|
//https://github.com/mikespook/mymysql driver
|
|
// Driver automatically registered in database/sql
|
|
var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
|
|
func init() {
|
|
Register("SET NAMES utf8")
|
|
sql.Register("mymysql", &d)
|
|
}
|
|
|
|
We see that all third-party database drivers implemented this function to register themselves, and Go uses a map to save user drivers inside of `databse/sql`.
|
|
|
|
var drivers = make(map[string]driver.Driver)
|
|
|
|
drivers[name] = driver
|
|
|
|
Therefore, this register function can register drivers as many as you want with all different name.
|
|
|
|
We always see following code when we use third-party drivers:
|
|
|
|
import (
|
|
"database/sql"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
Here the blank `_` is quite confusing for many beginners, and this is a great design in Go. We know this identifier is for discarding values from function return, and you have to use all imported packages in your code in Go. So when the blank is used with import means you need to execute init() function of that package without directly using it, which is for registering database driver.
|
|
|
|
## driver.Driver
|
|
|
|
`Driver` is a interface that has a method `Open(name string)` that returns a interface of `Conn`.
|
|
|
|
type Driver interface {
|
|
Open(name string) (Conn, error)
|
|
}
|
|
|
|
This is a one-time Conn, which means it can be used only once in one goroutine. The following code will occur errors:
|
|
|
|
...
|
|
go goroutineA (Conn) // query
|
|
go goroutineB (Conn) // insert
|
|
...
|
|
|
|
Because Go has no idea about which goroutine does what operation, so the query operation may get result of insert, vice-versa.
|
|
|
|
All third-party drivers should have this function to parse name of Conn and return results correctly.
|
|
|
|
## driver.Conn
|
|
|
|
This is a database connection interface with some methods, and as I said above, same Conn can only be used in one goroutine.
|
|
|
|
type Conn interface {
|
|
Prepare(query string) (Stmt, error)
|
|
Close() error
|
|
Begin() (Tx, error)
|
|
}
|
|
|
|
- `Prepare` returns prepare status of corresponding SQL commands for querying and deleting, etc.
|
|
- `Close` closes current connection and clean resources. Most of third-party drivers implemented some kinds of connection pool, so you don't need to cache connections unless you want to have unexpected errors.
|
|
- `Begin` returns a Tx that represents a affair handle, you can use it for querying, updating or affair roll back etc.
|
|
|
|
## driver.Stmt
|
|
|
|
This is a ready status and is corresponding with Conn, so it can only be used in one goroutine like Conn.
|
|
|
|
type Stmt interface {
|
|
Close() error
|
|
NumInput() int
|
|
Exec(args []Value) (Result, error)
|
|
Query(args []Value) (Rows, error)
|
|
}
|
|
|
|
- `Close` closes current connection, but it still returns rows data if it is doing query operation.
|
|
- `NumInput` returns the number of obligate arguments, database drivers should check caller's arguments when the result is greater than 0, and it returns -1 when database drivers don't know any obligate argument.
|
|
- `Exec` executes SQL commands of `update/insert` that are prepared in `Prepare`, returns `Result`.
|
|
- `Query` executes SQL commands of `select` that are prepared in `Prepare`, returns rows data.
|
|
|
|
## driver.Tx
|
|
|
|
Generally, affair handle only have submit or roll back, and database drivers only need to implement these two methods.
|
|
|
|
type Tx interface {
|
|
Commit() error
|
|
Rollback() error
|
|
}
|
|
|
|
## driver.Execer
|
|
|
|
This is an optional interface.
|
|
|
|
type Execer interface {
|
|
Exec(query string, args []Value) (Result, error)
|
|
}
|
|
|
|
If the driver doesn't implement this interface, then when you call DB.Exec, it automatically calls Prepare and returns Stmt, then executes Exec of Stmt, then closes Stmt.
|
|
|
|
## driver.Result
|
|
|
|
This is the interface for result of `update/insert` operations.
|
|
|
|
type Result interface {
|
|
LastInsertId() (int64, error)
|
|
RowsAffected() (int64, error)
|
|
}
|
|
|
|
- `LastInsertId` returns auto-increment Id number after insert operation from database.
|
|
- `RowsAffected` returns rows that affected after query operation.
|
|
|
|
## driver.Rows
|
|
|
|
This is the interface for result set of query operation.
|
|
|
|
type Rows interface {
|
|
Columns() []string
|
|
Close() error
|
|
Next(dest []Value) error
|
|
}
|
|
|
|
- `Columns` returns fields information of database tables, the slice is one-to-one correspondence to SQL query field, not all fields in that database table.
|
|
- `Close` closes Rows iterator.
|
|
- `Next` returns next data and assigns to dest, all string should be converted to byte array, and gets io.EOF error if no more data available.
|
|
|
|
## diriver.RowsAffected
|
|
|
|
This is a alias of int64, but it implemented Result interface.
|
|
|
|
type RowsAffected int64
|
|
|
|
func (RowsAffected) LastInsertId() (int64, error)
|
|
|
|
func (v RowsAffected) RowsAffected() (int64, error)
|
|
|
|
## driver.Value
|
|
|
|
This is a empty interface that can contain any kind of data.
|
|
|
|
type Value interface{}
|
|
|
|
The Value must be somewhat that drivers can operate or nil, so it should be one of following types:
|
|
|
|
int64
|
|
float64
|
|
bool
|
|
[]byte
|
|
string [*] Except Rows.Next which cannot return string
|
|
time.Time
|
|
|
|
## driver.ValueConverter
|
|
|
|
This defines a interface for converting normal values to driver.Value.
|
|
|
|
type ValueConverter interface {
|
|
ConvertValue(v interface{}) (Value, error)
|
|
}
|
|
|
|
This is commonly used in database drivers and has many good features:
|
|
|
|
- Convert driver.Value to corresponding database field type, for example convert int64 to uint16.
|
|
- Convert database query results to driver.Value.
|
|
- Convert driver.Value to user defined value in `scan` function.
|
|
|
|
## driver.Valuer
|
|
|
|
This defines a interface for returning driver.Value.
|
|
|
|
type Valuer interface {
|
|
Value() (Value, error)
|
|
}
|
|
|
|
Many types implemented this interface for conversion between driver.Value and itself.
|
|
|
|
At this point, you should have concepts about how to develop a database driver. Once you implement about interfaces for operations like add, delete, update, etc. There only left problems about communicating with specific database.
|
|
|
|
## database/sql
|
|
|
|
databse/sql defines more high-level methods above database/sql/driver for more convenient operations with databases, and it suggests you to implement a connection pool.
|
|
|
|
type DB struct {
|
|
driver driver.Driver
|
|
dsn string
|
|
mu sync.Mutex // protects freeConn and closed
|
|
freeConn []driver.Conn
|
|
closed bool
|
|
}
|
|
|
|
As you can see, Open function returns a DB that has a freeConn, and this is the simple connection pool. Its implementation is very simple or ugly, it uses `defer db.putConn(ci, err)` in function Db.prepare to put connection into connection pool. Every time you call Conn function, it checks length of freeCoon, if it's greater than 0 means there is a reusable connection and directly returns to you, otherwise it creates a new connection and returns.
|
|
|
|
## Links
|
|
|
|
- [Directory](preface.md)
|
|
- Previous section: [Database](05.0.md)
|
|
- Next section: [MySQL](05.2.md)
|