init core after sperated repository
This commit is contained in:
commit
a18c751d44
|
@ -0,0 +1 @@
|
||||||
|
go test -v -bench=. -run=XXX
|
|
@ -0,0 +1,77 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// default cache expired time
|
||||||
|
CacheExpired = 60 * time.Minute
|
||||||
|
// not use now
|
||||||
|
CacheMaxMemory = 256
|
||||||
|
// evey ten minutes to clear all expired nodes
|
||||||
|
CacheGcInterval = 10 * time.Minute
|
||||||
|
// each time when gc to removed max nodes
|
||||||
|
CacheGcMaxRemoved = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
// CacheStore is a interface to store cache
|
||||||
|
type CacheStore interface {
|
||||||
|
// key is primary key or composite primary key or unique key's value
|
||||||
|
// value is struct's pointer
|
||||||
|
// key format : <tablename>-p-<pk1>-<pk2>...
|
||||||
|
Put(key string, value interface{}) error
|
||||||
|
Get(key string) (interface{}, error)
|
||||||
|
Del(key string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cacher is an interface to provide cache
|
||||||
|
// id format : u-<pk1>-<pk2>...
|
||||||
|
type Cacher interface {
|
||||||
|
GetIds(tableName, sql string) interface{}
|
||||||
|
GetBean(tableName string, id string) interface{}
|
||||||
|
PutIds(tableName, sql string, ids interface{})
|
||||||
|
PutBean(tableName string, id string, obj interface{})
|
||||||
|
DelIds(tableName, sql string)
|
||||||
|
DelBean(tableName string, id string)
|
||||||
|
ClearIds(tableName string)
|
||||||
|
ClearBeans(tableName string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIds(ids []PK) (string, error) {
|
||||||
|
b, err := json.Marshal(ids)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeIds(s string) ([]PK, error) {
|
||||||
|
pks := make([]PK, 0)
|
||||||
|
err := json.Unmarshal([]byte(s), &pks)
|
||||||
|
return pks, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCacheSql(m Cacher, tableName, sql string, args interface{}) ([]PK, error) {
|
||||||
|
bytes := m.GetIds(tableName, GenSqlKey(sql, args))
|
||||||
|
if bytes == nil {
|
||||||
|
return nil, errors.New("Not Exist")
|
||||||
|
}
|
||||||
|
return decodeIds(bytes.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutCacheSql(m Cacher, ids []PK, tableName, sql string, args interface{}) error {
|
||||||
|
bytes, err := encodeIds(ids)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.PutIds(tableName, GenSqlKey(sql, args), bytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenSqlKey(sql string, args interface{}) string {
|
||||||
|
return fmt.Sprintf("%v-%v", sql, args)
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TWOSIDES = iota + 1
|
||||||
|
ONLYTODB
|
||||||
|
ONLYFROMDB
|
||||||
|
)
|
||||||
|
|
||||||
|
// database column
|
||||||
|
type Column struct {
|
||||||
|
Name string
|
||||||
|
FieldName string
|
||||||
|
SQLType SQLType
|
||||||
|
Length int
|
||||||
|
Length2 int
|
||||||
|
Nullable bool
|
||||||
|
Default string
|
||||||
|
Indexes map[string]bool
|
||||||
|
IsPrimaryKey bool
|
||||||
|
IsAutoIncrement bool
|
||||||
|
MapType int
|
||||||
|
IsCreated bool
|
||||||
|
IsUpdated bool
|
||||||
|
IsCascade bool
|
||||||
|
IsVersion bool
|
||||||
|
fieldPath []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
|
||||||
|
return &Column{name, fieldName, sqlType, len1, len2, nullable, "", make(map[string]bool), false, false,
|
||||||
|
TWOSIDES, false, false, false, false, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate column description string according dialect
|
||||||
|
func (col *Column) String(d Dialect) string {
|
||||||
|
sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
|
||||||
|
|
||||||
|
sql += d.SqlType(col) + " "
|
||||||
|
|
||||||
|
if col.IsPrimaryKey {
|
||||||
|
sql += "PRIMARY KEY "
|
||||||
|
if col.IsAutoIncrement {
|
||||||
|
sql += d.AutoIncrStr() + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if col.Nullable {
|
||||||
|
sql += "NULL "
|
||||||
|
} else {
|
||||||
|
sql += "NOT NULL "
|
||||||
|
}
|
||||||
|
|
||||||
|
if col.Default != "" {
|
||||||
|
sql += "DEFAULT " + col.Default + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (col *Column) StringNoPk(d Dialect) string {
|
||||||
|
sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
|
||||||
|
|
||||||
|
sql += d.SqlType(col) + " "
|
||||||
|
|
||||||
|
if col.Nullable {
|
||||||
|
sql += "NULL "
|
||||||
|
} else {
|
||||||
|
sql += "NOT NULL "
|
||||||
|
}
|
||||||
|
|
||||||
|
if col.Default != "" {
|
||||||
|
sql += "DEFAULT " + col.Default + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
// return col's filed of struct's value
|
||||||
|
func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) {
|
||||||
|
dataStruct := reflect.Indirect(reflect.ValueOf(bean))
|
||||||
|
return col.ValueOfV(&dataStruct)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
|
||||||
|
var fieldValue reflect.Value
|
||||||
|
var err error
|
||||||
|
if col.fieldPath == nil {
|
||||||
|
col.fieldPath = strings.Split(col.FieldName, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(col.fieldPath) == 1 {
|
||||||
|
fieldValue = dataStruct.FieldByName(col.FieldName)
|
||||||
|
} else if len(col.fieldPath) == 2 {
|
||||||
|
parentField := dataStruct.FieldByName(col.fieldPath[0])
|
||||||
|
if parentField.IsValid() {
|
||||||
|
fieldValue = parentField.FieldByName(col.fieldPath[1])
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("field %v is not valid", col.FieldName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("Unsupported mutliderive %v", col.FieldName)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &fieldValue, nil
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
// Conversion is an interface. A type implements Conversion will according
|
||||||
|
// the custom method to fill into database and retrieve from database.
|
||||||
|
type Conversion interface {
|
||||||
|
FromDB([]byte) error
|
||||||
|
ToDB() ([]byte, error)
|
||||||
|
}
|
|
@ -0,0 +1,566 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoMapPointer = errors.New("mp should be a map's pointer")
|
||||||
|
ErrNoStructPointer = errors.New("mp should be a map's pointer")
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||||
|
args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface())
|
||||||
|
return "?"
|
||||||
|
})
|
||||||
|
return query, args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||||
|
args = append(args, vv.Elem().FieldByName(src[1:]).Interface())
|
||||||
|
return "?"
|
||||||
|
})
|
||||||
|
return query, args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
*sql.DB
|
||||||
|
Mapper IMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(driverName, dataSourceName string) (*DB, error) {
|
||||||
|
db, err := sql.Open(driverName, dataSourceName)
|
||||||
|
return &DB{db, NewCacheMapper(&SnakeMapper{})}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
|
||||||
|
rows, err := db.DB.Query(query, args...)
|
||||||
|
return &Rows{rows, db.Mapper}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) {
|
||||||
|
query, args, err := MapToSlice(query, mp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db.Query(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) {
|
||||||
|
query, args, err := StructToSlice(query, st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db.Query(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Row struct {
|
||||||
|
*sql.Row
|
||||||
|
// One of these two will be non-nil:
|
||||||
|
err error // deferred error for easy chaining
|
||||||
|
Mapper IMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row *Row) Scan(dest ...interface{}) error {
|
||||||
|
if row.err != nil {
|
||||||
|
return row.err
|
||||||
|
}
|
||||||
|
return row.Row.Scan(dest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) QueryRow(query string, args ...interface{}) *Row {
|
||||||
|
row := db.DB.QueryRow(query, args...)
|
||||||
|
return &Row{row, nil, db.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) QueryRowMap(query string, mp interface{}) *Row {
|
||||||
|
query, args, err := MapToSlice(query, mp)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{nil, err, db.Mapper}
|
||||||
|
}
|
||||||
|
return db.QueryRow(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) QueryRowStruct(query string, st interface{}) *Row {
|
||||||
|
query, args, err := StructToSlice(query, st)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{nil, err, db.Mapper}
|
||||||
|
}
|
||||||
|
return db.QueryRow(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stmt struct {
|
||||||
|
*sql.Stmt
|
||||||
|
Mapper IMapper
|
||||||
|
names map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Prepare(query string) (*Stmt, error) {
|
||||||
|
names := make(map[string]int)
|
||||||
|
var i int
|
||||||
|
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||||
|
names[src[1:]] = i
|
||||||
|
i += 1
|
||||||
|
return "?"
|
||||||
|
})
|
||||||
|
|
||||||
|
stmt, err := db.DB.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Stmt{stmt, db.Mapper, names}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) {
|
||||||
|
vv := reflect.ValueOf(mp)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||||
|
return nil, errors.New("mp should be a map's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(s.names))
|
||||||
|
for k, i := range s.names {
|
||||||
|
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
|
||||||
|
}
|
||||||
|
return s.Stmt.Exec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) {
|
||||||
|
vv := reflect.ValueOf(st)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||||
|
return nil, errors.New("mp should be a map's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(s.names))
|
||||||
|
for k, i := range s.names {
|
||||||
|
args[i] = vv.Elem().FieldByName(k).Interface()
|
||||||
|
}
|
||||||
|
return s.Stmt.Exec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
|
||||||
|
rows, err := s.Stmt.Query(args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{rows, s.Mapper}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) {
|
||||||
|
vv := reflect.ValueOf(mp)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||||
|
return nil, errors.New("mp should be a map's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(s.names))
|
||||||
|
for k, i := range s.names {
|
||||||
|
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Query(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) {
|
||||||
|
vv := reflect.ValueOf(st)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||||
|
return nil, errors.New("mp should be a map's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(s.names))
|
||||||
|
for k, i := range s.names {
|
||||||
|
args[i] = vv.Elem().FieldByName(k).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Query(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) QueryRow(args ...interface{}) *Row {
|
||||||
|
row := s.Stmt.QueryRow(args...)
|
||||||
|
return &Row{row, nil, s.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) QueryRowMap(mp interface{}) *Row {
|
||||||
|
vv := reflect.ValueOf(mp)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||||
|
return &Row{nil, errors.New("mp should be a map's pointer"), s.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(s.names))
|
||||||
|
for k, i := range s.names {
|
||||||
|
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.QueryRow(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stmt) QueryRowStruct(st interface{}) *Row {
|
||||||
|
vv := reflect.ValueOf(st)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||||
|
return &Row{nil, errors.New("st should be a struct's pointer"), s.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(s.names))
|
||||||
|
for k, i := range s.names {
|
||||||
|
args[i] = vv.Elem().FieldByName(k).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.QueryRow(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
re = regexp.MustCompile(`[?](\w+)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// insert into (name) values (?)
|
||||||
|
// insert into (name) values (?name)
|
||||||
|
func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) {
|
||||||
|
query, args, err := MapToSlice(query, mp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db.DB.Exec(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) {
|
||||||
|
query, args, err := StructToSlice(query, st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db.DB.Exec(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rows struct {
|
||||||
|
*sql.Rows
|
||||||
|
Mapper IMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan data to a struct's pointer according field index
|
||||||
|
func (rs *Rows) ScanStruct(dest ...interface{}) error {
|
||||||
|
if len(dest) == 0 {
|
||||||
|
return errors.New("at least one struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
vvvs := make([]reflect.Value, len(dest))
|
||||||
|
for i, s := range dest {
|
||||||
|
vv := reflect.ValueOf(s)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||||
|
return errors.New("dest should be a struct's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
vvvs[i] = vv.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rs.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newDest := make([]interface{}, len(cols))
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
for _, vvv := range vvvs {
|
||||||
|
for j := 0; j < vvv.NumField(); j++ {
|
||||||
|
newDest[i] = vvv.Field(j).Addr().Interface()
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs.Rows.Scan(newDest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmptyScanner struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (EmptyScanner) Scan(src interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fieldCache = make(map[reflect.Type]map[string]int)
|
||||||
|
fieldCacheMutex sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func fieldByName(v reflect.Value, name string) reflect.Value {
|
||||||
|
t := v.Type()
|
||||||
|
fieldCacheMutex.RLock()
|
||||||
|
cache, ok := fieldCache[t]
|
||||||
|
fieldCacheMutex.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
cache = make(map[string]int)
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
cache[t.Field(i).Name] = i
|
||||||
|
}
|
||||||
|
fieldCacheMutex.Lock()
|
||||||
|
fieldCache[t] = cache
|
||||||
|
fieldCacheMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if i, ok := cache[name]; ok {
|
||||||
|
return v.Field(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.Zero(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan data to a struct's pointer according field name
|
||||||
|
func (rs *Rows) ScanStruct2(dest interface{}) error {
|
||||||
|
vv := reflect.ValueOf(dest)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct {
|
||||||
|
return errors.New("dest should be a struct's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rs.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDest := make([]interface{}, len(cols))
|
||||||
|
var v EmptyScanner
|
||||||
|
for j, name := range cols {
|
||||||
|
f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name))
|
||||||
|
if f.IsValid() {
|
||||||
|
newDest[j] = f.Addr().Interface()
|
||||||
|
} else {
|
||||||
|
newDest[j] = &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs.Rows.Scan(newDest...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheStruct struct {
|
||||||
|
value reflect.Value
|
||||||
|
idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
reflectCache = make(map[reflect.Type]*cacheStruct)
|
||||||
|
reflectCacheMutex sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReflectNew(typ reflect.Type) reflect.Value {
|
||||||
|
reflectCacheMutex.RLock()
|
||||||
|
cs, ok := reflectCache[typ]
|
||||||
|
reflectCacheMutex.RUnlock()
|
||||||
|
|
||||||
|
const newSize = 200
|
||||||
|
|
||||||
|
if !ok || cs.idx+1 > newSize-1 {
|
||||||
|
cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), newSize, newSize), 0}
|
||||||
|
reflectCacheMutex.Lock()
|
||||||
|
reflectCache[typ] = cs
|
||||||
|
reflectCacheMutex.Unlock()
|
||||||
|
} else {
|
||||||
|
reflectCacheMutex.Lock()
|
||||||
|
cs.idx = cs.idx + 1
|
||||||
|
reflectCacheMutex.Unlock()
|
||||||
|
}
|
||||||
|
return cs.value.Index(cs.idx).Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan data to a slice's pointer, slice's length should equal to columns' number
|
||||||
|
func (rs *Rows) ScanSlice(dest interface{}) error {
|
||||||
|
vv := reflect.ValueOf(dest)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Slice {
|
||||||
|
return errors.New("dest should be a slice's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
vvv := vv.Elem()
|
||||||
|
cols, err := rs.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDest := make([]interface{}, len(cols))
|
||||||
|
|
||||||
|
for j := 0; j < len(cols); j++ {
|
||||||
|
if j >= vvv.Len() {
|
||||||
|
newDest[j] = reflect.New(vvv.Type().Elem()).Interface()
|
||||||
|
} else {
|
||||||
|
newDest[j] = vvv.Index(j).Addr().Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rs.Rows.Scan(newDest...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
srcLen := vvv.Len()
|
||||||
|
for i := srcLen; i < len(cols); i++ {
|
||||||
|
vvv = reflect.Append(vvv, reflect.ValueOf(newDest[i]).Elem())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan data to a map's pointer
|
||||||
|
func (rs *Rows) ScanMap(dest interface{}) error {
|
||||||
|
vv := reflect.ValueOf(dest)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||||
|
return errors.New("dest should be a map's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rs.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDest := make([]interface{}, len(cols))
|
||||||
|
vvv := vv.Elem()
|
||||||
|
|
||||||
|
for i, _ := range cols {
|
||||||
|
newDest[i] = ReflectNew(vvv.Type().Elem()).Interface()
|
||||||
|
//v := reflect.New(vvv.Type().Elem())
|
||||||
|
//newDest[i] = v.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rs.Rows.Scan(newDest...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, name := range cols {
|
||||||
|
vname := reflect.ValueOf(name)
|
||||||
|
vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func (rs *Rows) ScanMap(dest interface{}) error {
|
||||||
|
vv := reflect.ValueOf(dest)
|
||||||
|
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map {
|
||||||
|
return errors.New("dest should be a map's pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rs.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDest := make([]interface{}, len(cols))
|
||||||
|
err = rs.ScanSlice(newDest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vvv := vv.Elem()
|
||||||
|
|
||||||
|
for i, name := range cols {
|
||||||
|
vname := reflect.ValueOf(name)
|
||||||
|
vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}*/
|
||||||
|
|
||||||
|
type Tx struct {
|
||||||
|
*sql.Tx
|
||||||
|
Mapper IMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Begin() (*Tx, error) {
|
||||||
|
tx, err := db.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Tx{tx, db.Mapper}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) Prepare(query string) (*Stmt, error) {
|
||||||
|
names := make(map[string]int)
|
||||||
|
var i int
|
||||||
|
query = re.ReplaceAllStringFunc(query, func(src string) string {
|
||||||
|
names[src[1:]] = i
|
||||||
|
i += 1
|
||||||
|
return "?"
|
||||||
|
})
|
||||||
|
|
||||||
|
stmt, err := tx.Tx.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Stmt{stmt, tx.Mapper, names}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
|
||||||
|
// TODO:
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) {
|
||||||
|
query, args, err := MapToSlice(query, mp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tx.Tx.Exec(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) {
|
||||||
|
query, args, err := StructToSlice(query, st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tx.Tx.Exec(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
|
||||||
|
rows, err := tx.Tx.Query(query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Rows{rows, tx.Mapper}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) {
|
||||||
|
query, args, err := MapToSlice(query, mp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tx.Query(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) {
|
||||||
|
query, args, err := StructToSlice(query, st)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tx.Query(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
|
||||||
|
row := tx.Tx.QueryRow(query, args...)
|
||||||
|
return &Row{row, nil, tx.Mapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row {
|
||||||
|
query, args, err := MapToSlice(query, mp)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{nil, err, tx.Mapper}
|
||||||
|
}
|
||||||
|
return tx.QueryRow(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row {
|
||||||
|
query, args, err := StructToSlice(query, st)
|
||||||
|
if err != nil {
|
||||||
|
return &Row{nil, err, tx.Mapper}
|
||||||
|
}
|
||||||
|
return tx.QueryRow(query, args...)
|
||||||
|
}
|
|
@ -0,0 +1,634 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
createTableSqlite3 = "CREATE TABLE IF NOT EXISTS `user` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, " +
|
||||||
|
"`title` TEXT NULL, `age` FLOAT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL, `created` datetime);"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Id int64
|
||||||
|
Name string
|
||||||
|
Title string
|
||||||
|
Age float32
|
||||||
|
Alias string
|
||||||
|
NickName string
|
||||||
|
Created time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOriQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?, ?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var Id int64
|
||||||
|
var Name, Title, Alias, NickName string
|
||||||
|
var Age float32
|
||||||
|
var Created time.Time
|
||||||
|
err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
//fmt.Println(Id, Name, Title, Age, Alias, NickName)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStructQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?, ?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var user User
|
||||||
|
err = rows.ScanStruct(&user)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if user.Name != "xlw" {
|
||||||
|
fmt.Println(user)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStruct2Query(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Mapper = NewCacheMapper(&SnakeMapper{})
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var user User
|
||||||
|
err = rows.ScanStruct2(&user)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if user.Name != "xlw" {
|
||||||
|
fmt.Println(user)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSliceInterfaceQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
slice := make([]interface{}, len(cols))
|
||||||
|
err = rows.ScanSlice(&slice)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if slice[1].(string) != "xlw" {
|
||||||
|
fmt.Println(slice)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func BenchmarkSliceBytesQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
slice := make([][]byte, len(cols))
|
||||||
|
err = rows.ScanSlice(&slice)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if string(slice[1]) != "xlw" {
|
||||||
|
fmt.Println(slice)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSliceStringQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
slice := make([]string, len(cols))
|
||||||
|
err = rows.ScanSlice(&slice)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if slice[1] != "xlw" {
|
||||||
|
fmt.Println(slice)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func BenchmarkMapInterfaceQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
err = rows.ScanMap(&m)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if m["name"].(string) != "xlw" {
|
||||||
|
fmt.Println(m)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func BenchmarkMapBytesQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
m := make(map[string][]byte)
|
||||||
|
err = rows.ScanMap(&m)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if string(m["name"]) != "xlw" {
|
||||||
|
fmt.Println(m)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMapStringQuery(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
m := make(map[string]string)
|
||||||
|
err = rows.ScanMap(&m)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
if m["name"] != "xlw" {
|
||||||
|
fmt.Println(m)
|
||||||
|
b.Error(errors.New("name should be xlw"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func BenchmarkExec(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)",
|
||||||
|
"xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now())
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExecMap(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
mp := map[string]interface{}{
|
||||||
|
"name": "xlw",
|
||||||
|
"title": "tester",
|
||||||
|
"age": 1.2,
|
||||||
|
"alias": "lunny",
|
||||||
|
"nick_name": "lunny xiao",
|
||||||
|
"created": time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name, created)
|
||||||
|
values (?name,?title,?age,?alias,?nick_name,?created)`,
|
||||||
|
&mp)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecMap(t *testing.T) {
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mp := map[string]interface{}{
|
||||||
|
"name": "xlw",
|
||||||
|
"title": "tester",
|
||||||
|
"age": 1.2,
|
||||||
|
"alias": "lunny",
|
||||||
|
"nick_name": "lunny xiao",
|
||||||
|
"created": time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name,created)
|
||||||
|
values (?name,?title,?age,?alias,?nick_name,?created)`,
|
||||||
|
&mp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.Query("select * from user")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var user User
|
||||||
|
err = rows.ScanStruct2(&user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("--", user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecStruct(t *testing.T) {
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := User{Name: "xlw",
|
||||||
|
Title: "tester",
|
||||||
|
Age: 1.2,
|
||||||
|
Alias: "lunny",
|
||||||
|
NickName: "lunny xiao",
|
||||||
|
Created: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name,created)
|
||||||
|
values (?Name,?Title,?Age,?Alias,?NickName,?Created)`,
|
||||||
|
&user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.QueryStruct("select * from user where name = ?Name", &user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var user User
|
||||||
|
err = rows.ScanStruct2(&user)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("1--", user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExecStruct(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
os.Remove("./test.db")
|
||||||
|
db, err := Open("sqlite3", "./test.db")
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec(createTableSqlite3)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
user := User{Name: "xlw",
|
||||||
|
Title: "tester",
|
||||||
|
Age: 1.2,
|
||||||
|
Alias: "lunny",
|
||||||
|
NickName: "lunny xiao",
|
||||||
|
Created: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name,created)
|
||||||
|
values (?Name,?Title,?Age,?Alias,?NickName,?Created)`,
|
||||||
|
&user)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dbType string
|
||||||
|
|
||||||
|
type Uri struct {
|
||||||
|
DbType dbType
|
||||||
|
Proto string
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
DbName string
|
||||||
|
User string
|
||||||
|
Passwd string
|
||||||
|
Charset string
|
||||||
|
Laddr string
|
||||||
|
Raddr string
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// a dialect is a driver's wrapper
|
||||||
|
type Dialect interface {
|
||||||
|
Init(*Uri, string, string) error
|
||||||
|
URI() *Uri
|
||||||
|
DBType() dbType
|
||||||
|
SqlType(t *Column) string
|
||||||
|
SupportInsertMany() bool
|
||||||
|
QuoteStr() string
|
||||||
|
AutoIncrStr() string
|
||||||
|
SupportEngine() bool
|
||||||
|
SupportCharset() bool
|
||||||
|
IndexOnTable() bool
|
||||||
|
|
||||||
|
IndexCheckSql(tableName, idxName string) (string, []interface{})
|
||||||
|
TableCheckSql(tableName string) (string, []interface{})
|
||||||
|
ColumnCheckSql(tableName, colName string) (string, []interface{})
|
||||||
|
|
||||||
|
GetColumns(tableName string) ([]string, map[string]*Column, error)
|
||||||
|
GetTables() ([]*Table, error)
|
||||||
|
GetIndexes(tableName string) (map[string]*Index, error)
|
||||||
|
|
||||||
|
CreateTableSql(table *Table, tableName, storeEngine, charset string) string
|
||||||
|
Filters() []Filter
|
||||||
|
|
||||||
|
DriverName() string
|
||||||
|
DataSourceName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Base struct {
|
||||||
|
dialect Dialect
|
||||||
|
driverName string
|
||||||
|
dataSourceName string
|
||||||
|
*Uri
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Init(dialect Dialect, uri *Uri, drivername, dataSourceName string) error {
|
||||||
|
b.dialect = dialect
|
||||||
|
b.driverName, b.dataSourceName = drivername, dataSourceName
|
||||||
|
b.Uri = uri
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) URI() *Uri {
|
||||||
|
return b.Uri
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) DBType() dbType {
|
||||||
|
return b.Uri.DbType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) DriverName() string {
|
||||||
|
return b.driverName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) DataSourceName() string {
|
||||||
|
return b.dataSourceName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Quote(c string) string {
|
||||||
|
return b.dialect.QuoteStr() + c + b.dialect.QuoteStr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string {
|
||||||
|
var sql string
|
||||||
|
sql = "CREATE TABLE IF NOT EXISTS "
|
||||||
|
if tableName == "" {
|
||||||
|
tableName = table.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += b.Quote(tableName) + " ("
|
||||||
|
|
||||||
|
pkList := table.PrimaryKeys
|
||||||
|
|
||||||
|
for _, colName := range table.ColumnsSeq() {
|
||||||
|
col := table.GetColumn(colName)
|
||||||
|
if col.IsPrimaryKey && len(pkList) == 1 {
|
||||||
|
sql += col.String(b.dialect)
|
||||||
|
} else {
|
||||||
|
sql += col.StringNoPk(b.dialect)
|
||||||
|
}
|
||||||
|
sql = strings.TrimSpace(sql)
|
||||||
|
sql += ", "
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pkList) > 1 {
|
||||||
|
sql += "PRIMARY KEY ( "
|
||||||
|
sql += b.Quote(strings.Join(pkList, b.Quote(",")))
|
||||||
|
sql += " ), "
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = sql[:len(sql)-2] + ")"
|
||||||
|
if b.dialect.SupportEngine() && storeEngine != "" {
|
||||||
|
sql += " ENGINE=" + storeEngine
|
||||||
|
}
|
||||||
|
if b.dialect.SupportCharset() {
|
||||||
|
if charset == "" {
|
||||||
|
charset = b.dialect.URI().Charset
|
||||||
|
}
|
||||||
|
sql += " DEFAULT CHARSET " + charset
|
||||||
|
}
|
||||||
|
sql += ";"
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dialects = map[dbType]Dialect{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterDialect(dbName dbType, dialect Dialect) {
|
||||||
|
if dialect == nil {
|
||||||
|
panic("core: Register dialect is nil")
|
||||||
|
}
|
||||||
|
if _, dup := dialects[dbName]; dup {
|
||||||
|
panic("core: Register called twice for dialect " + dbName)
|
||||||
|
}
|
||||||
|
dialects[dbName] = dialect
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryDialect(dbName dbType) Dialect {
|
||||||
|
return dialects[dbName]
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
type driver interface {
|
||||||
|
Parse(string, string) (*Uri, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
drivers = map[string]driver{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterDriver(driverName string, driver driver) {
|
||||||
|
if driver == nil {
|
||||||
|
panic("core: Register driver is nil")
|
||||||
|
}
|
||||||
|
if _, dup := drivers[driverName]; dup {
|
||||||
|
panic("core: Register called twice for driver " + driverName)
|
||||||
|
}
|
||||||
|
drivers[driverName] = driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryDriver(driverName string) driver {
|
||||||
|
return drivers[driverName]
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// Filter is an interface to filter SQL
|
||||||
|
type Filter interface {
|
||||||
|
Do(sql string, dialect Dialect, table *Table) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuoteFilter filter SQL replace ` to database's own quote character
|
||||||
|
type QuoteFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QuoteFilter) Do(sql string, dialect Dialect, table *Table) string {
|
||||||
|
return strings.Replace(sql, "`", dialect.QuoteStr(), -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdFilter filter SQL replace (id) to primary key column name
|
||||||
|
type IdFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type Quoter struct {
|
||||||
|
dialect Dialect
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQuoter(dialect Dialect) *Quoter {
|
||||||
|
return &Quoter{dialect}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Quoter) Quote(content string) string {
|
||||||
|
return q.dialect.QuoteStr() + content + q.dialect.QuoteStr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IdFilter) Do(sql string, dialect Dialect, table *Table) string {
|
||||||
|
quoter := NewQuoter(dialect)
|
||||||
|
if table != nil && len(table.PrimaryKeys) == 1 {
|
||||||
|
sql = strings.Replace(sql, "`(id)`", quoter.Quote(table.PrimaryKeys[0]), -1)
|
||||||
|
sql = strings.Replace(sql, quoter.Quote("(id)"), quoter.Quote(table.PrimaryKeys[0]), -1)
|
||||||
|
return strings.Replace(sql, "(id)", quoter.Quote(table.PrimaryKeys[0]), -1)
|
||||||
|
}
|
||||||
|
return sql
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
const (
|
||||||
|
IndexType = iota + 1
|
||||||
|
UniqueType
|
||||||
|
)
|
||||||
|
|
||||||
|
// database index
|
||||||
|
type Index struct {
|
||||||
|
Name string
|
||||||
|
Type int
|
||||||
|
Cols []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// add columns which will be composite index
|
||||||
|
func (index *Index) AddColumn(cols ...string) {
|
||||||
|
for _, col := range cols {
|
||||||
|
index.Cols = append(index.Cols, col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// new an index
|
||||||
|
func NewIndex(name string, indexType int) *Index {
|
||||||
|
return &Index{name, indexType, make([]string, 0)}
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// name translation between struct, fields names and table, column names
|
||||||
|
type IMapper interface {
|
||||||
|
Obj2Table(string) string
|
||||||
|
Table2Obj(string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheMapper struct {
|
||||||
|
oriMapper IMapper
|
||||||
|
obj2tableCache map[string]string
|
||||||
|
obj2tableMutex sync.RWMutex
|
||||||
|
table2objCache map[string]string
|
||||||
|
table2objMutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCacheMapper(mapper IMapper) *CacheMapper {
|
||||||
|
return &CacheMapper{oriMapper: mapper, obj2tableCache: make(map[string]string),
|
||||||
|
table2objCache: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CacheMapper) Obj2Table(o string) string {
|
||||||
|
m.obj2tableMutex.RLock()
|
||||||
|
t, ok := m.obj2tableCache[o]
|
||||||
|
m.obj2tableMutex.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
t = m.oriMapper.Obj2Table(o)
|
||||||
|
m.obj2tableMutex.Lock()
|
||||||
|
m.obj2tableCache[o] = t
|
||||||
|
m.obj2tableMutex.Unlock()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CacheMapper) Table2Obj(t string) string {
|
||||||
|
m.table2objMutex.RLock()
|
||||||
|
o, ok := m.table2objCache[t]
|
||||||
|
m.table2objMutex.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
o = m.oriMapper.Table2Obj(t)
|
||||||
|
m.table2objMutex.Lock()
|
||||||
|
m.table2objCache[t] = o
|
||||||
|
m.table2objMutex.Unlock()
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// SameMapper implements IMapper and provides same name between struct and
|
||||||
|
// database table
|
||||||
|
type SameMapper struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SameMapper) Obj2Table(o string) string {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SameMapper) Table2Obj(t string) string {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnakeMapper implements IMapper and provides name transaltion between
|
||||||
|
// struct and database table
|
||||||
|
type SnakeMapper struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func snakeCasedName(name string) string {
|
||||||
|
newstr := make([]rune, 0)
|
||||||
|
for idx, chr := range name {
|
||||||
|
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
|
||||||
|
if idx > 0 {
|
||||||
|
newstr = append(newstr, '_')
|
||||||
|
}
|
||||||
|
chr -= ('A' - 'a')
|
||||||
|
}
|
||||||
|
newstr = append(newstr, chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(newstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func pascal2Sql(s string) (d string) {
|
||||||
|
d = ""
|
||||||
|
lastIdx := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] >= 'A' && s[i] <= 'Z' {
|
||||||
|
if lastIdx < i {
|
||||||
|
d += s[lastIdx+1 : i]
|
||||||
|
}
|
||||||
|
if i != 0 {
|
||||||
|
d += "_"
|
||||||
|
}
|
||||||
|
d += string(s[i] + 32)
|
||||||
|
lastIdx = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d += s[lastIdx+1:]
|
||||||
|
return
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func (mapper SnakeMapper) Obj2Table(name string) string {
|
||||||
|
return snakeCasedName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func titleCasedName(name string) string {
|
||||||
|
newstr := make([]rune, 0)
|
||||||
|
upNextChar := true
|
||||||
|
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
|
for _, chr := range name {
|
||||||
|
switch {
|
||||||
|
case upNextChar:
|
||||||
|
upNextChar = false
|
||||||
|
if 'a' <= chr && chr <= 'z' {
|
||||||
|
chr -= ('a' - 'A')
|
||||||
|
}
|
||||||
|
case chr == '_':
|
||||||
|
upNextChar = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newstr = append(newstr, chr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(newstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mapper SnakeMapper) Table2Obj(name string) string {
|
||||||
|
return titleCasedName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// provide prefix table name support
|
||||||
|
type PrefixMapper struct {
|
||||||
|
Mapper IMapper
|
||||||
|
Prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mapper PrefixMapper) Obj2Table(name string) string {
|
||||||
|
return mapper.Prefix + mapper.Mapper.Obj2Table(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mapper PrefixMapper) Table2Obj(name string) string {
|
||||||
|
return mapper.Mapper.Table2Obj(name[len(mapper.Prefix):])
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrefixMapper(mapper IMapper, prefix string) PrefixMapper {
|
||||||
|
return PrefixMapper{mapper, prefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
// provide suffix table name support
|
||||||
|
type SuffixMapper struct {
|
||||||
|
Mapper IMapper
|
||||||
|
Suffix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mapper SuffixMapper) Obj2Table(name string) string {
|
||||||
|
return mapper.Suffix + mapper.Mapper.Obj2Table(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mapper SuffixMapper) Table2Obj(name string) string {
|
||||||
|
return mapper.Mapper.Table2Obj(name[len(mapper.Suffix):])
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper {
|
||||||
|
return SuffixMapper{mapper, suffix}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PK []interface{}
|
||||||
|
|
||||||
|
func NewPK(pks ...interface{}) *PK {
|
||||||
|
p := PK(pks)
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PK) ToString() (string, error) {
|
||||||
|
bs, err := json.Marshal(*p)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PK) FromString(content string) error {
|
||||||
|
return json.Unmarshal([]byte(content), p)
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPK(t *testing.T) {
|
||||||
|
p := NewPK(1, 3, "string")
|
||||||
|
str, err := p.ToString()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println(str)
|
||||||
|
|
||||||
|
s := &PK{}
|
||||||
|
err = s.FromString(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println(s)
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// database table
|
||||||
|
type Table struct {
|
||||||
|
Name string
|
||||||
|
Type reflect.Type
|
||||||
|
columnsSeq []string
|
||||||
|
columns map[string]*Column
|
||||||
|
Indexes map[string]*Index
|
||||||
|
PrimaryKeys []string
|
||||||
|
AutoIncrement string
|
||||||
|
Created map[string]bool
|
||||||
|
Updated string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) Columns() map[string]*Column {
|
||||||
|
return table.columns
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) ColumnsSeq() []string {
|
||||||
|
return table.columnsSeq
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEmptyTable() *Table {
|
||||||
|
return &Table{columnsSeq: make([]string, 0),
|
||||||
|
columns: make(map[string]*Column),
|
||||||
|
Indexes: make(map[string]*Index),
|
||||||
|
Created: make(map[string]bool),
|
||||||
|
PrimaryKeys: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTable(name string, t reflect.Type) *Table {
|
||||||
|
return &Table{Name: name, Type: t,
|
||||||
|
columnsSeq: make([]string, 0),
|
||||||
|
columns: make(map[string]*Column),
|
||||||
|
Indexes: make(map[string]*Index),
|
||||||
|
Created: make(map[string]bool),
|
||||||
|
PrimaryKeys: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) GetColumn(name string) *Column {
|
||||||
|
return table.columns[strings.ToLower(name)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// if has primary key, return column
|
||||||
|
func (table *Table) PKColumns() []*Column {
|
||||||
|
columns := make([]*Column, 0)
|
||||||
|
for _, name := range table.PrimaryKeys {
|
||||||
|
columns = append(columns, table.GetColumn(name))
|
||||||
|
}
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) AutoIncrColumn() *Column {
|
||||||
|
return table.GetColumn(table.AutoIncrement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (table *Table) VersionColumn() *Column {
|
||||||
|
return table.GetColumn(table.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a column to table
|
||||||
|
func (table *Table) AddColumn(col *Column) {
|
||||||
|
table.columnsSeq = append(table.columnsSeq, col.Name)
|
||||||
|
table.columns[strings.ToLower(col.Name)] = col
|
||||||
|
if col.IsPrimaryKey {
|
||||||
|
table.PrimaryKeys = append(table.PrimaryKeys, col.Name)
|
||||||
|
}
|
||||||
|
if col.IsAutoIncrement {
|
||||||
|
table.AutoIncrement = col.Name
|
||||||
|
}
|
||||||
|
if col.IsCreated {
|
||||||
|
table.Created[col.Name] = true
|
||||||
|
}
|
||||||
|
if col.IsUpdated {
|
||||||
|
table.Updated = col.Name
|
||||||
|
}
|
||||||
|
if col.IsVersion {
|
||||||
|
table.Version = col.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add an index or an unique to table
|
||||||
|
func (table *Table) AddIndex(index *Index) {
|
||||||
|
table.Indexes[index.Name] = index
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
POSTGRES = "postgres"
|
||||||
|
SQLITE = "sqlite3"
|
||||||
|
MYSQL = "mysql"
|
||||||
|
MSSQL = "mssql"
|
||||||
|
ORACLE = "oracle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// xorm SQL types
|
||||||
|
type SQLType struct {
|
||||||
|
Name string
|
||||||
|
DefaultLength int
|
||||||
|
DefaultLength2 int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLType) IsText() bool {
|
||||||
|
return s.Name == Char || s.Name == Varchar || s.Name == TinyText ||
|
||||||
|
s.Name == Text || s.Name == MediumText || s.Name == LongText
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLType) IsBlob() bool {
|
||||||
|
return (s.Name == TinyBlob) || (s.Name == Blob) ||
|
||||||
|
s.Name == MediumBlob || s.Name == LongBlob ||
|
||||||
|
s.Name == Binary || s.Name == VarBinary || s.Name == Bytea
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
Bit = "BIT"
|
||||||
|
TinyInt = "TINYINT"
|
||||||
|
SmallInt = "SMALLINT"
|
||||||
|
MediumInt = "MEDIUMINT"
|
||||||
|
Int = "INT"
|
||||||
|
Integer = "INTEGER"
|
||||||
|
BigInt = "BIGINT"
|
||||||
|
|
||||||
|
Char = "CHAR"
|
||||||
|
Varchar = "VARCHAR"
|
||||||
|
TinyText = "TINYTEXT"
|
||||||
|
Text = "TEXT"
|
||||||
|
MediumText = "MEDIUMTEXT"
|
||||||
|
LongText = "LONGTEXT"
|
||||||
|
|
||||||
|
Date = "DATE"
|
||||||
|
DateTime = "DATETIME"
|
||||||
|
Time = "TIME"
|
||||||
|
TimeStamp = "TIMESTAMP"
|
||||||
|
TimeStampz = "TIMESTAMPZ"
|
||||||
|
|
||||||
|
Decimal = "DECIMAL"
|
||||||
|
Numeric = "NUMERIC"
|
||||||
|
|
||||||
|
Real = "REAL"
|
||||||
|
Float = "FLOAT"
|
||||||
|
Double = "DOUBLE"
|
||||||
|
|
||||||
|
Binary = "BINARY"
|
||||||
|
VarBinary = "VARBINARY"
|
||||||
|
TinyBlob = "TINYBLOB"
|
||||||
|
Blob = "BLOB"
|
||||||
|
MediumBlob = "MEDIUMBLOB"
|
||||||
|
LongBlob = "LONGBLOB"
|
||||||
|
Bytea = "BYTEA"
|
||||||
|
|
||||||
|
Bool = "BOOL"
|
||||||
|
|
||||||
|
Serial = "SERIAL"
|
||||||
|
BigSerial = "BIGSERIAL"
|
||||||
|
|
||||||
|
SqlTypes = map[string]bool{
|
||||||
|
Bit: true,
|
||||||
|
TinyInt: true,
|
||||||
|
SmallInt: true,
|
||||||
|
MediumInt: true,
|
||||||
|
Int: true,
|
||||||
|
Integer: true,
|
||||||
|
BigInt: true,
|
||||||
|
|
||||||
|
Char: true,
|
||||||
|
Varchar: true,
|
||||||
|
TinyText: true,
|
||||||
|
Text: true,
|
||||||
|
MediumText: true,
|
||||||
|
LongText: true,
|
||||||
|
|
||||||
|
Date: true,
|
||||||
|
DateTime: true,
|
||||||
|
Time: true,
|
||||||
|
TimeStamp: true,
|
||||||
|
TimeStampz: true,
|
||||||
|
|
||||||
|
Decimal: true,
|
||||||
|
Numeric: true,
|
||||||
|
|
||||||
|
Binary: true,
|
||||||
|
VarBinary: true,
|
||||||
|
Real: true,
|
||||||
|
Float: true,
|
||||||
|
Double: true,
|
||||||
|
TinyBlob: true,
|
||||||
|
Blob: true,
|
||||||
|
MediumBlob: true,
|
||||||
|
LongBlob: true,
|
||||||
|
Bytea: true,
|
||||||
|
|
||||||
|
Bool: true,
|
||||||
|
|
||||||
|
Serial: true,
|
||||||
|
BigSerial: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
intTypes = sort.StringSlice{"*int", "*int16", "*int32", "*int8"}
|
||||||
|
uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparision
|
||||||
|
var (
|
||||||
|
c_EMPTY_STRING string
|
||||||
|
c_BOOL_DEFAULT bool
|
||||||
|
c_BYTE_DEFAULT byte
|
||||||
|
c_COMPLEX64_DEFAULT complex64
|
||||||
|
c_COMPLEX128_DEFAULT complex128
|
||||||
|
c_FLOAT32_DEFAULT float32
|
||||||
|
c_FLOAT64_DEFAULT float64
|
||||||
|
c_INT64_DEFAULT int64
|
||||||
|
c_UINT64_DEFAULT uint64
|
||||||
|
c_INT32_DEFAULT int32
|
||||||
|
c_UINT32_DEFAULT uint32
|
||||||
|
c_INT16_DEFAULT int16
|
||||||
|
c_UINT16_DEFAULT uint16
|
||||||
|
c_INT8_DEFAULT int8
|
||||||
|
c_UINT8_DEFAULT uint8
|
||||||
|
c_INT_DEFAULT int
|
||||||
|
c_UINT_DEFAULT uint
|
||||||
|
c_TIME_DEFAULT time.Time
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
IntType = reflect.TypeOf(c_INT_DEFAULT)
|
||||||
|
Int8Type = reflect.TypeOf(c_INT8_DEFAULT)
|
||||||
|
Int16Type = reflect.TypeOf(c_INT16_DEFAULT)
|
||||||
|
Int32Type = reflect.TypeOf(c_INT32_DEFAULT)
|
||||||
|
Int64Type = reflect.TypeOf(c_INT64_DEFAULT)
|
||||||
|
|
||||||
|
UintType = reflect.TypeOf(c_UINT_DEFAULT)
|
||||||
|
Uint8Type = reflect.TypeOf(c_UINT8_DEFAULT)
|
||||||
|
Uint16Type = reflect.TypeOf(c_UINT16_DEFAULT)
|
||||||
|
Uint32Type = reflect.TypeOf(c_UINT32_DEFAULT)
|
||||||
|
Uint64Type = reflect.TypeOf(c_UINT64_DEFAULT)
|
||||||
|
|
||||||
|
Float32Type = reflect.TypeOf(c_FLOAT32_DEFAULT)
|
||||||
|
Float64Type = reflect.TypeOf(c_FLOAT64_DEFAULT)
|
||||||
|
|
||||||
|
Complex64Type = reflect.TypeOf(c_COMPLEX64_DEFAULT)
|
||||||
|
Complex128Type = reflect.TypeOf(c_COMPLEX128_DEFAULT)
|
||||||
|
|
||||||
|
StringType = reflect.TypeOf(c_EMPTY_STRING)
|
||||||
|
BoolType = reflect.TypeOf(c_BOOL_DEFAULT)
|
||||||
|
ByteType = reflect.TypeOf(c_BYTE_DEFAULT)
|
||||||
|
|
||||||
|
TimeType = reflect.TypeOf(c_TIME_DEFAULT)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PtrIntType = reflect.PtrTo(IntType)
|
||||||
|
PtrInt8Type = reflect.PtrTo(Int8Type)
|
||||||
|
PtrInt16Type = reflect.PtrTo(Int16Type)
|
||||||
|
PtrInt32Type = reflect.PtrTo(Int32Type)
|
||||||
|
PtrInt64Type = reflect.PtrTo(Int64Type)
|
||||||
|
|
||||||
|
PtrUintType = reflect.PtrTo(UintType)
|
||||||
|
PtrUint8Type = reflect.PtrTo(Uint8Type)
|
||||||
|
PtrUint16Type = reflect.PtrTo(Uint16Type)
|
||||||
|
PtrUint32Type = reflect.PtrTo(Uint32Type)
|
||||||
|
PtrUint64Type = reflect.PtrTo(Uint64Type)
|
||||||
|
|
||||||
|
PtrFloat32Type = reflect.PtrTo(Float32Type)
|
||||||
|
PtrFloat64Type = reflect.PtrTo(Float64Type)
|
||||||
|
|
||||||
|
PtrComplex64Type = reflect.PtrTo(Complex64Type)
|
||||||
|
PtrComplex128Type = reflect.PtrTo(Complex128Type)
|
||||||
|
|
||||||
|
PtrStringType = reflect.PtrTo(StringType)
|
||||||
|
PtrBoolType = reflect.PtrTo(BoolType)
|
||||||
|
PtrByteType = reflect.PtrTo(ByteType)
|
||||||
|
|
||||||
|
PtrTimeType = reflect.PtrTo(TimeType)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Type2SQLType(t reflect.Type) (st SQLType) {
|
||||||
|
|
||||||
|
switch k := t.Kind(); k {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||||
|
st = SQLType{Int, 0, 0}
|
||||||
|
case reflect.Int64, reflect.Uint64:
|
||||||
|
st = SQLType{BigInt, 0, 0}
|
||||||
|
case reflect.Float32:
|
||||||
|
st = SQLType{Float, 0, 0}
|
||||||
|
case reflect.Float64:
|
||||||
|
st = SQLType{Double, 0, 0}
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
st = SQLType{Varchar, 64, 0}
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Map:
|
||||||
|
if t.Elem() == reflect.TypeOf(c_BYTE_DEFAULT) {
|
||||||
|
st = SQLType{Blob, 0, 0}
|
||||||
|
} else {
|
||||||
|
st = SQLType{Text, 0, 0}
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
st = SQLType{Bool, 0, 0}
|
||||||
|
case reflect.String:
|
||||||
|
st = SQLType{Varchar, 255, 0}
|
||||||
|
case reflect.Struct:
|
||||||
|
if t == reflect.TypeOf(c_TIME_DEFAULT) {
|
||||||
|
st = SQLType{DateTime, 0, 0}
|
||||||
|
} else {
|
||||||
|
// TODO need to handle association struct
|
||||||
|
st = SQLType{Text, 0, 0}
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
st, _ = ptrType2SQLType(t)
|
||||||
|
default:
|
||||||
|
st = SQLType{Text, 0, 0}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptrType2SQLType(t reflect.Type) (st SQLType, has bool) {
|
||||||
|
has = true
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case reflect.TypeOf(&c_EMPTY_STRING):
|
||||||
|
st = SQLType{Varchar, 255, 0}
|
||||||
|
return
|
||||||
|
case reflect.TypeOf(&c_BOOL_DEFAULT):
|
||||||
|
st = SQLType{Bool, 0, 0}
|
||||||
|
case reflect.TypeOf(&c_COMPLEX64_DEFAULT), reflect.TypeOf(&c_COMPLEX128_DEFAULT):
|
||||||
|
st = SQLType{Varchar, 64, 0}
|
||||||
|
case reflect.TypeOf(&c_FLOAT32_DEFAULT):
|
||||||
|
st = SQLType{Float, 0, 0}
|
||||||
|
case reflect.TypeOf(&c_FLOAT64_DEFAULT):
|
||||||
|
st = SQLType{Double, 0, 0}
|
||||||
|
case reflect.TypeOf(&c_INT64_DEFAULT), reflect.TypeOf(&c_UINT64_DEFAULT):
|
||||||
|
st = SQLType{BigInt, 0, 0}
|
||||||
|
case reflect.TypeOf(&c_TIME_DEFAULT):
|
||||||
|
st = SQLType{DateTime, 0, 0}
|
||||||
|
case reflect.TypeOf(&c_INT_DEFAULT), reflect.TypeOf(&c_INT32_DEFAULT), reflect.TypeOf(&c_INT8_DEFAULT), reflect.TypeOf(&c_INT16_DEFAULT), reflect.TypeOf(&c_UINT_DEFAULT), reflect.TypeOf(&c_UINT32_DEFAULT), reflect.TypeOf(&c_UINT8_DEFAULT), reflect.TypeOf(&c_UINT16_DEFAULT):
|
||||||
|
st = SQLType{Int, 0, 0}
|
||||||
|
default:
|
||||||
|
has = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// default sql type change to go types
|
||||||
|
func SQLType2Type(st SQLType) reflect.Type {
|
||||||
|
name := strings.ToUpper(st.Name)
|
||||||
|
switch name {
|
||||||
|
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, Serial:
|
||||||
|
return reflect.TypeOf(1)
|
||||||
|
case BigInt, BigSerial:
|
||||||
|
return reflect.TypeOf(int64(1))
|
||||||
|
case Float, Real:
|
||||||
|
return reflect.TypeOf(float32(1))
|
||||||
|
case Double:
|
||||||
|
return reflect.TypeOf(float64(1))
|
||||||
|
case Char, Varchar, TinyText, Text, MediumText, LongText:
|
||||||
|
return reflect.TypeOf("")
|
||||||
|
case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary:
|
||||||
|
return reflect.TypeOf([]byte{})
|
||||||
|
case Bool:
|
||||||
|
return reflect.TypeOf(true)
|
||||||
|
case DateTime, Date, Time, TimeStamp, TimeStampz:
|
||||||
|
return reflect.TypeOf(c_TIME_DEFAULT)
|
||||||
|
case Decimal, Numeric:
|
||||||
|
return reflect.TypeOf("")
|
||||||
|
default:
|
||||||
|
return reflect.TypeOf("")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue