Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Зачем и как написать свой database/sql драйвер

Зачем и как написать свой database/sql драйвер

Iskander (Alex) Sharipov

February 16, 2019
Tweet

More Decks by Iskander (Alex) Sharipov

Other Decks in Programming

Transcript

  1. Зачем? • Написание нового драйвера для SQL СУБД • или

    исправление существующего • Написание «наддрайвера» • для observability и отладки • Написание драйвера для NoSQL СУБД • или даже не для СУБД
  2. Зачем? • Написание нового драйвера для SQL СУБД • https://github.com/AlekSi/mysqlx

    • Написание «наддрайвера» • https://github.com/luna-duclos/instrumentedsql • Написание драйвера для NoSQL СУБД • https://github.com/AlekSi/sqlcsv
  3. • func IsScanValue(v interface{}) bool • func IsValue(v interface{}) bool

    • type ColumnConverter • type Conn • type Driver • type Execer • type NotNull • func (n NotNull) ConvertValue(v interface{}) (Value, error) • type Null • func (n Null) ConvertValue(v interface{}) (Value, error) • type Result • type Rows • type RowsAffected • func (RowsAffected) LastInsertId() (int64, error) • func (v RowsAffected) RowsAffected() (int64, error) • type Stmt • type Tx • type Value • type ValueConverter • type Valuer
  4. • func IsScanValue(v interface{}) bool • func IsValue(v interface{}) bool

    • type ColumnConverter • type Conn • type ConnBeginTx • type ConnPrepareContext • type Connector • type Driver • type DriverContext • type Execer • type ExecerContext • type IsolationLevel • type NamedValue • type NamedValueChecker • type NotNull • func (n NotNull) ConvertValue(v interface{}) (Value, error) • type Null • func (n Null) ConvertValue(v interface{}) (Value, error) • type Pinger • type Queryer • type QueryerContext • type Result • type Rows • type RowsAffected • func (RowsAffected) LastInsertId() (int64, error) • func (v RowsAffected) RowsAffected() (int64, error) • type RowsColumnTypeDatabaseTypeName • type RowsColumnTypeLength • type RowsColumnTypeNullable • type RowsColumnTypePrecisionScale • type RowsColumnTypeScanType • type RowsNextResultSet • type SessionResetter • type Stmt • type StmtExecContext • type StmtQueryContext • type Tx • type TxOptions • type Value • type ValueConverter • type Valuer
  5. 3 источника знаний • Код database/sql и database/sql/driver • Код

    существующих драйверов • но полно плохого и старого • Кровь, пот и падающий прод
  6. Driver // Database drivers
 // may implement DriverContext […]
 type

    Driver interface {
 Open(name string) (Conn, error)
 }
  7. Conn type Conn interface {
 Prepare(query string) (Stmt, error)
 Close()

    error
 
 // Deprecated: Drivers should
 // implement ConnBeginTx instead
 // (or additionally).
 Begin() (Tx, error)
 }
  8. Stmt type Stmt interface {
 Close() error
 
 // NumInput

    may also return -1 […]
 NumInput() int
 
 // Deprecated: Drivers should implement
 // StmtExecContext instead (or additionally).
 Exec(args []Value) (Result, error)
 // Deprecated: Drivers should implement
 // StmtQueryContext instead (or additionally).
 Query(args []Value) (Rows, error)
 }
  9. Value Value is a value that drivers must be able

    to handle. It is either nil, a type handled by a database driver's NamedValueChecker interface, or an instance of one of these types:
 int64
 float64
 bool
 []byte
 string
 time.Time type Value interface{}
  10. ErrBadConn ErrBadConn should be returned by a driver to signal

    to the sql package that a driver.Conn is in a bad state […] and the sql package should retry on a new connection. var ErrBadConn = errors.New("[…]")
  11. Минимум • driver.Open(name string) (Conn, error) • conn.Prepare(query string) (Stmt,

    error) • stmt.Exec(args []Value) (Result, error) • stmt.Query(args []Value) (Rows, error) • rows.Next(dest []Value) error
  12. QueryerContext If a Conn does not implement QueryerContext […] DB.Query

    will first prepare a query, execute the statement, and then close the statement. QueryerContext may return ErrSkip. type QueryerContext interface {
 QueryContext(context.Context, string,
 []NamedValue) (Rows, error)
 }
  13. ErrSkip 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("[…]")
  14. Valuer и Scanner type JSON interface{} func (j JSON) Value()

    (driver.Value, error)
 func (j *JSON) Scan(value interface{}) error var _ driver.Valuer = JSON{}
 var _ sql.Scanner = &JSON{}
  15. Опциональные интерфейсы func (c wrappedConn) Query(query string,
 args []driver.Value)
 (driver.Rows,

    error) {
 
 queryer, ok := c.parent.(driver.Queryer)
 if ok {
 return queryer.Query(query, args)
 }
 // ???
  16. Опциональные интерфейсы func (c wrappedConn) Query(query string,
 args []driver.Value)
 (driver.Rows,

    error) {
 
 queryer, ok := c.parent.(driver.Queryer)
 if ok {
 return queryer.Query(query, args)
 }
 return nil, driver.ErrSkip
  17. Опциональные интерфейсы • reflect • слишком много багов :( •

    Кодогенерация • только для конкретного драйвера • Реализация как в database/sql • SessionResetter – возвращаем ErrBadConn
  18. Context // Deprecated: Drivers should implement ExecerContext instead.
 type Execer

    interface {
 Exec(string, []Value) (Result, error)
 } type ExecerContext interface {
 ExecContext(ctx, string, []NamedValue)
 (Result, error)
 }
  19. Тестируйте • На реальных системах, с минимумом моков • На

    всех комбинациях Go и версий систем • Docker Compose отлично для этого подходит