Merge pull request #4 from go-xorm/dev

update Dev
This commit is contained in:
S.W.H 2014-04-13 11:11:46 +08:00
commit 7826f88534
81 changed files with 965 additions and 9139 deletions

View File

@ -1,2 +1,2 @@
[target] [target]
path = github.com/lunny/xorm path = github.com/go-xorm/xorm

View File

@ -1,14 +1,17 @@
## Contributing to xorm ## 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, much welcome. You can help with patch review, submitting bug reports,
or adding new functionality. There is no formal style guide, but or adding new functionality. There is no formal style guide, but
please conform to the style of existing code and general Go formatting please conform to the style of existing code and general Go formatting
conventions when submitting patches. 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 ### 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. proposed functionality.
### Bug reports ### 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 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 request with just the failing test case (you'll probably want to
pattern it after the tests in pattern it after the tests in
[base_test.go](https://github.com/lunny/xorm/blob/master/base_test.go) AND [base_test.go](https://github.com/go-xorm/xorm/blob/master/base_test.go) AND
[benchmark_base_test.go](https://github.com/lunny/xorm/blob/master/benchmark_base_test.go). [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. 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 ### New functionality

27
LICENSE Normal file
View File

@ -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.

View File

@ -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. Xorm is a simple and powerful ORM for Go.
[![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Build Status](https://drone.io/github.com/go-xorm/xorm/status.png)](https://drone.io/github.com/go-xorm/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
# Features # Features
@ -18,7 +18,7 @@ Xorm is a simple and powerful ORM for Go.
* Query Cache speed up * 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 * 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 * Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type
* Performance improvement for Get()/Find()/Iterate() * 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 # Installation
If you have [gopm](https://github.com/gpmgo/gopm) installed, If you have [gopm](https://github.com/gpmgo/gopm) installed,
gopm get github.com/lunny/xorm gopm get github.com/go-xorm/xorm
Or Or
go get github.com/lunny/xorm go get github.com/go-xorm/xorm
# Documents # 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 # 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) * [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) * [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) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/) * [Very Hour](http://veryhour.com/)
# Todo * [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
[Todo List](https://trello.com/b/IHsuAnhk/xorm)
# Discuss # Discuss
@ -94,7 +96,7 @@ Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xor
# Contributors # 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) * [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai) * [Nashtsai](https://github.com/nashtsai)

View File

@ -1,10 +1,10 @@
# xorm # 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库. 通过它可以使数据库操作非常简便。 xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
[![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) [![Build Status](https://drone.io/github.com/go-xorm/xorm/status.png)](https://drone.io/github.com/go-xorm/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm)
## 特性 ## 特性
@ -56,40 +56,42 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* 查询函数 Get()/Find()/Iterate() 在性能上的改进 * 查询函数 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](https://github.com/gpmgo/gopm) 进行安装:
gopm get github.com/lunny/xorm gopm get github.com/go-xorm/xorm
或者您也可以使用go工具进行安装 或者您也可以使用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) * [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) * [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) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/) * [Very Hour](http://veryhour.com/)
## Todo * [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
[开发计划](https://trello.com/b/IHsuAnhk/xorm)
## 讨论 ## 讨论
@ -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) * [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai) * [Nashtsai](https://github.com/nashtsai)

View File

@ -7,7 +7,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
type LRUCacher struct { type LRUCacher struct {

View File

@ -5,7 +5,7 @@ import (
"errors" "errors"
"sync" "sync"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
var ( var (

View File

@ -1 +0,0 @@
go test -v -bench=. -run=XXX

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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...)
}

View File

@ -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)
}
}
}

View File

@ -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]
}

View File

@ -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]
}

View File

@ -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
}

View File

@ -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)}
}

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
View File

@ -9,7 +9,7 @@ Installation
Make sure you have installed Go 1.1+ and then: 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 Create Engine
@ -137,6 +137,6 @@ The above 7 methods could use with condition methods.
engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find() engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find()
//SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id //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 package xorm

View File

@ -16,7 +16,7 @@
* **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.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 handlerAdded SetMaxConns(go1.2+) support; some bugs fixed. * **v0.2.2** : Postgres drivers now support lib/pq; Added method Iterate for record by record to handlerAdded 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.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.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.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.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping).

View File

@ -15,10 +15,10 @@
* **v0.2.3** : 改善了文档提供了乐观锁支持添加了带时区时间字段支持Mapper现在分成表名Mapper和字段名Mapper同时实现了表或字段的自定义前缀后缀Insert方法的返回值含义从id, err更改为 affected, err请大家注意添加了UseBool 和 Distinct函数。 * **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.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.1** : 新增数据库反转工具当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/go-xorm/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.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/lunny/xorm/blob/master/docs/QuickStart.md#21)删除废弃的MakeSession和Create函数。 * **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/lunny/xorm/blob/master/docs/QuickStart.md#21)。 * **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.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现可以选择不使用连接池使用系统连接池和使用自带连接池三种实现默认为SysConnectPool即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法在系统退出时应调用此方法。
* **v0.1.6** : 新增Conversion支持自定义类型到数据库类型的转换新增查询结构体自动检测匿名成员支持新增单向映射支持 * **v0.1.6** : 新增Conversion支持自定义类型到数据库类型的转换新增查询结构体自动检测匿名成员支持新增单向映射支持
* **v0.1.5** : 新增对多线程的支持新增Sql()函数支持任意sql语句的struct查询Get函数返回值变动MakeSession和Create函数被NewSession和NewEngine函数替代 * **v0.1.5** : 新增对多线程的支持新增Sql()函数支持任意sql语句的struct查询Get函数返回值变动MakeSession和Create函数被NewSession和NewEngine函数替代

View File

