xorm/db.go

224 lines
5.8 KiB
Go
Raw Normal View History

// Copyright 2019 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2014-04-08 12:57:04 +00:00
package core
import (
"context"
2014-04-08 12:57:04 +00:00
"database/sql"
"database/sql/driver"
"fmt"
2014-04-08 12:57:04 +00:00
"reflect"
"regexp"
2018-03-15 09:10:08 +00:00
"sync"
)
var (
DefaultCacheSize = 200
2014-04-08 12:57:04 +00:00
)
func MapToSlice(query string, mp interface{}) (string, []interface{}, error) {
vv := reflect.ValueOf(mp)
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
return "", []interface{}{}, ErrNoMapPointer
}
args := make([]interface{}, 0, len(vv.Elem().MapKeys()))
var err error
2014-04-08 12:57:04 +00:00
query = re.ReplaceAllStringFunc(query, func(src string) string {
v := vv.Elem().MapIndex(reflect.ValueOf(src[1:]))
if !v.IsValid() {
err = fmt.Errorf("map key %s is missing", src[1:])
} else {
args = append(args, v.Interface())
}
2014-04-08 12:57:04 +00:00
return "?"
})
return query, args, err
2014-04-08 12:57:04 +00:00
}
func StructToSlice(query string, st interface{}) (string, []interface{}, error) {
vv := reflect.ValueOf(st)
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
return "", []interface{}{}, ErrNoStructPointer
}
args := make([]interface{}, 0)
var err error
2014-04-08 12:57:04 +00:00
query = re.ReplaceAllStringFunc(query, func(src string) string {
fv := vv.Elem().FieldByName(src[1:]).Interface()
if v, ok := fv.(driver.Valuer); ok {
var value driver.Value
value, err = v.Value()
if err != nil {
return "?"
}
args = append(args, value)
} else {
args = append(args, fv)
}
2014-04-08 12:57:04 +00:00
return "?"
})
if err != nil {
return "", []interface{}{}, err
}
2014-04-08 12:57:04 +00:00
return query, args, nil
}
2018-03-15 09:10:08 +00:00
type cacheStruct struct {
value reflect.Value
idx int
}
// DB is a wrap of sql.DB with extra contents
2014-04-08 12:57:04 +00:00
type DB struct {
*sql.DB
2018-03-15 09:10:08 +00:00
Mapper IMapper
reflectCache map[reflect.Type]*cacheStruct
reflectCacheMutex sync.RWMutex
2014-04-08 12:57:04 +00:00
}
// Open opens a database
2014-04-08 12:57:04 +00:00
func Open(driverName, dataSourceName string) (*DB, error) {
db, err := sql.Open(driverName, dataSourceName)
2015-12-30 08:14:49 +00:00
if err != nil {
return nil, err
}
2018-03-15 09:10:08 +00:00
return &DB{
DB: db,
Mapper: NewCacheMapper(&SnakeMapper{}),
reflectCache: make(map[reflect.Type]*cacheStruct),
}, nil
2014-04-08 12:57:04 +00:00
}
// FromDB creates a DB from a sql.DB
2015-04-07 12:15:05 +00:00
func FromDB(db *sql.DB) *DB {
2018-03-15 09:10:08 +00:00
return &DB{
DB: db,
Mapper: NewCacheMapper(&SnakeMapper{}),
reflectCache: make(map[reflect.Type]*cacheStruct),
}
}
func (db *DB) reflectNew(typ reflect.Type) reflect.Value {
db.reflectCacheMutex.Lock()
defer db.reflectCacheMutex.Unlock()
cs, ok := db.reflectCache[typ]
if !ok || cs.idx+1 > DefaultCacheSize-1 {
cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), DefaultCacheSize, DefaultCacheSize), 0}
db.reflectCache[typ] = cs
} else {
cs.idx = cs.idx + 1
}
return cs.value.Index(cs.idx).Addr()
2015-04-07 12:15:05 +00:00
}
// QueryContext overwrites sql.DB.QueryContext
func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
rows, err := db.DB.QueryContext(ctx, query, args...)
2015-12-30 08:14:49 +00:00
if err != nil {
if rows != nil {
rows.Close()
}
return nil, err
}
2018-03-15 09:10:08 +00:00
return &Rows{rows, db}, nil
2014-04-08 12:57:04 +00:00
}
// Query overwrites sql.DB.Query
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
return db.QueryContext(context.Background(), query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryMapContext(ctx context.Context, query string, mp interface{}) (*Rows, error) {
query, args, err := MapToSlice(query, mp)
2014-04-08 12:57:04 +00:00
if err != nil {
return nil, err
}
return db.QueryContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) {
return db.QueryMapContext(context.Background(), query, mp)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryStructContext(ctx context.Context, query string, st interface{}) (*Rows, error) {
2014-04-08 12:57:04 +00:00
query, args, err := StructToSlice(query, st)
if err != nil {
return nil, err
}
return db.QueryContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
return db.QueryStructContext(context.Background(), query, st)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row {
rows, err := db.QueryContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
if err != nil {
return &Row{nil, err}
2014-04-08 12:57:04 +00:00
}
return &Row{rows, nil}
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryRow(query string, args ...interface{}) *Row {
return db.QueryRowContext(context.Background(), query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryRowMapContext(ctx context.Context, query string, mp interface{}) *Row {
query, args, err := MapToSlice(query, mp)
if err != nil {
return &Row{nil, err}
2014-04-08 12:57:04 +00:00
}
return db.QueryRowContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
return db.QueryRowMapContext(context.Background(), query, mp)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryRowStructContext(ctx context.Context, query string, st interface{}) *Row {
query, args, err := StructToSlice(query, st)
if err != nil {
return &Row{nil, err}
2014-04-08 12:57:04 +00:00
}
return db.QueryRowContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) QueryRowStruct(query string, st interface{}) *Row {
return db.QueryRowStructContext(context.Background(), query, st)
2014-04-08 12:57:04 +00:00
}
var (
re = regexp.MustCompile(`[?](\w+)`)
)
// insert into (name) values (?)
// insert into (name) values (?name)
func (db *DB) ExecMapContext(ctx context.Context, query string, mp interface{}) (sql.Result, error) {
2014-04-08 12:57:04 +00:00
query, args, err := MapToSlice(query, mp)
if err != nil {
return nil, err
}
return db.DB.ExecContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) {
return db.ExecMapContext(context.Background(), query, mp)
2014-04-08 12:57:04 +00:00
}
func (db *DB) ExecStructContext(ctx context.Context, query string, st interface{}) (sql.Result, error) {
2014-04-08 12:57:04 +00:00
query, args, err := StructToSlice(query, st)
if err != nil {
return nil, err
}
return db.DB.ExecContext(ctx, query, args...)
2014-04-08 12:57:04 +00:00
}
func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) {
return db.ExecStructContext(context.Background(), query, st)
2014-04-08 12:57:04 +00:00
}