However, the enum tree is not supported since they do not support them. But other than that, mysql and maria DB seem to both be supported.
218 lines
5.3 KiB
Go
218 lines
5.3 KiB
Go
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
//
|
|
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package mysql
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
)
|
|
|
|
type mysqlStmt struct {
|
|
mc *mysqlConn
|
|
id uint32
|
|
paramCount int
|
|
}
|
|
|
|
func (stmt *mysqlStmt) Close() error {
|
|
if stmt.mc == nil || stmt.mc.closed.Load() {
|
|
// driver.Stmt.Close can be called more than once, thus this function
|
|
// has to be idempotent.
|
|
// See also Issue #450 and golang/go#16019.
|
|
//errLog.Print(ErrInvalidConn)
|
|
return driver.ErrBadConn
|
|
}
|
|
|
|
err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
|
|
stmt.mc = nil
|
|
return err
|
|
}
|
|
|
|
func (stmt *mysqlStmt) NumInput() int {
|
|
return stmt.paramCount
|
|
}
|
|
|
|
func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
|
|
return converter{}
|
|
}
|
|
|
|
func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
|
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
|
return
|
|
}
|
|
|
|
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
|
if stmt.mc.closed.Load() {
|
|
stmt.mc.log(ErrInvalidConn)
|
|
return nil, driver.ErrBadConn
|
|
}
|
|
// Send command
|
|
err := stmt.writeExecutePacket(args)
|
|
if err != nil {
|
|
return nil, stmt.mc.markBadConn(err)
|
|
}
|
|
|
|
mc := stmt.mc
|
|
handleOk := stmt.mc.clearResult()
|
|
|
|
// Read Result
|
|
resLen, err := handleOk.readResultSetHeaderPacket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resLen > 0 {
|
|
// Columns
|
|
if err = mc.readUntilEOF(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Rows
|
|
if err := mc.readUntilEOF(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := handleOk.discardResults(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
copied := mc.result
|
|
return &copied, nil
|
|
}
|
|
|
|
func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
|
return stmt.query(args)
|
|
}
|
|
|
|
func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
|
if stmt.mc.closed.Load() {
|
|
stmt.mc.log(ErrInvalidConn)
|
|
return nil, driver.ErrBadConn
|
|
}
|
|
// Send command
|
|
err := stmt.writeExecutePacket(args)
|
|
if err != nil {
|
|
return nil, stmt.mc.markBadConn(err)
|
|
}
|
|
|
|
mc := stmt.mc
|
|
|
|
// Read Result
|
|
handleOk := stmt.mc.clearResult()
|
|
resLen, err := handleOk.readResultSetHeaderPacket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rows := new(binaryRows)
|
|
|
|
if resLen > 0 {
|
|
rows.mc = mc
|
|
rows.rs.columns, err = mc.readColumns(resLen)
|
|
} else {
|
|
rows.rs.done = true
|
|
|
|
switch err := rows.NextResultSet(); err {
|
|
case nil, io.EOF:
|
|
return rows, nil
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return rows, err
|
|
}
|
|
|
|
var jsonType = reflect.TypeOf(json.RawMessage{})
|
|
|
|
type converter struct{}
|
|
|
|
// ConvertValue mirrors the reference/default converter in database/sql/driver
|
|
// with _one_ exception. We support uint64 with their high bit and the default
|
|
// implementation does not. This function should be kept in sync with
|
|
// database/sql/driver defaultConverter.ConvertValue() except for that
|
|
// deliberate difference.
|
|
func (c converter) ConvertValue(v any) (driver.Value, error) {
|
|
if driver.IsValue(v) {
|
|
return v, nil
|
|
}
|
|
|
|
if vr, ok := v.(driver.Valuer); ok {
|
|
sv, err := callValuerValue(vr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if driver.IsValue(sv) {
|
|
return sv, nil
|
|
}
|
|
// A value returned from the Valuer interface can be "a type handled by
|
|
// a database driver's NamedValueChecker interface" so we should accept
|
|
// uint64 here as well.
|
|
if u, ok := sv.(uint64); ok {
|
|
return u, nil
|
|
}
|
|
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
|
|
}
|
|
rv := reflect.ValueOf(v)
|
|
switch rv.Kind() {
|
|
case reflect.Ptr:
|
|
// indirect pointers
|
|
if rv.IsNil() {
|
|
return nil, nil
|
|
} else {
|
|
return c.ConvertValue(rv.Elem().Interface())
|
|
}
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return rv.Int(), nil
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return rv.Uint(), nil
|
|
case reflect.Float32, reflect.Float64:
|
|
return rv.Float(), nil
|
|
case reflect.Bool:
|
|
return rv.Bool(), nil
|
|
case reflect.Slice:
|
|
switch t := rv.Type(); {
|
|
case t == jsonType:
|
|
return v, nil
|
|
case t.Elem().Kind() == reflect.Uint8:
|
|
return rv.Bytes(), nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
|
|
}
|
|
case reflect.String:
|
|
return rv.String(), nil
|
|
}
|
|
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
|
|
}
|
|
|
|
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
|
|
|
// callValuerValue returns vr.Value(), with one exception:
|
|
// If vr.Value is an auto-generated method on a pointer type and the
|
|
// pointer is nil, it would panic at runtime in the panicwrap
|
|
// method. Treat it like nil instead.
|
|
//
|
|
// This is so people can implement driver.Value on value types and
|
|
// still use nil pointers to those types to mean nil/NULL, just like
|
|
// string/*string.
|
|
//
|
|
// This is an exact copy of the same-named unexported function from the
|
|
// database/sql package.
|
|
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
|
|
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
|
|
rv.IsNil() &&
|
|
rv.Type().Elem().Implements(valuerReflectType) {
|
|
return nil, nil
|
|
}
|
|
return vr.Value()
|
|
}
|