mydb/adapter/mysql/database.go
2023-09-18 15:15:42 +08:00

169 lines
3.4 KiB
Go

// Package mysql wraps the github.com/go-sql-driver/mysql MySQL driver. See
// https://github.com/upper/db/adapter/mysql for documentation, particularities and usage
// examples.
package mysql
import (
"reflect"
"strings"
"database/sql"
"git.hexq.cn/tiglog/mydb"
"git.hexq.cn/tiglog/mydb/internal/sqladapter"
"git.hexq.cn/tiglog/mydb/internal/sqladapter/exql"
_ "github.com/go-sql-driver/mysql" // MySQL driver.
)
// database is the actual implementation of Database
type database struct {
}
func (*database) Template() *exql.Template {
return template
}
func (*database) OpenDSN(sess sqladapter.Session, dsn string) (*sql.DB, error) {
return sql.Open("mysql", dsn)
}
func (*database) Collections(sess sqladapter.Session) (collections []string, err error) {
q := sess.SQL().
Select("table_name").
From("information_schema.tables").
Where("table_schema = ?", sess.Name())
iter := q.Iterator()
defer iter.Close()
for iter.Next() {
var tableName string
if err := iter.Scan(&tableName); err != nil {
return nil, err
}
collections = append(collections, tableName)
}
if err := iter.Err(); err != nil {
return nil, err
}
return collections, nil
}
func (d *database) ConvertValue(in interface{}) interface{} {
switch v := in.(type) {
case *map[string]interface{}:
return (*JSONMap)(v)
case map[string]interface{}:
return (*JSONMap)(&v)
}
dv := reflect.ValueOf(in)
if dv.IsValid() {
if dv.Type().Kind() == reflect.Ptr {
dv = dv.Elem()
}
switch dv.Kind() {
case reflect.Map:
if reflect.TypeOf(in).Kind() == reflect.Ptr {
w := reflect.ValueOf(in)
z := reflect.New(w.Elem().Type())
w.Elem().Set(z.Elem())
}
return &JSON{in}
case reflect.Slice:
return &JSON{in}
}
}
return in
}
func (*database) Err(err error) error {
if err != nil {
// This error is not exported so we have to check it by its string value.
s := err.Error()
if strings.Contains(s, `many connections`) {
return mydb.ErrTooManyClients
}
}
return err
}
func (*database) NewCollection() sqladapter.CollectionAdapter {
return &collectionAdapter{}
}
func (*database) LookupName(sess sqladapter.Session) (string, error) {
q := sess.SQL().
Select(mydb.Raw("DATABASE() AS name"))
iter := q.Iterator()
defer iter.Close()
if iter.Next() {
var name string
if err := iter.Scan(&name); err != nil {
return "", err
}
return name, nil
}
return "", iter.Err()
}
func (*database) TableExists(sess sqladapter.Session, name string) error {
q := sess.SQL().
Select("table_name").
From("information_schema.tables").
Where("table_schema = ? AND table_name = ?", sess.Name(), name)
iter := q.Iterator()
defer iter.Close()
if iter.Next() {
var name string
if err := iter.Scan(&name); err != nil {
return err
}
return nil
}
if err := iter.Err(); err != nil {
return err
}
return mydb.ErrCollectionDoesNotExist
}
func (*database) PrimaryKeys(sess sqladapter.Session, tableName string) ([]string, error) {
q := sess.SQL().
Select("k.column_name").
From("information_schema.key_column_usage AS k").
Where(`
k.constraint_name = 'PRIMARY'
AND k.table_schema = ?
AND k.table_name = ?
`, sess.Name(), tableName).
OrderBy("k.ordinal_position")
iter := q.Iterator()
defer iter.Close()
pk := []string{}
for iter.Next() {
var k string
if err := iter.Scan(&k); err != nil {
return nil, err
}
pk = append(pk, k)
}
if err := iter.Err(); err != nil {
return nil, err
}
return pk, nil
}