commit
7826f88534
|
|
@ -1,2 +1,2 @@
|
|||
[target]
|
||||
path = github.com/lunny/xorm
|
||||
path = github.com/go-xorm/xorm
|
||||
|
|
@ -1,14 +1,17 @@
|
|||
## Contributing to xorm
|
||||
|
||||
`xorm` has a backlog of pull requests, but contributions are still very
|
||||
`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very
|
||||
much welcome. You can help with patch review, submitting bug reports,
|
||||
or adding new functionality. There is no formal style guide, but
|
||||
please conform to the style of existing code and general Go formatting
|
||||
conventions when submitting patches.
|
||||
|
||||
* [fork a repo](https://help.github.com/articles/fork-a-repo)
|
||||
* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request)
|
||||
|
||||
### Patch review
|
||||
|
||||
Help review existing open pull requests by commenting on the code or
|
||||
Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or
|
||||
proposed functionality.
|
||||
|
||||
### Bug reports
|
||||
|
|
@ -18,11 +21,11 @@ We appreciate any bug reports, but especially ones with self-contained
|
|||
further) test cases. It's especially helpful if you can submit a pull
|
||||
request with just the failing test case (you'll probably want to
|
||||
pattern it after the tests in
|
||||
[base_test.go](https://github.com/lunny/xorm/blob/master/base_test.go) AND
|
||||
[benchmark_base_test.go](https://github.com/lunny/xorm/blob/master/benchmark_base_test.go).
|
||||
[base_test.go](https://github.com/go-xorm/xorm/blob/master/base_test.go) AND
|
||||
[benchmark_base_test.go](https://github.com/go-xorm/xorm/blob/master/benchmark_base_test.go).
|
||||
|
||||
If you implements a new database interface, you maybe need to add a <databasename>_test.go file.
|
||||
For example, [mysql_test.go](https://github.com/lunny/xorm/blob/master/mysql_test.go)
|
||||
For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/mysql_test.go)
|
||||
|
||||
### New functionality
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2013 - 2014
|
||||
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.
|
||||
30
README.md
30
README.md
|
|
@ -1,8 +1,8 @@
|
|||
[中文](https://github.com/lunny/xorm/blob/master/README_CN.md)
|
||||
[中文](https://github.com/go-xorm/xorm/blob/master/README_CN.md)
|
||||
|
||||
Xorm is a simple and powerful ORM for Go.
|
||||
|
||||
[](https://drone.io/github.com/lunny/xorm/latest) [](http://gowalker.org/github.com/lunny/xorm) [](https://bitdeli.com/free "Bitdeli Badge")
|
||||
[](https://drone.io/github.com/go-xorm/xorm/latest) [](http://gowalker.org/github.com/go-xorm/xorm) [](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
# Features
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Xorm is a simple and powerful ORM for Go.
|
|||
|
||||
* Query Cache speed up
|
||||
|
||||
* Database Reverse support, See [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md)
|
||||
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md)
|
||||
|
||||
* Simple cascade loading support
|
||||
|
||||
|
|
@ -54,39 +54,41 @@ Drivers for Go's sql package which currently support database/sql includes:
|
|||
* Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type
|
||||
* Performance improvement for Get()/Find()/Iterate()
|
||||
|
||||
[More changelogs ...](https://github.com/lunny/xorm/blob/master/docs/Changelog.md)
|
||||
[More changelogs ...](https://github.com/go-xorm/xorm/blob/master/docs/Changelog.md)
|
||||
|
||||
# Installation
|
||||
|
||||
If you have [gopm](https://github.com/gpmgo/gopm) installed,
|
||||
|
||||
gopm get github.com/lunny/xorm
|
||||
gopm get github.com/go-xorm/xorm
|
||||
|
||||
Or
|
||||
|
||||
go get github.com/lunny/xorm
|
||||
go get github.com/go-xorm/xorm
|
||||
|
||||
# Documents
|
||||
|
||||
* [GoDoc](http://godoc.org/github.com/lunny/xorm)
|
||||
* [GoDoc](http://godoc.org/github.com/go-xorm/xorm)
|
||||
|
||||
* [GoWalker](http://gowalker.org/github.com/lunny/xorm)
|
||||
* [GoWalker](http://gowalker.org/github.com/go-xorm/xorm)
|
||||
|
||||
* [Quick Start](https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md)
|
||||
* [Quick Start](https://github.com/go-xorm/xorm/blob/master/docs/QuickStartEn.md)
|
||||
|
||||
# Cases
|
||||
|
||||
* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
|
||||
|
||||
* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
|
||||
|
||||
* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
|
||||
|
||||
* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
|
||||
|
||||
* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
|
||||
|
||||
* [Very Hour](http://veryhour.com/)
|
||||
|
||||
# Todo
|
||||
|
||||
[Todo List](https://trello.com/b/IHsuAnhk/xorm)
|
||||
* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
|
||||
|
||||
# Discuss
|
||||
|
||||
|
|
@ -94,7 +96,7 @@ Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xor
|
|||
|
||||
# Contributors
|
||||
|
||||
If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md)
|
||||
If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
|
||||
|
||||
* [Lunny](https://github.com/lunny)
|
||||
* [Nashtsai](https://github.com/nashtsai)
|
||||
|
|
@ -102,4 +104,4 @@ If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/
|
|||
# LICENSE
|
||||
|
||||
BSD License
|
||||
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
|
||||
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
|
||||
26
README_CN.md
26
README_CN.md
|
|
@ -1,10 +1,10 @@
|
|||
# xorm
|
||||
|
||||
[English](https://github.com/lunny/xorm/blob/master/README.md)
|
||||
[English](https://github.com/go-xorm/xorm/blob/master/README.md)
|
||||
|
||||
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
|
||||
|
||||
[](https://drone.io/github.com/lunny/xorm/latest) [](http://gowalker.org/github.com/lunny/xorm)
|
||||
[](https://drone.io/github.com/go-xorm/xorm/latest) [](http://gowalker.org/github.com/go-xorm/xorm)
|
||||
|
||||
## 特性
|
||||
|
||||
|
|
@ -56,40 +56,42 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
|
|||
* 查询函数 Get()/Find()/Iterate() 在性能上的改进
|
||||
|
||||
|
||||
[更多更新日志...](https://github.com/lunny/xorm/blob/master/docs/ChangelogCN.md)
|
||||
[更多更新日志...](https://github.com/go-xorm/xorm/blob/master/docs/ChangelogCN.md)
|
||||
|
||||
## 安装
|
||||
|
||||
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
|
||||
|
||||
gopm get github.com/lunny/xorm
|
||||
gopm get github.com/go-xorm/xorm
|
||||
|
||||
或者您也可以使用go工具进行安装:
|
||||
|
||||
go get github.com/lunny/xorm
|
||||
go get github.com/go-xorm/xorm
|
||||
|
||||
## 文档
|
||||
|
||||
* [快速开始](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md)
|
||||
* [快速开始](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md)
|
||||
|
||||
* [GoWalker代码文档](http://gowalker.org/github.com/lunny/xorm)
|
||||
* [GoWalker代码文档](http://gowalker.org/github.com/go-xorm/xorm)
|
||||
|
||||
* [Godoc代码文档](http://godoc.org/github.com/lunny/xorm)
|
||||
* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm)
|
||||
|
||||
|
||||
## 案例
|
||||
|
||||
* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
|
||||
|
||||
* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
|
||||
|
||||
* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
|
||||
|
||||
* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
|
||||
|
||||
* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
|
||||
|
||||
* [Very Hour](http://veryhour.com/)
|
||||
|
||||
## Todo
|
||||
|
||||
[开发计划](https://trello.com/b/IHsuAnhk/xorm)
|
||||
* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
|
||||
|
||||
## 讨论
|
||||
|
||||
|
|
@ -97,7 +99,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
|
|||
|
||||
# 贡献者
|
||||
|
||||
如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md)
|
||||
如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)
|
||||
|
||||
* [Lunny](https://github.com/lunny)
|
||||
* [Nashtsai](https://github.com/nashtsai)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type LRUCacher struct {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
go test -v -bench=. -run=XXX
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
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)
|
||||
}
|
||||
113
core/column.go
113
core/column.go
|
|
@ -1,113 +0,0 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
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)
|
||||
}
|
||||
566
core/db.go
566
core/db.go
|
|
@ -1,566 +0,0 @@
|
|||
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...)
|
||||
}
|
||||
634
core/db_test.go
634
core/db_test.go
|
|
@ -1,634 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
144
core/dialect.go
144
core/dialect.go
|
|
@ -1,144 +0,0 @@
|
|||
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]
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
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]
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
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)}
|
||||
}
|
||||
176
core/mapper.go
176
core/mapper.go
|
|
@ -1,176 +0,0 @@
|
|||
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}
|
||||
}
|
||||
25
core/pk.go
25
core/pk.go
|
|
@ -1,25 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
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
|
||||
}
|
||||
288
core/type.go
288
core/type.go
|
|
@ -1,288 +0,0 @@
|
|||
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("")
|
||||
}
|
||||
}
|
||||
4
doc.go
4
doc.go
|
|
@ -9,7 +9,7 @@ Installation
|
|||
|
||||
Make sure you have installed Go 1.1+ and then:
|
||||
|
||||
go get github.com/lunny/xorm
|
||||
go get github.com/go-xorm/xorm
|
||||
|
||||
Create Engine
|
||||
|
||||
|
|
@ -137,6 +137,6 @@ The above 7 methods could use with condition methods.
|
|||
engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find()
|
||||
//SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id
|
||||
|
||||
More usage, please visit https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md
|
||||
More usage, please visit https://github.com/go-xorm/xorm/blob/master/docs/QuickStartEn.md
|
||||
*/
|
||||
package xorm
|
||||
|
|
|
|||
|
|
@ -11,20 +11,20 @@
|
|||
|
||||
Improvements:
|
||||
* Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type
|
||||
* Performance improvement for Get()/Find()/Iterate()
|
||||
* Performance improvement for Get()/Find()/Iterate()
|
||||
|
||||
|
||||
* **v0.2.3** : Improved documents; Optimistic Locking support; Timestamp with time zone support; Mapper change to tableMapper and columnMapper & added PrefixMapper & SuffixMapper support custom table or column name's prefix and suffix;Insert now return affected, err instead of id, err; Added UseBool & Distinct;
|
||||
* **v0.2.2** : Postgres drivers now support lib/pq; Added method Iterate for record by record to handler;Added SetMaxConns(go1.2+) support; some bugs fixed.
|
||||
* **v0.2.1** : Added database reverse tool, now support generate go & c++ codes, see [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); some bug fixed.
|
||||
* **v0.2.0** : Added Cache supported, select is speeder up 3~5x; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes;
|
||||
* **v0.1.9** : Added postgres and mymysql supported; Added ` and ? supported on Raw SQL even if postgres; Added Cols, StoreEngine, Charset function, Added many column data type supported, please see [Mapping Rules](#mapping).
|
||||
* **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping).
|
||||
* **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit.
|
||||
* **v0.1.6** : Added conversion interface support; added struct derive support; added single mapping support
|
||||
* **v0.1.5** : Added multi threads support; added Sql() function for struct query; Get function changed return inteface; MakeSession and Create are instead with NewSession and NewEngine.
|
||||
* **v0.1.4** : Added simple cascade load support; added more data type supports.
|
||||
* **v0.1.3** : Find function now supports both slice and map; Add Table function for multi tables and temperory tables support
|
||||
* **v0.1.2** : Insert function now supports both struct and slice pointer parameters, batch inserting and auto transaction
|
||||
* **v0.1.1** : Add Id, In functions and improved README
|
||||
* **v0.2.1** : Added database reverse tool, now support generate go & c++ codes, see [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md); some bug fixed.
|
||||
* **v0.2.0** : Added Cache supported, select is speeder up 3~5x; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes;
|
||||
* **v0.1.9** : Added postgres and mymysql supported; Added ` and ? supported on Raw SQL even if postgres; Added Cols, StoreEngine, Charset function, Added many column data type supported, please see [Mapping Rules](#mapping).
|
||||
* **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping).
|
||||
* **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit.
|
||||
* **v0.1.6** : Added conversion interface support; added struct derive support; added single mapping support
|
||||
* **v0.1.5** : Added multi threads support; added Sql() function for struct query; Get function changed return inteface; MakeSession and Create are instead with NewSession and NewEngine.
|
||||
* **v0.1.4** : Added simple cascade load support; added more data type supports.
|
||||
* **v0.1.3** : Find function now supports both slice and map; Add Table function for multi tables and temperory tables support
|
||||
* **v0.1.2** : Insert function now supports both struct and slice pointer parameters, batch inserting and auto transaction
|
||||
* **v0.1.1** : Add Id, In functions and improved README
|
||||
* **v0.1.0** : Inital release.
|
||||
|
|
@ -15,10 +15,10 @@
|
|||
|
||||
* **v0.2.3** : 改善了文档;提供了乐观锁支持;添加了带时区时间字段支持;Mapper现在分成表名Mapper和字段名Mapper,同时实现了表或字段的自定义前缀后缀;Insert方法的返回值含义从id, err更改为 affected, err,请大家注意;添加了UseBool 和 Distinct函数。
|
||||
* **v0.2.2** : Postgres驱动新增了对lib/pq的支持;新增了逐条遍历方法Iterate;新增了SetMaxConns(go1.2+)支持,修复了bug若干;
|
||||
* **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); 修复了一些bug.
|
||||
* **v0.2.0** : 新增 [缓存](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构;
|
||||
* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数;SQL语句打印支持io.Writer接口,默认打印到控制台;新增更多的字段类型支持,详见 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21);删除废弃的MakeSession和Create函数。
|
||||
* **v0.1.8** : 新增联合index,联合unique支持,请查看 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21)。
|
||||
* **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md); 修复了一些bug.
|
||||
* **v0.2.0** : 新增 [缓存](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构;
|
||||
* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数;SQL语句打印支持io.Writer接口,默认打印到控制台;新增更多的字段类型支持,详见 [映射规则](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md#21);删除废弃的MakeSession和Create函数。
|
||||
* **v0.1.8** : 新增联合index,联合unique支持,请查看 [映射规则](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md#21)。
|
||||
* **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现,可以选择不使用连接池,使用系统连接池和使用自带连接池三种实现,默认为SysConnectPool,即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法,在系统退出时应调用此方法。
|
||||
* **v0.1.6** : 新增Conversion,支持自定义类型到数据库类型的转换;新增查询结构体自动检测匿名成员支持;新增单向映射支持;
|
||||
* **v0.1.5** : 新增对多线程的支持;新增Sql()函数;支持任意sql语句的struct查询;Get函数返回值变动;MakeSession和Create函数被NewSession和NewEngine函数替代;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ xorm 快速入门
|
|||
|
||||
* [1.创建Orm引擎](#10)
|
||||
* [2.定义表结构体](#20)
|
||||
* [2.1.名称映射规则](#21)
|
||||
* [2.2.前缀映射规则和后缀映射规则](#22)
|
||||
* [2.1.名称映射规则](#21)
|
||||
* [2.2.前缀映射,后缀映射和缓存映射](#22)
|
||||
* [2.3.使用Table和Tag改变名称映射](#23)
|
||||
* [2.4.Column属性定义](#24)
|
||||
* [2.5.Go与字段类型对应表](#25)
|
||||
|
|
@ -20,7 +20,7 @@ xorm 快速入门
|
|||
* [5.3.Get方法](#63)
|
||||
* [5.4.Find方法](#64)
|
||||
* [5.5.Iterate方法](#65)
|
||||
* [5.6.Count方法](#66)
|
||||
* [5.6.Count方法](#66)
|
||||
* [5.7.Rows方法](#67)
|
||||
* [6.更新数据](#70)
|
||||
* [6.1.乐观锁](#71)
|
||||
|
|
@ -29,12 +29,13 @@ xorm 快速入门
|
|||
* [9.执行SQL命令](#100)
|
||||
* [10.事务处理](#110)
|
||||
* [11.缓存](#120)
|
||||
* [12.xorm工具](#130)
|
||||
* [12.1.反转命令](#131)
|
||||
* [13.Examples](#140)
|
||||
* [14.案例](#150)
|
||||
* [15.那些年我们踩过的坑](#160)
|
||||
* [16.讨论](#170)
|
||||
* [12.事件](#125)
|
||||
* [13.xorm工具](#130)
|
||||
* [13.1.反转命令](#131)
|
||||
* [14.Examples](#140)
|
||||
* [15.案例](#150)
|
||||
* [16.那些年我们踩过的坑](#160)
|
||||
* [17.讨论](#170)
|
||||
|
||||
<a name="10" id="10"></a>
|
||||
## 1.创建Orm引擎
|
||||
|
|
@ -44,8 +45,8 @@ xorm 快速入门
|
|||
```Go
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
)
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
|
||||
defer engine.Close()
|
||||
```
|
||||
|
|
@ -55,23 +56,26 @@ or
|
|||
```Go
|
||||
import (
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
engine, err = xorm.NewEngine("sqlite3", "./test.db")
|
||||
defer engine.Close()
|
||||
defer engine.Close()
|
||||
```
|
||||
|
||||
一般如果只针对一个数据库进行操作,只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
|
||||
|
||||
xorm当前支持五种驱动四个数据库如下:
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
|
||||
>>>>>>> master
|
||||
|
||||
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
|
||||
|
||||
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
|
||||
|
||||
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
|
||||
|
||||
* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
|
||||
* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
|
||||
|
||||
* MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc)
|
||||
|
||||
|
|
@ -120,27 +124,39 @@ engine.SetMapper(SameMapper{})
|
|||
|
||||
同时需要注意的是:
|
||||
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
<<<<<<< HEAD
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
|
||||
|
||||
|
||||
=======
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
|
||||
|
||||
>>>>>>> master
|
||||
```Go
|
||||
engine.SetTableMapper(SameMapper{})
|
||||
engine.SetColumnMapper(SnakeMapper{})
|
||||
```
|
||||
|
||||
<a name="22" id="22"></a>
|
||||
### 2.2.前缀映射规则和后缀映射规则
|
||||
### 2.2.前缀映射,后缀映射和缓存映射
|
||||
|
||||
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
<<<<<<< HEAD
|
||||
*
|
||||
=======
|
||||
* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。
|
||||
>>>>>>> master
|
||||
|
||||
<a name="23" id="23"></a>
|
||||
### 2.3.使用Table和Tag改变名称映射
|
||||
|
||||
如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。
|
||||
|
||||
通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
|
||||
* 如果struct拥有`Tablename() string`的成员方法,那么此方法的返回值即是该struct默认对应的数据库表名。
|
||||
|
||||
* 通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
|
||||
|
||||
<a name="23" id="23"></a>
|
||||
### 2.4.Column属性定义
|
||||
|
|
@ -153,7 +169,7 @@ type User struct {
|
|||
}
|
||||
```
|
||||
|
||||
对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。
|
||||
对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。
|
||||
|
||||
具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写:
|
||||
|
||||
|
|
@ -165,7 +181,7 @@ type User struct {
|
|||
<td>pk</td><td>是否是Primary Key,如果在一个struct中有多个字段都使用了此标记,则这多个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型,复合主键支持这7种Go的数据类型的组合。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>当前支持30多种字段类型,详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td>
|
||||
<td>当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>autoincr</td><td>是否是自增</td>
|
||||
|
|
@ -211,7 +227,7 @@ type User struct {
|
|||
|
||||
- 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义
|
||||
|
||||
- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/lunny/xorm/blob/master/docs/AutoMap.md)
|
||||
- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md)
|
||||
|
||||
- 4.实现了Conversion接口的类型或者结构体,将根据接口的转换方式在类型和数据库记录之间进行相互转换。
|
||||
```Go
|
||||
|
|
@ -226,7 +242,7 @@ type Conversion interface {
|
|||
|
||||
如果不使用tag来定义field对应的数据库字段类型,那么系统会自动给出一个默认的字段类型,对应表如下:
|
||||
|
||||
[go类型<->数据库类型对应表](https://github.com/lunny/xorm/blob/master/docs/AutoMap.md)
|
||||
[go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md)
|
||||
|
||||
<a name="30" id="30"></a>
|
||||
## 3.表结构操作
|
||||
|
|
@ -356,7 +372,7 @@ affected, err := engine.Insert(user, &questions)
|
|||
```
|
||||
|
||||
这里需要注意以下几点:
|
||||
* 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。
|
||||
* 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。
|
||||
* 多条插入会自动生成`Insert into table values (),(),()`的语句,因此这样的语句有一个最大的记录数,根据经验测算在150条左右。大于150条后,生成的sql语句将太长可能导致执行失败。因此在插入大量数据时,目前需要自行分割成每150条插入一次。
|
||||
|
||||
<a name="60" id="60"></a>
|
||||
|
|
@ -407,7 +423,11 @@ engine.Cols("age", "name").Update(&user)
|
|||
// UPDATE user SET age=? AND name=?
|
||||
```
|
||||
|
||||
其中的参数"age", "name"也可以写成"age, name",两种写法均可
|
||||
* AllCols()
|
||||
查询或更新所有字段。
|
||||
|
||||
* MustCols(…string)
|
||||
某些字段必须更新。
|
||||
|
||||
* Omit(...string)
|
||||
和cols相反,此函数指定排除某些指定的字段。注意:此方法和Cols方法不可同时使用
|
||||
|
|
@ -504,7 +524,7 @@ has, err := engine.Get(user)
|
|||
1) 传入Slice用于返回数据
|
||||
|
||||
```Go
|
||||
everyone := make([]Userinfo, 0)
|
||||
everyone := make([]Userinfo, 0)
|
||||
err := engine.Find(&everyone)
|
||||
|
||||
pEveryOne := make([]*Userinfo, 0)
|
||||
|
|
@ -578,13 +598,21 @@ affected, err := engine.Id(id).Update(user)
|
|||
|
||||
这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择:
|
||||
|
||||
<<<<<<< HEAD
|
||||
1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。
|
||||
=======
|
||||
* 1.通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。
|
||||
>>>>>>> master
|
||||
|
||||
```Go
|
||||
affected, err := engine.Id(id).Cols("age").Update(&user)
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。
|
||||
=======
|
||||
* 2.通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。
|
||||
>>>>>>> master
|
||||
|
||||
```Go
|
||||
affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})
|
||||
|
|
@ -643,7 +671,7 @@ results, err := engine.Query(sql)
|
|||
|
||||
```Go
|
||||
sql = "update `userinfo` set username=? where id=?"
|
||||
res, err := engine.Exec(sql, "xiaolun", 1)
|
||||
res, err := engine.Exec(sql, "xiaolun", 1)
|
||||
```
|
||||
|
||||
<a name="110" id="110"></a>
|
||||
|
|
@ -678,9 +706,11 @@ if err != nil {
|
|||
err = session.Commit()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* 注意如果您使用的是mysql,数据库引擎为innodb事务才有效,myisam引擎是不支持事务的。
|
||||
|
||||
<a name="120" id="120"></a>
|
||||
## 11.缓存
|
||||
|
||||
|
|
@ -715,7 +745,7 @@ engine.MapCacher(&user, nil)
|
|||
1. 当使用了`Distinct`,`Having`,`GroupBy`方法将不会使用缓存
|
||||
|
||||
2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。
|
||||
|
||||
|
||||
3. 在使用Exec方法执行了方法之后,可能会导致缓存与数据库不一致的地方。因此如果启用缓存,尽量避免使用Exec。如果必须使用,则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如:
|
||||
|
||||
```Go
|
||||
|
|
@ -725,22 +755,48 @@ engine.ClearCache(new(User))
|
|||
|
||||
缓存的实现原理如下图所示:
|
||||
|
||||

|
||||

|
||||
|
||||
<a name="125" id="125"></a>
|
||||
## 12.事件
|
||||
xorm支持两种方式的事件,一种是在Struct中的特定方法来作为事件的方法,一种是在执行语句的过程中执行事件。
|
||||
|
||||
在Struct中作为成员方法的事件如下:
|
||||
|
||||
* BeforeInsert()
|
||||
|
||||
* BeforeUpdate()
|
||||
|
||||
* BeforeDelete()
|
||||
|
||||
* AfterInsert()
|
||||
|
||||
* AfterUpdate()
|
||||
|
||||
* AfterDelete()
|
||||
|
||||
在语句执行过程中的事件方法为:
|
||||
|
||||
* Before(beforeFunc interface{})
|
||||
|
||||
* After(afterFunc interface{})
|
||||
|
||||
其中beforeFunc和afterFunc的原型为func(bean interface{}).
|
||||
|
||||
<a name="130" id="130"></a>
|
||||
## 12.xorm工具
|
||||
## 13.xorm工具
|
||||
xorm工具提供了xorm命令,能够帮助做很多事情。
|
||||
|
||||
### 12.1.反转命令
|
||||
参见 [xorm工具](https://github.com/lunny/xorm/tree/master/xorm)
|
||||
### 13.1.反转命令
|
||||
参见 [xorm工具](https://github.com/go-xorm/xorm/tree/master/xorm)
|
||||
|
||||
<a name="140" id="140"></a>
|
||||
## 13.Examples
|
||||
## 14.Examples
|
||||
|
||||
请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples)
|
||||
请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples)
|
||||
|
||||
<a name="150" id="150"></a>
|
||||
## 14.案例
|
||||
## 15.案例
|
||||
|
||||
* [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
|
||||
|
||||
|
|
@ -751,15 +807,15 @@ xorm工具提供了xorm命令,能够帮助做很多事情。
|
|||
* [VeryHour](http://veryhour.com)
|
||||
|
||||
<a name="160" id="160"></a>
|
||||
## 15.那些年我们踩过的坑
|
||||
* 怎么同时使用xorm的tag和json的tag?
|
||||
|
||||
答:使用空格
|
||||
|
||||
```Go
|
||||
type User struct {
|
||||
Name string `json:"name" xorm:"name"`
|
||||
}
|
||||
## 16.那些年我们踩过的坑
|
||||
* 怎么同时使用xorm的tag和json的tag?
|
||||
|
||||
答:使用空格
|
||||
|
||||
```Go
|
||||
type User struct {
|
||||
Name string `json:"name" xorm:"name"`
|
||||
}
|
||||
```
|
||||
|
||||
* 我的struct里面包含bool类型,为什么它不能作为条件也没法用Update更新?
|
||||
|
|
@ -795,5 +851,5 @@ money float64 `xorm:"Numeric"`
|
|||
|
||||
|
||||
<a name="170" id="170"></a>
|
||||
## 16.讨论
|
||||
## 17.讨论
|
||||
请加入QQ群:280360085 进行讨论。
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ When using xorm, you can create multiple orm engines, an engine means a databse.
|
|||
```Go
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
)
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
|
||||
defer engine.Close()
|
||||
```
|
||||
|
|
@ -52,18 +52,18 @@ or
|
|||
```Go
|
||||
import (
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
engine, err = xorm.NewEngine("sqlite3", "./test.db")
|
||||
defer engine.Close()
|
||||
defer engine.Close()
|
||||
```
|
||||
|
||||
Generally, you can only create one engine. Engine supports run on go rutines.
|
||||
|
||||
xorm supports four drivers now:
|
||||
|
||||
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
|
||||
|
||||
* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL)
|
||||
|
||||
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
|
||||
|
||||
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
|
||||
|
|
@ -95,35 +95,52 @@ engine.Logger = f
|
|||
3.Engine support connection pool. The default pool is database/sql's and also you can use custom pool. Xorm provides two connection pool `xorm.NonConnectionPool` & `xorm.SimpleConnectPool`. If you want to use yourself pool, you can use `engine.SetPool` to set it.
|
||||
|
||||
* Use `engine.SetIdleConns()` to set idle connections.
|
||||
* Use `engine.SetMaxConns()` to set Max connections. This methods support only Go 1.2+.
|
||||
* Use `engine.SetMaxConns()` to set Max connections. This methods support only Go 1.2+.
|
||||
|
||||
<a name="20" id="20"></a>
|
||||
## 2.Define struct
|
||||
|
||||
xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下:
|
||||
xorm map a struct to a database table, the rule is below.
|
||||
|
||||
<a name="21" id="21"></a>
|
||||
### 2.1.名称映射规则
|
||||
### 2.1.name mapping rule
|
||||
|
||||
名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理,xorm内置了两种IMapper实现:`SnakeMapper` 和 `SameMapper`。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持相同的命名。
|
||||
use xorm.IMapper interface to implement. There are two IMapper implemented: `SnakeMapper` and `SameMapper`. SnakeMapper means struct name is word by word and table name or column name as 下划线. SameMapper means same name between struct and table.
|
||||
|
||||
当前SnakeMapper为默认值,如果需要改变时,在engine创建完成后使用
|
||||
SnakeMapper is the default.
|
||||
|
||||
```Go
|
||||
engine.Mapper = SameMapper{}
|
||||
```
|
||||
|
||||
同时需要注意的是:
|
||||
|
||||
* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
|
||||
|
||||
```Go
|
||||
engine.SetTableMapper(SameMapper{})
|
||||
engine.SetColumnMapper(SnakeMapper{})
|
||||
```
|
||||
|
||||
<a name="22" id="22"></a>
|
||||
### 2.2.前缀映射规则,后缀映射规则和缓存映射规则
|
||||
|
||||
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
|
||||
* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以起到在内存中缓存曾经映射过的命名映射。
|
||||
|
||||
当然,如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。
|
||||
|
||||
<a name="22" id="22"></a>
|
||||
### 2.2.使用Table和Tag改变名称映射
|
||||
### 2.3.使用Table和Tag改变名称映射
|
||||
|
||||
如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。
|
||||
|
||||
通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。
|
||||
|
||||
<a name="23" id="23"></a>
|
||||
### 2.3.Column属性定义
|
||||
### 2.4.Column属性定义
|
||||
我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如:
|
||||
|
||||
```
|
||||
|
|
@ -133,7 +150,7 @@ type User struct {
|
|||
}
|
||||
```
|
||||
|
||||
对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。
|
||||
对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。
|
||||
|
||||
具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写:
|
||||
|
||||
|
|
@ -145,7 +162,7 @@ type User struct {
|
|||
<td>pk</td><td>是否是Primary Key,当前仅支持int64类型</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>当前支持30多种字段类型,详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td>
|
||||
<td>当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)</td><td>字段类型</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>autoincr</td><td>是否是自增</td>
|
||||
|
|
@ -199,7 +216,7 @@ type Conversion interface {
|
|||
FromDB([]byte) error
|
||||
ToDB() ([]byte, error)
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
<a name="30" id="30"></a>
|
||||
## 3.表结构操作
|
||||
|
|
@ -247,7 +264,7 @@ xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到
|
|||
调用方法如下:
|
||||
```Go
|
||||
err := engine.Sync(new(User))
|
||||
```
|
||||
```
|
||||
|
||||
<a name="50" id="50"></a>
|
||||
## 4.插入数据
|
||||
|
|
@ -408,59 +425,59 @@ Having的参数字符串
|
|||
当从一个struct来生成查询条件或更新字段时,xorm会判断struct的field是否为0,"",nil,如果为以上则不当做查询条件或者更新内容。因为bool类型只有true和false两种值,因此默认所有bool类型不会作为查询条件或者更新字段。如果可以使用此方法,如果默认不传参数,则所有的bool字段都将会被使用,如果参数不为空,则参数中指定的为字段名,则这些字段对应的bool值将被使用。
|
||||
|
||||
* Cascade(bool)
|
||||
是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。
|
||||
是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。
|
||||
|
||||
<a name="50" id="50"></a>
|
||||
### 5.3.Get one record
|
||||
Fetch a single object by user
|
||||
|
||||
```Go
|
||||
var user = User{Id:27}
|
||||
has, err := engine.Get(&user)
|
||||
// or has, err := engine.Id(27).Get(&user)
|
||||
|
||||
var user = User{Name:"xlw"}
|
||||
has, err := engine.Get(&user)
|
||||
```
|
||||
### 5.3.Get one record
|
||||
Fetch a single object by user
|
||||
|
||||
```Go
|
||||
var user = User{Id:27}
|
||||
has, err := engine.Get(&user)
|
||||
// or has, err := engine.Id(27).Get(&user)
|
||||
|
||||
var user = User{Name:"xlw"}
|
||||
has, err := engine.Get(&user)
|
||||
```
|
||||
|
||||
<a name="60" id="60"></a>
|
||||
### 5.4.Find
|
||||
Fetch multipe objects into a slice or a map, use Find:
|
||||
|
||||
```Go
|
||||
var everyone []Userinfo
|
||||
err := engine.Find(&everyone)
|
||||
|
||||
users := make(map[int64]Userinfo)
|
||||
err := engine.Find(&users)
|
||||
```
|
||||
|
||||
* also you can use Where, Limit
|
||||
|
||||
```Go
|
||||
var allusers []Userinfo
|
||||
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
|
||||
```
|
||||
|
||||
* or you can use a struct query
|
||||
|
||||
```Go
|
||||
var tenusers []Userinfo
|
||||
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0
|
||||
```
|
||||
|
||||
* or In function
|
||||
|
||||
```Go
|
||||
var tenusers []Userinfo
|
||||
err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5)
|
||||
```
|
||||
|
||||
* The default will query all columns of a table. Use Cols function if you want to select some columns
|
||||
|
||||
```Go
|
||||
var tenusers []Userinfo
|
||||
err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name
|
||||
### 5.4.Find
|
||||
Fetch multipe objects into a slice or a map, use Find:
|
||||
|
||||
```Go
|
||||
var everyone []Userinfo
|
||||
err := engine.Find(&everyone)
|
||||
|
||||
users := make(map[int64]Userinfo)
|
||||
err := engine.Find(&users)
|
||||
```
|
||||
|
||||
* also you can use Where, Limit
|
||||
|
||||
```Go
|
||||
var allusers []Userinfo
|
||||
err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20
|
||||
```
|
||||
|
||||
* or you can use a struct query
|
||||
|
||||
```Go
|
||||
var tenusers []Userinfo
|
||||
err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0
|
||||
```
|
||||
|
||||
* or In function
|
||||
|
||||
```Go
|
||||
var tenusers []Userinfo
|
||||
err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5)
|
||||
```
|
||||
|
||||
* The default will query all columns of a table. Use Cols function if you want to select some columns
|
||||
|
||||
```Go
|
||||
var tenusers []Userinfo
|
||||
err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name
|
||||
```
|
||||
|
||||
<a name="70" id="70"></a>
|
||||
|
|
@ -525,44 +542,44 @@ engine.Id(1).Get(&user)
|
|||
engine.Id(1).Update(&user)
|
||||
// UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ?
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="80" id="80"></a>
|
||||
## 7.Delete one or more records
|
||||
Delete one or more records
|
||||
## 7.Delete one or more records
|
||||
Delete one or more records
|
||||
|
||||
* delete by id
|
||||
|
||||
```Go
|
||||
err := engine.Id(1).Delete(&User{})
|
||||
|
||||
```Go
|
||||
err := engine.Id(1).Delete(&User{})
|
||||
```
|
||||
|
||||
|
||||
* delete by other conditions
|
||||
|
||||
```Go
|
||||
err := engine.Delete(&User{Name:"xlw"})
|
||||
```
|
||||
```
|
||||
|
||||
<a name="90" id="90"></a>
|
||||
## 8.Execute SQL query
|
||||
|
||||
Of course, SQL execution is also provided.
|
||||
|
||||
If select then use Query
|
||||
|
||||
```Go
|
||||
sql := "select * from userinfo"
|
||||
results, err := engine.Query(sql)
|
||||
```
|
||||
<a name="90" id="90"></a>
|
||||
## 8.Execute SQL query
|
||||
|
||||
<a name="100" id="100"></a>
|
||||
## 9.Execute SQL command
|
||||
If insert, update or delete then use Exec
|
||||
|
||||
```Go
|
||||
sql = "update userinfo set username=? where id=?"
|
||||
res, err := engine.Exec(sql, "xiaolun", 1)
|
||||
```
|
||||
Of course, SQL execution is also provided.
|
||||
|
||||
If select then use Query
|
||||
|
||||
```Go
|
||||
sql := "select * from userinfo"
|
||||
results, err := engine.Query(sql)
|
||||
```
|
||||
|
||||
<a name="100" id="100"></a>
|
||||
## 9.Execute SQL command
|
||||
If insert, update or delete then use Exec
|
||||
|
||||
```Go
|
||||
sql = "update userinfo set username=? where id=?"
|
||||
res, err := engine.Exec(sql, "xiaolun", 1)
|
||||
```
|
||||
|
||||
<a name="110" id="110"></a>
|
||||
## 10.Transaction
|
||||
|
|
@ -596,8 +613,8 @@ if err != nil {
|
|||
err = session.Commit()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
<a name="120" id="120"></a>
|
||||
## 11.缓存
|
||||
|
|
@ -637,19 +654,19 @@ engine.ClearCache(new(User))
|
|||
|
||||
Cache implement theory below:
|
||||
|
||||

|
||||

|
||||
|
||||
<a name="130" id="130"></a>
|
||||
## 12.xorm tool
|
||||
xorm工具提供了xorm命令,能够帮助做很多事情。
|
||||
|
||||
### 12.1.Reverse command
|
||||
Please visit [xorm tool](https://github.com/lunny/xorm/tree/master/xorm)
|
||||
Please visit [xorm tool](https://github.com/go-xorm/xorm/tree/master/xorm)
|
||||
|
||||
<a name="140" id="140"></a>
|
||||
## 13.Examples
|
||||
|
||||
请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples)
|
||||
请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples)
|
||||
|
||||
<a name="150" id="150"></a>
|
||||
## 14.Cases
|
||||
|
|
@ -663,14 +680,14 @@ Please visit [xorm tool](https://github.com/lunny/xorm/tree/master/xorm)
|
|||
* [VeryHour](http://veryhour.com)
|
||||
|
||||
<a name="160"></a>
|
||||
## 15.FAQ
|
||||
|
||||
1.How the xorm tag use both with json?
|
||||
|
||||
Use space.
|
||||
|
||||
```Go
|
||||
type User struct {
|
||||
Name string `json:"name" xorm:"name"`
|
||||
}
|
||||
## 15.FAQ
|
||||
|
||||
1.How the xorm tag use both with json?
|
||||
|
||||
Use space.
|
||||
|
||||
```Go
|
||||
type User struct {
|
||||
Name string `json:"name" xorm:"name"`
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package drivers
|
||||
|
||||
import (
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
248
engine.go
248
engine.go
|
|
@ -6,14 +6,13 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// Engine is the major struct of xorm, it means a database manager.
|
||||
|
|
@ -27,16 +26,15 @@ type Engine struct {
|
|||
dialect core.Dialect
|
||||
Tables map[reflect.Type]*core.Table
|
||||
|
||||
mutex *sync.RWMutex
|
||||
ShowSQL bool
|
||||
ShowErr bool
|
||||
ShowDebug bool
|
||||
ShowWarn bool
|
||||
Pool IConnectPool
|
||||
Filters []core.Filter
|
||||
Logger io.Writer
|
||||
Cacher core.Cacher
|
||||
tableCachers map[reflect.Type]core.Cacher
|
||||
mutex *sync.RWMutex
|
||||
ShowSQL bool
|
||||
ShowErr bool
|
||||
ShowDebug bool
|
||||
ShowWarn bool
|
||||
Pool IConnectPool
|
||||
Filters []core.Filter
|
||||
Logger ILogger // io.Writer
|
||||
Cacher core.Cacher
|
||||
}
|
||||
|
||||
func (engine *Engine) SetMapper(mapper core.IMapper) {
|
||||
|
|
@ -118,9 +116,9 @@ func (engine *Engine) NoCascade() *Session {
|
|||
|
||||
// Set a table use a special cacher
|
||||
func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
|
||||
t := rType(bean)
|
||||
engine.autoMapType(t)
|
||||
engine.tableCachers[t] = cacher
|
||||
v := rValue(bean)
|
||||
engine.autoMapType(v)
|
||||
engine.Tables[v.Type()].Cacher = cacher
|
||||
}
|
||||
|
||||
// OpenDB provides a interface to operate database directly.
|
||||
|
|
@ -144,35 +142,44 @@ func (engine *Engine) Close() error {
|
|||
func (engine *Engine) Ping() error {
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
engine.LogSQL("PING DATABASE", engine.DriverName)
|
||||
engine.LogInfo("PING DATABASE", engine.DriverName)
|
||||
return session.Ping()
|
||||
}
|
||||
|
||||
// logging sql
|
||||
func (engine *Engine) LogSQL(contents ...interface{}) {
|
||||
func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
|
||||
if engine.ShowSQL {
|
||||
io.WriteString(engine.Logger, fmt.Sprintln(contents...))
|
||||
if len(sqlArgs) > 0 {
|
||||
engine.LogInfo("[sql]", sqlStr, "[args]", sqlArgs)
|
||||
} else {
|
||||
engine.LogInfo("[sql]", sqlStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logging error
|
||||
func (engine *Engine) LogError(contents ...interface{}) {
|
||||
if engine.ShowErr {
|
||||
io.WriteString(engine.Logger, fmt.Sprintln(contents...))
|
||||
engine.Logger.Err(fmt.Sprintln(contents...))
|
||||
}
|
||||
}
|
||||
|
||||
// logging error
|
||||
func (engine *Engine) LogInfo(contents ...interface{}) {
|
||||
engine.Logger.Info(fmt.Sprintln(contents...))
|
||||
}
|
||||
|
||||
// logging debug
|
||||
func (engine *Engine) LogDebug(contents ...interface{}) {
|
||||
if engine.ShowDebug {
|
||||
io.WriteString(engine.Logger, fmt.Sprintln(contents...))
|
||||
engine.Logger.Debug(fmt.Sprintln(contents...))
|
||||
}
|
||||
}
|
||||
|
||||
// logging warn
|
||||
func (engine *Engine) LogWarn(contents ...interface{}) {
|
||||
if engine.ShowWarn {
|
||||
io.WriteString(engine.Logger, fmt.Sprintln(contents...))
|
||||
engine.Logger.Warning(fmt.Sprintln(contents...))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -300,6 +307,18 @@ func (engine *Engine) Cols(columns ...string) *Session {
|
|||
return session.Cols(columns...)
|
||||
}
|
||||
|
||||
func (engine *Engine) AllCols() *Session {
|
||||
session := engine.NewSession()
|
||||
session.IsAutoClose = true
|
||||
return session.AllCols()
|
||||
}
|
||||
|
||||
func (engine *Engine) MustCols(columns ...string) *Session {
|
||||
session := engine.NewSession()
|
||||
session.IsAutoClose = true
|
||||
return session.MustCols(columns...)
|
||||
}
|
||||
|
||||
// Xorm automatically retrieve condition according struct, but
|
||||
// if struct has bool field, it will ignore them. So use UseBool
|
||||
// to tell system to do not ignore them.
|
||||
|
|
@ -387,12 +406,13 @@ func (engine *Engine) Having(conditions string) *Session {
|
|||
return session.Having(conditions)
|
||||
}
|
||||
|
||||
func (engine *Engine) autoMapType(t reflect.Type) *core.Table {
|
||||
func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
|
||||
t := v.Type()
|
||||
engine.mutex.RLock()
|
||||
table, ok := engine.Tables[t]
|
||||
engine.mutex.RUnlock()
|
||||
if !ok {
|
||||
table = engine.mapType(t)
|
||||
table = engine.mapType(v)
|
||||
engine.mutex.Lock()
|
||||
engine.Tables[t] = table
|
||||
engine.mutex.Unlock()
|
||||
|
|
@ -401,26 +421,66 @@ func (engine *Engine) autoMapType(t reflect.Type) *core.Table {
|
|||
}
|
||||
|
||||
func (engine *Engine) autoMap(bean interface{}) *core.Table {
|
||||
t := rType(bean)
|
||||
return engine.autoMapType(t)
|
||||
v := rValue(bean)
|
||||
return engine.autoMapType(v)
|
||||
}
|
||||
|
||||
func (engine *Engine) mapType(t reflect.Type) *core.Table {
|
||||
/*func (engine *Engine) mapType(t reflect.Type) *core.Table {
|
||||
return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
|
||||
table := core.NewEmptyTable()
|
||||
table.Name = tableMapper.Obj2Table(t.Name())
|
||||
*/
|
||||
func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
|
||||
if index, ok := table.Indexes[indexName]; ok {
|
||||
index.AddColumn(col.Name)
|
||||
col.Indexes[index.Name] = true
|
||||
} else {
|
||||
index := core.NewIndex(indexName, indexType)
|
||||
index.AddColumn(col.Name)
|
||||
table.AddIndex(index)
|
||||
col.Indexes[index.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) newTable() *core.Table {
|
||||
table := core.NewEmptyTable()
|
||||
table.Cacher = engine.Cacher
|
||||
return table
|
||||
}
|
||||
|
||||
func (engine *Engine) mapType(v reflect.Value) *core.Table {
|
||||
t := v.Type()
|
||||
table := engine.newTable()
|
||||
method := v.MethodByName("TableName")
|
||||
if !method.IsValid() {
|
||||
method = v.Addr().MethodByName("TableName")
|
||||
}
|
||||
if method.IsValid() {
|
||||
params := []reflect.Value{}
|
||||
results := method.Call(params)
|
||||
if len(results) == 1 {
|
||||
table.Name = results[0].Interface().(string)
|
||||
}
|
||||
}
|
||||
|
||||
if table.Name == "" {
|
||||
table.Name = engine.TableMapper.Obj2Table(t.Name())
|
||||
}
|
||||
table.Type = t
|
||||
|
||||
var idFieldColName string
|
||||
var err error
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
tag := t.Field(i).Tag
|
||||
ormTagStr := tag.Get(tagId)
|
||||
ormTagStr := tag.Get(engine.TagIdentifier)
|
||||
var col *core.Column
|
||||
fieldType := t.Field(i).Type
|
||||
fieldValue := v.Field(i)
|
||||
fieldType := fieldValue.Type()
|
||||
|
||||
if ormTagStr != "" {
|
||||
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false,
|
||||
|
|
@ -433,7 +493,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
}
|
||||
if (strings.ToUpper(tags[0]) == "EXTENDS") &&
|
||||
(fieldType.Kind() == reflect.Struct) {
|
||||
parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
|
||||
|
||||
//parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId)
|
||||
parentTable := engine.mapType(fieldValue)
|
||||
for _, col := range parentTable.Columns() {
|
||||
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName)
|
||||
table.AddColumn(col)
|
||||
|
|
@ -442,8 +504,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
continue
|
||||
}
|
||||
|
||||
var indexType int
|
||||
var indexName string
|
||||
indexNames := make(map[string]int)
|
||||
var isIndex, isUnique bool
|
||||
var preKey string
|
||||
for j, key := range tags {
|
||||
k := strings.ToUpper(key)
|
||||
switch {
|
||||
|
|
@ -456,8 +519,18 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
col.Nullable = false
|
||||
case k == "NULL":
|
||||
col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT")
|
||||
/*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"):
|
||||
col.IsAutoIncrement = true
|
||||
|
||||
autoStart := k[len("AUTOINCR")+1 : len(k)-1]
|
||||
autoStartInt, err := strconv.Atoi(autoStart)
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
col.AutoIncrStart = autoStartInt*/
|
||||
case k == "AUTOINCR":
|
||||
col.IsAutoIncrement = true
|
||||
//col.AutoIncrStart = 1
|
||||
case k == "DEFAULT":
|
||||
col.Default = tags[j+1]
|
||||
case k == "CREATED":
|
||||
|
|
@ -468,35 +541,46 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
case k == "UPDATED":
|
||||
col.IsUpdated = true
|
||||
case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"):
|
||||
indexType = core.IndexType
|
||||
indexName = k[len("INDEX")+1 : len(k)-1]
|
||||
indexName := k[len("INDEX")+1 : len(k)-1]
|
||||
indexNames[indexName] = core.IndexType
|
||||
case k == "INDEX":
|
||||
indexType = core.IndexType
|
||||
isIndex = true
|
||||
case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
|
||||
indexName = k[len("UNIQUE")+1 : len(k)-1]
|
||||
indexType = core.UniqueType
|
||||
indexName := k[len("UNIQUE")+1 : len(k)-1]
|
||||
indexNames[indexName] = core.UniqueType
|
||||
case k == "UNIQUE":
|
||||
indexType = core.UniqueType
|
||||
isUnique = true
|
||||
case k == "NOTNULL":
|
||||
col.Nullable = false
|
||||
case k == "NOT":
|
||||
default:
|
||||
if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
|
||||
if key != col.Default {
|
||||
if preKey != "DEFAULT" {
|
||||
col.Name = key[1 : len(key)-1]
|
||||
}
|
||||
} else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
|
||||
fs := strings.Split(k, "(")
|
||||
|
||||
if _, ok := core.SqlTypes[fs[0]]; !ok {
|
||||
preKey = k
|
||||
continue
|
||||
}
|
||||
col.SQLType = core.SQLType{fs[0], 0, 0}
|
||||
fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
|
||||
if len(fs2) == 2 {
|
||||
col.Length, _ = strconv.Atoi(fs2[0])
|
||||
col.Length2, _ = strconv.Atoi(fs2[1])
|
||||
col.Length, err = strconv.Atoi(fs2[0])
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
col.Length2, err = strconv.Atoi(fs2[1])
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
} else if len(fs2) == 1 {
|
||||
col.Length, _ = strconv.Atoi(fs2[0])
|
||||
col.Length, err = strconv.Atoi(fs2[0])
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, ok := core.SqlTypes[k]; ok {
|
||||
|
|
@ -505,8 +589,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
col.Name = key
|
||||
}
|
||||
}
|
||||
dialect.SqlType(col)
|
||||
engine.dialect.SqlType(col)
|
||||
}
|
||||
preKey = k
|
||||
}
|
||||
if col.SQLType.Name == "" {
|
||||
col.SQLType = core.Type2SQLType(fieldType)
|
||||
|
|
@ -519,40 +604,24 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
|
|||
}
|
||||
//fmt.Println("======", col)
|
||||
if col.Name == "" {
|
||||
col.Name = colMapper.Obj2Table(t.Field(i).Name)
|
||||
col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name)
|
||||
}
|
||||
if indexType == core.IndexType {
|
||||
if indexName == "" {
|
||||
indexName = col.Name
|
||||
}
|
||||
if index, ok := table.Indexes[indexName]; ok {
|
||||
index.AddColumn(col.Name)
|
||||
col.Indexes[index.Name] = true
|
||||
} else {
|
||||
index := core.NewIndex(indexName, core.IndexType)
|
||||
index.AddColumn(col.Name)
|
||||
table.AddIndex(index)
|
||||
col.Indexes[index.Name] = true
|
||||
}
|
||||
} else if indexType == core.UniqueType {
|
||||
if indexName == "" {
|
||||
indexName = col.Name
|
||||
}
|
||||
if index, ok := table.Indexes[indexName]; ok {
|
||||
index.AddColumn(col.Name)
|
||||
col.Indexes[index.Name] = true
|
||||
} else {
|
||||
index := core.NewIndex(indexName, core.UniqueType)
|
||||
index.AddColumn(col.Name)
|
||||
table.AddIndex(index)
|
||||
col.Indexes[index.Name] = true
|
||||
}
|
||||
|
||||
if isUnique {
|
||||
indexNames[col.Name] = core.UniqueType
|
||||
} else if isIndex {
|
||||
indexNames[col.Name] = core.IndexType
|
||||
}
|
||||
|
||||
for indexName, indexType := range indexNames {
|
||||
addIndex(indexName, table, col, indexType)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sqlType := core.Type2SQLType(fieldType)
|
||||
col = core.NewColumn(colMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType,
|
||||
sqlType.DefaultLength, sqlType.DefaultLength2, true)
|
||||
col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
|
||||
t.Field(i).Name, sqlType, sqlType.DefaultLength,
|
||||
sqlType.DefaultLength2, true)
|
||||
}
|
||||
if col.IsAutoIncrement {
|
||||
col.Nullable = false
|
||||
|
|
@ -582,19 +651,20 @@ func (engine *Engine) mapping(beans ...interface{}) (e error) {
|
|||
engine.mutex.Lock()
|
||||
defer engine.mutex.Unlock()
|
||||
for _, bean := range beans {
|
||||
t := rType(bean)
|
||||
engine.Tables[t] = engine.mapType(t)
|
||||
v := rValue(bean)
|
||||
engine.Tables[v.Type()] = engine.mapType(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If a table has any reocrd
|
||||
func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
|
||||
t := rType(bean)
|
||||
v := rValue(bean)
|
||||
t := v.Type()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return false, errors.New("bean should be a struct or struct's point")
|
||||
}
|
||||
engine.autoMapType(t)
|
||||
engine.autoMapType(v)
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
rows, err := session.Count(bean)
|
||||
|
|
@ -603,11 +673,11 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
|
|||
|
||||
// If a table is exist
|
||||
func (engine *Engine) IsTableExist(bean interface{}) (bool, error) {
|
||||
t := rType(bean)
|
||||
if t.Kind() != reflect.Struct {
|
||||
v := rValue(bean)
|
||||
if v.Type().Kind() != reflect.Struct {
|
||||
return false, errors.New("bean should be a struct or struct's point")
|
||||
}
|
||||
table := engine.autoMapType(t)
|
||||
table := engine.autoMapType(v)
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
has, err := session.isTableExist(table.Name)
|
||||
|
|
@ -646,9 +716,13 @@ func (engine *Engine) CreateUniques(bean interface{}) error {
|
|||
return session.CreateUniques(bean)
|
||||
}
|
||||
|
||||
func (engine *Engine) getCacher(t reflect.Type) core.Cacher {
|
||||
if cacher, ok := engine.tableCachers[t]; ok {
|
||||
return cacher
|
||||
func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
|
||||
return table.Cacher
|
||||
}
|
||||
|
||||
func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
|
||||
if table := engine.autoMapType(v); table != nil {
|
||||
return table.Cacher
|
||||
}
|
||||
return engine.Cacher
|
||||
}
|
||||
|
|
@ -660,7 +734,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
|
|||
return errors.New("error params")
|
||||
}
|
||||
table := engine.autoMap(bean)
|
||||
cacher := engine.getCacher(t)
|
||||
cacher := table.Cacher
|
||||
if cacher == nil {
|
||||
cacher = engine.Cacher
|
||||
}
|
||||
if cacher != nil {
|
||||
cacher.ClearIds(table.Name)
|
||||
cacher.DelBean(table.Name, id)
|
||||
|
|
@ -676,7 +753,10 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
|
|||
return errors.New("error params")
|
||||
}
|
||||
table := engine.autoMap(bean)
|
||||
cacher := engine.getCacher(t)
|
||||
cacher := table.Cacher
|
||||
if cacher == nil {
|
||||
cacher = engine.Cacher
|
||||
}
|
||||
if cacher != nil {
|
||||
cacher.ClearIds(table.Name)
|
||||
cacher.ClearBeans(table.Name)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/caches"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/go-xorm/xorm/caches"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"os"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"runtime"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"runtime"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
_ "github.com/bylevel/pq"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func indexNoCase(s, sep string) int {
|
||||
|
|
@ -38,9 +38,14 @@ func makeArray(elem string, count int) []string {
|
|||
return res
|
||||
}
|
||||
|
||||
func rValue(bean interface{}) reflect.Value {
|
||||
return reflect.Indirect(reflect.ValueOf(bean))
|
||||
}
|
||||
|
||||
func rType(bean interface{}) reflect.Type {
|
||||
sliceValue := reflect.Indirect(reflect.ValueOf(bean))
|
||||
return reflect.TypeOf(sliceValue.Interface())
|
||||
//return reflect.TypeOf(sliceValue.Interface())
|
||||
return sliceValue.Type()
|
||||
}
|
||||
|
||||
func structName(v reflect.Type) string {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// logger interface, log/syslog conform with this interface
|
||||
type ILogger interface {
|
||||
Debug(m string) (err error)
|
||||
Err(m string) (err error)
|
||||
Info(m string) (err error)
|
||||
Warning(m string) (err error)
|
||||
}
|
||||
|
||||
type SimpleLogger struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewSimpleLogger(out io.Writer) *SimpleLogger {
|
||||
return &SimpleLogger{
|
||||
logger: log.New(out, "[xorm] ", log.Ldate|log.Lmicroseconds)}
|
||||
}
|
||||
|
||||
func NewSimpleLogger2(out io.Writer, prefix string, flag int) *SimpleLogger {
|
||||
return &SimpleLogger{
|
||||
logger: log.New(out, prefix, flag)}
|
||||
}
|
||||
|
||||
func (s *SimpleLogger) Debug(m string) (err error) {
|
||||
s.logger.Println("[debug]", m)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SimpleLogger) Err(m string) (err error) {
|
||||
s.logger.Println("[error]", m)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SimpleLogger) Info(m string) (err error) {
|
||||
s.logger.Println("[info]", m)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SimpleLogger) Warning(m string) (err error) {
|
||||
s.logger.Println("[warning]", m)
|
||||
return
|
||||
}
|
||||
|
|
@ -1,67 +1,63 @@
|
|||
package dialects
|
||||
package xorm
|
||||
|
||||
import (
|
||||
//"crypto/tls"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
//"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
//"time"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDialect("mssql", &mssql{})
|
||||
}
|
||||
// func init() {
|
||||
// RegisterDialect("mssql", &mssql{})
|
||||
// }
|
||||
|
||||
type mssql struct {
|
||||
Base
|
||||
core.Base
|
||||
}
|
||||
|
||||
func (db *mssql) Init(uri *Uri, drivername, dataSourceName string) error {
|
||||
func (db *mssql) Init(uri *core.Uri, drivername, dataSourceName string) error {
|
||||
return db.Base.Init(db, uri, drivername, dataSourceName)
|
||||
}
|
||||
|
||||
func (db *mssql) SqlType(c *Column) string {
|
||||
func (db *mssql) SqlType(c *core.Column) string {
|
||||
var res string
|
||||
switch t := c.SQLType.Name; t {
|
||||
case Bool:
|
||||
res = TinyInt
|
||||
case Serial:
|
||||
case core.Bool:
|
||||
res = core.TinyInt
|
||||
case core.Serial:
|
||||
c.IsAutoIncrement = true
|
||||
c.IsPrimaryKey = true
|
||||
c.Nullable = false
|
||||
res = Int
|
||||
case BigSerial:
|
||||
res = core.Int
|
||||
case core.BigSerial:
|
||||
c.IsAutoIncrement = true
|
||||
c.IsPrimaryKey = true
|
||||
c.Nullable = false
|
||||
res = BigInt
|
||||
case Bytea, Blob, Binary, TinyBlob, MediumBlob, LongBlob:
|
||||
res = VarBinary
|
||||
res = core.BigInt
|
||||
case core.Bytea, core.Blob, core.Binary, core.TinyBlob, core.MediumBlob, core.LongBlob:
|
||||
res = core.VarBinary
|
||||
if c.Length == 0 {
|
||||
c.Length = 50
|
||||
}
|
||||
case TimeStamp:
|
||||
res = DateTime
|
||||
case TimeStampz:
|
||||
case core.TimeStamp:
|
||||
res = core.DateTime
|
||||
case core.TimeStampz:
|
||||
res = "DATETIMEOFFSET"
|
||||
c.Length = 7
|
||||
case MediumInt:
|
||||
res = Int
|
||||
case MediumText, TinyText, LongText:
|
||||
res = Text
|
||||
case Double:
|
||||
res = Real
|
||||
case core.MediumInt:
|
||||
res = core.Int
|
||||
case core.MediumText, core.TinyText, core.LongText:
|
||||
res = core.Text
|
||||
case core.Double:
|
||||
res = core.Real
|
||||
default:
|
||||
res = t
|
||||
}
|
||||
|
||||
if res == Int {
|
||||
return Int
|
||||
if res == core.Int {
|
||||
return core.Int
|
||||
}
|
||||
|
||||
var hasLen1 bool = (c.Length > 0)
|
||||
|
|
@ -90,6 +86,12 @@ func (db *mssql) AutoIncrStr() string {
|
|||
return "IDENTITY"
|
||||
}
|
||||
|
||||
func (db *mssql) DropTableSql(tableName string) string {
|
||||
return fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+
|
||||
"object_id(N'%s') and OBJECTPROPERTY(id, N'IsUserTable') = 1) "+
|
||||
"DROP TABLE \"%s\"", tableName, tableName)
|
||||
}
|
||||
|
||||
func (db *mssql) SupportCharset() bool {
|
||||
return false
|
||||
}
|
||||
|
|
@ -116,12 +118,12 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
|
|||
return sql, args
|
||||
}
|
||||
|
||||
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*Column, error) {
|
||||
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
|
||||
args := []interface{}{}
|
||||
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale
|
||||
from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id
|
||||
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale
|
||||
from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id
|
||||
where a.object_id=object_id('` + tableName + `')`
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -131,7 +133,7 @@ where a.object_id=object_id('` + tableName + `')`
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cols := make(map[string]*Column)
|
||||
cols := make(map[string]*core.Column)
|
||||
colSeq := make([]string, 0)
|
||||
for rows.Next() {
|
||||
var name, ctype, precision, scale string
|
||||
|
|
@ -141,7 +143,7 @@ where a.object_id=object_id('` + tableName + `')`
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
col := new(Column)
|
||||
col := new(core.Column)
|
||||
col.Indexes = make(map[string]bool)
|
||||
col.Length = maxLen
|
||||
col.Name = strings.Trim(name, "` ")
|
||||
|
|
@ -149,14 +151,14 @@ where a.object_id=object_id('` + tableName + `')`
|
|||
ct := strings.ToUpper(ctype)
|
||||
switch ct {
|
||||
case "DATETIMEOFFSET":
|
||||
col.SQLType = SQLType{TimeStampz, 0, 0}
|
||||
col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
|
||||
case "NVARCHAR":
|
||||
col.SQLType = SQLType{Varchar, 0, 0}
|
||||
col.SQLType = core.SQLType{core.Varchar, 0, 0}
|
||||
case "IMAGE":
|
||||
col.SQLType = SQLType{VarBinary, 0, 0}
|
||||
col.SQLType = core.SQLType{core.VarBinary, 0, 0}
|
||||
default:
|
||||
if _, ok := SqlTypes[ct]; ok {
|
||||
col.SQLType = SQLType{ct, 0, 0}
|
||||
if _, ok := core.SqlTypes[ct]; ok {
|
||||
col.SQLType = core.SQLType{ct, 0, 0}
|
||||
} else {
|
||||
return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v",
|
||||
ct, tableName, col.Name))
|
||||
|
|
@ -166,6 +168,10 @@ where a.object_id=object_id('` + tableName + `')`
|
|||
if col.SQLType.IsText() {
|
||||
if col.Default != "" {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
} else {
|
||||
if col.DefaultIsEmpty {
|
||||
col.Default = "''"
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
|
|
@ -174,10 +180,10 @@ where a.object_id=object_id('` + tableName + `')`
|
|||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
func (db *mssql) GetTables() ([]*Table, error) {
|
||||
func (db *mssql) GetTables() ([]*core.Table, error) {
|
||||
args := []interface{}{}
|
||||
s := `select name from sysobjects where xtype ='U'`
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -187,9 +193,9 @@ func (db *mssql) GetTables() ([]*Table, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]*Table, 0)
|
||||
tables := make([]*core.Table, 0)
|
||||
for rows.Next() {
|
||||
table := NewEmptyTable()
|
||||
table := core.NewEmptyTable()
|
||||
var name string
|
||||
err = rows.Scan(&name)
|
||||
if err != nil {
|
||||
|
|
@ -201,23 +207,23 @@ func (db *mssql) GetTables() ([]*Table, error) {
|
|||
return tables, nil
|
||||
}
|
||||
|
||||
func (db *mssql) GetIndexes(tableName string) (map[string]*Index, error) {
|
||||
func (db *mssql) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := `SELECT
|
||||
IXS.NAME AS [INDEX_NAME],
|
||||
C.NAME AS [COLUMN_NAME],
|
||||
IXS.is_unique AS [IS_UNIQUE],
|
||||
CASE IXCS.IS_INCLUDED_COLUMN
|
||||
WHEN 0 THEN 'NONE'
|
||||
ELSE 'INCLUDED' END AS [IS_INCLUDED_COLUMN]
|
||||
FROM SYS.INDEXES IXS
|
||||
INNER JOIN SYS.INDEX_COLUMNS IXCS
|
||||
ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID
|
||||
INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
|
||||
AND IXCS.COLUMN_ID=C.COLUMN_ID
|
||||
s := `SELECT
|
||||
IXS.NAME AS [INDEX_NAME],
|
||||
C.NAME AS [COLUMN_NAME],
|
||||
IXS.is_unique AS [IS_UNIQUE],
|
||||
CASE IXCS.IS_INCLUDED_COLUMN
|
||||
WHEN 0 THEN 'NONE'
|
||||
ELSE 'INCLUDED' END AS [IS_INCLUDED_COLUMN]
|
||||
FROM SYS.INDEXES IXS
|
||||
INNER JOIN SYS.INDEX_COLUMNS IXCS
|
||||
ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID
|
||||
INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
|
||||
AND IXCS.COLUMN_ID=C.COLUMN_ID
|
||||
WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
||||
`
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -227,7 +233,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
return nil, err
|
||||
}
|
||||
|
||||
indexes := make(map[string]*Index, 0)
|
||||
indexes := make(map[string]*core.Index, 0)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, colName, isUnique string
|
||||
|
|
@ -243,9 +249,9 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
}
|
||||
|
||||
if i {
|
||||
indexType = UniqueType
|
||||
indexType = core.UniqueType
|
||||
} else {
|
||||
indexType = IndexType
|
||||
indexType = core.IndexType
|
||||
}
|
||||
|
||||
colName = strings.Trim(colName, "` ")
|
||||
|
|
@ -254,10 +260,10 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
indexName = indexName[5+len(tableName) : len(indexName)]
|
||||
}
|
||||
|
||||
var index *Index
|
||||
var index *core.Index
|
||||
var ok bool
|
||||
if index, ok = indexes[indexName]; !ok {
|
||||
index = new(Index)
|
||||
index = new(core.Index)
|
||||
index.Type = indexType
|
||||
index.Name = indexName
|
||||
indexes[indexName] = index
|
||||
|
|
@ -267,7 +273,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
return indexes, nil
|
||||
}
|
||||
|
||||
func (db *mssql) CreateTablSql(table *Table, tableName, storeEngine, charset string) string {
|
||||
func (db *mssql) CreateTablSql(table *core.Table, tableName, storeEngine, charset string) string {
|
||||
var sql string
|
||||
if tableName == "" {
|
||||
tableName = table.Name
|
||||
|
|
@ -301,6 +307,6 @@ func (db *mssql) CreateTablSql(table *Table, tableName, storeEngine, charset str
|
|||
return sql
|
||||
}
|
||||
|
||||
func (db *mssql) Filters() []Filter {
|
||||
return []Filter{&IdFilter{}, &QuoteFilter{}}
|
||||
func (db *mssql) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dialects
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
|
@ -8,15 +8,15 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDialect("mysql", &mysql{})
|
||||
}
|
||||
// func init() {
|
||||
// RegisterDialect("mysql", &mysql{})
|
||||
// }
|
||||
|
||||
type mysql struct {
|
||||
Base
|
||||
core.Base
|
||||
net string
|
||||
addr string
|
||||
params map[string]string
|
||||
|
|
@ -28,29 +28,30 @@ type mysql struct {
|
|||
clientFoundRows bool
|
||||
}
|
||||
|
||||
func (db *mysql) Init(uri *Uri, drivername, dataSourceName string) error {
|
||||
func (db *mysql) Init(uri *core.Uri, drivername, dataSourceName string) error {
|
||||
return db.Base.Init(db, uri, drivername, dataSourceName)
|
||||
}
|
||||
|
||||
func (db *mysql) SqlType(c *Column) string {
|
||||
func (db *mysql) SqlType(c *core.Column) string {
|
||||
var res string
|
||||
switch t := c.SQLType.Name; t {
|
||||
case Bool:
|
||||
res = TinyInt
|
||||
case Serial:
|
||||
case core.Bool:
|
||||
res = core.TinyInt
|
||||
c.Length = 1
|
||||
case core.Serial:
|
||||
c.IsAutoIncrement = true
|
||||
c.IsPrimaryKey = true
|
||||
c.Nullable = false
|
||||
res = Int
|
||||
case BigSerial:
|
||||
res = core.Int
|
||||
case core.BigSerial:
|
||||
c.IsAutoIncrement = true
|
||||
c.IsPrimaryKey = true
|
||||
c.Nullable = false
|
||||
res = BigInt
|
||||
case Bytea:
|
||||
res = Blob
|
||||
case TimeStampz:
|
||||
res = Char
|
||||
res = core.BigInt
|
||||
case core.Bytea:
|
||||
res = core.Blob
|
||||
case core.TimeStampz:
|
||||
res = core.Char
|
||||
c.Length = 64
|
||||
default:
|
||||
res = t
|
||||
|
|
@ -109,23 +110,26 @@ func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
|
|||
return sql, args
|
||||
}
|
||||
|
||||
func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, error) {
|
||||
func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
|
||||
args := []interface{}{db.DbName, tableName}
|
||||
s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
|
||||
" `COLUMN_KEY`, `EXTRA` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer cnn.Close()
|
||||
|
||||
rows, err := cnn.Query(s, args...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cols := make(map[string]*Column)
|
||||
defer rows.Close()
|
||||
|
||||
cols := make(map[string]*core.Column)
|
||||
colSeq := make([]string, 0)
|
||||
for rows.Next() {
|
||||
col := new(Column)
|
||||
col := new(core.Column)
|
||||
col.Indexes = make(map[string]bool)
|
||||
|
||||
var columnName, isNullable, colType, colKey, extra string
|
||||
|
|
@ -163,8 +167,8 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, err
|
|||
colType = strings.ToUpper(colName)
|
||||
col.Length = len1
|
||||
col.Length2 = len2
|
||||
if _, ok := SqlTypes[colType]; ok {
|
||||
col.SQLType = SQLType{colType, len1, len2}
|
||||
if _, ok := core.SqlTypes[colType]; ok {
|
||||
col.SQLType = core.SQLType{colType, len1, len2}
|
||||
} else {
|
||||
return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType))
|
||||
}
|
||||
|
|
@ -191,10 +195,10 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, err
|
|||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
func (db *mysql) GetTables() ([]*Table, error) {
|
||||
func (db *mysql) GetTables() ([]*core.Table, error) {
|
||||
args := []interface{}{db.DbName}
|
||||
s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=?"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -204,9 +208,9 @@ func (db *mysql) GetTables() ([]*Table, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]*Table, 0)
|
||||
tables := make([]*core.Table, 0)
|
||||
for rows.Next() {
|
||||
table := NewEmptyTable()
|
||||
table := core.NewEmptyTable()
|
||||
var name, engine, tableRows string
|
||||
var autoIncr *string
|
||||
err = rows.Scan(&name, &engine, &tableRows, &autoIncr)
|
||||
|
|
@ -220,10 +224,10 @@ func (db *mysql) GetTables() ([]*Table, error) {
|
|||
return tables, nil
|
||||
}
|
||||
|
||||
func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
|
||||
func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
||||
args := []interface{}{db.DbName, tableName}
|
||||
s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -233,7 +237,7 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
indexes := make(map[string]*Index, 0)
|
||||
indexes := make(map[string]*core.Index, 0)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, colName, nonUnique string
|
||||
|
|
@ -247,9 +251,9 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
}
|
||||
|
||||
if "YES" == nonUnique || nonUnique == "1" {
|
||||
indexType = IndexType
|
||||
indexType = core.IndexType
|
||||
} else {
|
||||
indexType = UniqueType
|
||||
indexType = core.UniqueType
|
||||
}
|
||||
|
||||
colName = strings.Trim(colName, "` ")
|
||||
|
|
@ -258,10 +262,10 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
indexName = indexName[5+len(tableName) : len(indexName)]
|
||||
}
|
||||
|
||||
var index *Index
|
||||
var index *core.Index
|
||||
var ok bool
|
||||
if index, ok = indexes[indexName]; !ok {
|
||||
index = new(Index)
|
||||
index = new(core.Index)
|
||||
index.Type = indexType
|
||||
index.Name = indexName
|
||||
indexes[indexName] = index
|
||||
|
|
@ -271,6 +275,6 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
return indexes, nil
|
||||
}
|
||||
|
||||
func (db *mysql) Filters() []Filter {
|
||||
return []Filter{&IdFilter{}}
|
||||
func (db *mysql) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dialects
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -6,37 +6,37 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDialect("oracle", &oracle{})
|
||||
}
|
||||
// func init() {
|
||||
// RegisterDialect("oracle", &oracle{})
|
||||
// }
|
||||
|
||||
type oracle struct {
|
||||
Base
|
||||
core.Base
|
||||
}
|
||||
|
||||
func (db *oracle) Init(uri *Uri, drivername, dataSourceName string) error {
|
||||
func (db *oracle) Init(uri *core.Uri, drivername, dataSourceName string) error {
|
||||
return db.Base.Init(db, uri, drivername, dataSourceName)
|
||||
}
|
||||
|
||||
func (db *oracle) SqlType(c *Column) string {
|
||||
func (db *oracle) SqlType(c *core.Column) string {
|
||||
var res string
|
||||
switch t := c.SQLType.Name; t {
|
||||
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial:
|
||||
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool, core.Serial, core.BigSerial:
|
||||
return "NUMBER"
|
||||
case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea:
|
||||
return Blob
|
||||
case Time, DateTime, TimeStamp:
|
||||
res = TimeStamp
|
||||
case TimeStampz:
|
||||
case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea:
|
||||
return core.Blob
|
||||
case core.Time, core.DateTime, core.TimeStamp:
|
||||
res = core.TimeStamp
|
||||
case core.TimeStampz:
|
||||
res = "TIMESTAMP WITH TIME ZONE"
|
||||
case Float, Double, Numeric, Decimal:
|
||||
case core.Float, core.Double, core.Numeric, core.Decimal:
|
||||
res = "NUMBER"
|
||||
case Text, MediumText, LongText:
|
||||
case core.Text, core.MediumText, core.LongText:
|
||||
res = "CLOB"
|
||||
case Char, Varchar, TinyText:
|
||||
case core.Char, core.Varchar, core.TinyText:
|
||||
return "VARCHAR2"
|
||||
default:
|
||||
res = t
|
||||
|
|
@ -93,12 +93,12 @@ func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface
|
|||
" AND column_name = ?", args
|
||||
}
|
||||
|
||||
func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, error) {
|
||||
func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
|
||||
args := []interface{}{strings.ToUpper(tableName)}
|
||||
s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," +
|
||||
"nullable FROM USER_TAB_COLUMNS WHERE table_name = :1"
|
||||
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -109,10 +109,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
cols := make(map[string]*Column)
|
||||
cols := make(map[string]*core.Column)
|
||||
colSeq := make([]string, 0)
|
||||
for rows.Next() {
|
||||
col := new(Column)
|
||||
col := new(core.Column)
|
||||
col.Indexes = make(map[string]bool)
|
||||
|
||||
var colName, colDefault, nullable, dataType, dataPrecision, dataScale string
|
||||
|
|
@ -135,13 +135,13 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
|
|||
|
||||
switch dataType {
|
||||
case "VARCHAR2":
|
||||
col.SQLType = SQLType{Varchar, 0, 0}
|
||||
col.SQLType = core.SQLType{core.Varchar, 0, 0}
|
||||
case "TIMESTAMP WITH TIME ZONE":
|
||||
col.SQLType = SQLType{TimeStampz, 0, 0}
|
||||
col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
|
||||
default:
|
||||
col.SQLType = SQLType{strings.ToUpper(dataType), 0, 0}
|
||||
col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0}
|
||||
}
|
||||
if _, ok := SqlTypes[col.SQLType.Name]; !ok {
|
||||
if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
|
||||
return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType))
|
||||
}
|
||||
|
||||
|
|
@ -150,6 +150,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
|
|||
if col.SQLType.IsText() {
|
||||
if col.Default != "" {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
} else {
|
||||
if col.DefaultIsEmpty {
|
||||
col.Default = "''"
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
|
|
@ -159,10 +163,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
|
|||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
func (db *oracle) GetTables() ([]*Table, error) {
|
||||
func (db *oracle) GetTables() ([]*core.Table, error) {
|
||||
args := []interface{}{}
|
||||
s := "SELECT table_name FROM user_tables"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -172,9 +176,9 @@ func (db *oracle) GetTables() ([]*Table, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]*Table, 0)
|
||||
tables := make([]*core.Table, 0)
|
||||
for rows.Next() {
|
||||
table := NewEmptyTable()
|
||||
table := core.NewEmptyTable()
|
||||
err = rows.Scan(&table.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -185,12 +189,12 @@ func (db *oracle) GetTables() ([]*Table, error) {
|
|||
return tables, nil
|
||||
}
|
||||
|
||||
func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
|
||||
func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " +
|
||||
"WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1"
|
||||
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -201,7 +205,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*Index, 0)
|
||||
indexes := make(map[string]*core.Index, 0)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, colName, uniqueness string
|
||||
|
|
@ -214,15 +218,15 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
indexName = strings.Trim(indexName, `" `)
|
||||
|
||||
if uniqueness == "UNIQUE" {
|
||||
indexType = UniqueType
|
||||
indexType = core.UniqueType
|
||||
} else {
|
||||
indexType = IndexType
|
||||
indexType = core.IndexType
|
||||
}
|
||||
|
||||
var index *Index
|
||||
var index *core.Index
|
||||
var ok bool
|
||||
if index, ok = indexes[indexName]; !ok {
|
||||
index = new(Index)
|
||||
index = new(core.Index)
|
||||
index.Type = indexType
|
||||
index.Name = indexName
|
||||
indexes[indexName] = index
|
||||
|
|
@ -236,7 +240,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
type OracleSeqFilter struct {
|
||||
}
|
||||
|
||||
func (s *OracleSeqFilter) Do(sql string, dialect Dialect, table *Table) string {
|
||||
func (s *OracleSeqFilter) Do(sql string, dialect core.Dialect, table *core.Table) string {
|
||||
counts := strings.Count(sql, "?")
|
||||
for i := 1; i <= counts; i++ {
|
||||
newstr := ":" + fmt.Sprintf("%v", i)
|
||||
|
|
@ -245,6 +249,6 @@ func (s *OracleSeqFilter) Do(sql string, dialect Dialect, table *Table) string {
|
|||
return sql
|
||||
}
|
||||
|
||||
func (db *oracle) Filters() []Filter {
|
||||
return []Filter{&QuoteFilter{}, &OracleSeqFilter{}, &IdFilter{}}
|
||||
func (db *oracle) Filters() []core.Filter {
|
||||
return []core.Filter{&core.QuoteFilter{}, &OracleSeqFilter{}, &core.IdFilter{}}
|
||||
}
|
||||
7
pool.go
7
pool.go
|
|
@ -1,13 +1,12 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"github.com/lunny/xorm/core"
|
||||
//"fmt"
|
||||
"sync"
|
||||
//"sync/atomic"
|
||||
"container/list"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// Interface IConnecPool is a connection pool interface, all implements should implement
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package dialects
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -6,53 +6,53 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDialect("postgres", &postgres{})
|
||||
}
|
||||
// func init() {
|
||||
// RegisterDialect("postgres", &postgres{})
|
||||
// }
|
||||
|
||||
type postgres struct {
|
||||
Base
|
||||
core.Base
|
||||
}
|
||||
|
||||
func (db *postgres) Init(uri *Uri, drivername, dataSourceName string) error {
|
||||
func (db *postgres) Init(uri *core.Uri, drivername, dataSourceName string) error {
|
||||
return db.Base.Init(db, uri, drivername, dataSourceName)
|
||||
}
|
||||
|
||||
func (db *postgres) SqlType(c *Column) string {
|
||||
func (db *postgres) SqlType(c *core.Column) string {
|
||||
var res string
|
||||
switch t := c.SQLType.Name; t {
|
||||
case TinyInt:
|
||||
res = SmallInt
|
||||
case core.TinyInt:
|
||||
res = core.SmallInt
|
||||
return res
|
||||
case MediumInt, Int, Integer:
|
||||
case core.MediumInt, core.Int, core.Integer:
|
||||
if c.IsAutoIncrement {
|
||||
return Serial
|
||||
return core.Serial
|
||||
}
|
||||
return Integer
|
||||
case Serial, BigSerial:
|
||||
return core.Integer
|
||||
case core.Serial, core.BigSerial:
|
||||
c.IsAutoIncrement = true
|
||||
c.Nullable = false
|
||||
res = t
|
||||
case Binary, VarBinary:
|
||||
return Bytea
|
||||
case DateTime:
|
||||
res = TimeStamp
|
||||
case TimeStampz:
|
||||
case core.Binary, core.VarBinary:
|
||||
return core.Bytea
|
||||
case core.DateTime:
|
||||
res = core.TimeStamp
|
||||
case core.TimeStampz:
|
||||
return "timestamp with time zone"
|
||||
case Float:
|
||||
res = Real
|
||||
case TinyText, MediumText, LongText:
|
||||
res = Text
|
||||
case Blob, TinyBlob, MediumBlob, LongBlob:
|
||||
return Bytea
|
||||
case Double:
|
||||
case core.Float:
|
||||
res = core.Real
|
||||
case core.TinyText, core.MediumText, core.LongText:
|
||||
res = core.Text
|
||||
case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob:
|
||||
return core.Bytea
|
||||
case core.Double:
|
||||
return "DOUBLE PRECISION"
|
||||
default:
|
||||
if c.IsAutoIncrement {
|
||||
return Serial
|
||||
return core.Serial
|
||||
}
|
||||
res = t
|
||||
}
|
||||
|
|
@ -108,11 +108,11 @@ func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interfa
|
|||
" AND column_name = ?", args
|
||||
}
|
||||
|
||||
func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, error) {
|
||||
func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := "SELECT column_name, column_default, is_nullable, data_type, character_maximum_length" +
|
||||
", numeric_precision, numeric_precision_radix FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -121,12 +121,13 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cols := make(map[string]*Column)
|
||||
cols := make(map[string]*core.Column)
|
||||
colSeq := make([]string, 0)
|
||||
|
||||
for rows.Next() {
|
||||
col := new(Column)
|
||||
col := new(core.Column)
|
||||
col.Indexes = make(map[string]bool)
|
||||
|
||||
var colName, isNullable, dataType string
|
||||
var maxLenStr, colDefault, numPrecision, numRadix *string
|
||||
err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix)
|
||||
|
|
@ -160,21 +161,21 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
|
|||
|
||||
switch dataType {
|
||||
case "character varying", "character":
|
||||
col.SQLType = SQLType{Varchar, 0, 0}
|
||||
col.SQLType = core.SQLType{core.Varchar, 0, 0}
|
||||
case "timestamp without time zone":
|
||||
col.SQLType = SQLType{DateTime, 0, 0}
|
||||
col.SQLType = core.SQLType{core.DateTime, 0, 0}
|
||||
case "timestamp with time zone":
|
||||
col.SQLType = SQLType{TimeStampz, 0, 0}
|
||||
col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
|
||||
case "double precision":
|
||||
col.SQLType = SQLType{Double, 0, 0}
|
||||
col.SQLType = core.SQLType{core.Double, 0, 0}
|
||||
case "boolean":
|
||||
col.SQLType = SQLType{Bool, 0, 0}
|
||||
col.SQLType = core.SQLType{core.Bool, 0, 0}
|
||||
case "time without time zone":
|
||||
col.SQLType = SQLType{Time, 0, 0}
|
||||
col.SQLType = core.SQLType{core.Time, 0, 0}
|
||||
default:
|
||||
col.SQLType = SQLType{strings.ToUpper(dataType), 0, 0}
|
||||
col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0}
|
||||
}
|
||||
if _, ok := SqlTypes[col.SQLType.Name]; !ok {
|
||||
if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
|
||||
return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType))
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +184,10 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
|
|||
if col.SQLType.IsText() {
|
||||
if col.Default != "" {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
} else {
|
||||
if col.DefaultIsEmpty {
|
||||
col.Default = "''"
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
|
|
@ -192,10 +197,10 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
|
|||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
func (db *postgres) GetTables() ([]*Table, error) {
|
||||
func (db *postgres) GetTables() ([]*core.Table, error) {
|
||||
args := []interface{}{}
|
||||
s := "SELECT tablename FROM pg_tables where schemaname = 'public'"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -205,9 +210,9 @@ func (db *postgres) GetTables() ([]*Table, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]*Table, 0)
|
||||
tables := make([]*core.Table, 0)
|
||||
for rows.Next() {
|
||||
table := NewEmptyTable()
|
||||
table := core.NewEmptyTable()
|
||||
var name string
|
||||
err = rows.Scan(&name)
|
||||
if err != nil {
|
||||
|
|
@ -219,11 +224,11 @@ func (db *postgres) GetTables() ([]*Table, error) {
|
|||
return tables, nil
|
||||
}
|
||||
|
||||
func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
|
||||
func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := "SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' and tablename = $1"
|
||||
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -233,7 +238,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
indexes := make(map[string]*Index, 0)
|
||||
indexes := make(map[string]*core.Index, 0)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, indexdef string
|
||||
|
|
@ -245,9 +250,9 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
indexName = strings.Trim(indexName, `" `)
|
||||
|
||||
if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") {
|
||||
indexType = UniqueType
|
||||
indexType = core.UniqueType
|
||||
} else {
|
||||
indexType = IndexType
|
||||
indexType = core.IndexType
|
||||
}
|
||||
cs := strings.Split(indexdef, "(")
|
||||
colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
|
||||
|
|
@ -262,7 +267,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
}
|
||||
}
|
||||
|
||||
index := &Index{Name: indexName, Type: indexType, Cols: make([]string, 0)}
|
||||
index := &core.Index{Name: indexName, Type: indexType, Cols: make([]string, 0)}
|
||||
for _, colName := range colNames {
|
||||
index.Cols = append(index.Cols, strings.Trim(colName, `" `))
|
||||
}
|
||||
|
|
@ -271,23 +276,6 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
return indexes, nil
|
||||
}
|
||||
|
||||
// PgSeqFilter filter SQL replace ?, ? ... to $1, $2 ...
|
||||
type PgSeqFilter struct {
|
||||
}
|
||||
|
||||
func (s *PgSeqFilter) 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("$%v", i+1)
|
||||
}
|
||||
}
|
||||
res += segs[size-1]
|
||||
return res
|
||||
}
|
||||
|
||||
func (db *postgres) Filters() []Filter {
|
||||
return []Filter{&IdFilter{}, &QuoteFilter{}, &PgSeqFilter{}}
|
||||
func (db *postgres) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{"$", 1}}
|
||||
}
|
||||
15
rows.go
15
rows.go
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type Rows struct {
|
||||
|
|
@ -32,24 +32,23 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
|
|||
|
||||
defer rows.session.Statement.Init()
|
||||
|
||||
var sql string
|
||||
var sqlStr string
|
||||
var args []interface{}
|
||||
rows.session.Statement.RefTable = rows.session.Engine.autoMap(bean)
|
||||
if rows.session.Statement.RawSQL == "" {
|
||||
sql, args = rows.session.Statement.genGetSql(bean)
|
||||
sqlStr, args = rows.session.Statement.genGetSql(bean)
|
||||
} else {
|
||||
sql = rows.session.Statement.RawSQL
|
||||
sqlStr = rows.session.Statement.RawSQL
|
||||
args = rows.session.Statement.RawParams
|
||||
}
|
||||
|
||||
for _, filter := range rows.session.Engine.Filters {
|
||||
sql = filter.Do(sql, session.Engine.dialect, rows.session.Statement.RefTable)
|
||||
sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable)
|
||||
}
|
||||
|
||||
rows.session.Engine.LogSQL(sql)
|
||||
rows.session.Engine.LogSQL(args)
|
||||
rows.session.Engine.logSQL(sqlStr, args)
|
||||
|
||||
rows.stmt, err = rows.session.Db.Prepare(sql)
|
||||
rows.stmt, err = rows.session.Db.Prepare(sqlStr)
|
||||
if err != nil {
|
||||
rows.lastError = err
|
||||
defer rows.Close()
|
||||
|
|
|
|||
104
session.go
104
session.go
|
|
@ -11,7 +11,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// Struct Session keep a pointer to sql.DB and provides all execution of all
|
||||
|
|
@ -134,6 +134,16 @@ func (session *Session) Cols(columns ...string) *Session {
|
|||
return session
|
||||
}
|
||||
|
||||
func (session *Session) AllCols() *Session {
|
||||
session.Statement.AllCols()
|
||||
return session
|
||||
}
|
||||
|
||||
func (session *Session) MustCols(columns ...string) *Session {
|
||||
session.Statement.MustCols(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
func (session *Session) NoCascade() *Session {
|
||||
session.Statement.UseCascade = false
|
||||
return session
|
||||
|
|
@ -282,7 +292,7 @@ func (session *Session) Begin() error {
|
|||
session.IsCommitedOrRollbacked = false
|
||||
session.Tx = tx
|
||||
|
||||
session.Engine.LogSQL("BEGIN TRANSACTION")
|
||||
session.Engine.logSQL("BEGIN TRANSACTION")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -290,7 +300,7 @@ func (session *Session) Begin() error {
|
|||
// When using transaction, you can rollback if any error
|
||||
func (session *Session) Rollback() error {
|
||||
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
|
||||
session.Engine.LogSQL("ROLL BACK")
|
||||
session.Engine.logSQL(session.Engine.dialect.RollBackStr())
|
||||
session.IsCommitedOrRollbacked = true
|
||||
return session.Tx.Rollback()
|
||||
}
|
||||
|
|
@ -300,7 +310,7 @@ func (session *Session) Rollback() error {
|
|||
// When using transaction, Commit will commit all operations.
|
||||
func (session *Session) Commit() error {
|
||||
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
|
||||
session.Engine.LogSQL("COMMIT")
|
||||
session.Engine.logSQL("COMMIT")
|
||||
session.IsCommitedOrRollbacked = true
|
||||
var err error
|
||||
if err = session.Tx.Commit(); err == nil {
|
||||
|
|
@ -357,13 +367,14 @@ func cleanupProcessorsClosures(slices *[]func(interface{})) {
|
|||
}
|
||||
|
||||
func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error {
|
||||
dataStruct := reflect.Indirect(reflect.ValueOf(obj))
|
||||
dataStruct := rValue(obj)
|
||||
if dataStruct.Kind() != reflect.Struct {
|
||||
return errors.New("Expected a pointer to a struct")
|
||||
}
|
||||
|
||||
table := session.Engine.autoMapType(rType(obj))
|
||||
var col *core.Column
|
||||
table := session.Engine.autoMapType(dataStruct)
|
||||
|
||||
for key, data := range objMap {
|
||||
if col = table.GetColumn(key); col == nil {
|
||||
session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.Columns()))
|
||||
|
|
@ -419,8 +430,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er
|
|||
sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
|
||||
}
|
||||
|
||||
session.Engine.LogSQL(sqlStr)
|
||||
session.Engine.LogSQL(args)
|
||||
session.Engine.logSQL(sqlStr, args)
|
||||
|
||||
if session.IsAutoCommit {
|
||||
return session.innerExec(sqlStr, args...)
|
||||
|
|
@ -610,7 +620,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
|
|||
return false, ErrCacheFailed
|
||||
}
|
||||
|
||||
cacher := session.Engine.getCacher(session.Statement.RefTable.Type)
|
||||
cacher := session.Engine.getCacher2(session.Statement.RefTable)
|
||||
tableName := session.Statement.TableName()
|
||||
session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args)
|
||||
ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
|
||||
|
|
@ -699,7 +709,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
}
|
||||
|
||||
table := session.Statement.RefTable
|
||||
cacher := session.Engine.getCacher(t)
|
||||
cacher := session.Engine.getCacher2(table)
|
||||
ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args)
|
||||
if err != nil {
|
||||
//session.Engine.LogError(err)
|
||||
|
|
@ -928,6 +938,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
session.Statement.Limit(1)
|
||||
var sqlStr string
|
||||
var args []interface{}
|
||||
|
||||
session.Statement.RefTable = session.Engine.autoMap(bean)
|
||||
|
||||
if session.Statement.RawSQL == "" {
|
||||
|
|
@ -937,7 +948,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
args = session.Statement.RawParams
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(session.Statement.RefTable.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
|
||||
has, err := session.cacheGet(bean, sqlStr, args...)
|
||||
if err != ErrCacheFailed {
|
||||
return has, err
|
||||
|
|
@ -1052,12 +1063,14 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
if session.Statement.RefTable == nil {
|
||||
if sliceElementType.Kind() == reflect.Ptr {
|
||||
if sliceElementType.Elem().Kind() == reflect.Struct {
|
||||
table = session.Engine.autoMapType(sliceElementType.Elem())
|
||||
pv := reflect.New(sliceElementType.Elem())
|
||||
table = session.Engine.autoMapType(pv.Elem())
|
||||
} else {
|
||||
return errors.New("slice type")
|
||||
}
|
||||
} else if sliceElementType.Kind() == reflect.Struct {
|
||||
table = session.Engine.autoMapType(sliceElementType)
|
||||
pv := reflect.New(sliceElementType)
|
||||
table = session.Engine.autoMapType(pv.Elem())
|
||||
} else {
|
||||
return errors.New("slice type")
|
||||
}
|
||||
|
|
@ -1068,7 +1081,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
|
||||
if len(condiBean) > 0 {
|
||||
colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true,
|
||||
false, true, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, true, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
session.Statement.ConditionStr = strings.Join(colNames, " AND ")
|
||||
session.Statement.BeanArgs = args
|
||||
}
|
||||
|
|
@ -1090,7 +1104,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
|
|||
args = session.Statement.RawParams
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil &&
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil &&
|
||||
session.Statement.UseCache &&
|
||||
!session.Statement.IsDistinct {
|
||||
err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...)
|
||||
|
|
@ -1427,13 +1441,12 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
|
|||
}
|
||||
|
||||
func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
|
||||
|
||||
dataStruct := reflect.Indirect(reflect.ValueOf(bean))
|
||||
dataStruct := rValue(bean)
|
||||
if dataStruct.Kind() != reflect.Struct {
|
||||
return errors.New("Expected a pointer to a struct")
|
||||
}
|
||||
|
||||
table := session.Engine.autoMapType(rType(bean))
|
||||
table := session.Engine.autoMapType(dataStruct)
|
||||
|
||||
scanResultContainers := make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
|
|
@ -1480,7 +1493,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
|
|||
x := reflect.New(fieldType)
|
||||
err := json.Unmarshal([]byte(vv.String()), x.Interface())
|
||||
if err != nil {
|
||||
session.Engine.LogSQL(err)
|
||||
session.Engine.LogError(err)
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
|
|
@ -1534,7 +1547,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
|
|||
fieldValue.Set(vv)
|
||||
}
|
||||
} else if session.Statement.UseCascade {
|
||||
table := session.Engine.autoMapType(fieldValue.Type())
|
||||
table := session.Engine.autoMapType(*fieldValue)
|
||||
if table != nil {
|
||||
var x int64
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
|
|
@ -1700,8 +1713,7 @@ func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{})
|
|||
*sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable)
|
||||
}
|
||||
|
||||
session.Engine.LogSQL(*sqlStr)
|
||||
session.Engine.LogSQL(paramStr)
|
||||
session.Engine.logSQL(*sqlStr, paramStr)
|
||||
}
|
||||
|
||||
func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
|
||||
|
|
@ -1803,9 +1815,10 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
|
|||
}
|
||||
|
||||
bean := sliceValue.Index(0).Interface()
|
||||
sliceElementType := rType(bean)
|
||||
elementValue := rValue(bean)
|
||||
//sliceElementType := elementValue.Type()
|
||||
|
||||
table := session.Engine.autoMapType(sliceElementType)
|
||||
table := session.Engine.autoMapType(elementValue)
|
||||
session.Statement.RefTable = table
|
||||
|
||||
size := sliceValue.Len()
|
||||
|
|
@ -1903,7 +1916,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
|
|||
return 0, err
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheInsert(session.Statement.TableName())
|
||||
}
|
||||
|
||||
|
|
@ -2113,7 +2126,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
|
|||
v = x
|
||||
fieldValue.Set(reflect.ValueOf(v))
|
||||
} else if session.Statement.UseCascade {
|
||||
table := session.Engine.autoMapType(fieldValue.Type())
|
||||
table := session.Engine.autoMapType(*fieldValue)
|
||||
if table != nil {
|
||||
x, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
|
|
@ -2467,7 +2480,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
|
|||
case reflect.Complex64, reflect.Complex128:
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.LogSQL(err)
|
||||
session.Engine.LogError(err)
|
||||
return 0, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
|
|
@ -2479,7 +2492,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
|
|||
if col.SQLType.IsText() {
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.LogSQL(err)
|
||||
session.Engine.LogError(err)
|
||||
return 0, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
|
|
@ -2492,7 +2505,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
|
|||
} else {
|
||||
bytes, err = json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.LogSQL(err)
|
||||
session.Engine.LogError(err)
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
|
@ -2526,6 +2539,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
}
|
||||
|
||||
colPlaces := strings.Repeat("?, ", len(colNames))
|
||||
//fmt.Println(colNames, args)
|
||||
colPlaces = colPlaces[0 : len(colPlaces)-2]
|
||||
|
||||
sqlStr := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)",
|
||||
|
|
@ -2577,7 +2591,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
handleAfterInsertProcessorFunc(bean)
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheInsert(session.Statement.TableName())
|
||||
}
|
||||
|
||||
|
|
@ -2636,7 +2650,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
handleAfterInsertProcessorFunc(bean)
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheInsert(session.Statement.TableName())
|
||||
}
|
||||
|
||||
|
|
@ -2689,7 +2703,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
|
||||
// Method InsertOne insert only one struct into database as a record.
|
||||
// The in parameter bean must a struct or a point to struct. The return
|
||||
// parameter is lastInsertId and error
|
||||
// parameter is inserted and error
|
||||
func (session *Session) InsertOne(bean interface{}) (int64, error) {
|
||||
err := session.newDb()
|
||||
if err != nil {
|
||||
|
|
@ -2749,7 +2763,7 @@ func (session *Session) cacheInsert(tables ...string) error {
|
|||
}
|
||||
|
||||
table := session.Statement.RefTable
|
||||
cacher := session.Engine.getCacher(table.Type)
|
||||
cacher := session.Engine.getCacher2(table)
|
||||
|
||||
for _, t := range tables {
|
||||
session.Engine.LogDebug("cache clear:", t)
|
||||
|
|
@ -2783,7 +2797,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
|
|||
}
|
||||
}
|
||||
table := session.Statement.RefTable
|
||||
cacher := session.Engine.getCacher(table.Type)
|
||||
cacher := session.Engine.getCacher2(table)
|
||||
tableName := session.Statement.TableName()
|
||||
session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:])
|
||||
ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:])
|
||||
|
|
@ -2908,7 +2922,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
|
||||
if session.Statement.ColumnStr == "" {
|
||||
colNames, args = buildConditions(session.Engine, table, bean, false, false,
|
||||
false, false, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, false, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
} else {
|
||||
colNames, args, err = genCols(table, session, bean, true, true)
|
||||
if err != nil {
|
||||
|
|
@ -2942,7 +2957,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
|
||||
if len(condiBean) > 0 {
|
||||
condiColNames, condiArgs = buildConditions(session.Engine, session.Statement.RefTable, condiBean[0], true, true,
|
||||
false, true, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, true, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
}
|
||||
|
||||
var condition = ""
|
||||
|
|
@ -2965,6 +2981,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
|
||||
var sqlStr, inSql string
|
||||
var inArgs []interface{}
|
||||
doIncVer := false
|
||||
var verValue *reflect.Value
|
||||
if table.Version != "" && session.Statement.checkVersion {
|
||||
if condition != "" {
|
||||
condition = fmt.Sprintf("WHERE (%v) AND %v = ?", condition,
|
||||
|
|
@ -2987,12 +3005,13 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1",
|
||||
condition)
|
||||
|
||||
verValue, err := table.VersionColumn().ValueOf(bean)
|
||||
verValue, err = table.VersionColumn().ValueOf(bean)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
condiArgs = append(condiArgs, verValue.Interface())
|
||||
doIncVer = true
|
||||
} else {
|
||||
if condition != "" {
|
||||
condition = "WHERE " + condition
|
||||
|
|
@ -3019,9 +3038,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
res, err := session.exec(sqlStr, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if doIncVer {
|
||||
verValue.SetInt(verValue.Int() + 1)
|
||||
}
|
||||
|
||||
if cacher := session.Engine.getCacher(t); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
|
||||
//session.cacheUpdate(sqlStr, args...)
|
||||
cacher.ClearIds(session.Statement.TableName())
|
||||
cacher.ClearBeans(session.Statement.TableName())
|
||||
|
|
@ -3073,7 +3094,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
|
|||
return ErrCacheFailed
|
||||
}
|
||||
|
||||
cacher := session.Engine.getCacher(session.Statement.RefTable.Type)
|
||||
cacher := session.Engine.getCacher2(session.Statement.RefTable)
|
||||
tableName := session.Statement.TableName()
|
||||
ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
|
||||
if err != nil {
|
||||
|
|
@ -3139,7 +3160,8 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
table := session.Engine.autoMap(bean)
|
||||
session.Statement.RefTable = table
|
||||
colNames, args := buildConditions(session.Engine, table, bean, true, true,
|
||||
false, true, session.Statement.allUseBool, session.Statement.boolColumnMap)
|
||||
false, true, session.Statement.allUseBool, session.Statement.useAllCols,
|
||||
session.Statement.mustColumnMap)
|
||||
|
||||
var condition = ""
|
||||
|
||||
|
|
@ -3169,7 +3191,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
|
||||
args = append(session.Statement.Params, args...)
|
||||
|
||||
if cacher := session.Engine.getCacher(session.Statement.RefTable.Type); cacher != nil && session.Statement.UseCache {
|
||||
if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
|
||||
session.cacheDelete(sqlStr, args...)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +1,44 @@
|
|||
package dialects
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDialect("sqlite3", &sqlite3{})
|
||||
}
|
||||
// func init() {
|
||||
// RegisterDialect("sqlite3", &sqlite3{})
|
||||
// }
|
||||
|
||||
type sqlite3 struct {
|
||||
Base
|
||||
core.Base
|
||||
}
|
||||
|
||||
func (db *sqlite3) Init(uri *Uri, drivername, dataSourceName string) error {
|
||||
func (db *sqlite3) Init(uri *core.Uri, drivername, dataSourceName string) error {
|
||||
return db.Base.Init(db, uri, drivername, dataSourceName)
|
||||
}
|
||||
|
||||
func (db *sqlite3) SqlType(c *Column) string {
|
||||
func (db *sqlite3) SqlType(c *core.Column) string {
|
||||
switch t := c.SQLType.Name; t {
|
||||
case Date, DateTime, TimeStamp, Time:
|
||||
return Numeric
|
||||
case TimeStampz:
|
||||
return Text
|
||||
case Char, Varchar, TinyText, Text, MediumText, LongText:
|
||||
return Text
|
||||
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool:
|
||||
return Integer
|
||||
case Float, Double, Real:
|
||||
return Real
|
||||
case Decimal, Numeric:
|
||||
return Numeric
|
||||
case TinyBlob, Blob, MediumBlob, LongBlob, Bytea, Binary, VarBinary:
|
||||
return Blob
|
||||
case Serial, BigSerial:
|
||||
case core.Date, core.DateTime, core.TimeStamp, core.Time:
|
||||
return core.Numeric
|
||||
case core.TimeStampz:
|
||||
return core.Text
|
||||
case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText:
|
||||
return core.Text
|
||||
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool:
|
||||
return core.Integer
|
||||
case core.Float, core.Double, core.Real:
|
||||
return core.Real
|
||||
case core.Decimal, core.Numeric:
|
||||
return core.Numeric
|
||||
case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
|
||||
return core.Blob
|
||||
case core.Serial, core.BigSerial:
|
||||
c.IsPrimaryKey = true
|
||||
c.IsAutoIncrement = true
|
||||
c.Nullable = false
|
||||
return Integer
|
||||
return core.Integer
|
||||
default:
|
||||
return t
|
||||
}
|
||||
|
|
@ -84,10 +84,10 @@ func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interfac
|
|||
return sql, args
|
||||
}
|
||||
|
||||
func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, error) {
|
||||
func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -110,11 +110,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e
|
|||
nStart := strings.Index(name, "(")
|
||||
nEnd := strings.Index(name, ")")
|
||||
colCreates := strings.Split(name[nStart+1:nEnd], ",")
|
||||
cols := make(map[string]*Column)
|
||||
cols := make(map[string]*core.Column)
|
||||
colSeq := make([]string, 0)
|
||||
for _, colStr := range colCreates {
|
||||
fields := strings.Fields(strings.TrimSpace(colStr))
|
||||
col := new(Column)
|
||||
col := new(core.Column)
|
||||
col.Indexes = make(map[string]bool)
|
||||
col.Nullable = true
|
||||
for idx, field := range fields {
|
||||
|
|
@ -122,7 +122,7 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e
|
|||
col.Name = strings.Trim(field, "`[] ")
|
||||
continue
|
||||
} else if idx == 1 {
|
||||
col.SQLType = SQLType{field, 0, 0}
|
||||
col.SQLType = core.SQLType{field, 0, 0}
|
||||
}
|
||||
switch field {
|
||||
case "PRIMARY":
|
||||
|
|
@ -143,11 +143,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e
|
|||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
func (db *sqlite3) GetTables() ([]*Table, error) {
|
||||
func (db *sqlite3) GetTables() ([]*core.Table, error) {
|
||||
args := []interface{}{}
|
||||
s := "SELECT name FROM sqlite_master WHERE type='table'"
|
||||
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -158,9 +158,9 @@ func (db *sqlite3) GetTables() ([]*Table, error) {
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
tables := make([]*Table, 0)
|
||||
tables := make([]*core.Table, 0)
|
||||
for rows.Next() {
|
||||
table := NewEmptyTable()
|
||||
table := core.NewEmptyTable()
|
||||
err = rows.Scan(&table.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -173,10 +173,10 @@ func (db *sqlite3) GetTables() ([]*Table, error) {
|
|||
return tables, nil
|
||||
}
|
||||
|
||||
func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
|
||||
func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"
|
||||
cnn, err := Open(db.DriverName(), db.DataSourceName())
|
||||
cnn, err := core.Open(db.DriverName(), db.DataSourceName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*Index, 0)
|
||||
indexes := make(map[string]*core.Index, 0)
|
||||
for rows.Next() {
|
||||
var sql string
|
||||
err = rows.Scan(&sql)
|
||||
|
|
@ -199,7 +199,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
index := new(Index)
|
||||
index := new(core.Index)
|
||||
nNStart := strings.Index(sql, "INDEX")
|
||||
nNEnd := strings.Index(sql, "ON")
|
||||
if nNStart == -1 || nNEnd == -1 {
|
||||
|
|
@ -215,9 +215,9 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
}
|
||||
|
||||
if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
|
||||
index.Type = UniqueType
|
||||
index.Type = core.UniqueType
|
||||
} else {
|
||||
index.Type = IndexType
|
||||
index.Type = core.IndexType
|
||||
}
|
||||
|
||||
nStart := strings.Index(sql, "(")
|
||||
|
|
@ -234,6 +234,6 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
|
|||
return indexes, nil
|
||||
}
|
||||
|
||||
func (db *sqlite3) Filters() []Filter {
|
||||
return []Filter{&IdFilter{}}
|
||||
func (db *sqlite3) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}}
|
||||
}
|
||||
86
statement.go
86
statement.go
|
|
@ -1,15 +1,13 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
//"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
//"strconv"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type inParam struct {
|
||||
|
|
@ -32,6 +30,7 @@ type Statement struct {
|
|||
HavingStr string
|
||||
ColumnStr string
|
||||
columnMap map[string]bool
|
||||
useAllCols bool
|
||||
OmitStr string
|
||||
ConditionStr string
|
||||
AltTableName string
|
||||
|
|
@ -47,7 +46,7 @@ type Statement struct {
|
|||
IsDistinct bool
|
||||
allUseBool bool
|
||||
checkVersion bool
|
||||
boolColumnMap map[string]bool
|
||||
mustColumnMap map[string]bool
|
||||
inColumns map[string]*inParam
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +67,7 @@ func (statement *Statement) Init() {
|
|||
statement.columnMap = make(map[string]bool)
|
||||
statement.ConditionStr = ""
|
||||
statement.AltTableName = ""
|
||||
statement.IdParam = nil
|
||||
statement.RawSQL = ""
|
||||
statement.RawParams = make([]interface{}, 0)
|
||||
statement.BeanArgs = make([]interface{}, 0)
|
||||
|
|
@ -75,7 +75,7 @@ func (statement *Statement) Init() {
|
|||
statement.UseAutoTime = true
|
||||
statement.IsDistinct = false
|
||||
statement.allUseBool = false
|
||||
statement.boolColumnMap = make(map[string]bool)
|
||||
statement.mustColumnMap = make(map[string]bool)
|
||||
statement.checkVersion = true
|
||||
statement.inColumns = make(map[string]*inParam)
|
||||
}
|
||||
|
|
@ -118,11 +118,12 @@ func (statement *Statement) Or(querystring string, args ...interface{}) *Stateme
|
|||
|
||||
// tempororily set table name
|
||||
func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
||||
t := rType(tableNameOrBean)
|
||||
v := rValue(tableNameOrBean)
|
||||
t := v.Type()
|
||||
if t.Kind() == reflect.String {
|
||||
statement.AltTableName = tableNameOrBean.(string)
|
||||
} else if t.Kind() == reflect.Struct {
|
||||
statement.RefTable = statement.Engine.autoMapType(t)
|
||||
statement.RefTable = statement.Engine.autoMapType(v)
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
|
@ -210,7 +211,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
|||
if col.SQLType.IsText() {
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
engine.LogSQL(err)
|
||||
engine.LogError(err)
|
||||
continue
|
||||
}
|
||||
val = string(bytes)
|
||||
|
|
@ -227,7 +228,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
|||
} else {
|
||||
bytes, err = json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
engine.LogSQL(err)
|
||||
engine.LogError(err)
|
||||
continue
|
||||
}
|
||||
val = bytes
|
||||
|
|
@ -245,8 +246,9 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
|||
|
||||
// Auto generating conditions according a struct
|
||||
func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
||||
includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool,
|
||||
boolColumnMap map[string]bool) ([]string, []interface{}) {
|
||||
includeVersion bool, includeUpdated bool, includeNil bool,
|
||||
includeAutoIncr bool, allUseBool bool, useAllCols bool,
|
||||
mustColumnMap map[string]bool) ([]string, []interface{}) {
|
||||
|
||||
colNames := make([]string, 0)
|
||||
var args = make([]interface{}, 0)
|
||||
|
|
@ -274,7 +276,15 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
fieldValue := *fieldValuePtr
|
||||
fieldType := reflect.TypeOf(fieldValue.Interface())
|
||||
|
||||
requiredField := false
|
||||
requiredField := useAllCols
|
||||
if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok {
|
||||
if b {
|
||||
requiredField = true
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
if fieldValue.IsNil() {
|
||||
if includeNil {
|
||||
|
|
@ -297,8 +307,6 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
case reflect.Bool:
|
||||
if allUseBool || requiredField {
|
||||
val = fieldValue.Interface()
|
||||
} else if _, ok := boolColumnMap[col.Name]; ok {
|
||||
val = fieldValue.Interface()
|
||||
} else {
|
||||
// if a bool in a struct, it will not be as a condition because it default is false,
|
||||
// please use Where() instead
|
||||
|
|
@ -346,7 +354,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
val = t
|
||||
}
|
||||
} else {
|
||||
engine.autoMapType(fieldValue.Type())
|
||||
engine.autoMapType(fieldValue)
|
||||
if table, ok := engine.Tables[fieldValue.Type()]; ok {
|
||||
if len(table.PrimaryKeys) == 1 {
|
||||
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
|
||||
|
|
@ -373,7 +381,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
if col.SQLType.IsText() {
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
engine.LogSQL(err)
|
||||
engine.LogError(err)
|
||||
continue
|
||||
}
|
||||
val = string(bytes)
|
||||
|
|
@ -390,7 +398,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
|
|||
} else {
|
||||
bytes, err = json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
engine.LogSQL(err)
|
||||
engine.LogError(err)
|
||||
continue
|
||||
}
|
||||
val = bytes
|
||||
|
|
@ -534,13 +542,34 @@ func (statement *Statement) Cols(columns ...string) *Statement {
|
|||
return statement
|
||||
}
|
||||
|
||||
// Update use only: update all columns
|
||||
func (statement *Statement) AllCols() *Statement {
|
||||
statement.useAllCols = true
|
||||
return statement
|
||||
}
|
||||
|
||||
// Update use only: must update columns
|
||||
func (statement *Statement) MustCols(columns ...string) *Statement {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.mustColumnMap[strings.ToLower(nc)] = true
|
||||
}
|
||||
return statement
|
||||
}
|
||||
|
||||
// Update use only: not update columns
|
||||
/*func (statement *Statement) NotCols(columns ...string) *Statement {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.mustColumnMap[strings.ToLower(nc)] = false
|
||||
}
|
||||
return statement
|
||||
}*/
|
||||
|
||||
// indicates that use bool fields as update contents and query contiditions
|
||||
func (statement *Statement) UseBool(columns ...string) *Statement {
|
||||
if len(columns) > 0 {
|
||||
newColumns := col2NewCols(columns...)
|
||||
for _, nc := range newColumns {
|
||||
statement.boolColumnMap[strings.ToLower(nc)] = true
|
||||
}
|
||||
statement.MustCols(columns...)
|
||||
} else {
|
||||
statement.allUseBool = true
|
||||
}
|
||||
|
|
@ -676,13 +705,7 @@ func (s *Statement) genDelIndexSQL() []string {
|
|||
}
|
||||
|
||||
func (s *Statement) genDropSQL() string {
|
||||
if s.Engine.dialect.DBType() == core.MSSQL {
|
||||
return "IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'" +
|
||||
s.TableName() + "') and OBJECTPROPERTY(id, N'IsUserTable') = 1) " +
|
||||
"DROP TABLE " + s.Engine.Quote(s.TableName()) + ";"
|
||||
} else {
|
||||
return "DROP TABLE IF EXISTS " + s.Engine.Quote(s.TableName()) + ";"
|
||||
}
|
||||
return s.Engine.dialect.DropTableSql(s.TableName()) + ";"
|
||||
}
|
||||
|
||||
func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) {
|
||||
|
|
@ -690,7 +713,8 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
|
|||
statement.RefTable = table
|
||||
|
||||
colNames, args := buildConditions(statement.Engine, table, bean, true, true,
|
||||
false, true, statement.allUseBool, statement.boolColumnMap)
|
||||
false, true, statement.allUseBool, statement.useAllCols,
|
||||
statement.mustColumnMap)
|
||||
|
||||
statement.ConditionStr = strings.Join(colNames, " AND ")
|
||||
statement.BeanArgs = args
|
||||
|
|
@ -729,7 +753,7 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
|
|||
statement.RefTable = table
|
||||
|
||||
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false,
|
||||
true, statement.allUseBool, statement.boolColumnMap)
|
||||
true, statement.allUseBool, statement.useAllCols, statement.mustColumnMap)
|
||||
|
||||
statement.ConditionStr = strings.Join(colNames, " AND ")
|
||||
statement.BeanArgs = args
|
||||
|
|
|
|||
3985
tests/base_test.go
3985
tests/base_test.go
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +0,0 @@
|
|||
go test -v -bench=. -run=XXX
|
||||
|
|
@ -1 +0,0 @@
|
|||
go test -v -bench=. -run=XXX
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
)
|
||||
|
||||
type BigStruct struct {
|
||||
Id int64
|
||||
Name string
|
||||
Title string
|
||||
Age string
|
||||
Alias string
|
||||
NickName string
|
||||
}
|
||||
|
||||
func doBenchDriverInsert(db *sql.DB, b *testing.B) {
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := db.Exec(`insert into big_struct (name, title, age, alias, nick_name)
|
||||
values ('fafdasf', 'fadfa', 'afadfsaf', 'fadfafdsafd', 'fadfafdsaf')`)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func doBenchDriverFind(db *sql.DB, b *testing.B) {
|
||||
b.StopTimer()
|
||||
for i := 0; i < 50; i++ {
|
||||
_, err := db.Exec(`insert into big_struct (name, title, age, alias, nick_name)
|
||||
values ('fafdasf', 'fadfa', 'afadfsaf', 'fadfafdsafd', 'fadfafdsaf')`)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N/50; i++ {
|
||||
rows, err := db.Query("select * from big_struct limit 50")
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
for rows.Next() {
|
||||
s := &BigStruct{}
|
||||
rows.Scan(&s.Id, &s.Name, &s.Title, &s.Age, &s.Alias, &s.NickName)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func doBenchDriver(newdriver func() (*sql.DB, error), createTableSql,
|
||||
dropTableSql string, opFunc func(*sql.DB, *testing.B), t *testing.B) {
|
||||
db, err := newdriver()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTableSql)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
opFunc(db, t)
|
||||
|
||||
_, err = db.Exec(dropTableSql)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func doBenchInsert(engine *xorm.Engine, b *testing.B) {
|
||||
b.StopTimer()
|
||||
bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"}
|
||||
err := engine.CreateTables(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bs.Id = 0
|
||||
_, err = engine.Insert(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
err = engine.DropTables(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func doBenchFind(engine *xorm.Engine, b *testing.B) {
|
||||
b.StopTimer()
|
||||
bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"}
|
||||
err := engine.CreateTables(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
bs.Id = 0
|
||||
_, err = engine.Insert(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N/50; i++ {
|
||||
bss := new([]BigStruct)
|
||||
err = engine.Limit(50).Find(bss)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
err = engine.DropTables(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func doBenchFindPtr(engine *xorm.Engine, b *testing.B) {
|
||||
b.StopTimer()
|
||||
bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"}
|
||||
err := engine.CreateTables(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
bs.Id = 0
|
||||
_, err = engine.Insert(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N/50; i++ {
|
||||
bss := new([]*BigStruct)
|
||||
err = engine.Limit(50).Find(bss)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
err = engine.DropTables(bs)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
package tests
|
||||
|
||||
//
|
||||
// +build windows
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/lunny/godbc"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/caches"
|
||||
)
|
||||
|
||||
const mssqlConnStr = "driver={SQL Server};Server=192.168.20.135;Database=xorm_test; uid=sa; pwd=1234;"
|
||||
|
||||
func newMssqlEngine() (*xorm.Engine, error) {
|
||||
return xorm.NewEngine("odbc", mssqlConnStr)
|
||||
}
|
||||
|
||||
func TestMssql(t *testing.T) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func TestMssqlWithCache(t *testing.T) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func newMssqlDriverDB() (*sql.DB, error) {
|
||||
return sql.Open("odbc", mssqlConnStr)
|
||||
}
|
||||
|
||||
const (
|
||||
createTableMssql = `IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = 'big_struct' ) CREATE TABLE
|
||||
"big_struct" ("id" BIGINT PRIMARY KEY IDENTITY NOT NULL, "name" VARCHAR(255) NULL, "title" VARCHAR(255) NULL,
|
||||
"age" VARCHAR(255) NULL, "alias" VARCHAR(255) NULL, "nick_name" VARCHAR(255) NULL);
|
||||
`
|
||||
|
||||
dropTableMssql = "IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'big_struct') and OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE IF EXISTS `big_struct`;"
|
||||
)
|
||||
|
||||
func BenchmarkMssqlDriverInsert(t *testing.B) {
|
||||
doBenchDriver(newMssqlDriverDB, createTableMssql, dropTableMssql,
|
||||
doBenchDriverInsert, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlDriverFind(t *testing.B) {
|
||||
doBenchDriver(newMssqlDriverDB, createTableMssql, dropTableMssql,
|
||||
doBenchDriverFind, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlNoCacheInsert(t *testing.B) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlNoCacheFind(t *testing.B) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlNoCacheFindPtr(t *testing.B) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlCacheInsert(t *testing.B) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlCacheFind(t *testing.B) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMssqlCacheFindPtr(t *testing.B) {
|
||||
engine, err := newMssqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/caches"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
)
|
||||
|
||||
/*
|
||||
CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET
|
||||
utf8 COLLATE utf8_general_ci;
|
||||
*/
|
||||
|
||||
var showTestSql bool = true
|
||||
|
||||
func TestMyMysql(t *testing.T) {
|
||||
err := mymysqlDdlImport()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine, err := xorm.NewEngine("mymysql", "xorm_test/root/")
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestMyMysqlWithCache(t *testing.T) {
|
||||
err := mymysqlDdlImport()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine, err := xorm.NewEngine("mymysql", "xorm_test2/root/")
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func newMyMysqlEngine() (*xorm.Engine, error) {
|
||||
return xorm.NewEngine("mymysql", "xorm_test2/root/")
|
||||
}
|
||||
|
||||
func newMyMysqlDriverDB() (*sql.DB, error) {
|
||||
return sql.Open("mymysql", "xorm_test2/root/")
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlDriverInsert(t *testing.B) {
|
||||
doBenchDriver(newMyMysqlDriverDB, createTableMySql, dropTableMySql,
|
||||
doBenchDriverInsert, t)
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlDriverFind(t *testing.B) {
|
||||
doBenchDriver(newMyMysqlDriverDB, createTableMySql, dropTableMySql,
|
||||
doBenchDriverFind, t)
|
||||
}
|
||||
|
||||
func mymysqlDdlImport() error {
|
||||
engine, err := xorm.NewEngine("mymysql", "/root/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
sqlResults, _ := engine.Import("testdata/mysql_ddl.sql")
|
||||
engine.LogDebug("sql results: %v", sqlResults)
|
||||
engine.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlNoCacheInsert(t *testing.B) {
|
||||
engine, err := newMyMysqlEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlNoCacheFind(t *testing.B) {
|
||||
engine, err := newMyMysqlEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
//engine.ShowSQL = true
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlNoCacheFindPtr(t *testing.B) {
|
||||
engine, err := newMyMysqlEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
//engine.ShowSQL = true
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlCacheInsert(t *testing.B) {
|
||||
engine, err := newMyMysqlEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer engine.Close()
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlCacheFind(t *testing.B) {
|
||||
engine, err := newMyMysqlEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer engine.Close()
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMyMysqlCacheFindPtr(t *testing.B) {
|
||||
engine, err := newMyMysqlEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer engine.Close()
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/caches"
|
||||
"github.com/lunny/xorm/core"
|
||||
)
|
||||
|
||||
/*
|
||||
CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET
|
||||
utf8 COLLATE utf8_general_ci;
|
||||
*/
|
||||
|
||||
func TestMysql(t *testing.T) {
|
||||
err := mysqlDdlImport()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
engine, err := xorm.NewEngine("mysql", "root:@/xorm_test?charset=utf8")
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSnakeMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestMysqlSameMapper(t *testing.T) {
|
||||
err := mysqlDdlImport()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
engine, err := xorm.NewEngine("mysql", "root:@/xorm_test1?charset=utf8")
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
engine.SetMapper(core.SameMapper{})
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSameMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestMysqlWithCache(t *testing.T) {
|
||||
err := mysqlDdlImport()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
engine, err := xorm.NewEngine("mysql", "root:@/xorm_test2?charset=utf8")
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSnakeMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func TestMysqlWithCacheSameMapper(t *testing.T) {
|
||||
err := mysqlDdlImport()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
engine, err := xorm.NewEngine("mysql", "root:@/xorm_test3?charset=utf8")
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetMapper(core.SameMapper{})
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSameMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func newMysqlEngine() (*xorm.Engine, error) {
|
||||
return xorm.NewEngine("mysql", "root:@/xorm_test?charset=utf8")
|
||||
}
|
||||
|
||||
func mysqlDdlImport() error {
|
||||
engine, err := xorm.NewEngine("mysql", "root:@/?charset=utf8")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
sqlResults, _ := engine.Import("testdata/mysql_ddl.sql")
|
||||
engine.LogDebug("sql results: %v", sqlResults)
|
||||
engine.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMysqlDriverDB() (*sql.DB, error) {
|
||||
return sql.Open("mysql", "root:@/xorm_test?charset=utf8")
|
||||
}
|
||||
|
||||
const (
|
||||
createTableMySql = "CREATE TABLE IF NOT EXISTS `big_struct` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, `name` VARCHAR(255) NULL, `title` VARCHAR(255) NULL, `age` VARCHAR(255) NULL, `alias` VARCHAR(255) NULL, `nick_name` VARCHAR(255) NULL);"
|
||||
dropTableMySql = "DROP TABLE IF EXISTS `big_struct`;"
|
||||
)
|
||||
|
||||
func BenchmarkMysqlDriverInsert(t *testing.B) {
|
||||
doBenchDriver(newMysqlDriverDB, createTableMySql, dropTableMySql,
|
||||
doBenchDriverInsert, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlDriverFind(t *testing.B) {
|
||||
doBenchDriver(newMysqlDriverDB, createTableMySql, dropTableMySql,
|
||||
doBenchDriverFind, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlNoCacheInsert(t *testing.B) {
|
||||
engine, err := newMysqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlNoCacheFind(t *testing.B) {
|
||||
engine, err := newMysqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlNoCacheFindPtr(t *testing.B) {
|
||||
engine, err := newMysqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlCacheInsert(t *testing.B) {
|
||||
engine, err := newMysqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlCacheFind(t *testing.B) {
|
||||
engine, err := newMysqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkMysqlCacheFindPtr(t *testing.B) {
|
||||
engine, err := newMysqlEngine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/caches"
|
||||
"github.com/lunny/xorm/core"
|
||||
)
|
||||
|
||||
//var connStr string = "dbname=xorm_test user=lunny password=1234 sslmode=disable"
|
||||
|
||||
var connStr string = "dbname=xorm_test sslmode=disable"
|
||||
|
||||
func newPostgresEngine() (*xorm.Engine, error) {
|
||||
orm, err := xorm.NewEngine("postgres", connStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tables, err := orm.DBMetas()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, table := range tables {
|
||||
_, err = orm.Exec("drop table \"" + table.Name + "\"")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return orm, err
|
||||
}
|
||||
|
||||
func newPostgresDriverDB() (*sql.DB, error) {
|
||||
return sql.Open("postgres", connStr)
|
||||
}
|
||||
|
||||
func TestPostgres(t *testing.T) {
|
||||
engine, err := newPostgresEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer engine.Close()
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSnakeMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestPostgresWithCache(t *testing.T) {
|
||||
engine, err := newPostgresEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
defer engine.Close()
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSnakeMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func TestPostgresSameMapper(t *testing.T) {
|
||||
engine, err := newPostgresEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer engine.Close()
|
||||
engine.SetMapper(core.SameMapper{})
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSameMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestPostgresWithCacheSameMapper(t *testing.T) {
|
||||
engine, err := newPostgresEngine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
defer engine.Close()
|
||||
engine.SetMapper(core.SameMapper{})
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSameMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
const (
|
||||
createTablePostgres = `CREATE TABLE IF NOT EXISTS "big_struct" ("id" SERIAL PRIMARY KEY NOT NULL, "name" VARCHAR(255) NULL, "title" VARCHAR(255) NULL, "age" VARCHAR(255) NULL, "alias" VARCHAR(255) NULL, "nick_name" VARCHAR(255) NULL);`
|
||||
dropTablePostgres = `DROP TABLE IF EXISTS "big_struct";`
|
||||
)
|
||||
|
||||
func BenchmarkPostgresDriverInsert(t *testing.B) {
|
||||
doBenchDriver(newPostgresDriverDB, createTablePostgres, dropTablePostgres,
|
||||
doBenchDriverInsert, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresDriverFind(t *testing.B) {
|
||||
doBenchDriver(newPostgresDriverDB, createTablePostgres, dropTablePostgres,
|
||||
doBenchDriverFind, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresNoCacheInsert(t *testing.B) {
|
||||
engine, err := newPostgresEngine()
|
||||
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresNoCacheFind(t *testing.B) {
|
||||
engine, err := newPostgresEngine()
|
||||
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresNoCacheFindPtr(t *testing.B) {
|
||||
engine, err := newPostgresEngine()
|
||||
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresCacheInsert(t *testing.B) {
|
||||
engine, err := newPostgresEngine()
|
||||
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresCacheFind(t *testing.B) {
|
||||
engine, err := newPostgresEngine()
|
||||
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkPostgresCacheFindPtr(t *testing.B) {
|
||||
engine, err := newPostgresEngine()
|
||||
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/caches"
|
||||
"github.com/lunny/xorm/core"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func newSqlite3Engine() (*xorm.Engine, error) {
|
||||
os.Remove("./test.db")
|
||||
return xorm.NewEngine("sqlite3", "./test.db")
|
||||
}
|
||||
|
||||
func newSqlite3DriverDB() (*sql.DB, error) {
|
||||
os.Remove("./test.db")
|
||||
return sql.Open("sqlite3", "./test.db")
|
||||
}
|
||||
|
||||
func TestSqlite3(t *testing.T) {
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSnakeMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestSqlite3WithCache(t *testing.T) {
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSnakeMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
func TestSqlite3SameMapper(t *testing.T) {
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetMapper(core.SameMapper{})
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSameMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
testAll3(engine, t)
|
||||
}
|
||||
|
||||
func TestSqlite3WithCacheSameMapper(t *testing.T) {
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetMapper(core.SameMapper{})
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
engine.ShowSQL = showTestSql
|
||||
engine.ShowErr = showTestSql
|
||||
engine.ShowWarn = showTestSql
|
||||
engine.ShowDebug = showTestSql
|
||||
|
||||
testAll(engine, t)
|
||||
testAllSameMapper(engine, t)
|
||||
testAll2(engine, t)
|
||||
}
|
||||
|
||||
const (
|
||||
createTableSqlite3 = "CREATE TABLE IF NOT EXISTS `big_struct` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, `title` TEXT NULL, `age` TEXT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL);"
|
||||
dropTableSqlite3 = "DROP TABLE IF EXISTS `big_struct`;"
|
||||
)
|
||||
|
||||
func BenchmarkSqlite3DriverInsert(t *testing.B) {
|
||||
doBenchDriver(newSqlite3DriverDB, createTableSqlite3, dropTableSqlite3,
|
||||
doBenchDriverInsert, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3DriverFind(t *testing.B) {
|
||||
doBenchDriver(newSqlite3DriverDB, createTableSqlite3, dropTableSqlite3,
|
||||
doBenchDriverFind, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3NoCacheInsert(t *testing.B) {
|
||||
t.StopTimer()
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3NoCacheFind(t *testing.B) {
|
||||
t.StopTimer()
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3NoCacheFindPtr(t *testing.B) {
|
||||
t.StopTimer()
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
//engine.ShowSQL = true
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3CacheInsert(t *testing.B) {
|
||||
t.StopTimer()
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
doBenchInsert(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3CacheFind(t *testing.B) {
|
||||
t.StopTimer()
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
doBenchFind(engine, t)
|
||||
}
|
||||
|
||||
func BenchmarkSqlite3CacheFindPtr(t *testing.B) {
|
||||
t.StopTimer()
|
||||
engine, err := newSqlite3Engine()
|
||||
defer engine.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000))
|
||||
doBenchFindPtr(engine, t)
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
DROP DATABASE xorm_test;
|
||||
DROP DATABASE xorm_test1;
|
||||
DROP DATABASE xorm_test2;
|
||||
DROP DATABASE xorm_test3;
|
||||
CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
CREATE DATABASE IF NOT EXISTS xorm_test1 CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
CREATE DATABASE IF NOT EXISTS xorm_test2 CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
CREATE DATABASE IF NOT EXISTS xorm_test3 CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
40
xorm.go
40
xorm.go
|
|
@ -1,6 +1,7 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -9,16 +10,36 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lunny/xorm/caches"
|
||||
"github.com/lunny/xorm/core"
|
||||
_ "github.com/lunny/xorm/dialects"
|
||||
_ "github.com/lunny/xorm/drivers"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm/caches"
|
||||
_ "github.com/go-xorm/xorm/drivers"
|
||||
)
|
||||
|
||||
const (
|
||||
Version string = "0.4"
|
||||
)
|
||||
|
||||
func init() {
|
||||
provided_dialects := map[string]struct {
|
||||
dbType core.DbType
|
||||
get func() core.Dialect
|
||||
}{
|
||||
"odbc": {"mssql", func() core.Dialect { return &mssql{} }},
|
||||
"mysql": {"mysql", func() core.Dialect { return &mysql{} }},
|
||||
"mymysql": {"mysql", func() core.Dialect { return &mysql{} }},
|
||||
"oci8": {"oracle", func() core.Dialect { return &oracle{} }},
|
||||
"postgres": {"postgres", func() core.Dialect { return &postgres{} }},
|
||||
"sqlite3": {"sqlite3", func() core.Dialect { return &sqlite3{} }},
|
||||
}
|
||||
|
||||
for k, v := range provided_dialects {
|
||||
_, err := sql.Open(string(k), "")
|
||||
if err == nil {
|
||||
core.RegisterDialect(v.dbType, v.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func close(engine *Engine) {
|
||||
engine.Close()
|
||||
}
|
||||
|
|
@ -46,19 +67,22 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
engine := &Engine{DriverName: driverName,
|
||||
DataSourceName: dataSourceName, dialect: dialect,
|
||||
tableCachers: make(map[reflect.Type]core.Cacher)}
|
||||
engine := &Engine{
|
||||
DriverName: driverName,
|
||||
DataSourceName: dataSourceName,
|
||||
dialect: dialect,
|
||||
}
|
||||
|
||||
engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper)))
|
||||
|
||||
engine.Filters = dialect.Filters()
|
||||
|
||||
engine.Tables = make(map[reflect.Type]*core.Table)
|
||||
|
||||
engine.mutex = &sync.RWMutex{}
|
||||
engine.TagIdentifier = "xorm"
|
||||
|
||||
engine.Logger = os.Stdout
|
||||
engine.Logger = NewSimpleLogger(os.Stdout)
|
||||
|
||||
//engine.Pool = NewSimpleConnectPool()
|
||||
//engine.Pool = NewNoneConnectPool()
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
[deps]
|
||||
github.com/lunny/xorm=../
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# xorm tools
|
||||
|
||||
|
||||
xorm tools is a set of tools for database operation.
|
||||
|
||||
## Install
|
||||
|
||||
`go get github.com/lunny/xorm/xorm`
|
||||
|
||||
and you should install the depends below:
|
||||
|
||||
* github.com/lunny/xorm
|
||||
|
||||
* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
|
||||
|
||||
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
|
||||
|
||||
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
|
||||
|
||||
* Postgres: [github.com/bylevel/pq](https://github.com/bylevel/pq)
|
||||
|
||||
|
||||
## Reverse
|
||||
|
||||
After you installed the tool, you can type
|
||||
|
||||
`xorm help reverse`
|
||||
|
||||
to get help
|
||||
|
||||
example:
|
||||
|
||||
sqlite:
|
||||
`xorm reverse sqite3 test.db templates/goxorm`
|
||||
|
||||
mysql:
|
||||
`xorm reverse mysql root:@/xorm_test?charset=utf8 templates/goxorm`
|
||||
|
||||
mymysql:
|
||||
`xorm reverse mymysql xorm_test2/root/ templates/goxorm`
|
||||
|
||||
postgres:
|
||||
`xorm reverse postgres "dbname=xorm_test sslmode=disable" templates/goxorm`
|
||||
|
||||
will generated go files in `./model` directory
|
||||
|
||||
## Template and Config
|
||||
|
||||
Now, xorm tool supports go and c++ two languages and have go, goxorm, c++ three of default templates. In template directory, we can put a config file to control how to generating.
|
||||
|
||||
````
|
||||
lang=go
|
||||
genJson=1
|
||||
```
|
||||
|
||||
lang must be go or c++ now.
|
||||
genJson can be 1 or 0, if 1 then the struct will have json tag.
|
||||
|
||||
## LICENSE
|
||||
|
||||
BSD License
|
||||
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
|
||||
66
xorm/c++.go
66
xorm/c++.go
|
|
@ -1,66 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/lunny/xorm/core"
|
||||
)
|
||||
|
||||
var (
|
||||
CPlusTmpl LangTmpl = LangTmpl{
|
||||
template.FuncMap{"Mapper": mapper.Table2Obj,
|
||||
"Type": cPlusTypeStr,
|
||||
"UnTitle": unTitle,
|
||||
},
|
||||
nil,
|
||||
genCPlusImports,
|
||||
}
|
||||
)
|
||||
|
||||
func cPlusTypeStr(col *core.Column) string {
|
||||
tp := col.SQLType
|
||||
name := strings.ToUpper(tp.Name)
|
||||
switch name {
|
||||
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.Serial:
|
||||
return "int"
|
||||
case core.BigInt, core.BigSerial:
|
||||
return "__int64"
|
||||
case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText:
|
||||
return "tstring"
|
||||
case core.Date, core.DateTime, core.Time, core.TimeStamp:
|
||||
return "time_t"
|
||||
case core.Decimal, core.Numeric:
|
||||
return "tstring"
|
||||
case core.Real, core.Float:
|
||||
return "float"
|
||||
case core.Double:
|
||||
return "double"
|
||||
case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea:
|
||||
return "tstring"
|
||||
case core.Bool:
|
||||
return "bool"
|
||||
default:
|
||||
return "tstring"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func genCPlusImports(tables []*core.Table) map[string]string {
|
||||
imports := make(map[string]string)
|
||||
|
||||
for _, table := range tables {
|
||||
for _, col := range table.Columns() {
|
||||
switch cPlusTypeStr(col) {
|
||||
case "time_t":
|
||||
imports[`<time.h>`] = `<time.h>`
|
||||
case "tstring":
|
||||
imports["<string>"] = "<string>"
|
||||
//case "__int64":
|
||||
// imports[""] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return imports
|
||||
}
|
||||
78
xorm/cmd.go
78
xorm/cmd.go
|
|
@ -1,78 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Command is an implementation of a go command
|
||||
// like go build or go fix.
|
||||
type Command struct {
|
||||
// Run runs the command.
|
||||
// The args are the arguments after the command name.
|
||||
Run func(cmd *Command, args []string)
|
||||
|
||||
// UsageLine is the one-line usage message.
|
||||
// The first word in the line is taken to be the command name.
|
||||
UsageLine string
|
||||
|
||||
// Short is the short description shown in the 'go help' output.
|
||||
Short string
|
||||
|
||||
// Long is the long message shown in the 'go help <this-command>' output.
|
||||
Long string
|
||||
|
||||
// Flag is a set of flags specific to this command.
|
||||
Flags map[string]bool
|
||||
}
|
||||
|
||||
// Name returns the command's name: the first word in the usage line.
|
||||
func (c *Command) Name() string {
|
||||
name := c.UsageLine
|
||||
i := strings.Index(name, " ")
|
||||
if i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *Command) Usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Runnable reports whether the command can be run; otherwise
|
||||
// it is a documentation pseudo-command such as importpath.
|
||||
func (c *Command) Runnable() bool {
|
||||
return c.Run != nil
|
||||
}
|
||||
|
||||
// checkFlags checks if the flag exists with correct format.
|
||||
func checkFlags(flags map[string]bool, args []string, print func(string)) int {
|
||||
num := 0 // Number of valid flags, use to cut out.
|
||||
for i, f := range args {
|
||||
// Check flag prefix '-'.
|
||||
if !strings.HasPrefix(f, "-") {
|
||||
// Not a flag, finish check process.
|
||||
break
|
||||
}
|
||||
|
||||
// Check if it a valid flag.
|
||||
if v, ok := flags[f]; ok {
|
||||
flags[f] = !v
|
||||
if !v {
|
||||
print(f)
|
||||
} else {
|
||||
fmt.Println("DISABLE: " + f)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("[ERRO] Unknown flag: %s.\n", f)
|
||||
return -1
|
||||
}
|
||||
num = i + 1
|
||||
}
|
||||
|
||||
return num
|
||||
}
|
||||
260
xorm/go.go
260
xorm/go.go
|
|
@ -1,260 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
"github.com/lunny/xorm/core"
|
||||
)
|
||||
|
||||
var (
|
||||
GoLangTmpl LangTmpl = LangTmpl{
|
||||
template.FuncMap{"Mapper": mapper.Table2Obj,
|
||||
"Type": typestring,
|
||||
"Tag": tag,
|
||||
"UnTitle": unTitle,
|
||||
"gt": gt,
|
||||
"getCol": getCol,
|
||||
},
|
||||
formatGo,
|
||||
genGoImports,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
errBadComparisonType = errors.New("invalid type for comparison")
|
||||
errBadComparison = errors.New("incompatible types for comparison")
|
||||
errNoComparison = errors.New("missing argument for comparison")
|
||||
)
|
||||
|
||||
type kind int
|
||||
|
||||
const (
|
||||
invalidKind kind = iota
|
||||
boolKind
|
||||
complexKind
|
||||
intKind
|
||||
floatKind
|
||||
integerKind
|
||||
stringKind
|
||||
uintKind
|
||||
)
|
||||
|
||||
func basicKind(v reflect.Value) (kind, error) {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return boolKind, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return intKind, nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return uintKind, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return floatKind, nil
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return complexKind, nil
|
||||
case reflect.String:
|
||||
return stringKind, nil
|
||||
}
|
||||
return invalidKind, errBadComparisonType
|
||||
}
|
||||
|
||||
// eq evaluates the comparison a == b || a == c || ...
|
||||
func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
|
||||
v1 := reflect.ValueOf(arg1)
|
||||
k1, err := basicKind(v1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(arg2) == 0 {
|
||||
return false, errNoComparison
|
||||
}
|
||||
for _, arg := range arg2 {
|
||||
v2 := reflect.ValueOf(arg)
|
||||
k2, err := basicKind(v2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if k1 != k2 {
|
||||
return false, errBadComparison
|
||||
}
|
||||
truth := false
|
||||
switch k1 {
|
||||
case boolKind:
|
||||
truth = v1.Bool() == v2.Bool()
|
||||
case complexKind:
|
||||
truth = v1.Complex() == v2.Complex()
|
||||
case floatKind:
|
||||
truth = v1.Float() == v2.Float()
|
||||
case intKind:
|
||||
truth = v1.Int() == v2.Int()
|
||||
case stringKind:
|
||||
truth = v1.String() == v2.String()
|
||||
case uintKind:
|
||||
truth = v1.Uint() == v2.Uint()
|
||||
default:
|
||||
panic("invalid kind")
|
||||
}
|
||||
if truth {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// lt evaluates the comparison a < b.
|
||||
func lt(arg1, arg2 interface{}) (bool, error) {
|
||||
v1 := reflect.ValueOf(arg1)
|
||||
k1, err := basicKind(v1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
v2 := reflect.ValueOf(arg2)
|
||||
k2, err := basicKind(v2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if k1 != k2 {
|
||||
return false, errBadComparison
|
||||
}
|
||||
truth := false
|
||||
switch k1 {
|
||||
case boolKind, complexKind:
|
||||
return false, errBadComparisonType
|
||||
case floatKind:
|
||||
truth = v1.Float() < v2.Float()
|
||||
case intKind:
|
||||
truth = v1.Int() < v2.Int()
|
||||
case stringKind:
|
||||
truth = v1.String() < v2.String()
|
||||
case uintKind:
|
||||
truth = v1.Uint() < v2.Uint()
|
||||
default:
|
||||
panic("invalid kind")
|
||||
}
|
||||
return truth, nil
|
||||
}
|
||||
|
||||
// le evaluates the comparison <= b.
|
||||
func le(arg1, arg2 interface{}) (bool, error) {
|
||||
// <= is < or ==.
|
||||
lessThan, err := lt(arg1, arg2)
|
||||
if lessThan || err != nil {
|
||||
return lessThan, err
|
||||
}
|
||||
return eq(arg1, arg2)
|
||||
}
|
||||
|
||||
// gt evaluates the comparison a > b.
|
||||
func gt(arg1, arg2 interface{}) (bool, error) {
|
||||
// > is the inverse of <=.
|
||||
lessOrEqual, err := le(arg1, arg2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !lessOrEqual, nil
|
||||
}
|
||||
|
||||
func getCol(cols map[string]*core.Column, name string) *core.Column {
|
||||
return cols[name]
|
||||
}
|
||||
|
||||
func formatGo(src string) (string, error) {
|
||||
source, err := format.Source([]byte(src))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(source), nil
|
||||
}
|
||||
|
||||
func genGoImports(tables []*core.Table) map[string]string {
|
||||
imports := make(map[string]string)
|
||||
|
||||
for _, table := range tables {
|
||||
for _, col := range table.Columns() {
|
||||
if typestring(col) == "time.Time" {
|
||||
imports["time"] = "time"
|
||||
}
|
||||
}
|
||||
}
|
||||
return imports
|
||||
}
|
||||
|
||||
func typestring(col *core.Column) string {
|
||||
st := col.SQLType
|
||||
t := core.SQLType2Type(st)
|
||||
s := t.String()
|
||||
if s == "[]uint8" {
|
||||
return "[]byte"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func tag(table *core.Table, col *core.Column) string {
|
||||
isNameId := (mapper.Table2Obj(col.Name) == "Id")
|
||||
isIdPk := isNameId && typestring(col) == "int64"
|
||||
|
||||
res := make([]string, 0)
|
||||
if !col.Nullable {
|
||||
if !isIdPk {
|
||||
res = append(res, "not null")
|
||||
}
|
||||
}
|
||||
if col.IsPrimaryKey {
|
||||
if !isIdPk {
|
||||
res = append(res, "pk")
|
||||
}
|
||||
}
|
||||
if col.Default != "" {
|
||||
res = append(res, "default "+col.Default)
|
||||
}
|
||||
if col.IsAutoIncrement {
|
||||
if !isIdPk {
|
||||
res = append(res, "autoincr")
|
||||
}
|
||||
}
|
||||
if col.IsCreated {
|
||||
res = append(res, "created")
|
||||
}
|
||||
if col.IsUpdated {
|
||||
res = append(res, "updated")
|
||||
}
|
||||
for name, _ := range col.Indexes {
|
||||
index := table.Indexes[name]
|
||||
var uistr string
|
||||
if index.Type == core.UniqueType {
|
||||
uistr = "unique"
|
||||
} else if index.Type == core.IndexType {
|
||||
uistr = "index"
|
||||
}
|
||||
if len(index.Cols) > 1 {
|
||||
uistr += "(" + index.Name + ")"
|
||||
}
|
||||
res = append(res, uistr)
|
||||
}
|
||||
|
||||
nstr := col.SQLType.Name
|
||||
if col.Length != 0 {
|
||||
if col.Length2 != 0 {
|
||||
nstr += fmt.Sprintf("(%v, %v)", col.Length, col.Length2)
|
||||
} else {
|
||||
nstr += fmt.Sprintf("(%v)", col.Length)
|
||||
}
|
||||
}
|
||||
res = append(res, nstr)
|
||||
|
||||
var tags []string
|
||||
if genJson {
|
||||
tags = append(tags, "json:\""+col.Name+"\"")
|
||||
}
|
||||
if len(res) > 0 {
|
||||
tags = append(tags, "xorm:\""+strings.Join(res, " ")+"\"")
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
return "`" + strings.Join(tags, " ") + "`"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
51
xorm/lang.go
51
xorm/lang.go
|
|
@ -1,51 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"text/template"
|
||||
"github.com/lunny/xorm/core"
|
||||
)
|
||||
|
||||
type LangTmpl struct {
|
||||
Funcs template.FuncMap
|
||||
Formater func(string) (string, error)
|
||||
GenImports func([]*core.Table) map[string]string
|
||||
}
|
||||
|
||||
var (
|
||||
mapper = &core.SnakeMapper{}
|
||||
langTmpls = map[string]LangTmpl{
|
||||
"go": GoLangTmpl,
|
||||
"c++": CPlusTmpl,
|
||||
}
|
||||
)
|
||||
|
||||
func loadConfig(f string) map[string]string {
|
||||
bts, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
configs := make(map[string]string)
|
||||
lines := strings.Split(string(bts), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimRight(line, "\r")
|
||||
vs := strings.Split(line, "=")
|
||||
if len(vs) == 2 {
|
||||
configs[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1])
|
||||
}
|
||||
}
|
||||
return configs
|
||||
}
|
||||
|
||||
func unTitle(src string) string {
|
||||
if src == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(src) == 1 {
|
||||
return strings.ToLower(string(src[0]))
|
||||
} else {
|
||||
return strings.ToLower(string(src[0])) + src[1:]
|
||||
}
|
||||
}
|
||||
270
xorm/reverse.go
270
xorm/reverse.go
|
|
@ -1,270 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"text/template"
|
||||
|
||||
_ "github.com/bylevel/pq"
|
||||
"github.com/dvirsky/go-pylog/logging"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/lunny/xorm"
|
||||
"github.com/lunny/xorm/core"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
)
|
||||
|
||||
var CmdReverse = &Command{
|
||||
UsageLine: "reverse [-m] driverName datasourceName tmplPath [generatedPath]",
|
||||
Short: "reverse a db to codes",
|
||||
Long: `
|
||||
according database's tables and columns to generate codes for Go, C++ and etc.
|
||||
|
||||
-m Generated one go file for every table
|
||||
driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres
|
||||
datasourceName Database connection uri, for detail infomation please visit driver's project page
|
||||
tmplPath Template dir for generated. the default templates dir has provide 1 template
|
||||
generatedPath This parameter is optional, if blank, the default value is model, then will
|
||||
generated all codes in model dir
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
CmdReverse.Run = runReverse
|
||||
CmdReverse.Flags = map[string]bool{
|
||||
"-s": false,
|
||||
"-l": false,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
genJson bool = false
|
||||
)
|
||||
|
||||
func printReversePrompt(flag string) {
|
||||
}
|
||||
|
||||
type Tmpl struct {
|
||||
Tables []*core.Table
|
||||
Imports map[string]string
|
||||
Model string
|
||||
}
|
||||
|
||||
func dirExists(dir string) bool {
|
||||
d, e := os.Stat(dir)
|
||||
switch {
|
||||
case e != nil:
|
||||
return false
|
||||
case !d.IsDir():
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func runReverse(cmd *Command, args []string) {
|
||||
num := checkFlags(cmd.Flags, args, printReversePrompt)
|
||||
if num == -1 {
|
||||
return
|
||||
}
|
||||
args = args[num:]
|
||||
|
||||
if len(args) < 3 {
|
||||
fmt.Println("params error, please see xorm help reverse")
|
||||
return
|
||||
}
|
||||
|
||||
var isMultiFile bool = true
|
||||
if use, ok := cmd.Flags["-s"]; ok {
|
||||
isMultiFile = !use
|
||||
}
|
||||
|
||||
curPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Println(curPath)
|
||||
return
|
||||
}
|
||||
|
||||
var genDir string
|
||||
var model string
|
||||
if len(args) == 4 {
|
||||
|
||||
genDir, err = filepath.Abs(args[3])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
model = path.Base(genDir)
|
||||
} else {
|
||||
model = "model"
|
||||
genDir = path.Join(curPath, model)
|
||||
}
|
||||
|
||||
dir, err := filepath.Abs(args[2])
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !dirExists(dir) {
|
||||
logging.Error("Template %v path is not exist", dir)
|
||||
return
|
||||
}
|
||||
|
||||
var langTmpl LangTmpl
|
||||
var ok bool
|
||||
var lang string = "go"
|
||||
|
||||
cfgPath := path.Join(dir, "config")
|
||||
info, err := os.Stat(cfgPath)
|
||||
var configs map[string]string
|
||||
if err == nil && !info.IsDir() {
|
||||
configs = loadConfig(cfgPath)
|
||||
if l, ok := configs["lang"]; ok {
|
||||
lang = l
|
||||
}
|
||||
if j, ok := configs["genJson"]; ok {
|
||||
genJson, err = strconv.ParseBool(j)
|
||||
}
|
||||
}
|
||||
|
||||
if langTmpl, ok = langTmpls[lang]; !ok {
|
||||
fmt.Println("Unsupported programing language", lang)
|
||||
return
|
||||
}
|
||||
|
||||
os.MkdirAll(genDir, os.ModePerm)
|
||||
|
||||
Orm, err := xorm.NewEngine(args[0], args[1])
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
tables, err := Orm.DBMetas()
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
filepath.Walk(dir, func(f string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if info.Name() == "config" {
|
||||
return nil
|
||||
}
|
||||
|
||||
bs, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
t := template.New(f)
|
||||
t.Funcs(langTmpl.Funcs)
|
||||
|
||||
tmpl, err := t.Parse(string(bs))
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var w *os.File
|
||||
fileName := info.Name()
|
||||
newFileName := fileName[:len(fileName)-4]
|
||||
ext := path.Ext(newFileName)
|
||||
|
||||
if !isMultiFile {
|
||||
w, err = os.OpenFile(path.Join(genDir, newFileName), os.O_RDWR|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
imports := langTmpl.GenImports(tables)
|
||||
|
||||
tbls := make([]*core.Table, 0)
|
||||
for _, table := range tables {
|
||||
tbls = append(tbls, table)
|
||||
}
|
||||
|
||||
newbytes := bytes.NewBufferString("")
|
||||
|
||||
t := &Tmpl{Tables: tbls, Imports: imports, Model: model}
|
||||
err = tmpl.Execute(newbytes, t)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
tplcontent, err := ioutil.ReadAll(newbytes)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
var source string
|
||||
if langTmpl.Formater != nil {
|
||||
source, err = langTmpl.Formater(string(tplcontent))
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
source = string(tplcontent)
|
||||
}
|
||||
|
||||
w.WriteString(source)
|
||||
w.Close()
|
||||
} else {
|
||||
for _, table := range tables {
|
||||
// imports
|
||||
tbs := []*core.Table{table}
|
||||
imports := langTmpl.GenImports(tbs)
|
||||
|
||||
w, err := os.OpenFile(path.Join(genDir, unTitle(mapper.Table2Obj(table.Name))+ext), os.O_RDWR|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
newbytes := bytes.NewBufferString("")
|
||||
|
||||
t := &Tmpl{Tables: tbs, Imports: imports, Model: model}
|
||||
err = tmpl.Execute(newbytes, t)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
tplcontent, err := ioutil.ReadAll(newbytes)
|
||||
if err != nil {
|
||||
logging.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
var source string
|
||||
if langTmpl.Formater != nil {
|
||||
source, err = langTmpl.Formater(string(tplcontent))
|
||||
if err != nil {
|
||||
logging.Error("%v-%v", err, string(tplcontent))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
source = string(tplcontent)
|
||||
}
|
||||
|
||||
w.WriteString(source)
|
||||
w.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
147
xorm/shell.go
147
xorm/shell.go
|
|
@ -1,147 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lunny/xorm"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var CmdShell = &Command{
|
||||
UsageLine: "shell driverName datasourceName",
|
||||
Short: "a general shell to operate all kinds of database",
|
||||
Long: `
|
||||
general database's shell for sqlite3, mysql, postgres.
|
||||
|
||||
driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres
|
||||
datasourceName Database connection uri, for detail infomation please visit driver's project page
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
CmdShell.Run = runShell
|
||||
CmdShell.Flags = map[string]bool{}
|
||||
}
|
||||
|
||||
var engine *xorm.Engine
|
||||
|
||||
func shellHelp() {
|
||||
fmt.Println(`
|
||||
show tables show all tables
|
||||
columns <table_name> show table's column info
|
||||
indexes <table_name> show table's index info
|
||||
exit exit shell
|
||||
source <sql_file> exec sql file to current database
|
||||
dump [-nodata] <sql_file> dump structs or records to sql file
|
||||
help show this document
|
||||
<statement> SQL statement
|
||||
`)
|
||||
}
|
||||
|
||||
func runShell(cmd *Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
fmt.Println("params error, please see xorm help shell")
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
engine, err = xorm.NewEngine(args[0], args[1])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = engine.Ping()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
var scmd string
|
||||
fmt.Print("xorm$ ")
|
||||
for {
|
||||
var input string
|
||||
_, err := fmt.Scan(&input)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
if strings.ToLower(input) == "exit" {
|
||||
fmt.Println("bye")
|
||||
return
|
||||
}
|
||||
if !strings.HasSuffix(input, ";") {
|
||||
scmd = scmd + " " + input
|
||||
continue
|
||||
}
|
||||
scmd = scmd + " " + input
|
||||
lcmd := strings.TrimSpace(strings.ToLower(scmd))
|
||||
if strings.HasPrefix(lcmd, "select") {
|
||||
res, err := engine.Query(scmd + "\n")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
if len(res) <= 0 {
|
||||
fmt.Println("no records")
|
||||
} else {
|
||||
columns := make(map[string]int)
|
||||
for k, _ := range res[0] {
|
||||
columns[k] = len(k)
|
||||
}
|
||||
|
||||
for _, m := range res {
|
||||
for k, s := range m {
|
||||
l := len(string(s))
|
||||
if l > columns[k] {
|
||||
columns[k] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var maxlen = 0
|
||||
for _, l := range columns {
|
||||
maxlen = maxlen + l + 3
|
||||
}
|
||||
maxlen = maxlen + 1
|
||||
|
||||
fmt.Println(strings.Repeat("-", maxlen))
|
||||
fmt.Print("|")
|
||||
slice := make([]string, 0)
|
||||
for k, l := range columns {
|
||||
fmt.Print(" " + k + " ")
|
||||
fmt.Print(strings.Repeat(" ", l-len(k)))
|
||||
fmt.Print("|")
|
||||
slice = append(slice, k)
|
||||
}
|
||||
fmt.Print("\n")
|
||||
for _, r := range res {
|
||||
fmt.Print("|")
|
||||
for _, k := range slice {
|
||||
fmt.Print(" " + string(r[k]) + " ")
|
||||
fmt.Print(strings.Repeat(" ", columns[k]-len(string(r[k]))))
|
||||
fmt.Print("|")
|
||||
}
|
||||
fmt.Print("\n")
|
||||
}
|
||||
fmt.Println(strings.Repeat("-", maxlen))
|
||||
//fmt.Println(res)
|
||||
}
|
||||
}
|
||||
} else if lcmd == "show tables;" {
|
||||
/*tables, err := engine.DBMetas()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
|
||||
}*/
|
||||
} else {
|
||||
cnt, err := engine.Exec(scmd)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Printf("%d records changed.\n", cnt)
|
||||
}
|
||||
}
|
||||
scmd = ""
|
||||
fmt.Print("xorm$ ")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{{ range .Imports}}
|
||||
#include {{.}}
|
||||
{{ end }}
|
||||
|
||||
{{range .Tables}}class {{Mapper .Name}} {
|
||||
{{$table := .}}
|
||||
public:
|
||||
{{range .Columns}}{{$name := Mapper .Name}} {{Type .}} Get{{Mapper .Name}}() {
|
||||
return this->m_{{UnTitle $name}};
|
||||
}
|
||||
|
||||
void Set{{$name}}({{Type .}} {{UnTitle $name}}) {
|
||||
this->m_{{UnTitle $name}} = {{UnTitle $name}};
|
||||
}
|
||||
|
||||
{{end}}private:
|
||||
{{range .Columns}}{{$name := Mapper .Name}} {{Type .}} m_{{UnTitle $name}};
|
||||
{{end}}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
|
|
@ -1 +0,0 @@
|
|||
lang=c++
|
||||
|
|
@ -1 +0,0 @@
|
|||
lang=go
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package {{.Model}}
|
||||
|
||||
import (
|
||||
{{range .Imports}}"{{.}}"{{end}}
|
||||
)
|
||||
|
||||
{{range .Tables}}
|
||||
type {{Mapper .Name}} struct {
|
||||
{{$table := .}}
|
||||
{{range .Columns}} {{Mapper .Name}} {{Type .}}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
lang=go
|
||||
genJson=0
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package {{.Model}}
|
||||
|
||||
{{$ilen := len .Imports}}
|
||||
{{if gt $ilen 0}}
|
||||
import (
|
||||
{{range .Imports}}"{{.}}"{{end}}
|
||||
)
|
||||
{{end}}
|
||||
|
||||
{{range .Tables}}
|
||||
type {{Mapper .Name}} struct {
|
||||
{{$table := .}}
|
||||
{{$columns := .Columns}}
|
||||
{{range .ColumnsSeq}}{{$col := getCol $columns .}} {{Mapper $col.Name}} {{Type $col}} {{Tag $table $col}}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
162
xorm/xorm.go
162
xorm/xorm.go
|
|
@ -1,162 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dvirsky/go-pylog/logging"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// +build go1.1
|
||||
|
||||
// Test that go1.1 tag above is included in builds. main.go refers to this definition.
|
||||
const go11tag = true
|
||||
|
||||
const version = "0.1"
|
||||
|
||||
// Commands lists the available commands and help topics.
|
||||
// The order here is the order in which they are printed by 'gopm help'.
|
||||
var commands = []*Command{
|
||||
CmdReverse,
|
||||
CmdShell,
|
||||
}
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
}
|
||||
|
||||
func main() {
|
||||
logging.SetLevel(logging.ALL)
|
||||
// Check length of arguments.
|
||||
args := os.Args[1:]
|
||||
if len(args) < 1 {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
|
||||
// Show help documentation.
|
||||
if args[0] == "help" {
|
||||
help(args[1:])
|
||||
return
|
||||
}
|
||||
|
||||
// Check commands and run.
|
||||
for _, comm := range commands {
|
||||
if comm.Name() == args[0] && comm.Run != nil {
|
||||
comm.Run(comm, args[1:])
|
||||
exit()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "xorm: unknown subcommand %q\nRun 'xorm help' for usage.\n", args[0])
|
||||
setExitStatus(2)
|
||||
exit()
|
||||
}
|
||||
|
||||
var exitStatus = 0
|
||||
var exitMu sync.Mutex
|
||||
|
||||
func setExitStatus(n int) {
|
||||
exitMu.Lock()
|
||||
if exitStatus < n {
|
||||
exitStatus = n
|
||||
}
|
||||
exitMu.Unlock()
|
||||
}
|
||||
|
||||
var usageTemplate = `xorm is a database tool based xorm package.
|
||||
Usage:
|
||||
|
||||
xorm command [arguments]
|
||||
|
||||
The commands are:
|
||||
{{range .}}{{if .Runnable}}
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
||||
|
||||
Use "xorm help [command]" for more information about a command.
|
||||
|
||||
Additional help topics:
|
||||
{{range .}}{{if not .Runnable}}
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
||||
|
||||
Use "xorm help [topic]" for more information about that topic.
|
||||
|
||||
`
|
||||
|
||||
var helpTemplate = `{{if .Runnable}}usage: xorm {{.UsageLine}}
|
||||
|
||||
{{end}}{{.Long | trim}}
|
||||
`
|
||||
|
||||
// tmpl executes the given template text on data, writing the result to w.
|
||||
func tmpl(w io.Writer, text string, data interface{}) {
|
||||
t := template.New("top")
|
||||
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
|
||||
template.Must(t.Parse(text))
|
||||
if err := t.Execute(w, data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func capitalize(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
r, n := utf8.DecodeRuneInString(s)
|
||||
return string(unicode.ToTitle(r)) + s[n:]
|
||||
}
|
||||
|
||||
func printUsage(w io.Writer) {
|
||||
tmpl(w, usageTemplate, commands)
|
||||
}
|
||||
|
||||
func usage() {
|
||||
printUsage(os.Stderr)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// help implements the 'help' command.
|
||||
func help(args []string) {
|
||||
if len(args) == 0 {
|
||||
printUsage(os.Stdout)
|
||||
// not exit 2: succeeded at 'gopm help'.
|
||||
return
|
||||
}
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintf(os.Stderr, "usage: xorm help command\n\nToo many arguments given.\n")
|
||||
os.Exit(2) // failed at 'gopm help'
|
||||
}
|
||||
|
||||
arg := args[0]
|
||||
|
||||
for _, cmd := range commands {
|
||||
if cmd.Name() == arg {
|
||||
tmpl(os.Stdout, helpTemplate, cmd)
|
||||
// not exit 2: succeeded at 'gopm help cmd'.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'xorm help'.\n", arg)
|
||||
os.Exit(2) // failed at 'gopm help cmd'
|
||||
}
|
||||
|
||||
var atexitFuncs []func()
|
||||
|
||||
func atexit(f func()) {
|
||||
atexitFuncs = append(atexitFuncs, f)
|
||||
}
|
||||
|
||||
func exit() {
|
||||
for _, f := range atexitFuncs {
|
||||
f()
|
||||
}
|
||||
os.Exit(exitStatus)
|
||||
}
|
||||
Loading…
Reference in New Issue