@ -4,7 +4,7 @@ xorm 快速入门
* [1.创建Orm引擎](#10) * [1.创建Orm引擎](#10)
* [2.定义表结构体](#20) * [2.定义表结构体](#20)
* [2.1.名称映射规则](#21) * [2.1.名称映射规则](#21)
* [2.2.前缀映射规则和后缀映射规则](#22) * [2.2.前缀映射,后缀映射和缓存映射](#22)
* [2.3.使用Table和Tag改变名称映射](#23) * [2.3.使用Table和Tag改变名称映射](#23)
* [2.4.Column属性定义](#24) * [2.4.Column属性定义](#24)
* [2.5.Go与字段类型对应表](#25) * [2.5.Go与字段类型对应表](#25)
@ -29,12 +29,13 @@ xorm 快速入门
* [9.执行SQL命令](#100) * [9.执行SQL命令](#100)
* [10.事务处理](#110) * [10.事务处理](#110)
* [11.缓存](#120) * [11.缓存](#120)
* [12.xorm工具](#130) * [12.事件](#125)
* [12.1.反转命令](#131) * [13.xorm工具](#130)
* [13.Examples](#140) * [13.1.反转命令](#131)
* [14.案例](#150) * [14.Examples](#140)
* [15.那些年我们踩过的坑](#160) * [15.案例](#150)
* [16.讨论](#170) * [16.那些年我们踩过的坑](#160)
* [17.讨论](#170)
<a name="10" id="10"></a> <a name="10" id="10"></a>
## 1.创建Orm引擎 ## 1.创建Orm引擎
@ -44,7 +45,7 @@ xorm 快速入门
```Go ```Go
import ( import (
_ "github.com/go-sql-driver/mysql" _ "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") engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
defer engine.Close() defer engine.Close()
@ -55,7 +56,7 @@ or
```Go ```Go
import ( import (
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
) )
engine, err = xorm.NewEngine("sqlite3", "./test.db") engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close() defer engine.Close()
@ -64,8 +65,11 @@ defer engine.Close()
一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 一般如果只针对一个数据库进行操作只需要创建一个Engine即可。Engine支持在多GoRutine下使用。
xorm当前支持五种驱动四个数据库如下 xorm当前支持五种驱动四个数据库如下
<<<<<<< HEAD
=======
* 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)
>>>>>>> master
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
@ -120,27 +124,39 @@ engine.SetMapper(SameMapper{})
同时需要注意的是: 同时需要注意的是:
<<<<<<< HEAD
* 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。 * 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: * 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
=======
* 如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如:
>>>>>>> master
```Go ```Go
engine.SetTableMapper(SameMapper{}) engine.SetTableMapper(SameMapper{})
engine.SetColumnMapper(SnakeMapper{}) engine.SetColumnMapper(SnakeMapper{})
``` ```
<a name="22" id="22"></a> <a name="22" id="22"></a>
### 2.2.前缀映射规则和后缀映射规则 ### 2.2.前缀映射,后缀映射和缓存映射
* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 * 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 * 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。
<<<<<<< HEAD
* *
=======
* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。
>>>>>>> master
<a name="23" id="23"></a> <a name="23" id="23"></a>
### 2.3.使用Table和Tag改变名称映射 ### 2.3.使用Table和Tag改变名称映射
如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。 如果所有的命名都是按照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> <a name="23" id="23"></a>
### 2.4.Column属性定义 ### 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中的关键字均不区分大小写字段名区分大小写 具体的映射规则如下另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> <td>pk</td><td>是否是Primary Key如果在一个struct中有多个字段都使用了此标记则这多个字段构成了复合主键单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型复合主键支持这7种Go的数据类型的组合。</td>
</tr> </tr>
<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>
<tr> <tr>
<td>autoincr</td><td>是否是自增</td> <td>autoincr</td><td>是否是自增</td>
@ -211,7 +227,7 @@ type User struct {
- 2.string类型默认映射为varchar(255)如果需要不同的定义可以在tag中自定义 - 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接口的类型或者结构体将根据接口的转换方式在类型和数据库记录之间进行相互转换。 - 4.实现了Conversion接口的类型或者结构体将根据接口的转换方式在类型和数据库记录之间进行相互转换。
```Go ```Go
@ -226,7 +242,7 @@ type Conversion interface {
如果不使用tag来定义field对应的数据库字段类型那么系统会自动给出一个默认的字段类型对应表如下 如果不使用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> <a name="30" id="30"></a>
## 3.表结构操作 ## 3.表结构操作
@ -407,7 +423,11 @@ engine.Cols("age", "name").Update(&user)
// UPDATE user SET age=? AND name=? // UPDATE user SET age=? AND name=?
``` ```
其中的参数"age", "name"也可以写成"age, name",两种写法均可 * AllCols()
查询或更新所有字段。
* MustCols(…string)
某些字段必须更新。
* Omit(...string) * Omit(...string)
和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用 和cols相反此函数指定排除某些指定的字段。注意此方法和Cols方法不可同时使用
@ -578,13 +598,21 @@ affected, err := engine.Id(id).Update(user)
这里需要注意Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容因此如果需要更新一个值为0则此种方法将无法实现因此有两种选择 这里需要注意Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容因此如果需要更新一个值为0则此种方法将无法实现因此有两种选择
<<<<<<< HEAD
1. 通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。 1. 通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。
=======
* 1.通过添加Cols函数指定需要更新结构体中的哪些值未指定的将不更新指定了的即使为0也会更新。
>>>>>>> master
```Go ```Go
affected, err := engine.Id(id).Cols("age").Update(&user) affected, err := engine.Id(id).Cols("age").Update(&user)
``` ```
<<<<<<< HEAD
2. 通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。 2. 通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。
=======
* 2.通过传入map[string]interface{}来进行更新但这时需要额外指定更新到哪个表因为通过map是无法自动检测更新哪个表的。
>>>>>>> master
```Go ```Go
affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0})
@ -681,6 +709,8 @@ if err != nil {
} }
``` ```
* 注意如果您使用的是mysql数据库引擎为innodb事务才有效myisam引擎是不支持事务的。
<a name="120" id="120"></a> <a name="120" id="120"></a>
## 11.缓存 ## 11.缓存
@ -725,22 +755,48 @@ engine.ClearCache(new(User))
缓存的实现原理如下图所示: 缓存的实现原理如下图所示:
![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png) ![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png)
<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> <a name="130" id="130"></a>
## 12.xorm工具 ## 13.xorm工具
xorm工具提供了xorm命令能够帮助做很多事情。 xorm工具提供了xorm命令能够帮助做很多事情。
### 12.1.反转命令 ### 13.1.反转命令
参见 [xorm工具](https://github.com/lunny/xorm/tree/master/xorm) 参见 [xorm工具](https://github.com/go-xorm/xorm/tree/master/xorm)
<a name="140" id="140"></a> <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> <a name="150" id="150"></a>
## 14.案例 ## 15.案例
* [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) * [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
@ -751,7 +807,7 @@ xorm工具提供了xorm命令能够帮助做很多事情。
* [VeryHour](http://veryhour.com) * [VeryHour](http://veryhour.com)
<a name="160" id="160"></a> <a name="160" id="160"></a>
## 15.那些年我们踩过的坑 ## 16.那些年我们踩过的坑
* 怎么同时使用xorm的tag和json的tag * 怎么同时使用xorm的tag和json的tag
答:使用空格 答:使用空格
@ -795,5 +851,5 @@ money float64 `xorm:"Numeric"`
<a name="170" id="170"></a> <a name="170" id="170"></a>
## 16.讨论 ## 17.讨论
请加入QQ群280360085 进行讨论。 请加入QQ群280360085 进行讨论。

View File

@ -41,7 +41,7 @@ When using xorm, you can create multiple orm engines, an engine means a databse.
```Go ```Go
import ( import (
_ "github.com/go-sql-driver/mysql" _ "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") engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
defer engine.Close() defer engine.Close()
@ -52,7 +52,7 @@ or
```Go ```Go
import ( import (
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
) )
engine, err = xorm.NewEngine("sqlite3", "./test.db") engine, err = xorm.NewEngine("sqlite3", "./test.db")
defer engine.Close() defer engine.Close()
@ -100,30 +100,47 @@ engine.Logger = f
<a name="20" id="20"></a> <a name="20" id="20"></a>
## 2.Define struct ## 2.Define struct
xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下 xorm map a struct to a database table, the rule is below.
<a name="21" id="21"></a> <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 ```Go
engine.Mapper = SameMapper{} 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。 当然如果你使用了别的命名规则映射方案也可以自己实现一个IMapper。
<a name="22" id="22"></a> <a name="22" id="22"></a>
### 2.2.使用Table和Tag改变名称映射 ### 2.3.使用Table和Tag改变名称映射
如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。 如果所有的命名都是按照IMapper的映射来操作的那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时我们就需要别的机制来改变。
通过`engine.Table()`方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。 通过`engine.Table()`方法可以改变struct对应的数据库表的名称通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况单引号也可以不使用。
<a name="23" id="23"></a> <a name="23" id="23"></a>
### 2.3.Column属性定义 ### 2.4.Column属性定义
我们在field对应的Tag中对Column的一些属性进行定义定义的方法基本和我们写SQL定义表结构类似比如 我们在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中的关键字均不区分大小写字段名区分大小写 具体的映射规则如下另Tag中的关键字均不区分大小写字段名区分大小写
@ -145,7 +162,7 @@ type User struct {
<td>pk</td><td>是否是Primary Key当前仅支持int64类型</td> <td>pk</td><td>是否是Primary Key当前仅支持int64类型</td>
</tr> </tr>
<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>
<tr> <tr>
<td>autoincr</td><td>是否是自增</td> <td>autoincr</td><td>是否是自增</td>
@ -637,19 +654,19 @@ engine.ClearCache(new(User))
Cache implement theory below: Cache implement theory below:
![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png) ![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png)
<a name="130" id="130"></a> <a name="130" id="130"></a>
## 12.xorm tool ## 12.xorm tool
xorm工具提供了xorm命令能够帮助做很多事情。 xorm工具提供了xorm命令能够帮助做很多事情。
### 12.1.Reverse command ### 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> <a name="140" id="140"></a>
## 13.Examples ## 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> <a name="150" id="150"></a>
## 14.Cases ## 14.Cases

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"regexp" "regexp"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -5,7 +5,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -4,7 +4,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"regexp" "regexp"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -4,7 +4,7 @@ import (
"errors" "errors"
"strings" "strings"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

View File

@ -1,7 +1,7 @@
package drivers package drivers
import ( import (
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { func init() {

230
engine.go
View File

@ -6,14 +6,13 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
// Engine is the major struct of xorm, it means a database manager. // Engine is the major struct of xorm, it means a database manager.
@ -34,9 +33,8 @@ type Engine struct {
ShowWarn bool ShowWarn bool
Pool IConnectPool Pool IConnectPool
Filters []core.Filter Filters []core.Filter
Logger io.Writer Logger ILogger // io.Writer
Cacher core.Cacher Cacher core.Cacher
tableCachers map[reflect.Type]core.Cacher
} }
func (engine *Engine) SetMapper(mapper core.IMapper) { func (engine *Engine) SetMapper(mapper core.IMapper) {
@ -118,9 +116,9 @@ func (engine *Engine) NoCascade() *Session {
// Set a table use a special cacher // Set a table use a special cacher
func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
t := rType(bean) v := rValue(bean)
engine.autoMapType(t) engine.autoMapType(v)
engine.tableCachers[t] = cacher engine.Tables[v.Type()].Cacher = cacher
} }
// OpenDB provides a interface to operate database directly. // OpenDB provides a interface to operate database directly.
@ -144,35 +142,44 @@ func (engine *Engine) Close() error {
func (engine *Engine) Ping() error { func (engine *Engine) Ping() error {
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
engine.LogSQL("PING DATABASE", engine.DriverName) engine.LogInfo("PING DATABASE", engine.DriverName)
return session.Ping() return session.Ping()
} }
// logging sql // logging sql
func (engine *Engine) LogSQL(contents ...interface{}) { func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
if engine.ShowSQL { 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 // logging error
func (engine *Engine) LogError(contents ...interface{}) { func (engine *Engine) LogError(contents ...interface{}) {
if engine.ShowErr { 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 // logging debug
func (engine *Engine) LogDebug(contents ...interface{}) { func (engine *Engine) LogDebug(contents ...interface{}) {
if engine.ShowDebug { if engine.ShowDebug {
io.WriteString(engine.Logger, fmt.Sprintln(contents...)) engine.Logger.Debug(fmt.Sprintln(contents...))
} }
} }
// logging warn // logging warn
func (engine *Engine) LogWarn(contents ...interface{}) { func (engine *Engine) LogWarn(contents ...interface{}) {
if engine.ShowWarn { 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...) 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 // Xorm automatically retrieve condition according struct, but
// if struct has bool field, it will ignore them. So use UseBool // if struct has bool field, it will ignore them. So use UseBool
// to tell system to do not ignore them. // to tell system to do not ignore them.
@ -387,12 +406,13 @@ func (engine *Engine) Having(conditions string) *Session {
return session.Having(conditions) 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() engine.mutex.RLock()
table, ok := engine.Tables[t] table, ok := engine.Tables[t]
engine.mutex.RUnlock() engine.mutex.RUnlock()
if !ok { if !ok {
table = engine.mapType(t) table = engine.mapType(v)
engine.mutex.Lock() engine.mutex.Lock()
engine.Tables[t] = table engine.Tables[t] = table
engine.mutex.Unlock() engine.mutex.Unlock()
@ -401,26 +421,66 @@ func (engine *Engine) autoMapType(t reflect.Type) *core.Table {
} }
func (engine *Engine) autoMap(bean interface{}) *core.Table { func (engine *Engine) autoMap(bean interface{}) *core.Table {
t := rType(bean) v := rValue(bean)
return engine.autoMapType(t) 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) 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 { func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table {
table := core.NewEmptyTable() table := core.NewEmptyTable()
table.Name = tableMapper.Obj2Table(t.Name()) 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 table.Type = t
var idFieldColName string var idFieldColName string
var err error
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
tag := t.Field(i).Tag tag := t.Field(i).Tag
ormTagStr := tag.Get(tagId) ormTagStr := tag.Get(engine.TagIdentifier)
var col *core.Column var col *core.Column
fieldType := t.Field(i).Type fieldValue := v.Field(i)
fieldType := fieldValue.Type()
if ormTagStr != "" { if ormTagStr != "" {
col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, 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") && if (strings.ToUpper(tags[0]) == "EXTENDS") &&
(fieldType.Kind() == reflect.Struct) { (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() { for _, col := range parentTable.Columns() {
col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName) col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName)
table.AddColumn(col) table.AddColumn(col)
@ -442,8 +504,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
continue continue
} }
var indexType int indexNames := make(map[string]int)
var indexName string var isIndex, isUnique bool
var preKey string
for j, key := range tags { for j, key := range tags {
k := strings.ToUpper(key) k := strings.ToUpper(key)
switch { switch {
@ -456,8 +519,18 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
col.Nullable = false col.Nullable = false
case k == "NULL": case k == "NULL":
col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT") 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": case k == "AUTOINCR":
col.IsAutoIncrement = true col.IsAutoIncrement = true
//col.AutoIncrStart = 1
case k == "DEFAULT": case k == "DEFAULT":
col.Default = tags[j+1] col.Default = tags[j+1]
case k == "CREATED": case k == "CREATED":
@ -468,35 +541,46 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
case k == "UPDATED": case k == "UPDATED":
col.IsUpdated = true col.IsUpdated = true
case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"): 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": case k == "INDEX":
indexType = core.IndexType isIndex = true
case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"): case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
indexName = k[len("UNIQUE")+1 : len(k)-1] indexName := k[len("UNIQUE")+1 : len(k)-1]
indexType = core.UniqueType indexNames[indexName] = core.UniqueType
case k == "UNIQUE": case k == "UNIQUE":
indexType = core.UniqueType isUnique = true
case k == "NOTNULL": case k == "NOTNULL":
col.Nullable = false col.Nullable = false
case k == "NOT": case k == "NOT":
default: default:
if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") { if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
if key != col.Default { if preKey != "DEFAULT" {
col.Name = key[1 : len(key)-1] col.Name = key[1 : len(key)-1]
} }
} else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") { } else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
fs := strings.Split(k, "(") fs := strings.Split(k, "(")
if _, ok := core.SqlTypes[fs[0]]; !ok { if _, ok := core.SqlTypes[fs[0]]; !ok {
preKey = k
continue continue
} }
col.SQLType = core.SQLType{fs[0], 0, 0} col.SQLType = core.SQLType{fs[0], 0, 0}
fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",") fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
if len(fs2) == 2 { if len(fs2) == 2 {
col.Length, _ = strconv.Atoi(fs2[0]) col.Length, err = strconv.Atoi(fs2[0])
col.Length2, _ = strconv.Atoi(fs2[1]) if err != nil {
engine.LogError(err)
}
col.Length2, err = strconv.Atoi(fs2[1])
if err != nil {
engine.LogError(err)
}
} else if len(fs2) == 1 { } 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 { } else {
if _, ok := core.SqlTypes[k]; ok { if _, ok := core.SqlTypes[k]; ok {
@ -505,8 +589,9 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
col.Name = key col.Name = key
} }
} }
dialect.SqlType(col) engine.dialect.SqlType(col)
} }
preKey = k
} }
if col.SQLType.Name == "" { if col.SQLType.Name == "" {
col.SQLType = core.Type2SQLType(fieldType) col.SQLType = core.Type2SQLType(fieldType)
@ -519,40 +604,24 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp
} }
//fmt.Println("======", col) //fmt.Println("======", col)
if col.Name == "" { 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 == "" { if isUnique {
indexName = col.Name indexNames[col.Name] = core.UniqueType
} } else if isIndex {
if index, ok := table.Indexes[indexName]; ok { indexNames[col.Name] = core.IndexType
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
} }
for indexName, indexType := range indexNames {
addIndex(indexName, table, col, indexType)
} }
} }
} else { } else {
sqlType := core.Type2SQLType(fieldType) sqlType := core.Type2SQLType(fieldType)
col = core.NewColumn(colMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
sqlType.DefaultLength, sqlType.DefaultLength2, true) t.Field(i).Name, sqlType, sqlType.DefaultLength,
sqlType.DefaultLength2, true)
} }
if col.IsAutoIncrement { if col.IsAutoIncrement {
col.Nullable = false col.Nullable = false
@ -582,19 +651,20 @@ func (engine *Engine) mapping(beans ...interface{}) (e error) {
engine.mutex.Lock() engine.mutex.Lock()
defer engine.mutex.Unlock() defer engine.mutex.Unlock()
for _, bean := range beans { for _, bean := range beans {
t := rType(bean) v := rValue(bean)
engine.Tables[t] = engine.mapType(t) engine.Tables[v.Type()] = engine.mapType(v)
} }
return return
} }
// If a table has any reocrd // If a table has any reocrd
func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) { func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
t := rType(bean) v := rValue(bean)
t := v.Type()
if t.Kind() != reflect.Struct { if t.Kind() != reflect.Struct {
return false, errors.New("bean should be a struct or struct's point") return false, errors.New("bean should be a struct or struct's point")
} }
engine.autoMapType(t) engine.autoMapType(v)
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
rows, err := session.Count(bean) rows, err := session.Count(bean)
@ -603,11 +673,11 @@ func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
// If a table is exist // If a table is exist
func (engine *Engine) IsTableExist(bean interface{}) (bool, error) { func (engine *Engine) IsTableExist(bean interface{}) (bool, error) {
t := rType(bean) v := rValue(bean)
if t.Kind() != reflect.Struct { if v.Type().Kind() != reflect.Struct {
return false, errors.New("bean should be a struct or struct's point") return false, errors.New("bean should be a struct or struct's point")
} }
table := engine.autoMapType(t) table := engine.autoMapType(v)
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
has, err := session.isTableExist(table.Name) has, err := session.isTableExist(table.Name)
@ -646,9 +716,13 @@ func (engine *Engine) CreateUniques(bean interface{}) error {
return session.CreateUniques(bean) return session.CreateUniques(bean)
} }
func (engine *Engine) getCacher(t reflect.Type) core.Cacher { func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
if cacher, ok := engine.tableCachers[t]; ok { return table.Cacher
return cacher }
func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
if table := engine.autoMapType(v); table != nil {
return table.Cacher
} }
return engine.Cacher return engine.Cacher
} }
@ -660,7 +734,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
return errors.New("error params") return errors.New("error params")
} }
table := engine.autoMap(bean) table := engine.autoMap(bean)
cacher := engine.getCacher(t) cacher := table.Cacher
if cacher == nil {
cacher = engine.Cacher
}
if cacher != nil { if cacher != nil {
cacher.ClearIds(table.Name) cacher.ClearIds(table.Name)
cacher.DelBean(table.Name, id) cacher.DelBean(table.Name, id)
@ -676,7 +753,10 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
return errors.New("error params") return errors.New("error params")
} }
table := engine.autoMap(bean) table := engine.autoMap(bean)
cacher := engine.getCacher(t) cacher := table.Cacher
if cacher == nil {
cacher = engine.Cacher
}
if cacher != nil { if cacher != nil {
cacher.ClearIds(table.Name) cacher.ClearIds(table.Name)
cacher.ClearBeans(table.Name) cacher.ClearBeans(table.Name)

View File

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
"github.com/lunny/xorm/caches" "github.com/go-xorm/xorm/caches"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -3,7 +3,7 @@ package main
import ( import (
"fmt" "fmt"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"os" "os"
) )

View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -6,7 +6,7 @@ import (
"runtime" "runtime"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -6,7 +6,7 @@ import (
"runtime" "runtime"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
_ "github.com/bylevel/pq" _ "github.com/bylevel/pq"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/lunny/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func indexNoCase(s, sep string) int { func indexNoCase(s, sep string) int {
@ -38,9 +38,14 @@ func makeArray(elem string, count int) []string {
return res return res
} }
func rValue(bean interface{}) reflect.Value {
return reflect.Indirect(reflect.ValueOf(bean))
}
func rType(bean interface{}) reflect.Type { func rType(bean interface{}) reflect.Type {
sliceValue := reflect.Indirect(reflect.ValueOf(bean)) 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 { func structName(v reflect.Type) string {

48
logger.go Normal file
View File

@ -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
}

View File

@ -1,67 +1,63 @@
package dialects package xorm
import ( import (
//"crypto/tls"
"errors" "errors"
"fmt" "fmt"
//"regexp"
"strconv" "strconv"
"strings" "strings"
//"time"
. "github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { // func init() {
RegisterDialect("mssql", &mssql{}) // RegisterDialect("mssql", &mssql{})
} // }
type mssql struct { 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) 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 var res string
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case Bool: case core.Bool:
res = TinyInt res = core.TinyInt
case Serial: case core.Serial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.IsPrimaryKey = true c.IsPrimaryKey = true
c.Nullable = false c.Nullable = false
res = Int res = core.Int
case BigSerial: case core.BigSerial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.IsPrimaryKey = true c.IsPrimaryKey = true
c.Nullable = false c.Nullable = false
res = BigInt res = core.BigInt
case Bytea, Blob, Binary, TinyBlob, MediumBlob, LongBlob: case core.Bytea, core.Blob, core.Binary, core.TinyBlob, core.MediumBlob, core.LongBlob:
res = VarBinary res = core.VarBinary
if c.Length == 0 { if c.Length == 0 {
c.Length = 50 c.Length = 50
} }
case TimeStamp: case core.TimeStamp:
res = DateTime res = core.DateTime
case TimeStampz: case core.TimeStampz:
res = "DATETIMEOFFSET" res = "DATETIMEOFFSET"
c.Length = 7 c.Length = 7
case MediumInt: case core.MediumInt:
res = Int res = core.Int
case MediumText, TinyText, LongText: case core.MediumText, core.TinyText, core.LongText:
res = Text res = core.Text
case Double: case core.Double:
res = Real res = core.Real
default: default:
res = t res = t
} }
if res == Int { if res == core.Int {
return Int return core.Int
} }
var hasLen1 bool = (c.Length > 0) var hasLen1 bool = (c.Length > 0)
@ -90,6 +86,12 @@ func (db *mssql) AutoIncrStr() string {
return "IDENTITY" 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 { func (db *mssql) SupportCharset() bool {
return false return false
} }
@ -116,12 +118,12 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
return sql, args 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{}{} args := []interface{}{}
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale 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 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 + `')` 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -131,7 +133,7 @@ where a.object_id=object_id('` + tableName + `')`
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cols := make(map[string]*Column) cols := make(map[string]*core.Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for rows.Next() { for rows.Next() {
var name, ctype, precision, scale string var name, ctype, precision, scale string
@ -141,7 +143,7 @@ where a.object_id=object_id('` + tableName + `')`
return nil, nil, err return nil, nil, err
} }
col := new(Column) col := new(core.Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
col.Length = maxLen col.Length = maxLen
col.Name = strings.Trim(name, "` ") col.Name = strings.Trim(name, "` ")
@ -149,14 +151,14 @@ where a.object_id=object_id('` + tableName + `')`
ct := strings.ToUpper(ctype) ct := strings.ToUpper(ctype)
switch ct { switch ct {
case "DATETIMEOFFSET": case "DATETIMEOFFSET":
col.SQLType = SQLType{TimeStampz, 0, 0} col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
case "NVARCHAR": case "NVARCHAR":
col.SQLType = SQLType{Varchar, 0, 0} col.SQLType = core.SQLType{core.Varchar, 0, 0}
case "IMAGE": case "IMAGE":
col.SQLType = SQLType{VarBinary, 0, 0} col.SQLType = core.SQLType{core.VarBinary, 0, 0}
default: default:
if _, ok := SqlTypes[ct]; ok { if _, ok := core.SqlTypes[ct]; ok {
col.SQLType = SQLType{ct, 0, 0} col.SQLType = core.SQLType{ct, 0, 0}
} else { } else {
return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v", return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v",
ct, tableName, col.Name)) ct, tableName, col.Name))
@ -166,6 +168,10 @@ where a.object_id=object_id('` + tableName + `')`
if col.SQLType.IsText() { if col.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col
@ -174,10 +180,10 @@ where a.object_id=object_id('` + tableName + `')`
return colSeq, cols, nil return colSeq, cols, nil
} }
func (db *mssql) GetTables() ([]*Table, error) { func (db *mssql) GetTables() ([]*core.Table, error) {
args := []interface{}{} args := []interface{}{}
s := `select name from sysobjects where xtype ='U'` 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 { if err != nil {
return nil, err return nil, err
} }
@ -187,9 +193,9 @@ func (db *mssql) GetTables() ([]*Table, error) {
return nil, err return nil, err
} }
tables := make([]*Table, 0) tables := make([]*core.Table, 0)
for rows.Next() { for rows.Next() {
table := NewEmptyTable() table := core.NewEmptyTable()
var name string var name string
err = rows.Scan(&name) err = rows.Scan(&name)
if err != nil { if err != nil {
@ -201,7 +207,7 @@ func (db *mssql) GetTables() ([]*Table, error) {
return tables, nil 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} args := []interface{}{tableName}
s := `SELECT s := `SELECT
IXS.NAME AS [INDEX_NAME], IXS.NAME AS [INDEX_NAME],
@ -217,7 +223,7 @@ INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
AND IXCS.COLUMN_ID=C.COLUMN_ID AND IXCS.COLUMN_ID=C.COLUMN_ID
WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_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 { if err != nil {
return nil, err return nil, err
} }
@ -227,7 +233,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
return nil, err return nil, err
} }
indexes := make(map[string]*Index, 0) indexes := make(map[string]*core.Index, 0)
for rows.Next() { for rows.Next() {
var indexType int var indexType int
var indexName, colName, isUnique string var indexName, colName, isUnique string
@ -243,9 +249,9 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
} }
if i { if i {
indexType = UniqueType indexType = core.UniqueType
} else { } else {
indexType = IndexType indexType = core.IndexType
} }
colName = strings.Trim(colName, "` ") 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)] indexName = indexName[5+len(tableName) : len(indexName)]
} }
var index *Index var index *core.Index
var ok bool var ok bool
if index, ok = indexes[indexName]; !ok { if index, ok = indexes[indexName]; !ok {
index = new(Index) index = new(core.Index)
index.Type = indexType index.Type = indexType
index.Name = indexName index.Name = indexName
indexes[indexName] = index indexes[indexName] = index
@ -267,7 +273,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
return indexes, nil 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 var sql string
if tableName == "" { if tableName == "" {
tableName = table.Name tableName = table.Name
@ -301,6 +307,6 @@ func (db *mssql) CreateTablSql(table *Table, tableName, storeEngine, charset str
return sql return sql
} }
func (db *mssql) Filters() []Filter { func (db *mssql) Filters() []core.Filter {
return []Filter{&IdFilter{}, &QuoteFilter{}} return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
} }

View File

@ -1,4 +1,4 @@
package dialects package xorm
import ( import (
"crypto/tls" "crypto/tls"
@ -8,15 +8,15 @@ import (
"strings" "strings"
"time" "time"
. "github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { // func init() {
RegisterDialect("mysql", &mysql{}) // RegisterDialect("mysql", &mysql{})
} // }
type mysql struct { type mysql struct {
Base core.Base
net string net string
addr string addr string
params map[string]string params map[string]string
@ -28,29 +28,30 @@ type mysql struct {
clientFoundRows bool 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) 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 var res string
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case Bool: case core.Bool:
res = TinyInt res = core.TinyInt
case Serial: c.Length = 1
case core.Serial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.IsPrimaryKey = true c.IsPrimaryKey = true
c.Nullable = false c.Nullable = false
res = Int res = core.Int
case BigSerial: case core.BigSerial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.IsPrimaryKey = true c.IsPrimaryKey = true
c.Nullable = false c.Nullable = false
res = BigInt res = core.BigInt
case Bytea: case core.Bytea:
res = Blob res = core.Blob
case TimeStampz: case core.TimeStampz:
res = Char res = core.Char
c.Length = 64 c.Length = 64
default: default:
res = t res = t
@ -109,23 +110,26 @@ func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
return sql, args 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} args := []interface{}{db.DbName, tableName}
s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," + s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
" `COLUMN_KEY`, `EXTRA` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" " `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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
defer cnn.Close() defer cnn.Close()
rows, err := cnn.Query(s, args...) rows, err := cnn.Query(s, args...)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cols := make(map[string]*Column) defer rows.Close()
cols := make(map[string]*core.Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for rows.Next() { for rows.Next() {
col := new(Column) col := new(core.Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
var columnName, isNullable, colType, colKey, extra string 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) colType = strings.ToUpper(colName)
col.Length = len1 col.Length = len1
col.Length2 = len2 col.Length2 = len2
if _, ok := SqlTypes[colType]; ok { if _, ok := core.SqlTypes[colType]; ok {
col.SQLType = SQLType{colType, len1, len2} col.SQLType = core.SQLType{colType, len1, len2}
} else { } else {
return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType)) 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 return colSeq, cols, nil
} }
func (db *mysql) GetTables() ([]*Table, error) { func (db *mysql) GetTables() ([]*core.Table, error) {
args := []interface{}{db.DbName} args := []interface{}{db.DbName}
s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=?" 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 { if err != nil {
return nil, err return nil, err
} }
@ -204,9 +208,9 @@ func (db *mysql) GetTables() ([]*Table, error) {
return nil, err return nil, err
} }
tables := make([]*Table, 0) tables := make([]*core.Table, 0)
for rows.Next() { for rows.Next() {
table := NewEmptyTable() table := core.NewEmptyTable()
var name, engine, tableRows string var name, engine, tableRows string
var autoIncr *string var autoIncr *string
err = rows.Scan(&name, &engine, &tableRows, &autoIncr) err = rows.Scan(&name, &engine, &tableRows, &autoIncr)
@ -220,10 +224,10 @@ func (db *mysql) GetTables() ([]*Table, error) {
return tables, nil 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} args := []interface{}{db.DbName, tableName}
s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" 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 { if err != nil {
return nil, err return nil, err
} }
@ -233,7 +237,7 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
return nil, err return nil, err
} }
indexes := make(map[string]*Index, 0) indexes := make(map[string]*core.Index, 0)
for rows.Next() { for rows.Next() {
var indexType int var indexType int
var indexName, colName, nonUnique string var indexName, colName, nonUnique string
@ -247,9 +251,9 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
} }
if "YES" == nonUnique || nonUnique == "1" { if "YES" == nonUnique || nonUnique == "1" {
indexType = IndexType indexType = core.IndexType
} else { } else {
indexType = UniqueType indexType = core.UniqueType
} }
colName = strings.Trim(colName, "` ") 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)] indexName = indexName[5+len(tableName) : len(indexName)]
} }
var index *Index var index *core.Index
var ok bool var ok bool
if index, ok = indexes[indexName]; !ok { if index, ok = indexes[indexName]; !ok {
index = new(Index) index = new(core.Index)
index.Type = indexType index.Type = indexType
index.Name = indexName index.Name = indexName
indexes[indexName] = index indexes[indexName] = index
@ -271,6 +275,6 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) {
return indexes, nil return indexes, nil
} }
func (db *mysql) Filters() []Filter { func (db *mysql) Filters() []core.Filter {
return []Filter{&IdFilter{}} return []core.Filter{&core.IdFilter{}}
} }

View File

@ -1,4 +1,4 @@
package dialects package xorm
import ( import (
"errors" "errors"
@ -6,37 +6,37 @@ import (
"strconv" "strconv"
"strings" "strings"
. "github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { // func init() {
RegisterDialect("oracle", &oracle{}) // RegisterDialect("oracle", &oracle{})
} // }
type oracle struct { 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) 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 var res string
switch t := c.SQLType.Name; t { 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" return "NUMBER"
case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea:
return Blob return core.Blob
case Time, DateTime, TimeStamp: case core.Time, core.DateTime, core.TimeStamp:
res = TimeStamp res = core.TimeStamp
case TimeStampz: case core.TimeStampz:
res = "TIMESTAMP WITH TIME ZONE" res = "TIMESTAMP WITH TIME ZONE"
case Float, Double, Numeric, Decimal: case core.Float, core.Double, core.Numeric, core.Decimal:
res = "NUMBER" res = "NUMBER"
case Text, MediumText, LongText: case core.Text, core.MediumText, core.LongText:
res = "CLOB" res = "CLOB"
case Char, Varchar, TinyText: case core.Char, core.Varchar, core.TinyText:
return "VARCHAR2" return "VARCHAR2"
default: default:
res = t res = t
@ -93,12 +93,12 @@ func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface
" AND column_name = ?", args " 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)} args := []interface{}{strings.ToUpper(tableName)}
s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," +
"nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" "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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -109,10 +109,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
} }
defer rows.Close() defer rows.Close()
cols := make(map[string]*Column) cols := make(map[string]*core.Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for rows.Next() { for rows.Next() {
col := new(Column) col := new(core.Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
var colName, colDefault, nullable, dataType, dataPrecision, dataScale string 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 { switch dataType {
case "VARCHAR2": case "VARCHAR2":
col.SQLType = SQLType{Varchar, 0, 0} col.SQLType = core.SQLType{core.Varchar, 0, 0}
case "TIMESTAMP WITH TIME ZONE": case "TIMESTAMP WITH TIME ZONE":
col.SQLType = SQLType{TimeStampz, 0, 0} col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
default: 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)) 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.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col
@ -159,10 +163,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er
return colSeq, cols, nil return colSeq, cols, nil
} }
func (db *oracle) GetTables() ([]*Table, error) { func (db *oracle) GetTables() ([]*core.Table, error) {
args := []interface{}{} args := []interface{}{}
s := "SELECT table_name FROM user_tables" 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 { if err != nil {
return nil, err return nil, err
} }
@ -172,9 +176,9 @@ func (db *oracle) GetTables() ([]*Table, error) {
return nil, err return nil, err
} }
tables := make([]*Table, 0) tables := make([]*core.Table, 0)
for rows.Next() { for rows.Next() {
table := NewEmptyTable() table := core.NewEmptyTable()
err = rows.Scan(&table.Name) err = rows.Scan(&table.Name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -185,12 +189,12 @@ func (db *oracle) GetTables() ([]*Table, error) {
return tables, nil 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} args := []interface{}{tableName}
s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + 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" "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 { if err != nil {
return nil, err return nil, err
} }
@ -201,7 +205,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
} }
defer rows.Close() defer rows.Close()
indexes := make(map[string]*Index, 0) indexes := make(map[string]*core.Index, 0)
for rows.Next() { for rows.Next() {
var indexType int var indexType int
var indexName, colName, uniqueness string var indexName, colName, uniqueness string
@ -214,15 +218,15 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
indexName = strings.Trim(indexName, `" `) indexName = strings.Trim(indexName, `" `)
if uniqueness == "UNIQUE" { if uniqueness == "UNIQUE" {
indexType = UniqueType indexType = core.UniqueType
} else { } else {
indexType = IndexType indexType = core.IndexType
} }
var index *Index var index *core.Index
var ok bool var ok bool
if index, ok = indexes[indexName]; !ok { if index, ok = indexes[indexName]; !ok {
index = new(Index) index = new(core.Index)
index.Type = indexType index.Type = indexType
index.Name = indexName index.Name = indexName
indexes[indexName] = index indexes[indexName] = index
@ -236,7 +240,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
type OracleSeqFilter struct { 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, "?") counts := strings.Count(sql, "?")
for i := 1; i <= counts; i++ { for i := 1; i <= counts; i++ {
newstr := ":" + fmt.Sprintf("%v", i) newstr := ":" + fmt.Sprintf("%v", i)
@ -245,6 +249,6 @@ func (s *OracleSeqFilter) Do(sql string, dialect Dialect, table *Table) string {
return sql return sql
} }
func (db *oracle) Filters() []Filter { func (db *oracle) Filters() []core.Filter {
return []Filter{&QuoteFilter{}, &OracleSeqFilter{}, &IdFilter{}} return []core.Filter{&core.QuoteFilter{}, &OracleSeqFilter{}, &core.IdFilter{}}
} }

View File

@ -1,13 +1,12 @@
package xorm package xorm
import ( import (
"github.com/lunny/xorm/core"
//"fmt"
"sync"
//"sync/atomic"
"container/list" "container/list"
"reflect" "reflect"
"sync"
"time" "time"
"github.com/go-xorm/core"
) )
// Interface IConnecPool is a connection pool interface, all implements should implement // Interface IConnecPool is a connection pool interface, all implements should implement

View File

@ -1,4 +1,4 @@
package dialects package xorm
import ( import (
"errors" "errors"
@ -6,53 +6,53 @@ import (
"strconv" "strconv"
"strings" "strings"
. "github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { // func init() {
RegisterDialect("postgres", &postgres{}) // RegisterDialect("postgres", &postgres{})
} // }
type postgres struct { 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) 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 var res string
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case TinyInt: case core.TinyInt:
res = SmallInt res = core.SmallInt
return res return res
case MediumInt, Int, Integer: case core.MediumInt, core.Int, core.Integer:
if c.IsAutoIncrement { if c.IsAutoIncrement {
return Serial return core.Serial
} }
return Integer return core.Integer
case Serial, BigSerial: case core.Serial, core.BigSerial:
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.Nullable = false c.Nullable = false
res = t res = t
case Binary, VarBinary: case core.Binary, core.VarBinary:
return Bytea return core.Bytea
case DateTime: case core.DateTime:
res = TimeStamp res = core.TimeStamp
case TimeStampz: case core.TimeStampz:
return "timestamp with time zone" return "timestamp with time zone"
case Float: case core.Float:
res = Real res = core.Real
case TinyText, MediumText, LongText: case core.TinyText, core.MediumText, core.LongText:
res = Text res = core.Text
case Blob, TinyBlob, MediumBlob, LongBlob: case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob:
return Bytea return core.Bytea
case Double: case core.Double:
return "DOUBLE PRECISION" return "DOUBLE PRECISION"
default: default:
if c.IsAutoIncrement { if c.IsAutoIncrement {
return Serial return core.Serial
} }
res = t res = t
} }
@ -108,11 +108,11 @@ func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interfa
" AND column_name = ?", args " 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} args := []interface{}{tableName}
s := "SELECT column_name, column_default, is_nullable, data_type, character_maximum_length" + 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" ", 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -121,12 +121,13 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cols := make(map[string]*Column) cols := make(map[string]*core.Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for rows.Next() { for rows.Next() {
col := new(Column) col := new(core.Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
var colName, isNullable, dataType string var colName, isNullable, dataType string
var maxLenStr, colDefault, numPrecision, numRadix *string var maxLenStr, colDefault, numPrecision, numRadix *string
err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix) 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 { switch dataType {
case "character varying", "character": case "character varying", "character":
col.SQLType = SQLType{Varchar, 0, 0} col.SQLType = core.SQLType{core.Varchar, 0, 0}
case "timestamp without time zone": case "timestamp without time zone":
col.SQLType = SQLType{DateTime, 0, 0} col.SQLType = core.SQLType{core.DateTime, 0, 0}
case "timestamp with time zone": case "timestamp with time zone":
col.SQLType = SQLType{TimeStampz, 0, 0} col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
case "double precision": case "double precision":
col.SQLType = SQLType{Double, 0, 0} col.SQLType = core.SQLType{core.Double, 0, 0}
case "boolean": case "boolean":
col.SQLType = SQLType{Bool, 0, 0} col.SQLType = core.SQLType{core.Bool, 0, 0}
case "time without time zone": case "time without time zone":
col.SQLType = SQLType{Time, 0, 0} col.SQLType = core.SQLType{core.Time, 0, 0}
default: 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)) 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.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} else {
if col.DefaultIsEmpty {
col.Default = "''"
}
} }
} }
cols[col.Name] = col cols[col.Name] = col
@ -192,10 +197,10 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column,
return colSeq, cols, nil return colSeq, cols, nil
} }
func (db *postgres) GetTables() ([]*Table, error) { func (db *postgres) GetTables() ([]*core.Table, error) {
args := []interface{}{} args := []interface{}{}
s := "SELECT tablename FROM pg_tables where schemaname = 'public'" 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 { if err != nil {
return nil, err return nil, err
} }
@ -205,9 +210,9 @@ func (db *postgres) GetTables() ([]*Table, error) {
return nil, err return nil, err
} }
tables := make([]*Table, 0) tables := make([]*core.Table, 0)
for rows.Next() { for rows.Next() {
table := NewEmptyTable() table := core.NewEmptyTable()
var name string var name string
err = rows.Scan(&name) err = rows.Scan(&name)
if err != nil { if err != nil {
@ -219,11 +224,11 @@ func (db *postgres) GetTables() ([]*Table, error) {
return tables, nil 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} args := []interface{}{tableName}
s := "SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' and tablename = $1" 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 { if err != nil {
return nil, err return nil, err
} }
@ -233,7 +238,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
return nil, err return nil, err
} }
indexes := make(map[string]*Index, 0) indexes := make(map[string]*core.Index, 0)
for rows.Next() { for rows.Next() {
var indexType int var indexType int
var indexName, indexdef string var indexName, indexdef string
@ -245,9 +250,9 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) {
indexName = strings.Trim(indexName, `" `) indexName = strings.Trim(indexName, `" `)
if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") { if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") {
indexType = UniqueType indexType = core.UniqueType
} else { } else {
indexType = IndexType indexType = core.IndexType
} }
cs := strings.Split(indexdef, "(") cs := strings.Split(indexdef, "(")
colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") 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 { for _, colName := range colNames {
index.Cols = append(index.Cols, strings.Trim(colName, `" `)) 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 return indexes, nil
} }
// PgSeqFilter filter SQL replace ?, ? ... to $1, $2 ... func (db *postgres) Filters() []core.Filter {
type PgSeqFilter struct { return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{"$", 1}}
}
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{}}
} }

15
rows.go
View File

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
type Rows struct { type Rows struct {
@ -32,24 +32,23 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
defer rows.session.Statement.Init() defer rows.session.Statement.Init()
var sql string var sqlStr string
var args []interface{} var args []interface{}
rows.session.Statement.RefTable = rows.session.Engine.autoMap(bean) rows.session.Statement.RefTable = rows.session.Engine.autoMap(bean)
if rows.session.Statement.RawSQL == "" { if rows.session.Statement.RawSQL == "" {
sql, args = rows.session.Statement.genGetSql(bean) sqlStr, args = rows.session.Statement.genGetSql(bean)
} else { } else {
sql = rows.session.Statement.RawSQL sqlStr = rows.session.Statement.RawSQL
args = rows.session.Statement.RawParams args = rows.session.Statement.RawParams
} }
for _, filter := range rows.session.Engine.Filters { 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(sqlStr, args)
rows.session.Engine.LogSQL(args)
rows.stmt, err = rows.session.Db.Prepare(sql) rows.stmt, err = rows.session.Db.Prepare(sqlStr)
if err != nil { if err != nil {
rows.lastError = err rows.lastError = err
defer rows.Close() defer rows.Close()

View File

@ -11,7 +11,7 @@ import (
"strings" "strings"
"time" "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 // 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 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 { func (session *Session) NoCascade() *Session {
session.Statement.UseCascade = false session.Statement.UseCascade = false
return session return session
@ -282,7 +292,7 @@ func (session *Session) Begin() error {
session.IsCommitedOrRollbacked = false session.IsCommitedOrRollbacked = false
session.Tx = tx session.Tx = tx
session.Engine.LogSQL("BEGIN TRANSACTION") session.Engine.logSQL("BEGIN TRANSACTION")
} }
return nil return nil
} }
@ -290,7 +300,7 @@ func (session *Session) Begin() error {
// When using transaction, you can rollback if any error // When using transaction, you can rollback if any error
func (session *Session) Rollback() error { func (session *Session) Rollback() error {
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
session.Engine.LogSQL("ROLL BACK") session.Engine.logSQL(session.Engine.dialect.RollBackStr())
session.IsCommitedOrRollbacked = true session.IsCommitedOrRollbacked = true
return session.Tx.Rollback() return session.Tx.Rollback()
} }
@ -300,7 +310,7 @@ func (session *Session) Rollback() error {
// When using transaction, Commit will commit all operations. // When using transaction, Commit will commit all operations.
func (session *Session) Commit() error { func (session *Session) Commit() error {
if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
session.Engine.LogSQL("COMMIT") session.Engine.logSQL("COMMIT")
session.IsCommitedOrRollbacked = true session.IsCommitedOrRollbacked = true
var err error var err error
if err = session.Tx.Commit(); err == nil { 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 { 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 { if dataStruct.Kind() != reflect.Struct {
return errors.New("Expected a pointer to a struct") return errors.New("Expected a pointer to a struct")
} }
table := session.Engine.autoMapType(rType(obj))
var col *core.Column var col *core.Column
table := session.Engine.autoMapType(dataStruct)
for key, data := range objMap { for key, data := range objMap {
if col = table.GetColumn(key); col == nil { 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())) 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) sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
} }
session.Engine.LogSQL(sqlStr) session.Engine.logSQL(sqlStr, args)
session.Engine.LogSQL(args)
if session.IsAutoCommit { if session.IsAutoCommit {
return session.innerExec(sqlStr, args...) return session.innerExec(sqlStr, args...)
@ -610,7 +620,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
return false, ErrCacheFailed return false, ErrCacheFailed
} }
cacher := session.Engine.getCacher(session.Statement.RefTable.Type) cacher := session.Engine.getCacher2(session.Statement.RefTable)
tableName := session.Statement.TableName() tableName := session.Statement.TableName()
session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args) session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args)
ids, err := core.GetCacheSql(cacher, tableName, 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 table := session.Statement.RefTable
cacher := session.Engine.getCacher(t) cacher := session.Engine.getCacher2(table)
ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args) ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args)
if err != nil { if err != nil {
//session.Engine.LogError(err) //session.Engine.LogError(err)
@ -928,6 +938,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
session.Statement.Limit(1) session.Statement.Limit(1)
var sqlStr string var sqlStr string
var args []interface{} var args []interface{}
session.Statement.RefTable = session.Engine.autoMap(bean) session.Statement.RefTable = session.Engine.autoMap(bean)
if session.Statement.RawSQL == "" { if session.Statement.RawSQL == "" {
@ -937,7 +948,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
args = session.Statement.RawParams 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...) has, err := session.cacheGet(bean, sqlStr, args...)
if err != ErrCacheFailed { if err != ErrCacheFailed {
return has, err return has, err
@ -1052,12 +1063,14 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
if session.Statement.RefTable == nil { if session.Statement.RefTable == nil {
if sliceElementType.Kind() == reflect.Ptr { if sliceElementType.Kind() == reflect.Ptr {
if sliceElementType.Elem().Kind() == reflect.Struct { if sliceElementType.Elem().Kind() == reflect.Struct {
table = session.Engine.autoMapType(sliceElementType.Elem()) pv := reflect.New(sliceElementType.Elem())
table = session.Engine.autoMapType(pv.Elem())
} else { } else {
return errors.New("slice type") return errors.New("slice type")
} }
} else if sliceElementType.Kind() == reflect.Struct { } else if sliceElementType.Kind() == reflect.Struct {
table = session.Engine.autoMapType(sliceElementType) pv := reflect.New(sliceElementType)
table = session.Engine.autoMapType(pv.Elem())
} else { } else {
return errors.New("slice type") return errors.New("slice type")
} }
@ -1068,7 +1081,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
if len(condiBean) > 0 { if len(condiBean) > 0 {
colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true, 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.ConditionStr = strings.Join(colNames, " AND ")
session.Statement.BeanArgs = args session.Statement.BeanArgs = args
} }
@ -1090,7 +1104,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
args = session.Statement.RawParams 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.UseCache &&
!session.Statement.IsDistinct { !session.Statement.IsDistinct {
err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) 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 { func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
dataStruct := rValue(bean)
dataStruct := reflect.Indirect(reflect.ValueOf(bean))
if dataStruct.Kind() != reflect.Struct { if dataStruct.Kind() != reflect.Struct {
return errors.New("Expected a pointer to a 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)) scanResultContainers := make([]interface{}, len(fields))
for i := 0; i < len(fields); i++ { 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) x := reflect.New(fieldType)
err := json.Unmarshal([]byte(vv.String()), x.Interface()) err := json.Unmarshal([]byte(vv.String()), x.Interface())
if err != nil { if err != nil {
session.Engine.LogSQL(err) session.Engine.LogError(err)
return err return err
} }
fieldValue.Set(x.Elem()) fieldValue.Set(x.Elem())
@ -1534,7 +1547,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
fieldValue.Set(vv) fieldValue.Set(vv)
} }
} else if session.Statement.UseCascade { } else if session.Statement.UseCascade {
table := session.Engine.autoMapType(fieldValue.Type()) table := session.Engine.autoMapType(*fieldValue)
if table != nil { if table != nil {
var x int64 var x int64
if rawValueType.Kind() == reflect.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) *sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable)
} }
session.Engine.LogSQL(*sqlStr) session.Engine.logSQL(*sqlStr, paramStr)
session.Engine.LogSQL(paramStr)
} }
func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { 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() 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 session.Statement.RefTable = table
size := sliceValue.Len() size := sliceValue.Len()
@ -1903,7 +1916,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
return 0, err 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()) session.cacheInsert(session.Statement.TableName())
} }
@ -2113,7 +2126,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value,
v = x v = x
fieldValue.Set(reflect.ValueOf(v)) fieldValue.Set(reflect.ValueOf(v))
} else if session.Statement.UseCascade { } else if session.Statement.UseCascade {
table := session.Engine.autoMapType(fieldValue.Type()) table := session.Engine.autoMapType(*fieldValue)
if table != nil { if table != nil {
x, err := strconv.ParseInt(string(data), 10, 64) x, err := strconv.ParseInt(string(data), 10, 64)
if err != nil { if err != nil {
@ -2467,7 +2480,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
case reflect.Complex64, reflect.Complex128: case reflect.Complex64, reflect.Complex128:
bytes, err := json.Marshal(fieldValue.Interface()) bytes, err := json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
session.Engine.LogSQL(err) session.Engine.LogError(err)
return 0, err return 0, err
} }
return string(bytes), nil return string(bytes), nil
@ -2479,7 +2492,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
if col.SQLType.IsText() { if col.SQLType.IsText() {
bytes, err := json.Marshal(fieldValue.Interface()) bytes, err := json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
session.Engine.LogSQL(err) session.Engine.LogError(err)
return 0, err return 0, err
} }
return string(bytes), nil return string(bytes), nil
@ -2492,7 +2505,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
} else { } else {
bytes, err = json.Marshal(fieldValue.Interface()) bytes, err = json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
session.Engine.LogSQL(err) session.Engine.LogError(err)
return 0, err return 0, err
} }
} }
@ -2526,6 +2539,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
} }
colPlaces := strings.Repeat("?, ", len(colNames)) colPlaces := strings.Repeat("?, ", len(colNames))
//fmt.Println(colNames, args)
colPlaces = colPlaces[0 : len(colPlaces)-2] colPlaces = colPlaces[0 : len(colPlaces)-2]
sqlStr := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)", 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) 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()) session.cacheInsert(session.Statement.TableName())
} }
@ -2636,7 +2650,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
handleAfterInsertProcessorFunc(bean) 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()) 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. // 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 // 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) { func (session *Session) InsertOne(bean interface{}) (int64, error) {
err := session.newDb() err := session.newDb()
if err != nil { if err != nil {
@ -2749,7 +2763,7 @@ func (session *Session) cacheInsert(tables ...string) error {
} }
table := session.Statement.RefTable table := session.Statement.RefTable
cacher := session.Engine.getCacher(table.Type) cacher := session.Engine.getCacher2(table)
for _, t := range tables { for _, t := range tables {
session.Engine.LogDebug("cache clear:", t) session.Engine.LogDebug("cache clear:", t)
@ -2783,7 +2797,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
} }
} }
table := session.Statement.RefTable table := session.Statement.RefTable
cacher := session.Engine.getCacher(table.Type) cacher := session.Engine.getCacher2(table)
tableName := session.Statement.TableName() tableName := session.Statement.TableName()
session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:]) session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:])
ids, err := core.GetCacheSql(cacher, tableName, 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 == "" { if session.Statement.ColumnStr == "" {
colNames, args = buildConditions(session.Engine, table, bean, false, false, 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 { } else {
colNames, args, err = genCols(table, session, bean, true, true) colNames, args, err = genCols(table, session, bean, true, true)
if err != nil { if err != nil {
@ -2942,7 +2957,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
if len(condiBean) > 0 { if len(condiBean) > 0 {
condiColNames, condiArgs = buildConditions(session.Engine, session.Statement.RefTable, condiBean[0], true, true, 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 = "" var condition = ""
@ -2965,6 +2981,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
var sqlStr, inSql string var sqlStr, inSql string
var inArgs []interface{} var inArgs []interface{}
doIncVer := false
var verValue *reflect.Value
if table.Version != "" && session.Statement.checkVersion { if table.Version != "" && session.Statement.checkVersion {
if condition != "" { if condition != "" {
condition = fmt.Sprintf("WHERE (%v) AND %v = ?", 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", session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1",
condition) condition)
verValue, err := table.VersionColumn().ValueOf(bean) verValue, err = table.VersionColumn().ValueOf(bean)
if err != nil { if err != nil {
return 0, err return 0, err
} }
condiArgs = append(condiArgs, verValue.Interface()) condiArgs = append(condiArgs, verValue.Interface())
doIncVer = true
} else { } else {
if condition != "" { if condition != "" {
condition = "WHERE " + condition condition = "WHERE " + condition
@ -3019,9 +3038,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
res, err := session.exec(sqlStr, args...) res, err := session.exec(sqlStr, args...)
if err != nil { if err != nil {
return 0, err 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...) //session.cacheUpdate(sqlStr, args...)
cacher.ClearIds(session.Statement.TableName()) cacher.ClearIds(session.Statement.TableName())
cacher.ClearBeans(session.Statement.TableName()) cacher.ClearBeans(session.Statement.TableName())
@ -3073,7 +3094,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
return ErrCacheFailed return ErrCacheFailed
} }
cacher := session.Engine.getCacher(session.Statement.RefTable.Type) cacher := session.Engine.getCacher2(session.Statement.RefTable)
tableName := session.Statement.TableName() tableName := session.Statement.TableName()
ids, err := core.GetCacheSql(cacher, tableName, newsql, args) ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
if err != nil { if err != nil {
@ -3139,7 +3160,8 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
table := session.Engine.autoMap(bean) table := session.Engine.autoMap(bean)
session.Statement.RefTable = table session.Statement.RefTable = table
colNames, args := buildConditions(session.Engine, table, bean, true, true, 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 = "" var condition = ""
@ -3169,7 +3191,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
args = append(session.Statement.Params, args...) 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...) session.cacheDelete(sqlStr, args...)
} }

View File

@ -1,44 +1,44 @@
package dialects package xorm
import ( import (
"strings" "strings"
. "github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
func init() { // func init() {
RegisterDialect("sqlite3", &sqlite3{}) // RegisterDialect("sqlite3", &sqlite3{})
} // }
type sqlite3 struct { 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) 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 { switch t := c.SQLType.Name; t {
case Date, DateTime, TimeStamp, Time: case core.Date, core.DateTime, core.TimeStamp, core.Time:
return Numeric return core.Numeric
case TimeStampz: case core.TimeStampz:
return Text return core.Text
case Char, Varchar, TinyText, Text, MediumText, LongText: case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText:
return Text return core.Text
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool: case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool:
return Integer return core.Integer
case Float, Double, Real: case core.Float, core.Double, core.Real:
return Real return core.Real
case Decimal, Numeric: case core.Decimal, core.Numeric:
return Numeric return core.Numeric
case TinyBlob, Blob, MediumBlob, LongBlob, Bytea, Binary, VarBinary: case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
return Blob return core.Blob
case Serial, BigSerial: case core.Serial, core.BigSerial:
c.IsPrimaryKey = true c.IsPrimaryKey = true
c.IsAutoIncrement = true c.IsAutoIncrement = true
c.Nullable = false c.Nullable = false
return Integer return core.Integer
default: default:
return t return t
} }
@ -84,10 +84,10 @@ func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interfac
return sql, args 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} args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?" 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -110,11 +110,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e
nStart := strings.Index(name, "(") nStart := strings.Index(name, "(")
nEnd := strings.Index(name, ")") nEnd := strings.Index(name, ")")
colCreates := strings.Split(name[nStart+1:nEnd], ",") colCreates := strings.Split(name[nStart+1:nEnd], ",")
cols := make(map[string]*Column) cols := make(map[string]*core.Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for _, colStr := range colCreates { for _, colStr := range colCreates {
fields := strings.Fields(strings.TrimSpace(colStr)) fields := strings.Fields(strings.TrimSpace(colStr))
col := new(Column) col := new(core.Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
col.Nullable = true col.Nullable = true
for idx, field := range fields { 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, "`[] ") col.Name = strings.Trim(field, "`[] ")
continue continue
} else if idx == 1 { } else if idx == 1 {
col.SQLType = SQLType{field, 0, 0} col.SQLType = core.SQLType{field, 0, 0}
} }
switch field { switch field {
case "PRIMARY": case "PRIMARY":
@ -143,11 +143,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e
return colSeq, cols, nil return colSeq, cols, nil
} }
func (db *sqlite3) GetTables() ([]*Table, error) { func (db *sqlite3) GetTables() ([]*core.Table, error) {
args := []interface{}{} args := []interface{}{}
s := "SELECT name FROM sqlite_master WHERE type='table'" 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 { if err != nil {
return nil, err return nil, err
} }
@ -158,9 +158,9 @@ func (db *sqlite3) GetTables() ([]*Table, error) {
} }
defer rows.Close() defer rows.Close()
tables := make([]*Table, 0) tables := make([]*core.Table, 0)
for rows.Next() { for rows.Next() {
table := NewEmptyTable() table := core.NewEmptyTable()
err = rows.Scan(&table.Name) err = rows.Scan(&table.Name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -173,10 +173,10 @@ func (db *sqlite3) GetTables() ([]*Table, error) {
return tables, nil 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} args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?" 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 { if err != nil {
return nil, err return nil, err
} }
@ -187,7 +187,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
} }
defer rows.Close() defer rows.Close()
indexes := make(map[string]*Index, 0) indexes := make(map[string]*core.Index, 0)
for rows.Next() { for rows.Next() {
var sql string var sql string
err = rows.Scan(&sql) err = rows.Scan(&sql)
@ -199,7 +199,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
continue continue
} }
index := new(Index) index := new(core.Index)
nNStart := strings.Index(sql, "INDEX") nNStart := strings.Index(sql, "INDEX")
nNEnd := strings.Index(sql, "ON") nNEnd := strings.Index(sql, "ON")
if nNStart == -1 || nNEnd == -1 { 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") { if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
index.Type = UniqueType index.Type = core.UniqueType
} else { } else {
index.Type = IndexType index.Type = core.IndexType
} }
nStart := strings.Index(sql, "(") nStart := strings.Index(sql, "(")
@ -234,6 +234,6 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) {
return indexes, nil return indexes, nil
} }
func (db *sqlite3) Filters() []Filter { func (db *sqlite3) Filters() []core.Filter {
return []Filter{&IdFilter{}} return []core.Filter{&core.IdFilter{}}
} }

View File

@ -1,15 +1,13 @@
package xorm package xorm
import ( import (
//"bytes" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
//"strconv"
"encoding/json"
"strings" "strings"
"time" "time"
"github.com/lunny/xorm/core" "github.com/go-xorm/core"
) )
type inParam struct { type inParam struct {
@ -32,6 +30,7 @@ type Statement struct {
HavingStr string HavingStr string
ColumnStr string ColumnStr string
columnMap map[string]bool columnMap map[string]bool
useAllCols bool
OmitStr string OmitStr string
ConditionStr string ConditionStr string
AltTableName string AltTableName string
@ -47,7 +46,7 @@ type Statement struct {
IsDistinct bool IsDistinct bool
allUseBool bool allUseBool bool
checkVersion bool checkVersion bool
boolColumnMap map[string]bool mustColumnMap map[string]bool
inColumns map[string]*inParam inColumns map[string]*inParam
} }
@ -68,6 +67,7 @@ func (statement *Statement) Init() {
statement.columnMap = make(map[string]bool) statement.columnMap = make(map[string]bool)
statement.ConditionStr = "" statement.ConditionStr = ""
statement.AltTableName = "" statement.AltTableName = ""
statement.IdParam = nil
statement.RawSQL = "" statement.RawSQL = ""
statement.RawParams = make([]interface{}, 0) statement.RawParams = make([]interface{}, 0)
statement.BeanArgs = make([]interface{}, 0) statement.BeanArgs = make([]interface{}, 0)
@ -75,7 +75,7 @@ func (statement *Statement) Init() {
statement.UseAutoTime = true statement.UseAutoTime = true
statement.IsDistinct = false statement.IsDistinct = false
statement.allUseBool = false statement.allUseBool = false
statement.boolColumnMap = make(map[string]bool) statement.mustColumnMap = make(map[string]bool)
statement.checkVersion = true statement.checkVersion = true
statement.inColumns = make(map[string]*inParam) statement.inColumns = make(map[string]*inParam)
} }
@ -118,11 +118,12 @@ func (statement *Statement) Or(querystring string, args ...interface{}) *Stateme
// tempororily set table name // tempororily set table name
func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
t := rType(tableNameOrBean) v := rValue(tableNameOrBean)
t := v.Type()
if t.Kind() == reflect.String { if t.Kind() == reflect.String {
statement.AltTableName = tableNameOrBean.(string) statement.AltTableName = tableNameOrBean.(string)
} else if t.Kind() == reflect.Struct { } else if t.Kind() == reflect.Struct {
statement.RefTable = statement.Engine.autoMapType(t) statement.RefTable = statement.Engine.autoMapType(v)
} }
return statement return statement
} }
@ -210,7 +211,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
if col.SQLType.IsText() { if col.SQLType.IsText() {
bytes, err := json.Marshal(fieldValue.Interface()) bytes, err := json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
engine.LogSQL(err) engine.LogError(err)
continue continue
} }
val = string(bytes) val = string(bytes)
@ -227,7 +228,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
} else { } else {
bytes, err = json.Marshal(fieldValue.Interface()) bytes, err = json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
engine.LogSQL(err) engine.LogError(err)
continue continue
} }
val = bytes val = bytes
@ -245,8 +246,9 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
// Auto generating conditions according a struct // Auto generating conditions according a struct
func buildConditions(engine *Engine, table *core.Table, bean interface{}, func buildConditions(engine *Engine, table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, includeVersion bool, includeUpdated bool, includeNil bool,
boolColumnMap map[string]bool) ([]string, []interface{}) { includeAutoIncr bool, allUseBool bool, useAllCols bool,
mustColumnMap map[string]bool) ([]string, []interface{}) {
colNames := make([]string, 0) colNames := make([]string, 0)
var args = make([]interface{}, 0) var args = make([]interface{}, 0)
@ -274,7 +276,15 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
fieldValue := *fieldValuePtr fieldValue := *fieldValuePtr
fieldType := reflect.TypeOf(fieldValue.Interface()) 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 fieldType.Kind() == reflect.Ptr {
if fieldValue.IsNil() { if fieldValue.IsNil() {
if includeNil { if includeNil {
@ -297,8 +307,6 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
case reflect.Bool: case reflect.Bool:
if allUseBool || requiredField { if allUseBool || requiredField {
val = fieldValue.Interface() val = fieldValue.Interface()
} else if _, ok := boolColumnMap[col.Name]; ok {
val = fieldValue.Interface()
} else { } else {
// if a bool in a struct, it will not be as a condition because it default is false, // if a bool in a struct, it will not be as a condition because it default is false,
// please use Where() instead // please use Where() instead
@ -346,7 +354,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
val = t val = t
} }
} else { } else {
engine.autoMapType(fieldValue.Type()) engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok { if table, ok := engine.Tables[fieldValue.Type()]; ok {
if len(table.PrimaryKeys) == 1 { if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) 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() { if col.SQLType.IsText() {
bytes, err := json.Marshal(fieldValue.Interface()) bytes, err := json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
engine.LogSQL(err) engine.LogError(err)
continue continue
} }
val = string(bytes) val = string(bytes)
@ -390,7 +398,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{},
} else { } else {
bytes, err = json.Marshal(fieldValue.Interface()) bytes, err = json.Marshal(fieldValue.Interface())
if err != nil { if err != nil {
engine.LogSQL(err) engine.LogError(err)
continue continue
} }
val = bytes val = bytes
@ -534,13 +542,34 @@ func (statement *Statement) Cols(columns ...string) *Statement {
return 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 // indicates that use bool fields as update contents and query contiditions
func (statement *Statement) UseBool(columns ...string) *Statement { func (statement *Statement) UseBool(columns ...string) *Statement {
if len(columns) > 0 { if len(columns) > 0 {
newColumns := col2NewCols(columns...) statement.MustCols(columns...)
for _, nc := range newColumns {
statement.boolColumnMap[strings.ToLower(nc)] = true
}
} else { } else {
statement.allUseBool = true statement.allUseBool = true
} }
@ -676,13 +705,7 @@ func (s *Statement) genDelIndexSQL() []string {
} }
func (s *Statement) genDropSQL() string { func (s *Statement) genDropSQL() string {
if s.Engine.dialect.DBType() == core.MSSQL { return s.Engine.dialect.DropTableSql(s.TableName()) + ";"
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()) + ";"
}
} }
func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) { func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) {
@ -690,7 +713,8 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{})
statement.RefTable = table statement.RefTable = table
colNames, args := buildConditions(statement.Engine, table, bean, true, true, 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.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args statement.BeanArgs = args
@ -729,7 +753,7 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}
statement.RefTable = table statement.RefTable = table
colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, 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.ConditionStr = strings.Join(colNames, " AND ")
statement.BeanArgs = args statement.BeanArgs = args

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
go test -v -bench=. -run=XXX

View File

@ -1 +0,0 @@
go test -v -bench=. -run=XXX

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
View File

@ -1,6 +1,7 @@
package xorm package xorm
import ( import (
"database/sql"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -9,16 +10,36 @@ import (
"sync" "sync"
"time" "time"
"github.com/lunny/xorm/caches" "github.com/go-xorm/core"
"github.com/lunny/xorm/core" "github.com/go-xorm/xorm/caches"
_ "github.com/lunny/xorm/dialects" _ "github.com/go-xorm/xorm/drivers"
_ "github.com/lunny/xorm/drivers"
) )
const ( const (
Version string = "0.4" 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) { func close(engine *Engine) {
engine.Close() engine.Close()
} }
@ -46,19 +67,22 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
return nil, err return nil, err
} }
engine := &Engine{DriverName: driverName, engine := &Engine{
DataSourceName: dataSourceName, dialect: dialect, DriverName: driverName,
tableCachers: make(map[reflect.Type]core.Cacher)} DataSourceName: dataSourceName,
dialect: dialect,
}
engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper)))
engine.Filters = dialect.Filters() engine.Filters = dialect.Filters()
engine.Tables = make(map[reflect.Type]*core.Table) engine.Tables = make(map[reflect.Type]*core.Table)
engine.mutex = &sync.RWMutex{} engine.mutex = &sync.RWMutex{}
engine.TagIdentifier = "xorm" engine.TagIdentifier = "xorm"
engine.Logger = os.Stdout engine.Logger = NewSimpleLogger(os.Stdout)
//engine.Pool = NewSimpleConnectPool() //engine.Pool = NewSimpleConnectPool()
//engine.Pool = NewNoneConnectPool() //engine.Pool = NewNoneConnectPool()

View File

@ -1,2 +0,0 @@
[deps]
github.com/lunny/xorm=../

View File

@ -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/)

View File

@ -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
}

View File

@ -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
}

View File

@ -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 ""
}
}

View File

@ -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:]
}
}

View File

@ -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
})
}

View File

@ -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$ ")
}
}

View File

@ -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}}

View File

@ -1 +0,0 @@
lang=c++

View File

@ -1 +0,0 @@
lang=go

View File

@ -1,14 +0,0 @@
package {{.Model}}
import (
{{range .Imports}}"{{.}}"{{end}}
)
{{range .Tables}}
type {{Mapper .Name}} struct {
{{$table := .}}
{{range .Columns}} {{Mapper .Name}} {{Type .}}
{{end}}
}
{{end}}

View File

@ -1,2 +0,0 @@
lang=go
genJson=0

View File

@ -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}}

View File

@ -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)
}