Find(&repos) | 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). | Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true). | ||||
Where(env.cond()). | Where(env.cond()). | ||||
GroupBy("`repository`.id"). | GroupBy("`repository`.id"). | ||||
OrderBy("updated_unix DESC"). | 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) | Find(&repos) | ||||
} | } |
* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) | * 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) | * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) | ||||
# Changelog | # Changelog | ||||
* **v0.6.2** | |||||
* refactor tag parse methods | |||||
* add Scan features to Get | |||||
* add QueryString method | |||||
* **v0.6.0** | * **v0.6.0** | ||||
* remove support for ql | * 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` | * add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so `Where`, `And`, `Or` | ||||
# Installation | # 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 | go get github.com/go-xorm/xorm | ||||
# Documents | # Documents | ||||
err := engine.Sync2(new(User)) | 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 | ```Go | ||||
results, err := engine.Query("select * from user") | 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 | ```Go | ||||
affected, err := engine.Exec("update user set age = ? where name = ?", age, name) | 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 | ```Go | ||||
affected, err := engine.Insert(&user) | affected, err := engine.Insert(&user) | ||||
// SELECT * FROM user LIMIT 1 | // SELECT * FROM user LIMIT 1 | ||||
has, err := engine.Where("name = ?", name).Desc("id").Get(&user) | has, err := engine.Where("name = ?", name).Desc("id").Get(&user) | ||||
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 | // 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 | * Query multiple records from database, also you can use join and extends |
## 更新日志 | ## 更新日志 | ||||
* **v0.6.2** | |||||
* 重构Tag解析方式 | |||||
* Get方法新增类似Sacn的特性 | |||||
* 新增 QueryString 方法 | |||||
* **v0.6.0** | * **v0.6.0** | ||||
* 去除对 ql 的支持 | * 去除对 ql 的支持 | ||||
* 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数 | * 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数 | ||||
## 安装 | ## 安装 | ||||
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: | |||||
gopm get github.com/go-xorm/xorm | |||||
或者您也可以使用go工具进行安装: | |||||
go get github.com/go-xorm/xorm | go get github.com/go-xorm/xorm | ||||
## 文档 | ## 文档 | ||||
err := engine.Sync2(new(User)) | err := engine.Sync2(new(User)) | ||||
``` | ``` | ||||
* 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte | |||||
* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string | |||||
```Go | ```Go | ||||
results, err := engine.Query("select * from user") | results, err := engine.Query("select * from user") | ||||
results, err := engine.QueryString("select * from user") | |||||
``` | ``` | ||||
* 执行一个SQL语句 | |||||
* `Exec` 执行一个SQL语句 | |||||
```Go | ```Go | ||||
affected, err := engine.Exec("update user set age = ? where name = ?", age, name) | affected, err := engine.Exec("update user set age = ? where name = ?", age, name) | ||||
// SELECT * FROM user LIMIT 1 | // SELECT * FROM user LIMIT 1 | ||||
has, err := engine.Where("name = ?", name).Desc("id").Get(&user) | has, err := engine.Where("name = ?", name).Desc("id").Get(&user) | ||||
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 | // 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来组合使用 | * 查询多条记录,当然可以使用Join和extends来组合使用 |
xorm v0.6.0.1022 |
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) | 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) | |||||
} |
package xorm | package xorm | ||||
import ( | import ( | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
switch t := c.SQLType.Name; t { | switch t := c.SQLType.Name; t { | ||||
case core.Bool: | case core.Bool: | ||||
res = core.TinyInt | res = core.TinyInt | ||||
if c.Default == "true" { | |||||
if strings.EqualFold(c.Default, "true") { | |||||
c.Default = "1" | c.Default = "1" | ||||
} else if c.Default == "false" { | |||||
} else { | |||||
c.Default = "0" | c.Default = "0" | ||||
} | } | ||||
case core.Serial: | case core.Serial: | ||||
} | } | ||||
colName = strings.Trim(colName, "` ") | colName = strings.Trim(colName, "` ") | ||||
var isRegular bool | |||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | ||||
indexName = indexName[5+len(tableName):] | indexName = indexName[5+len(tableName):] | ||||
isRegular = true | |||||
} | } | ||||
var index *core.Index | var index *core.Index | ||||
index = new(core.Index) | index = new(core.Index) | ||||
index.Type = indexType | index.Type = indexType | ||||
index.Name = indexName | index.Name = indexName | ||||
index.IsRegular = isRegular | |||||
indexes[indexName] = index | indexes[indexName] = index | ||||
} | } | ||||
index.AddColumn(colName) | index.AddColumn(colName) | ||||
func (db *mssql) Filters() []core.Filter { | func (db *mssql) Filters() []core.Filter { | ||||
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}} | 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 | |||||
} |
import ( | import ( | ||||
"crypto/tls" | "crypto/tls" | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"regexp" | |||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
func (db *mysql) Filters() []core.Filter { | func (db *mysql) Filters() []core.Filter { | ||||
return []core.Filter{&core.IdFilter{}} | 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 | |||||
} |
package xorm | package xorm | ||||
import ( | import ( | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"regexp" | |||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
indexName = strings.Trim(indexName, `" `) | 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" { | if uniqueness == "UNIQUE" { | ||||
indexType = core.UniqueType | indexType = core.UniqueType | ||||
} else { | } else { | ||||
index = new(core.Index) | index = new(core.Index) | ||||
index.Type = indexType | index.Type = indexType | ||||
index.Name = indexName | index.Name = indexName | ||||
index.IsRegular = isRegular | |||||
indexes[indexName] = index | indexes[indexName] = index | ||||
} | } | ||||
index.AddColumn(colName) | index.AddColumn(colName) | ||||
func (db *oracle) Filters() []core.Filter { | func (db *oracle) Filters() []core.Filter { | ||||
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}} | 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 | |||||
} |
package xorm | package xorm | ||||
import ( | import ( | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"net/url" | |||||
"sort" | |||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
} | } | ||||
cs := strings.Split(indexdef, "(") | cs := strings.Split(indexdef, "(") | ||||
colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") | colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") | ||||
var isRegular bool | |||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | ||||
newIdxName := indexName[5+len(tableName):] | newIdxName := indexName[5+len(tableName):] | ||||
isRegular = true | |||||
if newIdxName != "" { | if newIdxName != "" { | ||||
indexName = newIdxName | indexName = newIdxName | ||||
} | } | ||||
for _, colName := range colNames { | for _, colName := range colNames { | ||||
index.Cols = append(index.Cols, strings.Trim(colName, `" `)) | index.Cols = append(index.Cols, strings.Trim(colName, `" `)) | ||||
} | } | ||||
index.IsRegular = isRegular | |||||
indexes[index.Name] = index | indexes[index.Name] = index | ||||
} | } | ||||
return indexes, nil | return indexes, nil | ||||
func (db *postgres) Filters() []core.Filter { | func (db *postgres) Filters() []core.Filter { | ||||
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}} | 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 | |||||
} |
} | } | ||||
indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []") | indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []") | ||||
var isRegular bool | |||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | ||||
index.Name = indexName[5+len(tableName):] | index.Name = indexName[5+len(tableName):] | ||||
isRegular = true | |||||
} else { | } else { | ||||
index.Name = indexName | index.Name = indexName | ||||
} | } | ||||
for _, col := range colIndexes { | for _, col := range colIndexes { | ||||
index.Cols = append(index.Cols, strings.Trim(col, "` []")) | index.Cols = append(index.Cols, strings.Trim(col, "` []")) | ||||
} | } | ||||
index.IsRegular = isRegular | |||||
indexes[index.Name] = index | indexes[index.Name] = index | ||||
} | } | ||||
func (db *sqlite3) Filters() []core.Filter { | func (db *sqlite3) Filters() []core.Filter { | ||||
return []core.Filter{&core.IdFilter{}} | 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 | |||||
} |
Raw Methods | 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 | 1. query a SQL string, the returned results is []map[string][]byte | ||||
ORM Methods | 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 | 1. Insert one or multiple records to database | ||||
3. Query multiple records from database | 3. Query multiple records from database | ||||
sliceOfStructs := new(Struct) | |||||
err := engine.Find(sliceOfStructs) | |||||
var sliceOfStructs []Struct | |||||
err := engine.Find(&sliceOfStructs) | |||||
// SELECT * FROM user | // 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, | 4. Query multiple records and record by record handle, there two methods, one is Iterate, | ||||
another is Rows | another is Rows | ||||
counts, err := engine.Count(&user) | counts, err := engine.Count(&user) | ||||
// SELECT count(*) AS total FROM 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 | 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 | // 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 | // SELECT * FROM user WHERE id1 = 1 AND id2 = 2 | ||||
engine.In("id", 1, 2, 3).Find(&users) | engine.In("id", 1, 2, 3).Find(&users) | ||||
// SELECT * FROM user WHERE id IN (1, 2, 3) | // 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) | // SELECT * FROM user WHERE id IN (1, 2, 3) | ||||
2. Where, And, Or | 2. Where, And, Or | ||||
// SELECT TOP 5 * FROM user // for mssql | // SELECT TOP 5 * FROM user // for mssql | ||||
// SELECT * FROM user LIMIT .. OFFSET 0 //for other databases | // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases | ||||
5. Sql, let you custom SQL | |||||
5. SQL, let you custom SQL | |||||
var users []User | var users []User | ||||
engine.Sql("select * from user").Find(&users) | |||||
engine.SQL("select * from user").Find(&users) | |||||
6. Cols, Omit, Distinct | 6. Cols, Omit, Distinct | ||||
DatabaseTZ *time.Location // The timezone of the database | DatabaseTZ *time.Location // The timezone of the database | ||||
disableGlobalCache bool | disableGlobalCache bool | ||||
tagHandlers map[string]tagHandler | |||||
} | } | ||||
// ShowSQL show SQL statement or not on logger if log level is great than INFO | // ShowSQL show SQL statement or not on logger if log level is great than INFO | ||||
} | } | ||||
// MapCacher Set a table use a special cacher | // 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) | v := rValue(bean) | ||||
tb := engine.autoMapType(v) | |||||
tb, err := engine.autoMapType(v) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
tb.Cacher = cacher | tb.Cacher = cacher | ||||
return nil | |||||
} | } | ||||
// NewDB provides an interface to operate database directly | // NewDB provides an interface to operate database directly | ||||
func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { | func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { | ||||
if engine.showSQL && !engine.showExecTime { | if engine.showSQL && !engine.showExecTime { | ||||
if len(sqlArgs) > 0 { | if len(sqlArgs) > 0 { | ||||
engine.logger.Infof("[sql] %v [args] %v", sqlStr, sqlArgs) | |||||
engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs) | |||||
} else { | } else { | ||||
engine.logger.Infof("[sql] %v", sqlStr) | |||||
engine.logger.Infof("[SQL] %v", sqlStr) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
stmt, res, err := executionBlock() | stmt, res, err := executionBlock() | ||||
execDuration := time.Since(b4ExecTime) | execDuration := time.Since(b4ExecTime) | ||||
if len(args) > 0 { | 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 { | } else { | ||||
engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration) | |||||
engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration) | |||||
} | } | ||||
return stmt, res, err | return stmt, res, err | ||||
} | } | ||||
return session.Having(conditions) | 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() | t := v.Type() | ||||
engine.mutex.Lock() | engine.mutex.Lock() | ||||
defer engine.mutex.Unlock() | defer engine.mutex.Unlock() | ||||
table, ok := engine.Tables[t] | table, ok := engine.Tables[t] | ||||
if !ok { | if !ok { | ||||
table = engine.mapType(v) | |||||
var err error | |||||
table, err = engine.mapType(v) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
engine.Tables[t] = table | engine.Tables[t] = table | ||||
if engine.Cacher != nil { | if engine.Cacher != nil { | ||||
if v.CanAddr() { | if v.CanAddr() { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return table | |||||
return table, nil | |||||
} | } | ||||
// GobRegister register one struct to gob for cache use | // GobRegister register one struct to gob for cache use | ||||
func (engine *Engine) GobRegister(v interface{}) *Engine { | func (engine *Engine) GobRegister(v interface{}) *Engine { | ||||
//fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v) | |||||
gob.Register(v) | gob.Register(v) | ||||
return engine | return engine | ||||
} | } | ||||
Name string | 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 | // TableInfo get table info according to bean's content | ||||
func (engine *Engine) TableInfo(bean interface{}) *Table { | func (engine *Engine) TableInfo(bean interface{}) *Table { | ||||
v := rValue(bean) | 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) { | func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { | ||||
tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() | 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() | t := v.Type() | ||||
table := engine.newTable() | table := engine.newTable() | ||||
if tb, ok := v.Interface().(TableName); ok { | if tb, ok := v.Interface().(TableName); ok { | ||||
table.Type = t | table.Type = t | ||||
var idFieldColName string | var idFieldColName string | ||||
var err error | |||||
var hasCacheTag, hasNoCacheTag bool | var hasCacheTag, hasNoCacheTag bool | ||||
for i := 0; i < t.NumField(); i++ { | for i := 0; i < t.NumField(); i++ { | ||||
if tags[0] == "-" { | if tags[0] == "-" { | ||||
continue | continue | ||||
} | } | ||||
var ctx = tagContext{ | |||||
table: table, | |||||
col: col, | |||||
fieldValue: fieldValue, | |||||
indexNames: make(map[string]int), | |||||
engine: engine, | |||||
} | |||||
if strings.ToUpper(tags[0]) == "EXTENDS" { | 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) | |||||
} | |||||
} | |||||
continue | |||||
default: | |||||
//TODO: warning | |||||
if err := ExtendsTagHandler(&ctx); err != nil { | |||||
return nil, err | |||||
} | } | ||||
continue | |||||
} | } | ||||
indexNames := make(map[string]int) | |||||
var isIndex, isUnique bool | |||||
var preKey string | |||||
for j, key := range tags { | for j, key := range tags { | ||||
if ctx.ignoreNext { | |||||
ctx.ignoreNext = false | |||||
continue | |||||
} | |||||
k := strings.ToUpper(key) | 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 | |||||
ctx.tagName = k | |||||
autoStart := k[len("AUTOINCR")+1 : len(k)-1] | |||||
autoStartInt, err := strconv.Atoi(autoStart) | |||||
if err != nil { | |||||
engine.LogError(err) | |||||
pStart := strings.Index(k, "(") | |||||
if pStart == 0 { | |||||
return nil, errors.New("( could not be the first charactor") | |||||
} | } | ||||
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 | |||||
if pStart > -1 { | |||||
if !strings.HasSuffix(k, ")") { | |||||
return nil, errors.New("cannot match ) charactor") | |||||
} | } | ||||
case k == "NOCACHE": | |||||
if !hasNoCacheTag { | |||||
hasNoCacheTag = true | |||||
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 | |||||
} | } | ||||
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 | |||||
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 | |||||
} | |||||
} 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 | |||||
} | |||||
} 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) | |||||
} | |||||
} | |||||
} | |||||
} else { | |||||
if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") { | |||||
col.Name = key[1 : len(key)-1] | |||||
} else { | } else { | ||||
if _, ok := core.SqlTypes[k]; ok { | |||||
col.SQLType = core.SQLType{Name: k} | |||||
} else if key != col.Default { | |||||
col.Name = key | |||||
} | |||||
col.Name = key | |||||
} | } | ||||
engine.dialect.SqlType(col) | |||||
} | } | ||||
preKey = k | |||||
if ctx.hasCacheTag { | |||||
hasCacheTag = true | |||||
} | |||||
if ctx.hasNoCacheTag { | |||||
hasNoCacheTag = true | |||||
} | |||||
} | } | ||||
if col.SQLType.Name == "" { | if col.SQLType.Name == "" { | ||||
col.SQLType = core.Type2SQLType(fieldType) | col.SQLType = core.Type2SQLType(fieldType) | ||||
} | } | ||||
engine.dialect.SqlType(col) | |||||
if col.Length == 0 { | if col.Length == 0 { | ||||
col.Length = col.SQLType.DefaultLength | col.Length = col.SQLType.DefaultLength | ||||
} | } | ||||
if col.Length2 == 0 { | if col.Length2 == 0 { | ||||
col.Length2 = col.SQLType.DefaultLength2 | col.Length2 = col.SQLType.DefaultLength2 | ||||
} | } | ||||
if col.Name == "" { | if col.Name == "" { | ||||
col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).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) | addIndex(indexName, table, col, indexType) | ||||
} | } | ||||
} | } | ||||
table.Cacher = nil | table.Cacher = nil | ||||
} | } | ||||
return table | |||||
return table, nil | |||||
} | } | ||||
// IsTableEmpty if a table has any reocrd | // IsTableEmpty if a table has any reocrd | ||||
// IDOfV get id from one value of struct | // IDOfV get id from one value of struct | ||||
func (engine *Engine) IDOfV(rv reflect.Value) core.PK { | 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) | 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)) | pk := make([]interface{}, len(table.PrimaryKeys)) | ||||
for i, col := range table.PKColumns() { | for i, col := range table.PKColumns() { | ||||
pkField := v.FieldByName(col.FieldName) | pkField := v.FieldByName(col.FieldName) | ||||
pk[i] = pkField.Uint() | pk[i] = pkField.Uint() | ||||
} | } | ||||
} | } | ||||
return core.PK(pk) | |||||
return core.PK(pk), nil | |||||
} | } | ||||
// CreateIndexes create indexes | // CreateIndexes create indexes | ||||
return table.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 | // ClearCacheBean if enabled cache, clear the cache bean | ||||
func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { | func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { | ||||
v := rValue(bean) | v := rValue(bean) | ||||
return errors.New("error params") | return errors.New("error params") | ||||
} | } | ||||
tableName := engine.tbName(v) | tableName := engine.tbName(v) | ||||
table := engine.autoMapType(v) | |||||
table, err := engine.autoMapType(v) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
cacher := table.Cacher | cacher := table.Cacher | ||||
if cacher == nil { | if cacher == nil { | ||||
cacher = engine.Cacher | cacher = engine.Cacher | ||||
return errors.New("error params") | return errors.New("error params") | ||||
} | } | ||||
tableName := engine.tbName(v) | tableName := engine.tbName(v) | ||||
table := engine.autoMapType(v) | |||||
table, err := engine.autoMapType(v) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
cacher := table.Cacher | cacher := table.Cacher | ||||
if cacher == nil { | if cacher == nil { | ||||
cacher = engine.Cacher | cacher = engine.Cacher | ||||
for _, bean := range beans { | for _, bean := range beans { | ||||
v := rValue(bean) | v := rValue(bean) | ||||
tableName := engine.tbName(v) | 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() | s := engine.NewSession() | ||||
defer s.Close() | defer s.Close() | ||||
return session.Query(sql, paramStr...) | 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 | // Insert one or more records | ||||
func (engine *Engine) Insert(beans ...interface{}) (int64, error) { | func (engine *Engine) Insert(beans ...interface{}) (int64, error) { | ||||
session := engine.NewSession() | session := engine.NewSession() |
// 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 | |||||
} |
return true | 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 { | func int64ToIntValue(id int64, tp reflect.Type) reflect.Value { | ||||
var v interface{} | var v interface{} | ||||
switch tp.Kind() { | switch tp.Kind() { |
// 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 | |||||
} |
// 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 | |||||
} |
// 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 | |||||
} |
// 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 | |||||
} |
// 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 | |||||
} |
type Rows struct { | type Rows struct { | ||||
NoTypeCheck bool | NoTypeCheck bool | ||||
session *Session | |||||
stmt *core.Stmt | |||||
rows *core.Rows | |||||
fields []string | |||||
fieldsCount int | |||||
beanType reflect.Type | |||||
lastError error | |||||
session *Session | |||||
stmt *core.Stmt | |||||
rows *core.Rows | |||||
fields []string | |||||
beanType reflect.Type | |||||
lastError error | |||||
} | } | ||||
func newRows(session *Session, bean interface{}) (*Rows, error) { | func newRows(session *Session, bean interface{}) (*Rows, error) { | ||||
rows.Close() | rows.Close() | ||||
return nil, err | return nil, err | ||||
} | } | ||||
rows.fieldsCount = len(rows.fields) | |||||
return rows, nil | return rows, nil | ||||
} | } | ||||
return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) | 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 | return err | ||||
} | } | ||||
// 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 | |||||
} |
// 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 | |||||
} |
// 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 | |||||
} | |||||
} |
return err | return err | ||||
} | } | ||||
var newElemFunc func() reflect.Value | |||||
var newElemFunc func(fields []string) reflect.Value | |||||
elemType := containerValue.Type().Elem() | elemType := containerValue.Type().Elem() | ||||
var isPointer bool | |||||
if elemType.Kind() == reflect.Ptr { | if elemType.Kind() == reflect.Ptr { | ||||
newElemFunc = func() reflect.Value { | |||||
return reflect.New(elemType.Elem()) | |||||
} | |||||
} else { | |||||
newElemFunc = func() reflect.Value { | |||||
return reflect.New(elemType) | |||||
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 | |||||
} | } | ||||
return reflect.New(elemType) | |||||
} | } | ||||
var containerValueSetFunc func(*reflect.Value, core.PK) error | var containerValueSetFunc func(*reflect.Value, core.PK) error | ||||
if containerValue.Kind() == reflect.Slice { | 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 | |||||
} | |||||
} else { | |||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error { | |||||
containerValue.Set(reflect.Append(containerValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) | |||||
return nil | |||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error { | |||||
if isPointer { | |||||
containerValue.Set(reflect.Append(containerValue, newValue.Elem().Addr())) | |||||
} else { | |||||
containerValue.Set(reflect.Append(containerValue, newValue.Elem())) | |||||
} | } | ||||
return nil | |||||
} | } | ||||
} else { | } else { | ||||
keyType := containerValue.Type().Key() | keyType := containerValue.Type().Key() | ||||
return errors.New("don't support multiple primary key's map has non-slice key type") | 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 | |||||
containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error { | |||||
keyValue := reflect.New(keyType) | |||||
err := convertPKToValue(table, keyValue.Interface(), pk) | |||||
if err != nil { | |||||
return err | |||||
} | } | ||||
} 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(), reflect.Indirect(reflect.ValueOf(newValue.Interface()))) | |||||
return nil | |||||
if isPointer { | |||||
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr()) | |||||
} else { | |||||
containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem()) | |||||
} | } | ||||
return nil | |||||
} | } | ||||
} | } | ||||
var newValue = newElemFunc() | |||||
dataStruct := rValue(newValue.Interface()) | |||||
if dataStruct.Kind() == reflect.Struct { | |||||
return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, containerValueSetFunc) | |||||
if elemType.Kind() == reflect.Struct { | |||||
var newValue = newElemFunc(fields) | |||||
dataStruct := rValue(newValue.Interface()) | |||||
tb, err := session.Engine.autoMapType(dataStruct) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return session.rows2Beans(rawRows, fields, len(fields), tb, newElemFunc, containerValueSetFunc) | |||||
} | } | ||||
for rawRows.Next() { | for rawRows.Next() { | ||||
var newValue = newElemFunc() | |||||
var newValue = newElemFunc(fields) | |||||
bean := newValue.Interface() | 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 | return err | ||||
} | } | ||||
if rv.Kind() != reflect.Ptr { | if rv.Kind() != reflect.Ptr { | ||||
rv = rv.Addr() | rv = rv.Addr() | ||||
} | } | ||||
id := session.Engine.IdOfV(rv) | |||||
id, err := session.Engine.idOfV(rv) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
sid, err := id.ToString() | sid, err := id.ToString() | ||||
if err != nil { | if err != nil { | ||||
return err | return err |
defer session.Close() | 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 sqlStr string | ||||
var args []interface{} | var args []interface{} | ||||
args = session.Statement.RawParams | 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 && | if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && | ||||
!session.Statement.unscoped { | !session.Statement.unscoped { | ||||
has, err := session.cacheGet(bean, sqlStr, args...) | has, err := session.cacheGet(bean, sqlStr, args...) | ||||
} | } | ||||
} | } | ||||
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 rawRows *core.Rows | ||||
var err error | var err error | ||||
session.queryPreprocess(&sqlStr, args...) | |||||
if session.IsAutoCommit { | if session.IsAutoCommit { | ||||
_, rawRows, err = session.innerQuery(sqlStr, args...) | _, rawRows, err = session.innerQuery(sqlStr, args...) | ||||
} else { | } else { | ||||
defer rawRows.Close() | defer rawRows.Close() | ||||
if rawRows.Next() { | if rawRows.Next() { | ||||
fields, err := rawRows.Columns() | |||||
if err == nil { | |||||
_, err = session.row2Bean(rawRows, fields, len(fields), bean) | |||||
switch beanKind { | |||||
case reflect.Struct: | |||||
fields, err := rawRows.Columns() | |||||
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 true, err | ||||
} | } | ||||
return false, nil | return false, nil | ||||
} | } | ||||
cacheBean := cacher.GetBean(tableName, sid) | cacheBean := cacher.GetBean(tableName, sid) | ||||
if cacheBean == nil { | 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 | cacheBean = bean | ||||
has, err = session.nocacheGet(cacheBean, sqlStr, args...) | |||||
has, err = session.nocacheGet(reflect.Struct, cacheBean, sqlStr, args...) | |||||
if err != nil || !has { | if err != nil || !has { | ||||
return has, err | return has, err | ||||
} | } |
} | } | ||||
cleanupProcessorsClosures(&session.beforeClosures) | cleanupProcessorsClosures(&session.beforeClosures) | ||||
statement := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", | |||||
session.Engine.Quote(session.Statement.TableName()), | |||||
session.Engine.QuoteStr(), | |||||
strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()), | |||||
session.Engine.QuoteStr(), | |||||
strings.Join(colMultiPlaces, "),(")) | |||||
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()), | |||||
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...) | res, err := session.exec(statement, args...) | ||||
if err != nil { | if err != nil { | ||||
return 0, err | return 0, err | ||||
// remove the expr columns | // remove the expr columns | ||||
for i, colName := range colNames { | for i, colName := range colNames { | ||||
if colName == v.colName { | 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:]...) | |||||
} | } | ||||
} | } | ||||
exprColVals = append(exprColVals, v.expr) | exprColVals = append(exprColVals, v.expr) | ||||
} | } | ||||
colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns)) | |||||
colPlaces := strings.Repeat("?, ", len(colNames) - len(exprColumns)) | |||||
if len(exprColVals) > 0 { | if len(exprColVals) > 0 { | ||||
colPlaces = colPlaces + strings.Join(exprColVals, ", ") | colPlaces = colPlaces + strings.Join(exprColVals, ", ") | ||||
} else { | } else { | ||||
colPlaces = colPlaces[0 : len(colPlaces)-2] | |||||
colPlaces = colPlaces[0 : len(colPlaces) - 2] | |||||
} | } | ||||
sqlStr := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", | sqlStr := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", |
return rows2maps(rows) | 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) { | func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { | ||||
defer session.resetStatement() | defer session.resetStatement() | ||||
if session.IsAutoClose { | if session.IsAutoClose { | ||||
return session.query(sqlStr, paramStr...) | 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 | // for string | ||||
// ============================= | // ============================= |
for _, bean := range beans { | for _, bean := range beans { | ||||
v := rValue(bean) | v := rValue(bean) | ||||
table := engine.mapType(v) | |||||
table, err := engine.mapType(v) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
structTables = append(structTables, table) | structTables = append(structTables, table) | ||||
var tbName = session.tbNameNoSchema(table) | var tbName = session.tbNameNoSchema(table) | ||||
session.queryPreprocess(&sqlStr, args...) | session.queryPreprocess(&sqlStr, args...) | ||||
var err error | var err error | ||||
var res = make([]int64, 0, len(columnNames)) | |||||
var res = make([]int64, len(columnNames), len(columnNames)) | |||||
if session.IsAutoCommit { | if session.IsAutoCommit { | ||||
err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res) | err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res) | ||||
} else { | } else { |
var condSQL string | var condSQL string | ||||
cond := session.Statement.cond.And(autoCond) | cond := session.Statement.cond.And(autoCond) | ||||
doIncVer := false | |||||
var doIncVer = (table != nil && table.Version != "" && session.Statement.checkVersion) | |||||
var verValue *reflect.Value | var verValue *reflect.Value | ||||
if table != nil && table.Version != "" && session.Statement.checkVersion { | |||||
if doIncVer { | |||||
verValue, err = table.VersionColumn().ValueOf(bean) | verValue, err = table.VersionColumn().ValueOf(bean) | ||||
if err != nil { | if err != nil { | ||||
return 0, err | return 0, err | ||||
} | } | ||||
cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()}) | cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()}) | ||||
condSQL, condArgs, _ = builder.ToSQL(cond) | |||||
if len(condSQL) > 0 { | |||||
condSQL = "WHERE " + condSQL | |||||
} | |||||
if st.LimitN > 0 { | |||||
condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) | |||||
} | |||||
colNames = append(colNames, session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1") | |||||
} | |||||
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) | |||||
condSQL, condArgs, _ = builder.ToSQL(cond) | |||||
if len(condSQL) > 0 { | |||||
condSQL = "WHERE " + condSQL | |||||
} | |||||
doIncVer = true | |||||
} else { | |||||
condSQL, condArgs, _ = builder.ToSQL(cond) | |||||
if len(condSQL) > 0 { | |||||
condSQL = "WHERE " + condSQL | |||||
} | |||||
if st.OrderStr != "" { | |||||
condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr) | |||||
} | |||||
if st.LimitN > 0 { | |||||
// 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) | 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 SET %v %v", | |||||
session.Engine.Quote(session.Statement.TableName()), | |||||
strings.Join(colNames, ", "), | |||||
condSQL) | |||||
} | } | ||||
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...)...) | res, err := session.exec(sqlStr, append(args, condArgs...)...) | ||||
if err != nil { | if err != nil { | ||||
return 0, err | return 0, err |
// 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 | |||||
} |
Engine *Engine | Engine *Engine | ||||
Start int | Start int | ||||
LimitN int | LimitN int | ||||
IdParam *core.PK | |||||
idParam *core.PK | |||||
OrderStr string | OrderStr string | ||||
JoinStr string | JoinStr string | ||||
joinArgs []interface{} | joinArgs []interface{} | ||||
statement.columnMap = make(map[string]bool) | statement.columnMap = make(map[string]bool) | ||||
statement.AltTableName = "" | statement.AltTableName = "" | ||||
statement.tableName = "" | statement.tableName = "" | ||||
statement.IdParam = nil | |||||
statement.idParam = nil | |||||
statement.RawSQL = "" | statement.RawSQL = "" | ||||
statement.RawParams = make([]interface{}, 0) | statement.RawParams = make([]interface{}, 0) | ||||
statement.UseCache = true | statement.UseCache = true | ||||
// In generate "Where column IN (?) " statement | // In generate "Where column IN (?) " statement | ||||
func (statement *Statement) In(column string, args ...interface{}) *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) | statement.cond = statement.cond.And(in) | ||||
return statement | return statement | ||||
} | } | ||||
// NotIn generate "Where column NOT IN (?) " statement | // NotIn generate "Where column NOT IN (?) " statement | ||||
func (statement *Statement) NotIn(column string, args ...interface{}) *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 | 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) | statement.tableName = statement.Engine.tbName(v) | ||||
return nil | |||||
} | } | ||||
// Table tempororily set table name, the parameter could be a string or a pointer of struct | // Table tempororily set table name, the parameter could be a string or a pointer of struct | ||||
if t.Kind() == reflect.String { | if t.Kind() == reflect.String { | ||||
statement.AltTableName = tableNameOrBean.(string) | statement.AltTableName = tableNameOrBean.(string) | ||||
} else if t.Kind() == reflect.Struct { | } else if t.Kind() == reflect.Struct { | ||||
statement.RefTable = statement.Engine.autoMapType(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) | statement.AltTableName = statement.Engine.tbName(v) | ||||
} | } | ||||
return statement | return statement | ||||
if fieldValue == reflect.Zero(fieldType) { | if fieldValue == reflect.Zero(fieldType) { | ||||
continue | 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 | continue | ||||
} | } | ||||
} | } | ||||
} else if col.SQLType.IsBlob() { | } else if col.SQLType.IsBlob() { | ||||
var bytes []byte | var bytes []byte | ||||
var err error | var err error | ||||
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) && | |||||
if fieldType.Kind() == reflect.Slice && | |||||
fieldType.Elem().Kind() == reflect.Uint8 { | fieldType.Elem().Kind() == reflect.Uint8 { | ||||
if fieldValue.Len() > 0 { | if fieldValue.Len() > 0 { | ||||
val = fieldValue.Bytes() | val = fieldValue.Bytes() | ||||
} else { | } else { | ||||
continue | continue | ||||
} | } | ||||
} else if fieldType.Kind() == reflect.Array && | |||||
fieldType.Elem().Kind() == reflect.Uint8 { | |||||
val = fieldValue.Slice(0, 0).Interface() | |||||
} else { | } else { | ||||
bytes, err = json.Marshal(fieldValue.Interface()) | bytes, err = json.Marshal(fieldValue.Interface()) | ||||
if err != nil { | if err != nil { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
case reflect.Array, reflect.Slice, reflect.Map: | |||||
case reflect.Array: | |||||
continue | |||||
case reflect.Slice, reflect.Map: | |||||
if fieldValue == reflect.Zero(fieldType) { | if fieldValue == reflect.Zero(fieldType) { | ||||
continue | continue | ||||
} | } | ||||
return statement.tableName | 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 = ?" | // ID generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?" | ||||
func (statement *Statement) ID(id interface{}) *Statement { | func (statement *Statement) ID(id interface{}) *Statement { | ||||
idValue := reflect.ValueOf(id) | idValue := reflect.ValueOf(id) | ||||
switch idType { | switch idType { | ||||
case ptrPkType: | case ptrPkType: | ||||
if pkPtr, ok := (id).(*core.PK); ok { | if pkPtr, ok := (id).(*core.PK); ok { | ||||
statement.IdParam = pkPtr | |||||
statement.idParam = pkPtr | |||||
return statement | return statement | ||||
} | } | ||||
case pkType: | case pkType: | ||||
if pk, ok := (id).(core.PK); ok { | if pk, ok := (id).(core.PK); ok { | ||||
statement.IdParam = &pk | |||||
statement.idParam = &pk | |||||
return statement | return statement | ||||
} | } | ||||
} | } | ||||
switch idType.Kind() { | switch idType.Kind() { | ||||
case reflect.String: | case reflect.String: | ||||
statement.IdParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()} | |||||
statement.idParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()} | |||||
return statement | return statement | ||||
} | } | ||||
statement.IdParam = &core.PK{id} | |||||
statement.idParam = &core.PK{id} | |||||
return statement | return statement | ||||
} | } | ||||
} | } | ||||
func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}) { | 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 | var columnStr = statement.ColumnStr | ||||
if len(statement.selectStr) > 0 { | if len(statement.selectStr) > 0 { | ||||
if len(columnStr) == 0 { | if len(columnStr) == 0 { | ||||
if len(statement.GroupByStr) > 0 { | if len(statement.GroupByStr) > 0 { | ||||
columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) | 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...) | return statement.genSelectSQL(columnStr, condSQL), append(statement.joinArgs, condArgs...) | ||||
} | } | ||||
var sumStrs = make([]string, 0, len(columns)) | var sumStrs = make([]string, 0, len(columns)) | ||||
for _, colName := range 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) | condSQL, condArgs, _ := statement.genConds(bean) | ||||
func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) { | func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) { | ||||
var distinct string | var distinct string | ||||
if statement.IsDistinct { | |||||
if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { | |||||
distinct = "DISTINCT " | distinct = "DISTINCT " | ||||
} | } | ||||
} | } | ||||
func (statement *Statement) processIDParam() { | func (statement *Statement) processIDParam() { | ||||
if statement.IdParam == nil { | |||||
if statement.idParam == nil { | |||||
return | return | ||||
} | } | ||||
for i, col := range statement.RefTable.PKColumns() { | for i, col := range statement.RefTable.PKColumns() { | ||||
var colName = statement.colName(col, statement.TableName()) | 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 { | } else { | ||||
statement.cond = statement.cond.And(builder.Eq{colName: ""}) | statement.cond = statement.cond.And(builder.Eq{colName: ""}) | ||||
} | } |
// 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 | |||||
} |
const ( | const ( | ||||
// Version show the xorm's version | // Version show the xorm's version | ||||
Version string = "0.6.0.1022" | |||||
Version string = "0.6.2.0401" | |||||
) | ) | ||||
func regDrvsNDialects() bool { | func regDrvsNDialects() bool { | ||||
mutex: &sync.RWMutex{}, | mutex: &sync.RWMutex{}, | ||||
TagIdentifier: "xorm", | TagIdentifier: "xorm", | ||||
TZLocation: time.Local, | TZLocation: time.Local, | ||||
tagHandlers: defaultTagHandlers, | |||||
} | } | ||||
logger := NewSimpleLogger(os.Stdout) | logger := NewSimpleLogger(os.Stdout) |
"revisionTime": "2016-08-11T02:11:45Z" | "revisionTime": "2016-08-11T02:11:45Z" | ||||
}, | }, | ||||
{ | { | ||||
"checksumSHA1": "COlm4o3G1rUSqr33iumtjY1qKD8=", | |||||
"checksumSHA1": "3FEBM0FYERf8jpaResApwcQpr40=", | |||||
"path": "github.com/go-xorm/xorm", | "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=", | "checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=", |