fix migrate failed and org dashboard failed on MSSQL database (#1448)
This commit is contained in:
parent
cf6699fb4f
commit
5acfc7c4bc
|
@ -670,13 +670,30 @@ func (env *accessibleReposEnv) Repos(page, pageSize int) ([]*Repository, error)
|
|||
Find(&repos)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) MirrorRepos() ([]*Repository, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
return repos, x.
|
||||
Select("`repository`.*").
|
||||
func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
|
||||
repoIDs := make([]int64, 0, 10)
|
||||
return repoIDs, x.
|
||||
Table("repository").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
|
||||
Where(env.cond()).
|
||||
GroupBy("`repository`.id").
|
||||
OrderBy("updated_unix DESC").
|
||||
Cols("`repository`.id").
|
||||
Find(&repoIDs)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) MirrorRepos() ([]*Repository, error) {
|
||||
repoIDs, err := env.MirrorRepoIDs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MirrorRepoIDs: %v", err)
|
||||
}
|
||||
|
||||
repos := make([]*Repository, 0, len(repoIDs))
|
||||
if len(repoIDs) <= 0 {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
return repos, x.
|
||||
In("`repository`.id", repoIDs).
|
||||
Find(&repos)
|
||||
}
|
||||
|
|
|
@ -46,12 +46,15 @@ Drivers for Go's sql package which currently support database/sql includes:
|
|||
|
||||
* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
|
||||
|
||||
* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
|
||||
|
||||
* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
|
||||
|
||||
# Changelog
|
||||
|
||||
* **v0.6.2**
|
||||
* refactor tag parse methods
|
||||
* add Scan features to Get
|
||||
* add QueryString method
|
||||
|
||||
* **v0.6.0**
|
||||
* remove support for ql
|
||||
* add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so `Where`, `And`, `Or`
|
||||
|
@ -79,12 +82,6 @@ methods can use `builder.Cond` as parameter
|
|||
|
||||
# Installation
|
||||
|
||||
If you have [gopm](https://github.com/gpmgo/gopm) installed,
|
||||
|
||||
gopm get github.com/go-xorm/xorm
|
||||
|
||||
Or
|
||||
|
||||
go get github.com/go-xorm/xorm
|
||||
|
||||
# Documents
|
||||
|
@ -119,19 +116,21 @@ type User struct {
|
|||
err := engine.Sync2(new(User))
|
||||
```
|
||||
|
||||
* Query a SQL string, the returned results is []map[string][]byte
|
||||
* `Query` runs a SQL string, the returned results is `[]map[string][]byte`, `QueryString` returns `[]map[string]string`.
|
||||
|
||||
```Go
|
||||
results, err := engine.Query("select * from user")
|
||||
|
||||
results, err := engine.QueryString("select * from user")
|
||||
```
|
||||
|
||||
* Execute a SQL string, the returned results
|
||||
* `Execute` runs a SQL string, it returns `affetcted` and `error`
|
||||
|
||||
```Go
|
||||
affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
|
||||
```
|
||||
|
||||
* Insert one or multiple records to database
|
||||
* `Insert` one or multiple records to database
|
||||
|
||||
```Go
|
||||
affected, err := engine.Insert(&user)
|
||||
|
@ -153,6 +152,18 @@ has, err := engine.Get(&user)
|
|||
// SELECT * FROM user LIMIT 1
|
||||
has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
|
||||
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
|
||||
var name string
|
||||
has, err := engine.Where("id = ?", id).Cols("name").Get(&name)
|
||||
// SELECT name FROM user WHERE id = ?
|
||||
var id int64
|
||||
has, err := engine.Where("name = ?", name).Cols("id").Get(&id)
|
||||
// SELECT id FROM user WHERE name = ?
|
||||
var valuesMap = make(map[string]string)
|
||||
has, err := engine.Where("id = ?", id).Get(&valuesMap)
|
||||
// SELECT * FROM user WHERE id = ?
|
||||
var valuesSlice = make([]interface{}, len(cols))
|
||||
has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
|
||||
// SELECT col1, col2, col3 FROM user WHERE id = ?
|
||||
```
|
||||
|
||||
* Query multiple records from database, also you can use join and extends
|
||||
|
|
|
@ -54,6 +54,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
|
|||
|
||||
## 更新日志
|
||||
|
||||
* **v0.6.2**
|
||||
* 重构Tag解析方式
|
||||
* Get方法新增类似Sacn的特性
|
||||
* 新增 QueryString 方法
|
||||
|
||||
* **v0.6.0**
|
||||
* 去除对 ql 的支持
|
||||
* 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数
|
||||
|
@ -81,12 +86,6 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
|
|||
|
||||
## 安装
|
||||
|
||||
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
|
||||
|
||||
gopm get github.com/go-xorm/xorm
|
||||
|
||||
或者您也可以使用go工具进行安装:
|
||||
|
||||
go get github.com/go-xorm/xorm
|
||||
|
||||
## 文档
|
||||
|
@ -121,13 +120,15 @@ type User struct {
|
|||
err := engine.Sync2(new(User))
|
||||
```
|
||||
|
||||
* 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte
|
||||
* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string
|
||||
|
||||
```Go
|
||||
results, err := engine.Query("select * from user")
|
||||
|
||||
results, err := engine.QueryString("select * from user")
|
||||
```
|
||||
|
||||
* 执行一个SQL语句
|
||||
* `Exec` 执行一个SQL语句
|
||||
|
||||
```Go
|
||||
affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
|
||||
|
@ -155,6 +156,18 @@ has, err := engine.Get(&user)
|
|||
// SELECT * FROM user LIMIT 1
|
||||
has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
|
||||
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
|
||||
var name string
|
||||
has, err := engine.Where("id = ?", id).Cols("name").Get(&name)
|
||||
// SELECT name FROM user WHERE id = ?
|
||||
var id int64
|
||||
has, err := engine.Where("name = ?", name).Cols("id").Get(&id)
|
||||
// SELECT id FROM user WHERE name = ?
|
||||
var valuesMap = make(map[string]string)
|
||||
has, err := engine.Where("id = ?", id).Get(&valuesMap)
|
||||
// SELECT * FROM user WHERE id = ?
|
||||
var valuesSlice = make([]interface{}, len(cols))
|
||||
has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
|
||||
// SELECT col1, col2, col3 FROM user WHERE id = ?
|
||||
```
|
||||
|
||||
* 查询多条记录,当然可以使用Join和extends来组合使用
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
xorm v0.6.0.1022
|
|
@ -247,3 +247,40 @@ func convertAssign(dest, src interface{}) error {
|
|||
|
||||
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
|
||||
}
|
||||
|
||||
func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
|
||||
switch tp.Kind() {
|
||||
case reflect.Int64:
|
||||
return vv.Int(), nil
|
||||
case reflect.Int:
|
||||
return int(vv.Int()), nil
|
||||
case reflect.Int32:
|
||||
return int32(vv.Int()), nil
|
||||
case reflect.Int16:
|
||||
return int16(vv.Int()), nil
|
||||
case reflect.Int8:
|
||||
return int8(vv.Int()), nil
|
||||
case reflect.Uint64:
|
||||
return vv.Uint(), nil
|
||||
case reflect.Uint:
|
||||
return uint(vv.Uint()), nil
|
||||
case reflect.Uint32:
|
||||
return uint32(vv.Uint()), nil
|
||||
case reflect.Uint16:
|
||||
return uint16(vv.Uint()), nil
|
||||
case reflect.Uint8:
|
||||
return uint8(vv.Uint()), nil
|
||||
case reflect.String:
|
||||
return vv.String(), nil
|
||||
case reflect.Slice:
|
||||
if tp.Elem().Kind() == reflect.Uint8 {
|
||||
v, err := strconv.ParseInt(string(vv.Interface().([]byte)), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -215,9 +216,9 @@ func (db *mssql) SqlType(c *core.Column) string {
|
|||
switch t := c.SQLType.Name; t {
|
||||
case core.Bool:
|
||||
res = core.TinyInt
|
||||
if c.Default == "true" {
|
||||
if strings.EqualFold(c.Default, "true") {
|
||||
c.Default = "1"
|
||||
} else if c.Default == "false" {
|
||||
} else {
|
||||
c.Default = "0"
|
||||
}
|
||||
case core.Serial:
|
||||
|
@ -467,9 +468,10 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
}
|
||||
|
||||
colName = strings.Trim(colName, "` ")
|
||||
|
||||
var isRegular bool
|
||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
|
||||
indexName = indexName[5+len(tableName):]
|
||||
isRegular = true
|
||||
}
|
||||
|
||||
var index *core.Index
|
||||
|
@ -478,6 +480,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
index = new(core.Index)
|
||||
index.Type = indexType
|
||||
index.Name = indexName
|
||||
index.IsRegular = isRegular
|
||||
indexes[indexName] = index
|
||||
}
|
||||
index.AddColumn(colName)
|
||||
|
@ -526,3 +529,25 @@ func (db *mssql) ForUpdateSql(query string) string {
|
|||
func (db *mssql) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
|
||||
}
|
||||
|
||||
type odbcDriver struct {
|
||||
}
|
||||
|
||||
func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
kv := strings.Split(dataSourceName, ";")
|
||||
var dbName string
|
||||
|
||||
for _, c := range kv {
|
||||
vv := strings.Split(strings.TrimSpace(c), "=")
|
||||
if len(vv) == 2 {
|
||||
switch strings.ToLower(vv[0]) {
|
||||
case "database":
|
||||
dbName = vv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if dbName == "" {
|
||||
return nil, errors.New("no db name provided")
|
||||
}
|
||||
return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
|
||||
}
|
|
@ -6,7 +6,9 @@ package xorm
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -486,3 +488,93 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
|||
func (db *mysql) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}}
|
||||
}
|
||||
|
||||
type mymysqlDriver struct {
|
||||
}
|
||||
|
||||
func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.MYSQL}
|
||||
|
||||
pd := strings.SplitN(dataSourceName, "*", 2)
|
||||
if len(pd) == 2 {
|
||||
// Parse protocol part of URI
|
||||
p := strings.SplitN(pd[0], ":", 2)
|
||||
if len(p) != 2 {
|
||||
return nil, errors.New("Wrong protocol part of URI")
|
||||
}
|
||||
db.Proto = p[0]
|
||||
options := strings.Split(p[1], ",")
|
||||
db.Raddr = options[0]
|
||||
for _, o := range options[1:] {
|
||||
kv := strings.SplitN(o, "=", 2)
|
||||
var k, v string
|
||||
if len(kv) == 2 {
|
||||
k, v = kv[0], kv[1]
|
||||
} else {
|
||||
k, v = o, "true"
|
||||
}
|
||||
switch k {
|
||||
case "laddr":
|
||||
db.Laddr = v
|
||||
case "timeout":
|
||||
to, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.Timeout = to
|
||||
default:
|
||||
return nil, errors.New("Unknown option: " + k)
|
||||
}
|
||||
}
|
||||
// Remove protocol part
|
||||
pd = pd[1:]
|
||||
}
|
||||
// Parse database part of URI
|
||||
dup := strings.SplitN(pd[0], "/", 3)
|
||||
if len(dup) != 3 {
|
||||
return nil, errors.New("Wrong database part of URI")
|
||||
}
|
||||
db.DbName = dup[0]
|
||||
db.User = dup[1]
|
||||
db.Passwd = dup[2]
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
type mysqlDriver struct {
|
||||
}
|
||||
|
||||
func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
||||
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
|
||||
`\/(?P<dbname>.*?)` + // /dbname
|
||||
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1¶mN=valueN]
|
||||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
//tlsConfigRegister := make(map[string]*tls.Config)
|
||||
names := dsnPattern.SubexpNames()
|
||||
|
||||
uri := &core.Uri{DbType: core.MYSQL}
|
||||
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
uri.DbName = match
|
||||
case "params":
|
||||
if len(match) > 0 {
|
||||
kvs := strings.Split(match, "&")
|
||||
for _, kv := range kvs {
|
||||
splits := strings.Split(kv, "=")
|
||||
if len(splits) == 2 {
|
||||
switch splits[0] {
|
||||
case "charset":
|
||||
uri.Charset = splits[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return uri, nil
|
||||
}
|
|
@ -5,7 +5,9 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -822,6 +824,12 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
|||
|
||||
indexName = strings.Trim(indexName, `" `)
|
||||
|
||||
var isRegular bool
|
||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
|
||||
indexName = indexName[5+len(tableName):]
|
||||
isRegular = true
|
||||
}
|
||||
|
||||
if uniqueness == "UNIQUE" {
|
||||
indexType = core.UniqueType
|
||||
} else {
|
||||
|
@ -834,6 +842,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
|||
index = new(core.Index)
|
||||
index.Type = indexType
|
||||
index.Name = indexName
|
||||
index.IsRegular = isRegular
|
||||
indexes[indexName] = index
|
||||
}
|
||||
index.AddColumn(colName)
|
||||
|
@ -844,3 +853,54 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
|
|||
func (db *oracle) Filters() []core.Filter {
|
||||
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}}
|
||||
}
|
||||
|
||||
type goracleDriver struct {
|
||||
}
|
||||
|
||||
func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.ORACLE}
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
||||
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
|
||||
`\/(?P<dbname>.*?)` + // /dbname
|
||||
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1¶mN=valueN]
|
||||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
//tlsConfigRegister := make(map[string]*tls.Config)
|
||||
names := dsnPattern.SubexpNames()
|
||||
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
db.DbName = match
|
||||
}
|
||||
}
|
||||
if db.DbName == "" {
|
||||
return nil, errors.New("dbname is empty")
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
type oci8Driver struct {
|
||||
}
|
||||
|
||||
//dataSourceName=user/password@ipv4:port/dbname
|
||||
//dataSourceName=user/password@[ipv6]:port/dbname
|
||||
func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.ORACLE}
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
|
||||
`(?P<net>.*)` + // ip:port
|
||||
`\/(?P<dbname>.*)`) // dbname
|
||||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
names := dsnPattern.SubexpNames()
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
db.DbName = match
|
||||
}
|
||||
}
|
||||
if db.DbName == "" {
|
||||
return nil, errors.New("dbname is empty")
|
||||
}
|
||||
return db, nil
|
||||
}
|
|
@ -5,7 +5,10 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -1075,9 +1078,10 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
|
|||
}
|
||||
cs := strings.Split(indexdef, "(")
|
||||
colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
|
||||
|
||||
var isRegular bool
|
||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
|
||||
newIdxName := indexName[5+len(tableName):]
|
||||
isRegular = true
|
||||
if newIdxName != "" {
|
||||
indexName = newIdxName
|
||||
}
|
||||
|
@ -1087,6 +1091,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
|
|||
for _, colName := range colNames {
|
||||
index.Cols = append(index.Cols, strings.Trim(colName, `" `))
|
||||
}
|
||||
index.IsRegular = isRegular
|
||||
indexes[index.Name] = index
|
||||
}
|
||||
return indexes, nil
|
||||
|
@ -1095,3 +1100,107 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
|
|||
func (db *postgres) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}}
|
||||
}
|
||||
|
||||
type pqDriver struct {
|
||||
}
|
||||
|
||||
type values map[string]string
|
||||
|
||||
func (vs values) Set(k, v string) {
|
||||
vs[k] = v
|
||||
}
|
||||
|
||||
func (vs values) Get(k string) (v string) {
|
||||
return vs[k]
|
||||
}
|
||||
|
||||
func errorf(s string, args ...interface{}) {
|
||||
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
|
||||
}
|
||||
|
||||
func parseURL(connstr string) (string, error) {
|
||||
u, err := url.Parse(connstr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u.Scheme != "postgresql" && u.Scheme != "postgres" {
|
||||
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
|
||||
}
|
||||
|
||||
var kvs []string
|
||||
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
|
||||
accrue := func(k, v string) {
|
||||
if v != "" {
|
||||
kvs = append(kvs, k+"="+escaper.Replace(v))
|
||||
}
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
v := u.User.Username()
|
||||
accrue("user", v)
|
||||
|
||||
v, _ = u.User.Password()
|
||||
accrue("password", v)
|
||||
}
|
||||
|
||||
i := strings.Index(u.Host, ":")
|
||||
if i < 0 {
|
||||
accrue("host", u.Host)
|
||||
} else {
|
||||
accrue("host", u.Host[:i])
|
||||
accrue("port", u.Host[i+1:])
|
||||
}
|
||||
|
||||
if u.Path != "" {
|
||||
accrue("dbname", u.Path[1:])
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
for k := range q {
|
||||
accrue(k, q.Get(k))
|
||||
}
|
||||
|
||||
sort.Strings(kvs) // Makes testing easier (not a performance concern)
|
||||
return strings.Join(kvs, " "), nil
|
||||
}
|
||||
|
||||
func parseOpts(name string, o values) {
|
||||
if len(name) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
|
||||
ps := strings.Split(name, " ")
|
||||
for _, p := range ps {
|
||||
kv := strings.Split(p, "=")
|
||||
if len(kv) < 2 {
|
||||
errorf("invalid option: %q", p)
|
||||
}
|
||||
o.Set(kv[0], kv[1])
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.POSTGRES}
|
||||
o := make(values)
|
||||
var err error
|
||||
if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
|
||||
dataSourceName, err = parseURL(dataSourceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
parseOpts(dataSourceName, o)
|
||||
|
||||
db.DbName = o.Get("dbname")
|
||||
if db.DbName == "" {
|
||||
return nil, errors.New("dbname is empty")
|
||||
}
|
||||
/*db.Schema = o.Get("schema")
|
||||
if len(db.Schema) == 0 {
|
||||
db.Schema = "public"
|
||||
}*/
|
||||
return db, nil
|
||||
}
|
|
@ -405,8 +405,10 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
|
|||
}
|
||||
|
||||
indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
|
||||
var isRegular bool
|
||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
|
||||
index.Name = indexName[5+len(tableName):]
|
||||
isRegular = true
|
||||
} else {
|
||||
index.Name = indexName
|
||||
}
|
||||
|
@ -425,6 +427,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
|
|||
for _, col := range colIndexes {
|
||||
index.Cols = append(index.Cols, strings.Trim(col, "` []"))
|
||||
}
|
||||
index.IsRegular = isRegular
|
||||
indexes[index.Name] = index
|
||||
}
|
||||
|
||||
|
@ -434,3 +437,10 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
|
|||
func (db *sqlite3) Filters() []core.Filter {
|
||||
return []core.Filter{&core.IdFilter{}}
|
||||
}
|
||||
|
||||
type sqlite3Driver struct {
|
||||
}
|
||||
|
||||
func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
|
||||
}
|
|
@ -24,7 +24,7 @@ Generally, one engine for an application is enough. You can set it as package va
|
|||
|
||||
Raw Methods
|
||||
|
||||
Xorm also support raw sql execution:
|
||||
XORM also support raw SQL execution:
|
||||
|
||||
1. query a SQL string, the returned results is []map[string][]byte
|
||||
|
||||
|
@ -36,7 +36,7 @@ Xorm also support raw sql execution:
|
|||
|
||||
ORM Methods
|
||||
|
||||
There are 7 major ORM methods and many helpful methods to use to operate database.
|
||||
There are 8 major ORM methods and many helpful methods to use to operate database.
|
||||
|
||||
1. Insert one or multiple records to database
|
||||
|
||||
|
@ -58,10 +58,18 @@ There are 7 major ORM methods and many helpful methods to use to operate databas
|
|||
|
||||
3. Query multiple records from database
|
||||
|
||||
sliceOfStructs := new(Struct)
|
||||
err := engine.Find(sliceOfStructs)
|
||||
var sliceOfStructs []Struct
|
||||
err := engine.Find(&sliceOfStructs)
|
||||
// SELECT * FROM user
|
||||
|
||||
var mapOfStructs = make(map[int64]Struct)
|
||||
err := engine.Find(&mapOfStructs)
|
||||
// SELECT * FROM user
|
||||
|
||||
var int64s []int64
|
||||
err := engine.Table("user").Cols("id").Find(&int64s)
|
||||
// SELECT id FROM user
|
||||
|
||||
4. Query multiple records and record by record handle, there two methods, one is Iterate,
|
||||
another is Rows
|
||||
|
||||
|
@ -91,20 +99,31 @@ another is Rows
|
|||
counts, err := engine.Count(&user)
|
||||
// SELECT count(*) AS total FROM user
|
||||
|
||||
8. Sum records
|
||||
|
||||
sumFloat64, err := engine.Sum(&user, "id")
|
||||
// SELECT sum(id) from user
|
||||
|
||||
sumFloat64s, err := engine.Sums(&user, "id1", "id2")
|
||||
// SELECT sum(id1), sum(id2) from user
|
||||
|
||||
sumInt64s, err := engine.SumsInt(&user, "id1", "id2")
|
||||
// SELECT sum(id1), sum(id2) from user
|
||||
|
||||
Conditions
|
||||
|
||||
The above 7 methods could use with condition methods chainable.
|
||||
Attention: the above 7 methods should be the last chainable method.
|
||||
The above 8 methods could use with condition methods chainable.
|
||||
Attention: the above 8 methods should be the last chainable method.
|
||||
|
||||
1. Id, In
|
||||
1. ID, In
|
||||
|
||||
engine.Id(1).Get(&user) // for single primary key
|
||||
engine.ID(1).Get(&user) // for single primary key
|
||||
// SELECT * FROM user WHERE id = 1
|
||||
engine.Id(core.PK{1, 2}).Get(&user) // for composite primary keys
|
||||
engine.ID(core.PK{1, 2}).Get(&user) // for composite primary keys
|
||||
// SELECT * FROM user WHERE id1 = 1 AND id2 = 2
|
||||
engine.In("id", 1, 2, 3).Find(&users)
|
||||
// SELECT * FROM user WHERE id IN (1, 2, 3)
|
||||
engine.In("id", []int{1, 2, 3})
|
||||
engine.In("id", []int{1, 2, 3}).Find(&users)
|
||||
// SELECT * FROM user WHERE id IN (1, 2, 3)
|
||||
|
||||
2. Where, And, Or
|
||||
|
@ -127,10 +146,10 @@ Attention: the above 7 methods should be the last chainable method.
|
|||
// SELECT TOP 5 * FROM user // for mssql
|
||||
// SELECT * FROM user LIMIT .. OFFSET 0 //for other databases
|
||||
|
||||
5. Sql, let you custom SQL
|
||||
5. SQL, let you custom SQL
|
||||
|
||||
var users []User
|
||||
engine.Sql("select * from user").Find(&users)
|
||||
engine.SQL("select * from user").Find(&users)
|
||||
|
||||
6. Cols, Omit, Distinct
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ type Engine struct {
|
|||
DatabaseTZ *time.Location // The timezone of the database
|
||||
|
||||
disableGlobalCache bool
|
||||
|
||||
tagHandlers map[string]tagHandler
|
||||
}
|
||||
|
||||
// ShowSQL show SQL statement or not on logger if log level is great than INFO
|
||||
|
@ -215,10 +217,15 @@ func (engine *Engine) NoCascade() *Session {
|
|||
}
|
||||
|
||||
// MapCacher 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) error {
|
||||
v := rValue(bean)
|
||||
tb := engine.autoMapType(v)
|
||||
tb, err := engine.autoMapType(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tb.Cacher = cacher
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDB provides an interface to operate database directly
|
||||
|
@ -260,9 +267,9 @@ func (engine *Engine) Ping() error {
|
|||
func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
|
||||
if engine.showSQL && !engine.showExecTime {
|
||||
if len(sqlArgs) > 0 {
|
||||
engine.logger.Infof("[sql] %v [args] %v", sqlStr, sqlArgs)
|
||||
engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs)
|
||||
} else {
|
||||
engine.logger.Infof("[sql] %v", sqlStr)
|
||||
engine.logger.Infof("[SQL] %v", sqlStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,9 +280,9 @@ func (engine *Engine) logSQLQueryTime(sqlStr string, args []interface{}, executi
|
|||
stmt, res, err := executionBlock()
|
||||
execDuration := time.Since(b4ExecTime)
|
||||
if len(args) > 0 {
|
||||
engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration)
|
||||
engine.logger.Infof("[SQL] %s %v - took: %v", sqlStr, args, execDuration)
|
||||
} else {
|
||||
engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration)
|
||||
engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration)
|
||||
}
|
||||
return stmt, res, err
|
||||
}
|
||||
|
@ -774,13 +781,18 @@ func (engine *Engine) Having(conditions string) *Session {
|
|||
return session.Having(conditions)
|
||||
}
|
||||
|
||||
func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
|
||||
func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) {
|
||||
t := v.Type()
|
||||
engine.mutex.Lock()
|
||||
defer engine.mutex.Unlock()
|
||||
table, ok := engine.Tables[t]
|
||||
if !ok {
|
||||
table = engine.mapType(v)
|
||||
var err error
|
||||
table, err = engine.mapType(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
engine.Tables[t] = table
|
||||
if engine.Cacher != nil {
|
||||
if v.CanAddr() {
|
||||
|
@ -790,12 +802,11 @@ func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
|
|||
}
|
||||
}
|
||||
}
|
||||
return table
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// GobRegister register one struct to gob for cache use
|
||||
func (engine *Engine) GobRegister(v interface{}) *Engine {
|
||||
//fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v)
|
||||
gob.Register(v)
|
||||
return engine
|
||||
}
|
||||
|
@ -806,10 +817,19 @@ type Table struct {
|
|||
Name string
|
||||
}
|
||||
|
||||
// IsValid if table is valid
|
||||
func (t *Table) IsValid() bool {
|
||||
return t.Table != nil && len(t.Name) > 0
|
||||
}
|
||||
|
||||
// TableInfo get table info according to bean's content
|
||||
func (engine *Engine) TableInfo(bean interface{}) *Table {
|
||||
v := rValue(bean)
|
||||
return &Table{engine.autoMapType(v), engine.tbName(v)}
|
||||
tb, err := engine.autoMapType(v)
|
||||
if err != nil {
|
||||
engine.logger.Error(err)
|
||||
}
|
||||
return &Table{tb, engine.tbName(v)}
|
||||
}
|
||||
|
||||
func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
|
||||
|
@ -842,7 +862,7 @@ var (
|
|||
tpTableName = reflect.TypeOf((*TableName)(nil)).Elem()
|
||||
)
|
||||
|
||||
func (engine *Engine) mapType(v reflect.Value) *core.Table {
|
||||
func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
|
||||
t := v.Type()
|
||||
table := engine.newTable()
|
||||
if tb, ok := v.Interface().(TableName); ok {
|
||||
|
@ -861,7 +881,6 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
|
|||
table.Type = t
|
||||
|
||||
var idFieldColName string
|
||||
var err error
|
||||
var hasCacheTag, hasNoCacheTag bool
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
|
@ -881,186 +900,94 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
|
|||
if tags[0] == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
var ctx = tagContext{
|
||||
table: table,
|
||||
col: col,
|
||||
fieldValue: fieldValue,
|
||||
indexNames: make(map[string]int),
|
||||
engine: engine,
|
||||
}
|
||||
|
||||
if strings.ToUpper(tags[0]) == "EXTENDS" {
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.Ptr:
|
||||
f := fieldValue.Type().Elem()
|
||||
if f.Kind() == reflect.Struct {
|
||||
fieldPtr := fieldValue
|
||||
fieldValue = fieldValue.Elem()
|
||||
if !fieldValue.IsValid() || fieldPtr.IsNil() {
|
||||
fieldValue = reflect.New(f).Elem()
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
parentTable := engine.mapType(fieldValue)
|
||||
for _, col := range parentTable.Columns() {
|
||||
col.FieldName = fmt.Sprintf("%v.%v", t.Field(i).Name, col.FieldName)
|
||||
table.AddColumn(col)
|
||||
for indexName, indexType := range col.Indexes {
|
||||
addIndex(indexName, table, col, indexType)
|
||||
}
|
||||
if err := ExtendsTagHandler(&ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
default:
|
||||
//TODO: warning
|
||||
}
|
||||
}
|
||||
|
||||
indexNames := make(map[string]int)
|
||||
var isIndex, isUnique bool
|
||||
var preKey string
|
||||
for j, key := range tags {
|
||||
k := strings.ToUpper(key)
|
||||
switch {
|
||||
case k == "<-":
|
||||
col.MapType = core.ONLYFROMDB
|
||||
case k == "->":
|
||||
col.MapType = core.ONLYTODB
|
||||
case k == "PK":
|
||||
col.IsPrimaryKey = true
|
||||
col.Nullable = false
|
||||
case k == "NULL":
|
||||
if j == 0 {
|
||||
col.Nullable = true
|
||||
} else {
|
||||
col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT")
|
||||
}
|
||||
// TODO: for postgres how add autoincr?
|
||||
/*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"):
|
||||
col.IsAutoIncrement = true
|
||||
|
||||
autoStart := k[len("AUTOINCR")+1 : len(k)-1]
|
||||
autoStartInt, err := strconv.Atoi(autoStart)
|
||||
if err != nil {
|
||||
engine.LogError(err)
|
||||
}
|
||||
col.AutoIncrStart = autoStartInt*/
|
||||
case k == "AUTOINCR":
|
||||
col.IsAutoIncrement = true
|
||||
//col.AutoIncrStart = 1
|
||||
case k == "DEFAULT":
|
||||
col.Default = tags[j+1]
|
||||
case k == "CREATED":
|
||||
col.IsCreated = true
|
||||
case k == "VERSION":
|
||||
col.IsVersion = true
|
||||
col.Default = "1"
|
||||
case k == "UTC":
|
||||
col.TimeZone = time.UTC
|
||||
case k == "LOCAL":
|
||||
col.TimeZone = time.Local
|
||||
case strings.HasPrefix(k, "LOCALE(") && strings.HasSuffix(k, ")"):
|
||||
location := k[len("LOCALE")+1 : len(k)-1]
|
||||
col.TimeZone, err = time.LoadLocation(location)
|
||||
if err != nil {
|
||||
engine.logger.Error(err)
|
||||
}
|
||||
case k == "UPDATED":
|
||||
col.IsUpdated = true
|
||||
case k == "DELETED":
|
||||
col.IsDeleted = true
|
||||
case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"):
|
||||
indexName := k[len("INDEX")+1 : len(k)-1]
|
||||
indexNames[indexName] = core.IndexType
|
||||
case k == "INDEX":
|
||||
isIndex = true
|
||||
case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
|
||||
indexName := k[len("UNIQUE")+1 : len(k)-1]
|
||||
indexNames[indexName] = core.UniqueType
|
||||
case k == "UNIQUE":
|
||||
isUnique = true
|
||||
case k == "NOTNULL":
|
||||
col.Nullable = false
|
||||
case k == "CACHE":
|
||||
if !hasCacheTag {
|
||||
hasCacheTag = true
|
||||
}
|
||||
case k == "NOCACHE":
|
||||
if !hasNoCacheTag {
|
||||
hasNoCacheTag = true
|
||||
}
|
||||
case k == "NOT":
|
||||
default:
|
||||
if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
|
||||
if preKey != "DEFAULT" {
|
||||
col.Name = key[1 : len(key)-1]
|
||||
}
|
||||
} else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
|
||||
fs := strings.Split(k, "(")
|
||||
|
||||
if _, ok := core.SqlTypes[fs[0]]; !ok {
|
||||
preKey = k
|
||||
if ctx.ignoreNext {
|
||||
ctx.ignoreNext = false
|
||||
continue
|
||||
}
|
||||
col.SQLType = core.SQLType{Name: fs[0]}
|
||||
if fs[0] == core.Enum && fs[1][0] == '\'' { //enum
|
||||
options := strings.Split(fs[1][0:len(fs[1])-1], ",")
|
||||
col.EnumOptions = make(map[string]int)
|
||||
for k, v := range options {
|
||||
v = strings.TrimSpace(v)
|
||||
v = strings.Trim(v, "'")
|
||||
col.EnumOptions[v] = k
|
||||
|
||||
k := strings.ToUpper(key)
|
||||
ctx.tagName = k
|
||||
|
||||
pStart := strings.Index(k, "(")
|
||||
if pStart == 0 {
|
||||
return nil, errors.New("( could not be the first charactor")
|
||||
}
|
||||
} else if fs[0] == core.Set && fs[1][0] == '\'' { //set
|
||||
options := strings.Split(fs[1][0:len(fs[1])-1], ",")
|
||||
col.SetOptions = make(map[string]int)
|
||||
for k, v := range options {
|
||||
v = strings.TrimSpace(v)
|
||||
v = strings.Trim(v, "'")
|
||||
col.SetOptions[v] = k
|
||||
if pStart > -1 {
|
||||
if !strings.HasSuffix(k, ")") {
|
||||
return nil, errors.New("cannot match ) charactor")
|
||||
}
|
||||
|
||||
ctx.tagName = k[:pStart]
|
||||
ctx.params = strings.Split(k[pStart+1:len(k)-1], ",")
|
||||
}
|
||||
|
||||
if j > 0 {
|
||||
ctx.preTag = strings.ToUpper(tags[j-1])
|
||||
}
|
||||
if j < len(tags)-1 {
|
||||
ctx.nextTag = strings.ToUpper(tags[j+1])
|
||||
} else {
|
||||
ctx.nextTag = ""
|
||||
}
|
||||
|
||||
if h, ok := engine.tagHandlers[ctx.tagName]; ok {
|
||||
if err := h(&ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
|
||||
if len(fs2) == 2 {
|
||||
col.Length, err = strconv.Atoi(fs2[0])
|
||||
if err != nil {
|
||||
engine.logger.Error(err)
|
||||
}
|
||||
col.Length2, err = strconv.Atoi(fs2[1])
|
||||
if err != nil {
|
||||
engine.logger.Error(err)
|
||||
}
|
||||
} else if len(fs2) == 1 {
|
||||
col.Length, err = strconv.Atoi(fs2[0])
|
||||
if err != nil {
|
||||
engine.logger.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") {
|
||||
col.Name = key[1 : len(key)-1]
|
||||
} else {
|
||||
if _, ok := core.SqlTypes[k]; ok {
|
||||
col.SQLType = core.SQLType{Name: k}
|
||||
} else if key != col.Default {
|
||||
col.Name = key
|
||||
}
|
||||
}
|
||||
engine.dialect.SqlType(col)
|
||||
|
||||
if ctx.hasCacheTag {
|
||||
hasCacheTag = true
|
||||
}
|
||||
preKey = k
|
||||
if ctx.hasNoCacheTag {
|
||||
hasNoCacheTag = true
|
||||
}
|
||||
}
|
||||
|
||||
if col.SQLType.Name == "" {
|
||||
col.SQLType = core.Type2SQLType(fieldType)
|
||||
}
|
||||
engine.dialect.SqlType(col)
|
||||
if col.Length == 0 {
|
||||
col.Length = col.SQLType.DefaultLength
|
||||
}
|
||||
if col.Length2 == 0 {
|
||||
col.Length2 = col.SQLType.DefaultLength2
|
||||
}
|
||||
|
||||
if col.Name == "" {
|
||||
col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name)
|
||||
}
|
||||
|
||||
if isUnique {
|
||||
indexNames[col.Name] = core.UniqueType
|
||||
} else if isIndex {
|
||||
indexNames[col.Name] = core.IndexType
|
||||
if ctx.isUnique {
|
||||
ctx.indexNames[col.Name] = core.UniqueType
|
||||
} else if ctx.isIndex {
|
||||
ctx.indexNames[col.Name] = core.IndexType
|
||||
}
|
||||
|
||||
for indexName, indexType := range indexNames {
|
||||
for indexName, indexType := range ctx.indexNames {
|
||||
addIndex(indexName, table, col, indexType)
|
||||
}
|
||||
}
|
||||
|
@ -1114,7 +1041,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
|
|||
table.Cacher = nil
|
||||
}
|
||||
|
||||
return table
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// IsTableEmpty if a table has any reocrd
|
||||
|
@ -1152,8 +1079,21 @@ func (engine *Engine) IdOfV(rv reflect.Value) core.PK {
|
|||
|
||||
// IDOfV get id from one value of struct
|
||||
func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
|
||||
pk, err := engine.idOfV(rv)
|
||||
if err != nil {
|
||||
engine.logger.Error(err)
|
||||
return nil
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
||||
func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) {
|
||||
v := reflect.Indirect(rv)
|
||||
table := engine.autoMapType(v)
|
||||
table, err := engine.autoMapType(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk := make([]interface{}, len(table.PrimaryKeys))
|
||||
for i, col := range table.PKColumns() {
|
||||
pkField := v.FieldByName(col.FieldName)
|
||||
|
@ -1166,7 +1106,7 @@ func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
|
|||
pk[i] = pkField.Uint()
|
||||
}
|
||||
}
|
||||
return core.PK(pk)
|
||||
return core.PK(pk), nil
|
||||
}
|
||||
|
||||
// CreateIndexes create indexes
|
||||
|
@ -1187,13 +1127,6 @@ func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
|
|||
return table.Cacher
|
||||
}
|
||||
|
||||
func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
|
||||
if table := engine.autoMapType(v); table != nil {
|
||||
return table.Cacher
|
||||
}
|
||||
return engine.Cacher
|
||||
}
|
||||
|
||||
// ClearCacheBean if enabled cache, clear the cache bean
|
||||
func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
|
||||
v := rValue(bean)
|
||||
|
@ -1202,7 +1135,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
|
|||
return errors.New("error params")
|
||||
}
|
||||
tableName := engine.tbName(v)
|
||||
table := engine.autoMapType(v)
|
||||
table, err := engine.autoMapType(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacher := table.Cacher
|
||||
if cacher == nil {
|
||||
cacher = engine.Cacher
|
||||
|
@ -1223,7 +1159,11 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
|
|||
return errors.New("error params")
|
||||
}
|
||||
tableName := engine.tbName(v)
|
||||
table := engine.autoMapType(v)
|
||||
table, err := engine.autoMapType(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cacher := table.Cacher
|
||||
if cacher == nil {
|
||||
cacher = engine.Cacher
|
||||
|
@ -1243,7 +1183,11 @@ func (engine *Engine) Sync(beans ...interface{}) error {
|
|||
for _, bean := range beans {
|
||||
v := rValue(bean)
|
||||
tableName := engine.tbName(v)
|
||||
table := engine.autoMapType(v)
|
||||
table, err := engine.autoMapType(v)
|
||||
fmt.Println(v, table, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := engine.NewSession()
|
||||
defer s.Close()
|
||||
|
@ -1426,6 +1370,13 @@ func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice [
|
|||
return session.Query(sql, paramStr...)
|
||||
}
|
||||
|
||||
// QueryString runs a raw sql and return records as []map[string]string
|
||||
func (engine *Engine) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) {
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
return session.QueryString(sqlStr, args...)
|
||||
}
|
||||
|
||||
// Insert one or more records
|
||||
func (engine *Engine) Insert(beans ...interface{}) (int64, error) {
|
||||
session := engine.NewSession()
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// func init() {
|
||||
// core.RegisterDriver("goracle", &goracleDriver{})
|
||||
// }
|
||||
|
||||
type goracleDriver struct {
|
||||
}
|
||||
|
||||
func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.ORACLE}
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
||||
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
|
||||
`\/(?P<dbname>.*?)` + // /dbname
|
||||
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1¶mN=valueN]
|
||||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
//tlsConfigRegister := make(map[string]*tls.Config)
|
||||
names := dsnPattern.SubexpNames()
|
||||
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
db.DbName = match
|
||||
}
|
||||
}
|
||||
if db.DbName == "" {
|
||||
return nil, errors.New("dbname is empty")
|
||||
}
|
||||
return db, nil
|
||||
}
|
|
@ -180,6 +180,20 @@ func isStructZero(v reflect.Value) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func isArrayValueZero(v reflect.Value) bool {
|
||||
if !v.IsValid() || v.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if !isZero(v.Index(i).Interface()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func int64ToIntValue(id int64, tp reflect.Type) reflect.Value {
|
||||
var v interface{}
|
||||
switch tp.Kind() {
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type mymysqlDriver struct {
|
||||
}
|
||||
|
||||
func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.MYSQL}
|
||||
|
||||
pd := strings.SplitN(dataSourceName, "*", 2)
|
||||
if len(pd) == 2 {
|
||||
// Parse protocol part of URI
|
||||
p := strings.SplitN(pd[0], ":", 2)
|
||||
if len(p) != 2 {
|
||||
return nil, errors.New("Wrong protocol part of URI")
|
||||
}
|
||||
db.Proto = p[0]
|
||||
options := strings.Split(p[1], ",")
|
||||
db.Raddr = options[0]
|
||||
for _, o := range options[1:] {
|
||||
kv := strings.SplitN(o, "=", 2)
|
||||
var k, v string
|
||||
if len(kv) == 2 {
|
||||
k, v = kv[0], kv[1]
|
||||
} else {
|
||||
k, v = o, "true"
|
||||
}
|
||||
switch k {
|
||||
case "laddr":
|
||||
db.Laddr = v
|
||||
case "timeout":
|
||||
to, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.Timeout = to
|
||||
default:
|
||||
return nil, errors.New("Unknown option: " + k)
|
||||
}
|
||||
}
|
||||
// Remove protocol part
|
||||
pd = pd[1:]
|
||||
}
|
||||
// Parse database part of URI
|
||||
dup := strings.SplitN(pd[0], "/", 3)
|
||||
if len(dup) != 3 {
|
||||
return nil, errors.New("Wrong database part of URI")
|
||||
}
|
||||
db.DbName = dup[0]
|
||||
db.User = dup[1]
|
||||
db.Passwd = dup[2]
|
||||
|
||||
return db, nil
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type mysqlDriver struct {
|
||||
}
|
||||
|
||||
func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
||||
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
|
||||
`\/(?P<dbname>.*?)` + // /dbname
|
||||
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1¶mN=valueN]
|
||||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
//tlsConfigRegister := make(map[string]*tls.Config)
|
||||
names := dsnPattern.SubexpNames()
|
||||
|
||||
uri := &core.Uri{DbType: core.MYSQL}
|
||||
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
uri.DbName = match
|
||||
case "params":
|
||||
if len(match) > 0 {
|
||||
kvs := strings.Split(match, "&")
|
||||
for _, kv := range kvs {
|
||||
splits := strings.Split(kv, "=")
|
||||
if len(splits) == 2 {
|
||||
switch splits[0] {
|
||||
case "charset":
|
||||
uri.Charset = splits[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return uri, nil
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type oci8Driver struct {
|
||||
}
|
||||
|
||||
//dataSourceName=user/password@ipv4:port/dbname
|
||||
//dataSourceName=user/password@[ipv6]:port/dbname
|
||||
func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.ORACLE}
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
|
||||
`(?P<net>.*)` + // ip:port
|
||||
`\/(?P<dbname>.*)`) // dbname
|
||||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
names := dsnPattern.SubexpNames()
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
db.DbName = match
|
||||
}
|
||||
}
|
||||
if db.DbName == "" {
|
||||
return nil, errors.New("dbname is empty")
|
||||
}
|
||||
return db, nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type odbcDriver struct {
|
||||
}
|
||||
|
||||
func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
kv := strings.Split(dataSourceName, ";")
|
||||
var dbName string
|
||||
|
||||
for _, c := range kv {
|
||||
vv := strings.Split(strings.TrimSpace(c), "=")
|
||||
if len(vv) == 2 {
|
||||
switch strings.ToLower(vv[0]) {
|
||||
case "database":
|
||||
dbName = vv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if dbName == "" {
|
||||
return nil, errors.New("no db name provided")
|
||||
}
|
||||
return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type pqDriver struct {
|
||||
}
|
||||
|
||||
type values map[string]string
|
||||
|
||||
func (vs values) Set(k, v string) {
|
||||
vs[k] = v
|
||||
}
|
||||
|
||||
func (vs values) Get(k string) (v string) {
|
||||
return vs[k]
|
||||
}
|
||||
|
||||
func errorf(s string, args ...interface{}) {
|
||||
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
|
||||
}
|
||||
|
||||
func parseURL(connstr string) (string, error) {
|
||||
u, err := url.Parse(connstr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u.Scheme != "postgresql" && u.Scheme != "postgres" {
|
||||
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
|
||||
}
|
||||
|
||||
var kvs []string
|
||||
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
|
||||
accrue := func(k, v string) {
|
||||
if v != "" {
|
||||
kvs = append(kvs, k+"="+escaper.Replace(v))
|
||||
}
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
v := u.User.Username()
|
||||
accrue("user", v)
|
||||
|
||||
v, _ = u.User.Password()
|
||||
accrue("password", v)
|
||||
}
|
||||
|
||||
i := strings.Index(u.Host, ":")
|
||||
if i < 0 {
|
||||
accrue("host", u.Host)
|
||||
} else {
|
||||
accrue("host", u.Host[:i])
|
||||
accrue("port", u.Host[i+1:])
|
||||
}
|
||||
|
||||
if u.Path != "" {
|
||||
accrue("dbname", u.Path[1:])
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
for k := range q {
|
||||
accrue(k, q.Get(k))
|
||||
}
|
||||
|
||||
sort.Strings(kvs) // Makes testing easier (not a performance concern)
|
||||
return strings.Join(kvs, " "), nil
|
||||
}
|
||||
|
||||
func parseOpts(name string, o values) {
|
||||
if len(name) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
|
||||
ps := strings.Split(name, " ")
|
||||
for _, p := range ps {
|
||||
kv := strings.Split(p, "=")
|
||||
if len(kv) < 2 {
|
||||
errorf("invalid option: %q", p)
|
||||
}
|
||||
o.Set(kv[0], kv[1])
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
db := &core.Uri{DbType: core.POSTGRES}
|
||||
o := make(values)
|
||||
var err error
|
||||
if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
|
||||
dataSourceName, err = parseURL(dataSourceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
parseOpts(dataSourceName, o)
|
||||
|
||||
db.DbName = o.Get("dbname")
|
||||
if db.DbName == "" {
|
||||
return nil, errors.New("dbname is empty")
|
||||
}
|
||||
/*db.Schema = o.Get("schema")
|
||||
if len(db.Schema) == 0 {
|
||||
db.Schema = "public"
|
||||
}*/
|
||||
return db, nil
|
||||
}
|
|
@ -20,7 +20,6 @@ type Rows struct {
|
|||
stmt *core.Stmt
|
||||
rows *core.Rows
|
||||
fields []string
|
||||
fieldsCount int
|
||||
beanType reflect.Type
|
||||
lastError error
|
||||
}
|
||||
|
@ -82,7 +81,6 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
|
|||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
rows.fieldsCount = len(rows.fields)
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
@ -114,7 +112,10 @@ func (rows *Rows) Scan(bean interface{}) error {
|
|||
return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType)
|
||||
}
|
||||
|
||||
_, err := rows.session.row2Bean(rows.rows, rows.fields, rows.fieldsCount, bean)
|
||||
dataStruct := rValue(bean)
|
||||
rows.session.Statement.setRefValue(dataStruct)
|
||||
_, err := rows.session.row2Bean(rows.rows, rows.fields, len(rows.fields), bean, &dataStruct, rows.session.Statement.RefTable)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
// Incr provides a query string like "count = count + 1"
|
||||
func (session *Session) Incr(column string, arg ...interface{}) *Session {
|
||||
session.Statement.Incr(column, arg...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Decr provides a query string like "count = count - 1"
|
||||
func (session *Session) Decr(column string, arg ...interface{}) *Session {
|
||||
session.Statement.Decr(column, arg...)
|
||||
return session
|
||||
}
|
||||
|
||||
// SetExpr provides a query string like "column = {expression}"
|
||||
func (session *Session) SetExpr(column string, expression string) *Session {
|
||||
session.Statement.SetExpr(column, expression)
|
||||
return session
|
||||
}
|
||||
|
||||
// Select provides some columns to special
|
||||
func (session *Session) Select(str string) *Session {
|
||||
session.Statement.Select(str)
|
||||
return session
|
||||
}
|
||||
|
||||
// Cols provides some columns to special
|
||||
func (session *Session) Cols(columns ...string) *Session {
|
||||
session.Statement.Cols(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
// AllCols ask all columns
|
||||
func (session *Session) AllCols() *Session {
|
||||
session.Statement.AllCols()
|
||||
return session
|
||||
}
|
||||
|
||||
// MustCols specify some columns must use even if they are empty
|
||||
func (session *Session) MustCols(columns ...string) *Session {
|
||||
session.Statement.MustCols(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
// UseBool automatically retrieve condition according struct, but
|
||||
// if struct has bool field, it will ignore them. So use UseBool
|
||||
// to tell system to do not ignore them.
|
||||
// If no parameters, it will use all the bool field of struct, or
|
||||
// it will use parameters's columns
|
||||
func (session *Session) UseBool(columns ...string) *Session {
|
||||
session.Statement.UseBool(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Distinct use for distinct columns. Caution: when you are using cache,
|
||||
// distinct will not be cached because cache system need id,
|
||||
// but distinct will not provide id
|
||||
func (session *Session) Distinct(columns ...string) *Session {
|
||||
session.Statement.Distinct(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Omit Only not use the parameters as select or update columns
|
||||
func (session *Session) Omit(columns ...string) *Session {
|
||||
session.Statement.Omit(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Nullable Set null when column is zero-value and nullable for update
|
||||
func (session *Session) Nullable(columns ...string) *Session {
|
||||
session.Statement.Nullable(columns...)
|
||||
return session
|
||||
}
|
||||
|
||||
// NoAutoTime means do not automatically give created field and updated field
|
||||
// the current time on the current session temporarily
|
||||
func (session *Session) NoAutoTime() *Session {
|
||||
session.Statement.UseAutoTime = false
|
||||
return session
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import "github.com/go-xorm/builder"
|
||||
|
||||
// Sql provides raw sql input parameter. When you have a complex SQL statement
|
||||
// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
|
||||
//
|
||||
// Deprecated: use SQL instead.
|
||||
func (session *Session) Sql(query string, args ...interface{}) *Session {
|
||||
return session.SQL(query, args...)
|
||||
}
|
||||
|
||||
// SQL provides raw sql input parameter. When you have a complex SQL statement
|
||||
// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
|
||||
func (session *Session) SQL(query interface{}, args ...interface{}) *Session {
|
||||
session.Statement.SQL(query, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Where provides custom query condition.
|
||||
func (session *Session) Where(query interface{}, args ...interface{}) *Session {
|
||||
session.Statement.Where(query, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
// And provides custom query condition.
|
||||
func (session *Session) And(query interface{}, args ...interface{}) *Session {
|
||||
session.Statement.And(query, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Or provides custom query condition.
|
||||
func (session *Session) Or(query interface{}, args ...interface{}) *Session {
|
||||
session.Statement.Or(query, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Id provides converting id as a query condition
|
||||
//
|
||||
// Deprecated: use ID instead
|
||||
func (session *Session) Id(id interface{}) *Session {
|
||||
return session.ID(id)
|
||||
}
|
||||
|
||||
// ID provides converting id as a query condition
|
||||
func (session *Session) ID(id interface{}) *Session {
|
||||
session.Statement.ID(id)
|
||||
return session
|
||||
}
|
||||
|
||||
// In provides a query string like "id in (1, 2, 3)"
|
||||
func (session *Session) In(column string, args ...interface{}) *Session {
|
||||
session.Statement.In(column, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
// NotIn provides a query string like "id in (1, 2, 3)"
|
||||
func (session *Session) NotIn(column string, args ...interface{}) *Session {
|
||||
session.Statement.NotIn(column, args...)
|
||||
return session
|
||||
}
|
||||
|
||||
// Conds returns session query conditions
|
||||
func (session *Session) Conds() builder.Cond {
|
||||
return session.Statement.cond
|
||||
}
|
|
@ -0,0 +1,673 @@
|
|||
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) {
|
||||
sdata := strings.TrimSpace(data)
|
||||
var x time.Time
|
||||
var err error
|
||||
|
||||
if sdata == "0000-00-00 00:00:00" ||
|
||||
sdata == "0001-01-01 00:00:00" {
|
||||
} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
|
||||
// time stamp
|
||||
sd, err := strconv.ParseInt(sdata, 10, 64)
|
||||
if err == nil {
|
||||
x = time.Unix(sd, 0)
|
||||
// !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion
|
||||
if col.TimeZone == nil {
|
||||
x = x.In(session.Engine.TZLocation)
|
||||
} else {
|
||||
x = x.In(col.TimeZone)
|
||||
}
|
||||
session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
} else {
|
||||
session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
}
|
||||
} else if len(sdata) > 19 && strings.Contains(sdata, "-") {
|
||||
x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation)
|
||||
session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
if err != nil {
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation)
|
||||
session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
}
|
||||
if err != nil {
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation)
|
||||
session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
}
|
||||
|
||||
} else if len(sdata) == 19 && strings.Contains(sdata, "-") {
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation)
|
||||
session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
|
||||
x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation)
|
||||
session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
} else if col.SQLType.Name == core.Time {
|
||||
if strings.Contains(sdata, " ") {
|
||||
ssd := strings.Split(sdata, " ")
|
||||
sdata = ssd[1]
|
||||
}
|
||||
|
||||
sdata = strings.TrimSpace(sdata)
|
||||
if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
|
||||
sdata = sdata[len(sdata)-8:]
|
||||
}
|
||||
|
||||
st := fmt.Sprintf("2006-01-02 %v", sdata)
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation)
|
||||
session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
|
||||
} else {
|
||||
outErr = fmt.Errorf("unsupported time format %v", sdata)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
|
||||
return
|
||||
}
|
||||
outTime = x
|
||||
return
|
||||
}
|
||||
|
||||
func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) {
|
||||
return session.str2Time(col, string(data))
|
||||
}
|
||||
|
||||
// convert a db data([]byte) to a field value
|
||||
func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error {
|
||||
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
|
||||
return structConvert.FromDB(data)
|
||||
}
|
||||
|
||||
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
|
||||
return structConvert.FromDB(data)
|
||||
}
|
||||
|
||||
var v interface{}
|
||||
key := col.Name
|
||||
fieldType := fieldValue.Type()
|
||||
|
||||
switch fieldType.Kind() {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
x := reflect.New(fieldType)
|
||||
if len(data) > 0 {
|
||||
err := json.Unmarshal(data, x.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
case reflect.Slice, reflect.Array, reflect.Map:
|
||||
v = data
|
||||
t := fieldType.Elem()
|
||||
k := t.Kind()
|
||||
if col.SQLType.IsText() {
|
||||
x := reflect.New(fieldType)
|
||||
if len(data) > 0 {
|
||||
err := json.Unmarshal(data, x.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
} else if col.SQLType.IsBlob() {
|
||||
if k == reflect.Uint8 {
|
||||
fieldValue.Set(reflect.ValueOf(v))
|
||||
} else {
|
||||
x := reflect.New(fieldType)
|
||||
if len(data) > 0 {
|
||||
err := json.Unmarshal(data, x.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ErrUnSupportedType
|
||||
}
|
||||
case reflect.String:
|
||||
fieldValue.SetString(string(data))
|
||||
case reflect.Bool:
|
||||
d := string(data)
|
||||
v, err := strconv.ParseBool(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as bool: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(v))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
sdata := string(data)
|
||||
var x int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == core.Bit &&
|
||||
session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
|
||||
if len(data) == 1 {
|
||||
x = int64(data[0])
|
||||
} else {
|
||||
x = 0
|
||||
}
|
||||
} else if strings.HasPrefix(sdata, "0x") {
|
||||
x, err = strconv.ParseInt(sdata, 16, 64)
|
||||
} else if strings.HasPrefix(sdata, "0") {
|
||||
x, err = strconv.ParseInt(sdata, 8, 64)
|
||||
} else if strings.EqualFold(sdata, "true") {
|
||||
x = 1
|
||||
} else if strings.EqualFold(sdata, "false") {
|
||||
x = 0
|
||||
} else {
|
||||
x, err = strconv.ParseInt(sdata, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.SetInt(x)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
x, err := strconv.ParseFloat(string(data), 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as float64: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.SetFloat(x)
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
x, err := strconv.ParseUint(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.SetUint(x)
|
||||
//Currently only support Time type
|
||||
case reflect.Struct:
|
||||
// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
|
||||
if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
|
||||
if err := nulVal.Scan(data); err != nil {
|
||||
return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error())
|
||||
}
|
||||
} else {
|
||||
if fieldType.ConvertibleTo(core.TimeType) {
|
||||
x, err := session.byte2Time(col, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v = x
|
||||
fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
|
||||
} else if session.Statement.UseCascade {
|
||||
table, err := session.Engine.autoMapType(*fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: current only support 1 primary key
|
||||
if len(table.PrimaryKeys) > 1 {
|
||||
panic("unsupported composited primary key cascade")
|
||||
}
|
||||
var pk = make(core.PK, len(table.PrimaryKeys))
|
||||
rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
|
||||
pk[0], err = str2PK(string(data), rawValueType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isPKZero(pk) {
|
||||
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
|
||||
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
|
||||
// property to be fetched lazily
|
||||
structInter := reflect.New(fieldValue.Type())
|
||||
newsession := session.Engine.NewSession()
|
||||
defer newsession.Close()
|
||||
has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if has {
|
||||
v = structInter.Elem().Interface()
|
||||
fieldValue.Set(reflect.ValueOf(v))
|
||||
} else {
|
||||
return errors.New("cascade obj is not exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
// !nashtsai! TODO merge duplicated codes above
|
||||
//typeStr := fieldType.String()
|
||||
switch fieldType.Elem().Kind() {
|
||||
// case "*string":
|
||||
case core.StringType.Kind():
|
||||
x := string(data)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*bool":
|
||||
case core.BoolType.Kind():
|
||||
d := string(data)
|
||||
v, err := strconv.ParseBool(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as bool: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType))
|
||||
// case "*complex64":
|
||||
case core.Complex64Type.Kind():
|
||||
var x complex64
|
||||
if len(data) > 0 {
|
||||
err := json.Unmarshal(data, &x)
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
}
|
||||
// case "*complex128":
|
||||
case core.Complex128Type.Kind():
|
||||
var x complex128
|
||||
if len(data) > 0 {
|
||||
err := json.Unmarshal(data, &x)
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
}
|
||||
// case "*float64":
|
||||
case core.Float64Type.Kind():
|
||||
x, err := strconv.ParseFloat(string(data), 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as float64: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*float32":
|
||||
case core.Float32Type.Kind():
|
||||
var x float32
|
||||
x1, err := strconv.ParseFloat(string(data), 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as float32: %s", key, err.Error())
|
||||
}
|
||||
x = float32(x1)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*uint64":
|
||||
case core.Uint64Type.Kind():
|
||||
var x uint64
|
||||
x, err := strconv.ParseUint(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*uint":
|
||||
case core.UintType.Kind():
|
||||
var x uint
|
||||
x1, err := strconv.ParseUint(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
x = uint(x1)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*uint32":
|
||||
case core.Uint32Type.Kind():
|
||||
var x uint32
|
||||
x1, err := strconv.ParseUint(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
x = uint32(x1)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*uint8":
|
||||
case core.Uint8Type.Kind():
|
||||
var x uint8
|
||||
x1, err := strconv.ParseUint(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
x = uint8(x1)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*uint16":
|
||||
case core.Uint16Type.Kind():
|
||||
var x uint16
|
||||
x1, err := strconv.ParseUint(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
x = uint16(x1)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*int64":
|
||||
case core.Int64Type.Kind():
|
||||
sdata := string(data)
|
||||
var x int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == core.Bit &&
|
||||
strings.Contains(session.Engine.DriverName(), "mysql") {
|
||||
if len(data) == 1 {
|
||||
x = int64(data[0])
|
||||
} else {
|
||||
x = 0
|
||||
}
|
||||
} else if strings.HasPrefix(sdata, "0x") {
|
||||
x, err = strconv.ParseInt(sdata, 16, 64)
|
||||
} else if strings.HasPrefix(sdata, "0") {
|
||||
x, err = strconv.ParseInt(sdata, 8, 64)
|
||||
} else {
|
||||
x, err = strconv.ParseInt(sdata, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*int":
|
||||
case core.IntType.Kind():
|
||||
sdata := string(data)
|
||||
var x int
|
||||
var x1 int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == core.Bit &&
|
||||
strings.Contains(session.Engine.DriverName(), "mysql") {
|
||||
if len(data) == 1 {
|
||||
x = int(data[0])
|
||||
} else {
|
||||
x = 0
|
||||
}
|
||||
} else if strings.HasPrefix(sdata, "0x") {
|
||||
x1, err = strconv.ParseInt(sdata, 16, 64)
|
||||
x = int(x1)
|
||||
} else if strings.HasPrefix(sdata, "0") {
|
||||
x1, err = strconv.ParseInt(sdata, 8, 64)
|
||||
x = int(x1)
|
||||
} else {
|
||||
x1, err = strconv.ParseInt(sdata, 10, 64)
|
||||
x = int(x1)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*int32":
|
||||
case core.Int32Type.Kind():
|
||||
sdata := string(data)
|
||||
var x int32
|
||||
var x1 int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == core.Bit &&
|
||||
session.Engine.dialect.DBType() == core.MYSQL {
|
||||
if len(data) == 1 {
|
||||
x = int32(data[0])
|
||||
} else {
|
||||
x = 0
|
||||
}
|
||||
} else if strings.HasPrefix(sdata, "0x") {
|
||||
x1, err = strconv.ParseInt(sdata, 16, 64)
|
||||
x = int32(x1)
|
||||
} else if strings.HasPrefix(sdata, "0") {
|
||||
x1, err = strconv.ParseInt(sdata, 8, 64)
|
||||
x = int32(x1)
|
||||
} else {
|
||||
x1, err = strconv.ParseInt(sdata, 10, 64)
|
||||
x = int32(x1)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*int8":
|
||||
case core.Int8Type.Kind():
|
||||
sdata := string(data)
|
||||
var x int8
|
||||
var x1 int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == core.Bit &&
|
||||
strings.Contains(session.Engine.DriverName(), "mysql") {
|
||||
if len(data) == 1 {
|
||||
x = int8(data[0])
|
||||
} else {
|
||||
x = 0
|
||||
}
|
||||
} else if strings.HasPrefix(sdata, "0x") {
|
||||
x1, err = strconv.ParseInt(sdata, 16, 64)
|
||||
x = int8(x1)
|
||||
} else if strings.HasPrefix(sdata, "0") {
|
||||
x1, err = strconv.ParseInt(sdata, 8, 64)
|
||||
x = int8(x1)
|
||||
} else {
|
||||
x1, err = strconv.ParseInt(sdata, 10, 64)
|
||||
x = int8(x1)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*int16":
|
||||
case core.Int16Type.Kind():
|
||||
sdata := string(data)
|
||||
var x int16
|
||||
var x1 int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == core.Bit &&
|
||||
strings.Contains(session.Engine.DriverName(), "mysql") {
|
||||
if len(data) == 1 {
|
||||
x = int16(data[0])
|
||||
} else {
|
||||
x = 0
|
||||
}
|
||||
} else if strings.HasPrefix(sdata, "0x") {
|
||||
x1, err = strconv.ParseInt(sdata, 16, 64)
|
||||
x = int16(x1)
|
||||
} else if strings.HasPrefix(sdata, "0") {
|
||||
x1, err = strconv.ParseInt(sdata, 8, 64)
|
||||
x = int16(x1)
|
||||
} else {
|
||||
x1, err = strconv.ParseInt(sdata, 10, 64)
|
||||
x = int16(x1)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("arg %v as int: %s", key, err.Error())
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*SomeStruct":
|
||||
case reflect.Struct:
|
||||
switch fieldType {
|
||||
// case "*.time.Time":
|
||||
case core.PtrTimeType:
|
||||
x, err := session.byte2Time(col, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v = x
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
default:
|
||||
if session.Statement.UseCascade {
|
||||
structInter := reflect.New(fieldType.Elem())
|
||||
table, err := session.Engine.autoMapType(structInter.Elem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(table.PrimaryKeys) > 1 {
|
||||
panic("unsupported composited primary key cascade")
|
||||
}
|
||||
var pk = make(core.PK, len(table.PrimaryKeys))
|
||||
rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
|
||||
pk[0], err = str2PK(string(data), rawValueType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isPKZero(pk) {
|
||||
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
|
||||
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
|
||||
// property to be fetched lazily
|
||||
newsession := session.Engine.NewSession()
|
||||
defer newsession.Close()
|
||||
has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if has {
|
||||
v = structInter.Interface()
|
||||
fieldValue.Set(reflect.ValueOf(v))
|
||||
} else {
|
||||
return errors.New("cascade obj is not exist")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// convert a field value of a struct to interface for put into db
|
||||
func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) {
|
||||
if fieldValue.CanAddr() {
|
||||
if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
|
||||
data, err := fieldConvert.ToDB()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if col.SQLType.IsBlob() {
|
||||
return data, nil
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
}
|
||||
|
||||
if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
|
||||
data, err := fieldConvert.ToDB()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if col.SQLType.IsBlob() {
|
||||
return data, nil
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
fieldType := fieldValue.Type()
|
||||
k := fieldType.Kind()
|
||||
if k == reflect.Ptr {
|
||||
if fieldValue.IsNil() {
|
||||
return nil, nil
|
||||
} else if !fieldValue.IsValid() {
|
||||
session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid")
|
||||
return nil, nil
|
||||
} else {
|
||||
// !nashtsai! deference pointer type to instance type
|
||||
fieldValue = fieldValue.Elem()
|
||||
fieldType = fieldValue.Type()
|
||||
k = fieldType.Kind()
|
||||
}
|
||||
}
|
||||
|
||||
switch k {
|
||||
case reflect.Bool:
|
||||
return fieldValue.Bool(), nil
|
||||
case reflect.String:
|
||||
return fieldValue.String(), nil
|
||||
case reflect.Struct:
|
||||
if fieldType.ConvertibleTo(core.TimeType) {
|
||||
t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
|
||||
if session.Engine.dialect.DBType() == core.MSSQL {
|
||||
if t.IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
tf := session.Engine.FormatTime(col.SQLType.Name, t)
|
||||
return tf, nil
|
||||
}
|
||||
|
||||
if !col.SQLType.IsJson() {
|
||||
// !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
|
||||
if v, ok := fieldValue.Interface().(driver.Valuer); ok {
|
||||
return v.Value()
|
||||
}
|
||||
|
||||
fieldTable, err := session.Engine.autoMapType(fieldValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fieldTable.PrimaryKeys) == 1 {
|
||||
pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
|
||||
return pkField.Interface(), nil
|
||||
}
|
||||
return 0, fmt.Errorf("no primary key for col %v", col.Name)
|
||||
}
|
||||
|
||||
if col.SQLType.IsText() {
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return 0, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
} else if col.SQLType.IsBlob() {
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return 0, err
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type())
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return 0, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
if !fieldValue.IsValid() {
|
||||
return fieldValue.Interface(), nil
|
||||
}
|
||||
|
||||
if col.SQLType.IsText() {
|
||||
bytes, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return 0, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
} else if col.SQLType.IsBlob() {
|
||||
var bytes []byte
|
||||
var err error
|
||||
if (k == reflect.Array || k == reflect.Slice) &&
|
||||
(fieldValue.Type().Elem().Kind() == reflect.Uint8) {
|
||||
bytes = fieldValue.Bytes()
|
||||
} else {
|
||||
bytes, err = json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
session.Engine.logger.Error(err)
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
return nil, ErrUnSupportedType
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return int64(fieldValue.Uint()), nil
|
||||
default:
|
||||
return fieldValue.Interface(), nil
|
||||
}
|
||||
}
|
|
@ -169,31 +169,43 @@ func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Va
|
|||
return err
|
||||
}
|
||||
|
||||
var newElemFunc func() reflect.Value
|
||||
var newElemFunc func(fields []string) reflect.Value
|
||||
elemType := containerValue.Type().Elem()
|
||||
var isPointer bool
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
newElemFunc = func() reflect.Value {
|
||||
return reflect.New(elemType.Elem())
|
||||
isPointer = true
|
||||
elemType = elemType.Elem()
|
||||
}
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
return errors.New("pointer to pointer is not supported")
|
||||
}
|
||||
|
||||
newElemFunc = func(fields []string) reflect.Value {
|
||||
switch elemType.Kind() {
|
||||
case reflect.Slice:
|
||||
slice := reflect.MakeSlice(elemType, len(fields), len(fields))
|
||||
x := reflect.New(slice.Type())
|
||||
x.Elem().Set(slice)
|
||||
return x
|
||||
case reflect.Map:
|
||||
mp := reflect.MakeMap(elemType)
|
||||
x := reflect.New(mp.Type())
|
||||
x.Elem().Set(mp)
|
||||
return x
|
||||
}
|
||||
} else {
|
||||
newElemFunc = func() reflect.Value {
|
||||
return reflect.New(elemType)
|
||||
}
|
||||
}
|
||||
|
||||
var containerValueSetFunc func(*reflect.Value, core.PK) error
|
||||
|
||||
if containerValue.Kind() == reflect.Slice {
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
|
||||
containerValue.Set(reflect.Append(containerValue, reflect.ValueOf(newValue.Interface())))
|
||||
return nil
|
||||
}
|
||||
if isPointer {
|
||||
containerValue.Set(reflect.Append(containerValue, newValue.Elem().Addr()))
|
||||
} else {
|
||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
|
||||
containerValue.Set(reflect.Append(containerValue, reflect.Indirect(reflect.ValueOf(newValue.Interface()))))
|
||||
return nil
|
||||
containerValue.Set(reflect.Append(containerValue, newValue.Elem()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
keyType := containerValue.Type().Key()
|
||||
|
@ -204,40 +216,45 @@ func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Va
|
|||
return errors.New("don't support multiple primary key's map has non-slice key type")
|
||||
}
|
||||
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
|
||||
keyValue := reflect.New(keyType)
|
||||
err := convertPKToValue(table, keyValue.Interface(), pk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerValue.SetMapIndex(keyValue.Elem(), reflect.ValueOf(newValue.Interface()))
|
||||
return nil
|
||||
}
|
||||
if isPointer {
|
||||
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr())
|
||||
} else {
|
||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
|
||||
keyValue := reflect.New(keyType)
|
||||
err := convertPKToValue(table, keyValue.Interface(), pk)
|
||||
if err != nil {
|
||||
return err
|
||||
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem())
|
||||
}
|
||||
containerValue.SetMapIndex(keyValue.Elem(), reflect.Indirect(reflect.ValueOf(newValue.Interface())))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var newValue = newElemFunc()
|
||||
if elemType.Kind() == reflect.Struct {
|
||||
var newValue = newElemFunc(fields)
|
||||
dataStruct := rValue(newValue.Interface())
|
||||
if dataStruct.Kind() == reflect.Struct {
|
||||
return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, containerValueSetFunc)
|
||||
tb, err := session.Engine.autoMapType(dataStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return session.rows2Beans(rawRows, fields, len(fields), tb, newElemFunc, containerValueSetFunc)
|
||||
}
|
||||
|
||||
for rawRows.Next() {
|
||||
var newValue = newElemFunc()
|
||||
var newValue = newElemFunc(fields)
|
||||
bean := newValue.Interface()
|
||||
|
||||
if err := rawRows.Scan(bean); err != nil {
|
||||
switch elemType.Kind() {
|
||||
case reflect.Slice:
|
||||
err = rawRows.ScanSlice(bean)
|
||||
case reflect.Map:
|
||||
err = rawRows.ScanMap(bean)
|
||||
default:
|
||||
err = rawRows.Scan(bean)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -394,7 +411,10 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
if rv.Kind() != reflect.Ptr {
|
||||
rv = rv.Addr()
|
||||
}
|
||||
id := session.Engine.IdOfV(rv)
|
||||
id, err := session.Engine.idOfV(rv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sid, err := id.ToString()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -20,7 +20,14 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
defer session.Close()
|
||||
}
|
||||
|
||||
session.Statement.setRefValue(rValue(bean))
|
||||
beanValue := reflect.ValueOf(bean)
|
||||
if beanValue.Kind() != reflect.Ptr {
|
||||
return false, errors.New("needs a pointer")
|
||||
}
|
||||
|
||||
if beanValue.Elem().Kind() == reflect.Struct {
|
||||
session.Statement.setRefValue(beanValue.Elem())
|
||||
}
|
||||
|
||||
var sqlStr string
|
||||
var args []interface{}
|
||||
|
@ -36,7 +43,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
args = session.Statement.RawParams
|
||||
}
|
||||
|
||||
if session.canCache() {
|
||||
if session.canCache() && beanValue.Elem().Kind() == reflect.Struct {
|
||||
if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil &&
|
||||
!session.Statement.unscoped {
|
||||
has, err := session.cacheGet(bean, sqlStr, args...)
|
||||
|
@ -46,13 +53,14 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return session.nocacheGet(bean, sqlStr, args...)
|
||||
return session.nocacheGet(beanValue.Elem().Kind(), bean, sqlStr, args...)
|
||||
}
|
||||
|
||||
func (session *Session) nocacheGet(bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
|
||||
func (session *Session) nocacheGet(beanKind reflect.Kind, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
|
||||
session.queryPreprocess(&sqlStr, args...)
|
||||
|
||||
var rawRows *core.Rows
|
||||
var err error
|
||||
session.queryPreprocess(&sqlStr, args...)
|
||||
if session.IsAutoCommit {
|
||||
_, rawRows, err = session.innerQuery(sqlStr, args...)
|
||||
} else {
|
||||
|
@ -65,10 +73,24 @@ func (session *Session) nocacheGet(bean interface{}, sqlStr string, args ...inte
|
|||
defer rawRows.Close()
|
||||
|
||||
if rawRows.Next() {
|
||||
switch beanKind {
|
||||
case reflect.Struct:
|
||||
fields, err := rawRows.Columns()
|
||||
if err == nil {
|
||||
_, err = session.row2Bean(rawRows, fields, len(fields), bean)
|
||||
if err != nil {
|
||||
// WARN: Alougth rawRows return true, but get fields failed
|
||||
return true, err
|
||||
}
|
||||
dataStruct := rValue(bean)
|
||||
session.Statement.setRefValue(dataStruct)
|
||||
_, err = session.row2Bean(rawRows, fields, len(fields), bean, &dataStruct, session.Statement.RefTable)
|
||||
case reflect.Slice:
|
||||
err = rawRows.ScanSlice(bean)
|
||||
case reflect.Map:
|
||||
err = rawRows.ScanMap(bean)
|
||||
default:
|
||||
err = rawRows.Scan(bean)
|
||||
}
|
||||
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
|
@ -145,20 +167,8 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
|
|||
}
|
||||
cacheBean := cacher.GetBean(tableName, sid)
|
||||
if cacheBean == nil {
|
||||
/*newSession := session.Engine.NewSession()
|
||||
defer newSession.Close()
|
||||
cacheBean = reflect.New(structValue.Type()).Interface()
|
||||
newSession.Id(id).NoCache()
|
||||
if session.Statement.AltTableName != "" {
|
||||
newSession.Table(session.Statement.AltTableName)
|
||||
}
|
||||
if !session.Statement.UseCascade {
|
||||
newSession.NoCascade()
|
||||
}
|
||||
has, err = newSession.Get(cacheBean)
|
||||
*/
|
||||
cacheBean = bean
|
||||
has, err = session.nocacheGet(cacheBean, sqlStr, args...)
|
||||
has, err = session.nocacheGet(reflect.Struct, cacheBean, sqlStr, args...)
|
||||
if err != nil || !has {
|
||||
return has, err
|
||||
}
|
||||
|
|
|
@ -210,13 +210,29 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
|
|||
}
|
||||
cleanupProcessorsClosures(&session.beforeClosures)
|
||||
|
||||
statement := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
|
||||
var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)"
|
||||
var statement string
|
||||
if session.Engine.dialect.DBType() == core.ORACLE {
|
||||
sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL"
|
||||
temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (",
|
||||
session.Engine.Quote(session.Statement.TableName()),
|
||||
session.Engine.QuoteStr(),
|
||||
strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()),
|
||||
strings.Join(colNames, session.Engine.QuoteStr() + ", " + session.Engine.QuoteStr()),
|
||||
session.Engine.QuoteStr())
|
||||
statement = fmt.Sprintf(sql,
|
||||
session.Engine.Quote(session.Statement.TableName()),
|
||||
session.Engine.QuoteStr(),
|
||||
strings.Join(colNames, session.Engine.QuoteStr() + ", " + session.Engine.QuoteStr()),
|
||||
session.Engine.QuoteStr(),
|
||||
strings.Join(colMultiPlaces, temp))
|
||||
} else {
|
||||
statement = fmt.Sprintf(sql,
|
||||
session.Engine.Quote(session.Statement.TableName()),
|
||||
session.Engine.QuoteStr(),
|
||||
strings.Join(colNames, session.Engine.QuoteStr() + ", " + session.Engine.QuoteStr()),
|
||||
session.Engine.QuoteStr(),
|
||||
strings.Join(colMultiPlaces, "),("))
|
||||
|
||||
}
|
||||
res, err := session.exec(statement, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -309,8 +325,8 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
// remove the expr columns
|
||||
for i, colName := range colNames {
|
||||
if colName == v.colName {
|
||||
colNames = append(colNames[:i], colNames[i+1:]...)
|
||||
args = append(args[:i], args[i+1:]...)
|
||||
colNames = append(colNames[:i], colNames[i + 1:]...)
|
||||
args = append(args[:i], args[i + 1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,11 +335,11 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
exprColVals = append(exprColVals, v.expr)
|
||||
}
|
||||
|
||||
colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns))
|
||||
colPlaces := strings.Repeat("?, ", len(colNames) - len(exprColumns))
|
||||
if len(exprColVals) > 0 {
|
||||
colPlaces = colPlaces + strings.Join(exprColVals, ", ")
|
||||
} else {
|
||||
colPlaces = colPlaces[0 : len(colPlaces)-2]
|
||||
colPlaces = colPlaces[0 : len(colPlaces) - 2]
|
||||
}
|
||||
|
||||
sqlStr := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
|
||||
|
|
|
@ -70,7 +70,7 @@ func (session *Session) innerQuery2(sqlStr string, params ...interface{}) ([]map
|
|||
return rows2maps(rows)
|
||||
}
|
||||
|
||||
// Query a raw sql and return records as []map[string][]byte
|
||||
// Query runs a raw sql and return records as []map[string][]byte
|
||||
func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
|
||||
defer session.resetStatement()
|
||||
if session.IsAutoClose {
|
||||
|
@ -80,6 +80,15 @@ func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSl
|
|||
return session.query(sqlStr, paramStr...)
|
||||
}
|
||||
|
||||
// QueryString runs a raw sql and return records as []map[string]string
|
||||
func (session *Session) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) {
|
||||
defer session.resetStatement()
|
||||
if session.IsAutoClose {
|
||||
defer session.Close()
|
||||
}
|
||||
return session.query2(sqlStr, args...)
|
||||
}
|
||||
|
||||
// =============================
|
||||
// for string
|
||||
// =============================
|
||||
|
|
|
@ -306,7 +306,10 @@ func (session *Session) Sync2(beans ...interface{}) error {
|
|||
|
||||
for _, bean := range beans {
|
||||
v := rValue(bean)
|
||||
table := engine.mapType(v)
|
||||
table, err := engine.mapType(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
structTables = append(structTables, table)
|
||||
var tbName = session.tbNameNoSchema(table)
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int6
|
|||
session.queryPreprocess(&sqlStr, args...)
|
||||
|
||||
var err error
|
||||
var res = make([]int64, 0, len(columnNames))
|
||||
var res = make([]int64, len(columnNames), len(columnNames))
|
||||
if session.IsAutoCommit {
|
||||
err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res)
|
||||
} else {
|
||||
|
|
|
@ -253,47 +253,58 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
var condSQL string
|
||||
cond := session.Statement.cond.And(autoCond)
|
||||
|
||||
doIncVer := false
|
||||
var doIncVer = (table != nil && table.Version != "" && session.Statement.checkVersion)
|
||||
var verValue *reflect.Value
|
||||
if table != nil && table.Version != "" && session.Statement.checkVersion {
|
||||
if doIncVer {
|
||||
verValue, err = table.VersionColumn().ValueOf(bean)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()})
|
||||
condSQL, condArgs, _ = builder.ToSQL(cond)
|
||||
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
colNames = append(colNames, session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1")
|
||||
}
|
||||
|
||||
if st.LimitN > 0 {
|
||||
condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
|
||||
}
|
||||
|
||||
sqlStr = fmt.Sprintf("UPDATE %v SET %v, %v %v",
|
||||
session.Engine.Quote(session.Statement.TableName()),
|
||||
strings.Join(colNames, ", "),
|
||||
session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1",
|
||||
condSQL)
|
||||
|
||||
doIncVer = true
|
||||
} else {
|
||||
condSQL, condArgs, _ = builder.ToSQL(cond)
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
|
||||
if st.LimitN > 0 {
|
||||
condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
|
||||
if st.OrderStr != "" {
|
||||
condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr)
|
||||
}
|
||||
|
||||
sqlStr = fmt.Sprintf("UPDATE %v SET %v %v",
|
||||
// TODO: Oracle support needed
|
||||
var top string
|
||||
if st.LimitN > 0 {
|
||||
if st.Engine.dialect.DBType() == core.MYSQL {
|
||||
condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
|
||||
} else if st.Engine.dialect.DBType() == core.SQLITE {
|
||||
tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
|
||||
cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)",
|
||||
session.Engine.Quote(session.Statement.TableName()), tempCondSQL), condArgs...))
|
||||
condSQL, condArgs, _ = builder.ToSQL(cond)
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
} else if st.Engine.dialect.DBType() == core.POSTGRES {
|
||||
tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
|
||||
cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)",
|
||||
session.Engine.Quote(session.Statement.TableName()), tempCondSQL), condArgs...))
|
||||
condSQL, condArgs, _ = builder.ToSQL(cond)
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
} else if st.Engine.dialect.DBType() == core.MSSQL {
|
||||
top = fmt.Sprintf("top (%d) ", st.LimitN)
|
||||
}
|
||||
}
|
||||
|
||||
sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v",
|
||||
top,
|
||||
session.Engine.Quote(session.Statement.TableName()),
|
||||
strings.Join(colNames, ", "),
|
||||
condSQL)
|
||||
}
|
||||
|
||||
res, err := session.exec(sqlStr, append(args, condArgs...)...)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
// func init() {
|
||||
// core.RegisterDriver("sqlite3", &sqlite3Driver{})
|
||||
// }
|
||||
|
||||
type sqlite3Driver struct {
|
||||
}
|
||||
|
||||
func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
|
||||
return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
|
||||
}
|
|
@ -39,7 +39,7 @@ type Statement struct {
|
|||
Engine *Engine
|
||||
Start int
|
||||
LimitN int
|
||||
IdParam *core.PK
|
||||
idParam *core.PK
|
||||
OrderStr string
|
||||
JoinStr string
|
||||
joinArgs []interface{}
|
||||
|
@ -91,7 +91,7 @@ func (statement *Statement) Init() {
|
|||
statement.columnMap = make(map[string]bool)
|
||||
statement.AltTableName = ""
|
||||
statement.tableName = ""
|
||||
statement.IdParam = nil
|
||||
statement.idParam = nil
|
||||
statement.RawSQL = ""
|
||||
statement.RawParams = make([]interface{}, 0)
|
||||
statement.UseCache = true
|
||||
|
@ -195,29 +195,26 @@ func (statement *Statement) Or(query interface{}, args ...interface{}) *Statemen
|
|||
|
||||
// In generate "Where column IN (?) " statement
|
||||
func (statement *Statement) In(column string, args ...interface{}) *Statement {
|
||||
if len(args) == 0 {
|
||||
return statement
|
||||
}
|
||||
|
||||
in := builder.In(column, args...)
|
||||
in := builder.In(statement.Engine.Quote(column), args...)
|
||||
statement.cond = statement.cond.And(in)
|
||||
return statement
|
||||
}
|
||||
|
||||
// NotIn generate "Where column NOT IN (?) " statement
|
||||
func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
|
||||
if len(args) == 0 {
|
||||
return statement
|
||||
}
|
||||
|
||||
in := builder.NotIn(column, args...)
|
||||
statement.cond = statement.cond.And(in)
|
||||
notIn := builder.NotIn(statement.Engine.Quote(column), args...)
|
||||
statement.cond = statement.cond.And(notIn)
|
||||
return statement
|
||||
}
|
||||
|
||||
func (statement *Statement) setRefValue(v reflect.Value) {
|
||||
statement.RefTable = statement.Engine.autoMapType(reflect.Indirect(v))
|
||||
func (statement *Statement) setRefValue(v reflect.Value) error {
|
||||
var err error
|
||||
statement.RefTable, err = statement.Engine.autoMapType(reflect.Indirect(v))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.tableName = statement.Engine.tbName(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Table tempororily set table name, the parameter could be a string or a pointer of struct
|
||||
|
@ -227,7 +224,12 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
|
|||
if t.Kind() == reflect.String {
|
||||
statement.AltTableName = tableNameOrBean.(string)
|
||||
} else if t.Kind() == reflect.Struct {
|
||||
statement.RefTable = statement.Engine.autoMapType(v)
|
||||
var err error
|
||||
statement.RefTable, err = statement.Engine.autoMapType(v)
|
||||
if err != nil {
|
||||
statement.Engine.logger.Error(err)
|
||||
return statement
|
||||
}
|
||||
statement.AltTableName = statement.Engine.tbName(v)
|
||||
}
|
||||
return statement
|
||||
|
@ -418,7 +420,11 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
|
|||
if fieldValue == reflect.Zero(fieldType) {
|
||||
continue
|
||||
}
|
||||
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
|
||||
if fieldType.Kind() == reflect.Array {
|
||||
if isArrayValueZero(fieldValue) {
|
||||
continue
|
||||
}
|
||||
} else if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -433,13 +439,16 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
|
|||
} else if col.SQLType.IsBlob() {
|
||||
var bytes []byte
|
||||
var err error
|
||||
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
|
||||
if fieldType.Kind() == reflect.Slice &&
|
||||
fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
if fieldValue.Len() > 0 {
|
||||
val = fieldValue.Bytes()
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if fieldType.Kind() == reflect.Array &&
|
||||
fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
val = fieldValue.Slice(0, 0).Interface()
|
||||
} else {
|
||||
bytes, err = json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
|
@ -651,7 +660,9 @@ func buildConds(engine *Engine, table *core.Table, bean interface{},
|
|||
}
|
||||
}
|
||||
}
|
||||
case reflect.Array, reflect.Slice, reflect.Map:
|
||||
case reflect.Array:
|
||||
continue
|
||||
case reflect.Slice, reflect.Map:
|
||||
if fieldValue == reflect.Zero(fieldType) {
|
||||
continue
|
||||
}
|
||||
|
@ -706,13 +717,6 @@ func (statement *Statement) TableName() string {
|
|||
return statement.tableName
|
||||
}
|
||||
|
||||
// Id generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?"
|
||||
//
|
||||
// Deprecated: use ID instead
|
||||
func (statement *Statement) Id(id interface{}) *Statement {
|
||||
return statement.ID(id)
|
||||
}
|
||||
|
||||
// ID generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?"
|
||||
func (statement *Statement) ID(id interface{}) *Statement {
|
||||
idValue := reflect.ValueOf(id)
|
||||
|
@ -721,23 +725,23 @@ func (statement *Statement) ID(id interface{}) *Statement {
|
|||
switch idType {
|
||||
case ptrPkType:
|
||||
if pkPtr, ok := (id).(*core.PK); ok {
|
||||
statement.IdParam = pkPtr
|
||||
statement.idParam = pkPtr
|
||||
return statement
|
||||
}
|
||||
case pkType:
|
||||
if pk, ok := (id).(core.PK); ok {
|
||||
statement.IdParam = &pk
|
||||
statement.idParam = &pk
|
||||
return statement
|
||||
}
|
||||
}
|
||||
|
||||
switch idType.Kind() {
|
||||
case reflect.String:
|
||||
statement.IdParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()}
|
||||
statement.idParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()}
|
||||
return statement
|
||||
}
|
||||
|
||||
statement.IdParam = &core.PK{id}
|
||||
statement.idParam = &core.PK{id}
|
||||
return statement
|
||||
}
|
||||
|
||||
|
@ -1120,7 +1124,11 @@ func (statement *Statement) genConds(bean interface{}) (string, []interface{}, e
|
|||
}
|
||||
|
||||
func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}) {
|
||||
statement.setRefValue(rValue(bean))
|
||||
v := rValue(bean)
|
||||
isStruct := v.Kind() == reflect.Struct
|
||||
if isStruct {
|
||||
statement.setRefValue(v)
|
||||
}
|
||||
|
||||
var columnStr = statement.ColumnStr
|
||||
if len(statement.selectStr) > 0 {
|
||||
|
@ -1139,14 +1147,22 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{})
|
|||
if len(columnStr) == 0 {
|
||||
if len(statement.GroupByStr) > 0 {
|
||||
columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
|
||||
} else {
|
||||
columnStr = "*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
condSQL, condArgs, _ := statement.genConds(bean)
|
||||
if len(columnStr) == 0 {
|
||||
columnStr = "*"
|
||||
}
|
||||
|
||||
var condSQL string
|
||||
var condArgs []interface{}
|
||||
if isStruct {
|
||||
condSQL, condArgs, _ = statement.genConds(bean)
|
||||
} else {
|
||||
condSQL, condArgs, _ = builder.ToSQL(statement.cond)
|
||||
}
|
||||
|
||||
return statement.genSelectSQL(columnStr, condSQL), append(statement.joinArgs, condArgs...)
|
||||
}
|
||||
|
@ -1172,7 +1188,7 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri
|
|||
|
||||
var sumStrs = make([]string, 0, len(columns))
|
||||
for _, colName := range columns {
|
||||
sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", colName))
|
||||
sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", statement.Engine.Quote(colName)))
|
||||
}
|
||||
|
||||
condSQL, condArgs, _ := statement.genConds(bean)
|
||||
|
@ -1182,7 +1198,7 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri
|
|||
|
||||
func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) {
|
||||
var distinct string
|
||||
if statement.IsDistinct {
|
||||
if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") {
|
||||
distinct = "DISTINCT "
|
||||
}
|
||||
|
||||
|
@ -1289,14 +1305,14 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) {
|
|||
}
|
||||
|
||||
func (statement *Statement) processIDParam() {
|
||||
if statement.IdParam == nil {
|
||||
if statement.idParam == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, col := range statement.RefTable.PKColumns() {
|
||||
var colName = statement.colName(col, statement.TableName())
|
||||
if i < len(*(statement.IdParam)) {
|
||||
statement.cond = statement.cond.And(builder.Eq{colName: (*(statement.IdParam))[i]})
|
||||
if i < len(*(statement.idParam)) {
|
||||
statement.cond = statement.cond.And(builder.Eq{colName: (*(statement.idParam))[i]})
|
||||
} else {
|
||||
statement.cond = statement.cond.And(builder.Eq{colName: ""})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
)
|
||||
|
||||
type tagContext struct {
|
||||
tagName string
|
||||
params []string
|
||||
preTag, nextTag string
|
||||
table *core.Table
|
||||
col *core.Column
|
||||
fieldValue reflect.Value
|
||||
isIndex bool
|
||||
isUnique bool
|
||||
indexNames map[string]int
|
||||
engine *Engine
|
||||
hasCacheTag bool
|
||||
hasNoCacheTag bool
|
||||
ignoreNext bool
|
||||
}
|
||||
|
||||
// tagHandler describes tag handler for XORM
|
||||
type tagHandler func(ctx *tagContext) error
|
||||
|
||||
var (
|
||||
// defaultTagHandlers enumerates all the default tag handler
|
||||
defaultTagHandlers = map[string]tagHandler{
|
||||
"<-": OnlyFromDBTagHandler,
|
||||
"->": OnlyToDBTagHandler,
|
||||
"PK": PKTagHandler,
|
||||
"NULL": NULLTagHandler,
|
||||
"NOT": IgnoreTagHandler,
|
||||
"AUTOINCR": AutoIncrTagHandler,
|
||||
"DEFAULT": DefaultTagHandler,
|
||||
"CREATED": CreatedTagHandler,
|
||||
"UPDATED": UpdatedTagHandler,
|
||||
"DELETED": DeletedTagHandler,
|
||||
"VERSION": VersionTagHandler,
|
||||
"UTC": UTCTagHandler,
|
||||
"LOCAL": LocalTagHandler,
|
||||
"NOTNULL": NotNullTagHandler,
|
||||
"INDEX": IndexTagHandler,
|
||||
"UNIQUE": UniqueTagHandler,
|
||||
"CACHE": CacheTagHandler,
|
||||
"NOCACHE": NoCacheTagHandler,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for k := range core.SqlTypes {
|
||||
defaultTagHandlers[k] = SQLTypeTagHandler
|
||||
}
|
||||
}
|
||||
|
||||
// IgnoreTagHandler describes ignored tag handler
|
||||
func IgnoreTagHandler(ctx *tagContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnlyFromDBTagHandler describes mapping direction tag handler
|
||||
func OnlyFromDBTagHandler(ctx *tagContext) error {
|
||||
ctx.col.MapType = core.ONLYFROMDB
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnlyToDBTagHandler describes mapping direction tag handler
|
||||
func OnlyToDBTagHandler(ctx *tagContext) error {
|
||||
ctx.col.MapType = core.ONLYTODB
|
||||
return nil
|
||||
}
|
||||
|
||||
// PKTagHandler decribes primary key tag handler
|
||||
func PKTagHandler(ctx *tagContext) error {
|
||||
ctx.col.IsPrimaryKey = true
|
||||
ctx.col.Nullable = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// NULLTagHandler describes null tag handler
|
||||
func NULLTagHandler(ctx *tagContext) error {
|
||||
ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT")
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotNullTagHandler describes notnull tag handler
|
||||
func NotNullTagHandler(ctx *tagContext) error {
|
||||
ctx.col.Nullable = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// AutoIncrTagHandler describes autoincr tag handler
|
||||
func AutoIncrTagHandler(ctx *tagContext) error {
|
||||
ctx.col.IsAutoIncrement = true
|
||||
/*
|
||||
if len(ctx.params) > 0 {
|
||||
autoStartInt, err := strconv.Atoi(ctx.params[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.col.AutoIncrStart = autoStartInt
|
||||
} else {
|
||||
ctx.col.AutoIncrStart = 1
|
||||
}
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultTagHandler describes default tag handler
|
||||
func DefaultTagHandler(ctx *tagContext) error {
|
||||
if len(ctx.params) > 0 {
|
||||
ctx.col.Default = ctx.params[0]
|
||||
} else {
|
||||
ctx.col.Default = ctx.nextTag
|
||||
ctx.ignoreNext = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatedTagHandler describes created tag handler
|
||||
func CreatedTagHandler(ctx *tagContext) error {
|
||||
ctx.col.IsCreated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionTagHandler describes version tag handler
|
||||
func VersionTagHandler(ctx *tagContext) error {
|
||||
ctx.col.IsVersion = true
|
||||
ctx.col.Default = "1"
|
||||
return nil
|
||||
}
|
||||
|
||||
// UTCTagHandler describes utc tag handler
|
||||
func UTCTagHandler(ctx *tagContext) error {
|
||||
ctx.col.TimeZone = time.UTC
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalTagHandler describes local tag handler
|
||||
func LocalTagHandler(ctx *tagContext) error {
|
||||
if len(ctx.params) == 0 {
|
||||
ctx.col.TimeZone = time.Local
|
||||
} else {
|
||||
var err error
|
||||
ctx.col.TimeZone, err = time.LoadLocation(ctx.params[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdatedTagHandler describes updated tag handler
|
||||
func UpdatedTagHandler(ctx *tagContext) error {
|
||||
ctx.col.IsUpdated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeletedTagHandler describes deleted tag handler
|
||||
func DeletedTagHandler(ctx *tagContext) error {
|
||||
ctx.col.IsDeleted = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// IndexTagHandler describes index tag handler
|
||||
func IndexTagHandler(ctx *tagContext) error {
|
||||
if len(ctx.params) > 0 {
|
||||
ctx.indexNames[ctx.params[0]] = core.IndexType
|
||||
} else {
|
||||
ctx.isIndex = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UniqueTagHandler describes unique tag handler
|
||||
func UniqueTagHandler(ctx *tagContext) error {
|
||||
if len(ctx.params) > 0 {
|
||||
ctx.indexNames[ctx.params[0]] = core.UniqueType
|
||||
} else {
|
||||
ctx.isUnique = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SQLTypeTagHandler describes SQL Type tag handler
|
||||
func SQLTypeTagHandler(ctx *tagContext) error {
|
||||
ctx.col.SQLType = core.SQLType{Name: ctx.tagName}
|
||||
if len(ctx.params) > 0 {
|
||||
if ctx.tagName == core.Enum {
|
||||
ctx.col.EnumOptions = make(map[string]int)
|
||||
for k, v := range ctx.params {
|
||||
v = strings.TrimSpace(v)
|
||||
v = strings.Trim(v, "'")
|
||||
ctx.col.EnumOptions[v] = k
|
||||
}
|
||||
} else if ctx.tagName == core.Set {
|
||||
ctx.col.SetOptions = make(map[string]int)
|
||||
for k, v := range ctx.params {
|
||||
v = strings.TrimSpace(v)
|
||||
v = strings.Trim(v, "'")
|
||||
ctx.col.SetOptions[v] = k
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if len(ctx.params) == 2 {
|
||||
ctx.col.Length, err = strconv.Atoi(ctx.params[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.col.Length2, err = strconv.Atoi(ctx.params[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if len(ctx.params) == 1 {
|
||||
ctx.col.Length, err = strconv.Atoi(ctx.params[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtendsTagHandler describes extends tag handler
|
||||
func ExtendsTagHandler(ctx *tagContext) error {
|
||||
var fieldValue = ctx.fieldValue
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.Ptr:
|
||||
f := fieldValue.Type().Elem()
|
||||
if f.Kind() == reflect.Struct {
|
||||
fieldPtr := fieldValue
|
||||
fieldValue = fieldValue.Elem()
|
||||
if !fieldValue.IsValid() || fieldPtr.IsNil() {
|
||||
fieldValue = reflect.New(f).Elem()
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
parentTable, err := ctx.engine.mapType(fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, col := range parentTable.Columns() {
|
||||
col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName)
|
||||
ctx.table.AddColumn(col)
|
||||
for indexName, indexType := range col.Indexes {
|
||||
addIndex(indexName, ctx.table, col, indexType)
|
||||
}
|
||||
}
|
||||
default:
|
||||
//TODO: warning
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CacheTagHandler describes cache tag handler
|
||||
func CacheTagHandler(ctx *tagContext) error {
|
||||
if !ctx.hasCacheTag {
|
||||
ctx.hasCacheTag = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoCacheTagHandler describes nocache tag handler
|
||||
func NoCacheTagHandler(ctx *tagContext) error {
|
||||
if !ctx.hasNoCacheTag {
|
||||
ctx.hasNoCacheTag = true
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
const (
|
||||
// Version show the xorm's version
|
||||
Version string = "0.6.0.1022"
|
||||
Version string = "0.6.2.0401"
|
||||
)
|
||||
|
||||
func regDrvsNDialects() bool {
|
||||
|
@ -86,6 +86,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
|
|||
mutex: &sync.RWMutex{},
|
||||
TagIdentifier: "xorm",
|
||||
TZLocation: time.Local,
|
||||
tagHandlers: defaultTagHandlers,
|
||||
}
|
||||
|
||||
logger := NewSimpleLogger(os.Stdout)
|
||||
|
|
|
@ -455,10 +455,10 @@
|
|||
"revisionTime": "2016-08-11T02:11:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "COlm4o3G1rUSqr33iumtjY1qKD8=",
|
||||
"checksumSHA1": "3FEBM0FYERf8jpaResApwcQpr40=",
|
||||
"path": "github.com/go-xorm/xorm",
|
||||
"revision": "1bc93ba022236fcc94092fa40105b96e1d1d2346",
|
||||
"revisionTime": "2017-02-20T09:51:59Z"
|
||||
"revision": "7e70eb82224bc950d4fb936036e925a51947c245",
|
||||
"revisionTime": "2017-04-02T10:02:47Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=",
|
||||
|
|
Loading…
Reference in New Issue