pax_global_header00006660000000000000000000000064125737051640014524gustar00rootroot0000000000000052 comment=286763cd3af4dca72f34c0c642714bc01fff27a8 core-0.4.4/000077500000000000000000000000001257370516400124615ustar00rootroot00000000000000core-0.4.4/.gitignore000066400000000000000000000000051257370516400144440ustar00rootroot00000000000000*.db core-0.4.4/LICENSE000066400000000000000000000027531257370516400134750ustar00rootroot00000000000000Copyright (c) 2013 - 2015 Lunny Xiao All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the {organization} nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. core-0.4.4/README.md000066400000000000000000000043211257370516400137400ustar00rootroot00000000000000Core is a lightweight wrapper of sql.DB. # Open ```Go db, _ := core.Open(db, connstr) ``` # SetMapper ```Go db.SetMapper(SameMapper()) ``` ## Scan usage ### Scan ```Go rows, _ := db.Query() for rows.Next() { rows.Scan() } ``` ### ScanMap ```Go rows, _ := db.Query() for rows.Next() { rows.ScanMap() ``` ### ScanSlice You can use `[]string`, `[][]byte`, `[]interface{}`, `[]*string`, `[]sql.NullString` to ScanSclice. Notice, slice's length should be equal or less than select columns. ```Go rows, _ := db.Query() cols, _ := rows.Columns() for rows.Next() { var s = make([]string, len(cols)) rows.ScanSlice(&s) } ``` ```Go rows, _ := db.Query() cols, _ := rows.Columns() for rows.Next() { var s = make([]*string, len(cols)) rows.ScanSlice(&s) } ``` ### ScanStruct ```Go rows, _ := db.Query() for rows.Next() { rows.ScanStructByName() rows.ScanStructByIndex() } ``` ## Query usage ```Go rows, err := db.Query("select * from table where name = ?", name) user = User{ Name:"lunny", } rows, err := db.QueryStruct("select * from table where name = ?Name", &user) var user = map[string]interface{}{ "name": "lunny", } rows, err = db.QueryMap("select * from table where name = ?name", &user) ``` ## QueryRow usage ```Go row := db.QueryRow("select * from table where name = ?", name) user = User{ Name:"lunny", } row := db.QueryRowStruct("select * from table where name = ?Name", &user) var user = map[string]interface{}{ "name": "lunny", } row = db.QueryRowMap("select * from table where name = ?name", &user) ``` ## Exec usage ```Go db.Exec("insert into user (`name`, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", name, title, age, alias...) user = User{ Name:"lunny", Title:"test", Age: 18, } result, err = db.ExecStruct("insert into user (`name`, title, age, alias, nick_name,created) values (?Name,?Title,?Age,?Alias,?NickName,?Created)", &user) var user = map[string]interface{}{ "Name": "lunny", "Title": "test", "Age": 18, } result, err = db.ExecMap("insert into user (`name`, title, age, alias, nick_name,created) values (?Name,?Title,?Age,?Alias,?NickName,?Created)", &user) ```core-0.4.4/benchmark.sh000077500000000000000000000000351257370516400147500ustar00rootroot00000000000000go test -v -bench=. -run=XXX core-0.4.4/cache.go000066400000000000000000000037711257370516400140630ustar00rootroot00000000000000package core import ( "errors" "fmt" "time" "bytes" "encoding/gob" ) 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 ) var ( ErrCacheMiss = errors.New("xorm/cache: key not found.") ErrNotStored = errors.New("xorm/cache: not stored.") ) // CacheStore is a interface to store cache type CacheStore interface { // key is primary key or composite primary key // value is struct's pointer // key format : -p--... 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--... 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) { buf := new(bytes.Buffer) enc := gob.NewEncoder(buf) err := enc.Encode(ids) return buf.String(), err } func decodeIds(s string) ([]PK, error) { pks := make([]PK, 0) dec := gob.NewDecoder(bytes.NewBufferString(s)) err := dec.Decode(&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) } core-0.4.4/column.go000066400000000000000000000076121257370516400143130ustar00rootroot00000000000000package core import ( "errors" "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 IsDeleted bool IsCascade bool IsVersion bool fieldPath []string DefaultIsEmpty bool EnumOptions map[string]int SetOptions map[string]int } func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { return &Column{ Name: name, FieldName: fieldName, SQLType: sqlType, Length: len1, Length2: len2, Nullable: nullable, Default: "", Indexes: make(map[string]bool), IsPrimaryKey: false, IsAutoIncrement: false, MapType: TWOSIDES, IsCreated: false, IsUpdated: false, IsDeleted: false, IsCascade: false, IsVersion: false, fieldPath: nil, DefaultIsEmpty: false, EnumOptions: make(map[string]int), } } // 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 d.ShowCreateNull() { 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 d.ShowCreateNull() { 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 if col.fieldPath == nil { col.fieldPath = strings.Split(col.FieldName, ".") } if dataStruct.Type().Kind() == reflect.Map { var keyValue reflect.Value if len(col.fieldPath) == 1 { keyValue = reflect.ValueOf(col.FieldName) } else if len(col.fieldPath) == 2 { keyValue = reflect.ValueOf(col.fieldPath[1]) } else { return nil, fmt.Errorf("Unsupported mutliderive %v", col.FieldName) } fieldValue = dataStruct.MapIndex(keyValue) return &fieldValue, nil } 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() { if parentField.Kind() == reflect.Struct { fieldValue = parentField.FieldByName(col.fieldPath[1]) } else if parentField.Kind() == reflect.Ptr { if parentField.IsNil() { parentField.Set(reflect.New(parentField.Type().Elem())) fieldValue = parentField.Elem().FieldByName(col.fieldPath[1]) } else { parentField = parentField.Elem() if parentField.IsValid() { fieldValue = parentField.FieldByName(col.fieldPath[1]) } else { return nil, fmt.Errorf("field %v is not valid", col.FieldName) } } } } else { // so we can use a different struct as conditions fieldValue = dataStruct.FieldByName(col.fieldPath[1]) } } else { return nil, fmt.Errorf("Unsupported mutliderive %v", col.FieldName) } if !fieldValue.IsValid() { return nil, errors.New("no find field matched") } return &fieldValue, nil } core-0.4.4/converstion.go000066400000000000000000000003541257370516400153630ustar00rootroot00000000000000package 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) } core-0.4.4/db.go000066400000000000000000000311321257370516400133750ustar00rootroot00000000000000package core import ( "database/sql" "errors" "reflect" "regexp" "sync" ) 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 FromDB(db *sql.DB) *DB { return &DB{db, NewCacheMapper(&SnakeMapper{})} } 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) ScanStructByIndex(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) ScanStructByName(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...) } core-0.4.4/db_test.go000066400000000000000000000300571257370516400144410ustar00rootroot00000000000000package core import ( "errors" "fmt" "os" "testing" "time" _ "github.com/go-sql-driver/mysql" _ "github.com/mattn/go-sqlite3" ) var ( //dbtype string = "sqlite3" dbtype string = "mysql" createTableSql string ) type User struct { Id int64 Name string Title string Age float32 Alias string NickName string Created time.Time } func init() { switch dbtype { case "sqlite3": createTableSql = "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);" case "mysql": createTableSql = "CREATE TABLE IF NOT EXISTS `user` (`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, `name` TEXT NULL, " + "`title` TEXT NULL, `age` FLOAT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL, `created` datetime);" default: panic("no db type") } } func testOpen() (*DB, error) { switch dbtype { case "sqlite3": os.Remove("./test.db") return Open("sqlite3", "./test.db") case "mysql": return Open("mysql", "root:@/core_test?charset=utf8") default: panic("no db type") } } func BenchmarkOriQuery(b *testing.B) { b.StopTimer() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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.ScanStructByIndex(&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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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.ScanStructByName(&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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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) } fmt.Println(slice) 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(createTableSql) 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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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(createTableSql) 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(createTableSql) 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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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) { db, err := testOpen() if err != nil { t.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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.ScanStructByName(&user) if err != nil { t.Error(err) } fmt.Println("--", user) } } func TestExecStruct(t *testing.T) { db, err := testOpen() if err != nil { t.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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.ScanStructByName(&user) if err != nil { t.Error(err) } fmt.Println("1--", user) } } func BenchmarkExecStruct(b *testing.B) { b.StopTimer() db, err := testOpen() if err != nil { b.Error(err) } defer db.Close() _, err = db.Exec(createTableSql) 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) } } } core-0.4.4/dialect.go000066400000000000000000000144421257370516400144220ustar00rootroot00000000000000package core import ( "fmt" "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 { SetLogger(logger ILogger) Init(*DB, *Uri, string, string) error URI() *Uri DB() *DB DBType() DbType SqlType(*Column) string FormatBytes(b []byte) string DriverName() string DataSourceName() string QuoteStr() string IsReserved(string) bool Quote(string) string AndStr() string OrStr() string EqStr() string RollBackStr() string AutoIncrStr() string SupportInsertMany() bool SupportEngine() bool SupportCharset() bool SupportDropIfExists() bool IndexOnTable() bool ShowCreateNull() bool IndexCheckSql(tableName, idxName string) (string, []interface{}) TableCheckSql(tableName string) (string, []interface{}) IsColumnExist(tableName string, colName string) (bool, error) CreateTableSql(table *Table, tableName, storeEngine, charset string) string DropTableSql(tableName string) string CreateIndexSql(tableName string, index *Index) string DropIndexSql(tableName string, index *Index) string ModifyColumnSql(tableName string, col *Column) string ForUpdateSql(query string) string //CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error //MustDropTable(tableName string) error GetColumns(tableName string) ([]string, map[string]*Column, error) GetTables() ([]*Table, error) GetIndexes(tableName string) (map[string]*Index, error) Filters() []Filter } func OpenDialect(dialect Dialect) (*DB, error) { return Open(dialect.DriverName(), dialect.DataSourceName()) } type Base struct { db *DB dialect Dialect driverName string dataSourceName string Logger ILogger *Uri } func (b *Base) DB() *DB { return b.db } func (b *Base) SetLogger(logger ILogger) { b.Logger = logger } func (b *Base) Init(db *DB, dialect Dialect, uri *Uri, drivername, dataSourceName string) error { b.db, b.dialect, b.Uri = db, dialect, uri b.driverName, b.dataSourceName = drivername, dataSourceName return nil } func (b *Base) URI() *Uri { return b.Uri } func (b *Base) DBType() DbType { return b.Uri.DbType } func (b *Base) FormatBytes(bs []byte) string { return fmt.Sprintf("0x%x", bs) } func (b *Base) DriverName() string { return b.driverName } func (b *Base) ShowCreateNull() bool { return true } func (b *Base) DataSourceName() string { return b.dataSourceName } func (b *Base) AndStr() string { return "AND" } func (b *Base) OrStr() string { return "OR" } func (b *Base) EqStr() string { return "=" } func (db *Base) RollBackStr() string { return "ROLL BACK" } func (db *Base) SupportDropIfExists() bool { return true } func (db *Base) DropTableSql(tableName string) string { return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName) } func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) { rows, err := db.DB().Query(query, args...) if db.Logger != nil { db.Logger.Info("[sql]", query, args) } if err != nil { return false, err } defer rows.Close() if rows.Next() { return true, nil } return false, nil } func (db *Base) IsColumnExist(tableName, colName string) (bool, error) { query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?" query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1) return db.HasRecords(query, db.DbName, tableName, colName) } /* func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error { sql, args := db.dialect.TableCheckSql(tableName) rows, err := db.DB().Query(sql, args...) if db.Logger != nil { db.Logger.Info("[sql]", sql, args) } if err != nil { return err } defer rows.Close() if rows.Next() { return nil } sql = db.dialect.CreateTableSql(table, tableName, storeEngine, charset) _, err = db.DB().Exec(sql) if db.Logger != nil { db.Logger.Info("[sql]", sql) } return err }*/ func (db *Base) CreateIndexSql(tableName string, index *Index) string { quote := db.dialect.Quote var unique string var idxName string if index.Type == UniqueType { unique = " UNIQUE" } idxName = index.XName(tableName) return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique, quote(idxName), quote(tableName), quote(strings.Join(index.Cols, quote(",")))) } func (db *Base) DropIndexSql(tableName string, index *Index) string { quote := db.dialect.Quote var name string if index.IsRegular { name = index.XName(tableName) } else { name = index.Name } return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName)) } func (db *Base) ModifyColumnSql(tableName string, col *Column) string { return fmt.Sprintf("alter table %s MODIFY COLUMN %s", tableName, col.StringNoPk(db.dialect)) } 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.dialect.Quote(tableName) sql += " (" if len(table.ColumnsSeq()) > 0 { 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.dialect.Quote(strings.Join(pkList, b.dialect.Quote(","))) sql += " ), " } sql = sql[:len(sql)-2] } sql += ")" if b.dialect.SupportEngine() && storeEngine != "" { sql += " ENGINE=" + storeEngine } if b.dialect.SupportCharset() { if len(charset) == 0 { charset = b.dialect.URI().Charset } if len(charset) > 0 { sql += " DEFAULT CHARSET " + charset } } return sql } func (b *Base) ForUpdateSql(query string) string { return query + " FOR UPDATE" } var ( dialects = map[DbType]func() Dialect{} ) func RegisterDialect(dbName DbType, dialectFunc func() Dialect) { if dialectFunc == nil { panic("core: Register dialect is nil") } dialects[dbName] = dialectFunc // !nashtsai! allow override dialect } func QueryDialect(dbName DbType) Dialect { return dialects[dbName]() } core-0.4.4/driver.go000066400000000000000000000007771257370516400143160ustar00rootroot00000000000000package 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] } func RegisteredDriverSize() int { return len(drivers) } core-0.4.4/error.go000066400000000000000000000004001257370516400141330ustar00rootroot00000000000000package core import "errors" var ( ErrNoMapPointer = errors.New("mp should be a map's pointer") ErrNoStructPointer = errors.New("mp should be a struct's pointer") //ErrNotExist = errors.New("Not exist") //ErrIgnore = errors.New("Ignore") ) core-0.4.4/filter.go000066400000000000000000000027631257370516400143050ustar00rootroot00000000000000package core import ( "fmt" "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 } // SeqFilter filter SQL replace ?, ? ... to $1, $2 ... type SeqFilter struct { Prefix string Start int } func (s *SeqFilter) Do(sql string, dialect Dialect, table *Table) string { segs := strings.Split(sql, "?") size := len(segs) res := "" for i, c := range segs { if i < size-1 { res += c + fmt.Sprintf("%s%v", s.Prefix, i+s.Start) } } res += segs[size-1] return res } core-0.4.4/ilogger.go000066400000000000000000000013171257370516400144420ustar00rootroot00000000000000package core type LogLevel int const ( // !nashtsai! following level also match syslog.Priority value LOG_UNKNOWN LogLevel = iota - 2 LOG_OFF LogLevel = iota - 1 LOG_ERR LogLevel = iota + 3 LOG_WARNING LOG_INFO LogLevel = iota + 6 LOG_DEBUG ) // logger interface type ILogger interface { Debug(v ...interface{}) (err error) Debugf(format string, v ...interface{}) (err error) Err(v ...interface{}) (err error) Errf(format string, v ...interface{}) (err error) Info(v ...interface{}) (err error) Infof(format string, v ...interface{}) (err error) Warning(v ...interface{}) (err error) Warningf(format string, v ...interface{}) (err error) Level() LogLevel SetLevel(l LogLevel) (err error) } core-0.4.4/index.go000066400000000000000000000022021257370516400141130ustar00rootroot00000000000000package core import ( "fmt" "sort" "strings" ) const ( IndexType = iota + 1 UniqueType ) // database index type Index struct { IsRegular bool Name string Type int Cols []string } func (index *Index) XName(tableName string) string { if !strings.HasPrefix(index.Name, "UQE_") && !strings.HasPrefix(index.Name, "IDX_") { if index.Type == UniqueType { return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) } return fmt.Sprintf("IDX_%v_%v", tableName, index.Name) } return index.Name } // add columns which will be composite index func (index *Index) AddColumn(cols ...string) { for _, col := range cols { index.Cols = append(index.Cols, col) } } func (index *Index) Equal(dst *Index) bool { if index.Type != dst.Type { return false } if len(index.Cols) != len(dst.Cols) { return false } sort.StringSlice(index.Cols).Sort() sort.StringSlice(dst.Cols).Sort() for i := 0; i < len(index.Cols); i++ { if index.Cols[i] != dst.Cols[i] { return false } } return true } // new an index func NewIndex(name string, indexType int) *Index { return &Index{true, name, indexType, make([]string, 0)} } core-0.4.4/mapper.go000066400000000000000000000121441257370516400142760ustar00rootroot00000000000000package 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 (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) } // GonicMapper implements IMapper. It will consider initialisms when mapping names. // E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid type GonicMapper map[string]bool func isASCIIUpper(r rune) bool { return 'A' <= r && r <= 'Z' } func toASCIIUpper(r rune) rune { if 'a' <= r && r <= 'z' { r -= ('a' - 'A') } return r } func gonicCasedName(name string) string { newstr := make([]rune, 0, len(name)+3) for idx, chr := range name { if isASCIIUpper(chr) && idx > 0 { if !isASCIIUpper(newstr[len(newstr)-1]) { newstr = append(newstr, '_') } } if !isASCIIUpper(chr) && idx > 1 { l := len(newstr) if isASCIIUpper(newstr[l-1]) && isASCIIUpper(newstr[l-2]) { newstr = append(newstr, newstr[l-1]) newstr[l-1] = '_' } } newstr = append(newstr, chr) } return strings.ToLower(string(newstr)) } func (mapper GonicMapper) Obj2Table(name string) string { return gonicCasedName(name) } func (mapper GonicMapper) Table2Obj(name string) string { newstr := make([]rune, 0) name = strings.ToLower(name) parts := strings.Split(name, "_") for _, p := range parts { _, isInitialism := mapper[strings.ToUpper(p)] for i, r := range p { if i == 0 || isInitialism { r = toASCIIUpper(r) } newstr = append(newstr, r) } } return string(newstr) } // A GonicMapper that contains a list of common initialisms taken from golang/lint var LintGonicMapper = GonicMapper{ "API": true, "ASCII": true, "CPU": true, "CSS": true, "DNS": true, "EOF": true, "GUID": true, "HTML": true, "HTTP": true, "HTTPS": true, "ID": true, "IP": true, "JSON": true, "LHS": true, "QPS": true, "RAM": true, "RHS": true, "RPC": true, "SLA": true, "SMTP": true, "SSH": true, "TLS": true, "TTL": true, "UI": true, "UID": true, "UUID": true, "URI": true, "URL": true, "UTF8": true, "VM": true, "XML": true, "XSRF": true, "XSS": true, } // 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.Mapper.Obj2Table(name) + mapper.Suffix } func (mapper SuffixMapper) Table2Obj(name string) string { return mapper.Mapper.Table2Obj(name[:len(name)-len(mapper.Suffix)]) } func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper { return SuffixMapper{mapper, suffix} } core-0.4.4/mapper_test.go000066400000000000000000000021711257370516400153340ustar00rootroot00000000000000package core import ( "testing" ) func TestGonicMapperFromObj(t *testing.T) { testCases := map[string]string{ "HTTPLib": "http_lib", "id": "id", "ID": "id", "IDa": "i_da", "iDa": "i_da", "IDAa": "id_aa", "aID": "a_id", "aaID": "aa_id", "aaaID": "aaa_id", "MyREalFunkYLONgNAME": "my_r_eal_funk_ylo_ng_name", } for in, expected := range testCases { out := gonicCasedName(in) if out != expected { t.Errorf("Given %s, expected %s but got %s", in, expected, out) } } } func TestGonicMapperToObj(t *testing.T) { testCases := map[string]string{ "http_lib": "HTTPLib", "id": "ID", "ida": "Ida", "id_aa": "IDAa", "aa_id": "AaID", "my_r_eal_funk_ylo_ng_name": "MyREalFunkYloNgName", } for in, expected := range testCases { out := LintGonicMapper.Table2Obj(in) if out != expected { t.Errorf("Given %s, expected %s but got %s", in, expected, out) } } } core-0.4.4/pk.go000066400000000000000000000006521257370516400134250ustar00rootroot00000000000000package core import ( "bytes" "encoding/gob" ) type PK []interface{} func NewPK(pks ...interface{}) *PK { p := PK(pks) return &p } func (p *PK) ToString() (string, error) { buf := new(bytes.Buffer) enc := gob.NewEncoder(buf) err := enc.Encode(*p) return buf.String(), err } func (p *PK) FromString(content string) error { dec := gob.NewDecoder(bytes.NewBufferString(content)) err := dec.Decode(p) return err } core-0.4.4/pk_test.go000066400000000000000000000007731257370516400144700ustar00rootroot00000000000000package core import ( "fmt" "reflect" "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) if len(*p) != len(*s) { t.Fatal("p", *p, "should be equal", *s) } for i, ori := range *p { if ori != (*s)[i] { t.Fatal("ori", ori, reflect.ValueOf(ori), "should be equal", (*s)[i], reflect.ValueOf((*s)[i])) } } } core-0.4.4/table.go000066400000000000000000000054401257370516400141020ustar00rootroot00000000000000package core import ( "reflect" "strings" ) // database table type Table struct { Name string Type reflect.Type columnsSeq []string columnsMap map[string][]*Column columns []*Column Indexes map[string]*Index PrimaryKeys []string AutoIncrement string Created map[string]bool Updated string Deleted string Version string Cacher Cacher StoreEngine string Charset string } func (table *Table) Columns() []*Column { return table.columns } func (table *Table) ColumnsSeq() []string { return table.columnsSeq } func NewEmptyTable() *Table { return NewTable("", nil) } func NewTable(name string, t reflect.Type) *Table { return &Table{Name: name, Type: t, columnsSeq: make([]string, 0), columns: make([]*Column, 0), columnsMap: 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 { if c, ok := table.columnsMap[strings.ToLower(name)]; ok { return c[0] } return nil } func (table *Table) GetColumnIdx(name string, idx int) *Column { if c, ok := table.columnsMap[strings.ToLower(name)]; ok { if idx < len(c) { return c[idx] } } return nil } // if has primary key, return column func (table *Table) PKColumns() []*Column { columns := make([]*Column, len(table.PrimaryKeys)) for i, name := range table.PrimaryKeys { columns[i] = table.GetColumn(name) } return columns } func (table *Table) ColumnType(name string) reflect.Type { t, _ := table.Type.FieldByName(name) return t.Type } func (table *Table) AutoIncrColumn() *Column { return table.GetColumn(table.AutoIncrement) } func (table *Table) VersionColumn() *Column { return table.GetColumn(table.Version) } func (table *Table) UpdatedColumn() *Column { return table.GetColumn(table.Updated) } func (table *Table) DeletedColumn() *Column { return table.GetColumn(table.Deleted) } // add a column to table func (table *Table) AddColumn(col *Column) { table.columnsSeq = append(table.columnsSeq, col.Name) table.columns = append(table.columns, col) colName := strings.ToLower(col.Name) if c, ok := table.columnsMap[colName]; ok { table.columnsMap[colName] = append(c, col) } else { table.columnsMap[colName] = []*Column{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.IsDeleted { table.Deleted = 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 } core-0.4.4/type.go000066400000000000000000000174311257370516400137770ustar00rootroot00000000000000package 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 } const ( UNKNOW_TYPE = iota TEXT_TYPE BLOB_TYPE TIME_TYPE NUMERIC_TYPE ) func (s *SQLType) IsType(st int) bool { if t, ok := SqlTypes[s.Name]; ok && t == st { return true } return false } func (s *SQLType) IsText() bool { return s.IsType(TEXT_TYPE) } func (s *SQLType) IsBlob() bool { return s.IsType(BLOB_TYPE) } func (s *SQLType) IsTime() bool { return s.IsType(TIME_TYPE) } func (s *SQLType) IsNumeric() bool { return s.IsType(NUMERIC_TYPE) } func (s *SQLType) IsJson() bool { return s.Name == Json } var ( Bit = "BIT" TinyInt = "TINYINT" SmallInt = "SMALLINT" MediumInt = "MEDIUMINT" Int = "INT" Integer = "INTEGER" BigInt = "BIGINT" Enum = "ENUM" Set = "SET" Char = "CHAR" Varchar = "VARCHAR" NVarchar = "NVARCHAR" TinyText = "TINYTEXT" Text = "TEXT" Clob = "CLOB" MediumText = "MEDIUMTEXT" LongText = "LONGTEXT" Uuid = "UUID" 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" Json = "JSON" SqlTypes = map[string]int{ Bit: NUMERIC_TYPE, TinyInt: NUMERIC_TYPE, SmallInt: NUMERIC_TYPE, MediumInt: NUMERIC_TYPE, Int: NUMERIC_TYPE, Integer: NUMERIC_TYPE, BigInt: NUMERIC_TYPE, Enum: TEXT_TYPE, Set: TEXT_TYPE, Json: TEXT_TYPE, Char: TEXT_TYPE, Varchar: TEXT_TYPE, NVarchar: TEXT_TYPE, TinyText: TEXT_TYPE, Text: TEXT_TYPE, MediumText: TEXT_TYPE, LongText: TEXT_TYPE, Uuid: TEXT_TYPE, Clob: TEXT_TYPE, Date: TIME_TYPE, DateTime: TIME_TYPE, Time: TIME_TYPE, TimeStamp: TIME_TYPE, TimeStampz: TIME_TYPE, Decimal: NUMERIC_TYPE, Numeric: NUMERIC_TYPE, Real: NUMERIC_TYPE, Float: NUMERIC_TYPE, Double: NUMERIC_TYPE, Binary: BLOB_TYPE, VarBinary: BLOB_TYPE, TinyBlob: BLOB_TYPE, Blob: BLOB_TYPE, MediumBlob: BLOB_TYPE, LongBlob: BLOB_TYPE, Bytea: BLOB_TYPE, Bool: NUMERIC_TYPE, Serial: NUMERIC_TYPE, BigSerial: NUMERIC_TYPE, } 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.ConvertibleTo(TimeType) { 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, NVarchar, TinyText, Text, MediumText, LongText, Enum, Set, Uuid, Clob: 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("") } }