fix migrate failed and org dashboard failed on MSSQL database (#1448)
parent
cf6699fb4f
commit
5acfc7c4bc
@ -1 +0,0 @@ |
|||||||
xorm v0.6.0.1022 |
|
@ -1,42 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"regexp" |
|
||||||
|
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
// func init() {
|
|
||||||
// core.RegisterDriver("goracle", &goracleDriver{})
|
|
||||||
// }
|
|
||||||
|
|
||||||
type goracleDriver struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
db := &core.Uri{DbType: core.ORACLE} |
|
||||||
dsnPattern := regexp.MustCompile( |
|
||||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
|
||||||
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
|
|
||||||
`\/(?P<dbname>.*?)` + // /dbname
|
|
||||||
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1¶mN=valueN]
|
|
||||||
matches := dsnPattern.FindStringSubmatch(dataSourceName) |
|
||||||
//tlsConfigRegister := make(map[string]*tls.Config)
|
|
||||||
names := dsnPattern.SubexpNames() |
|
||||||
|
|
||||||
for i, match := range matches { |
|
||||||
switch names[i] { |
|
||||||
case "dbname": |
|
||||||
db.DbName = match |
|
||||||
} |
|
||||||
} |
|
||||||
if db.DbName == "" { |
|
||||||
return nil, errors.New("dbname is empty") |
|
||||||
} |
|
||||||
return db, nil |
|
||||||
} |
|
@ -1,65 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"strings" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
type mymysqlDriver struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
db := &core.Uri{DbType: core.MYSQL} |
|
||||||
|
|
||||||
pd := strings.SplitN(dataSourceName, "*", 2) |
|
||||||
if len(pd) == 2 { |
|
||||||
// Parse protocol part of URI
|
|
||||||
p := strings.SplitN(pd[0], ":", 2) |
|
||||||
if len(p) != 2 { |
|
||||||
return nil, errors.New("Wrong protocol part of URI") |
|
||||||
} |
|
||||||
db.Proto = p[0] |
|
||||||
options := strings.Split(p[1], ",") |
|
||||||
db.Raddr = options[0] |
|
||||||
for _, o := range options[1:] { |
|
||||||
kv := strings.SplitN(o, "=", 2) |
|
||||||
var k, v string |
|
||||||
if len(kv) == 2 { |
|
||||||
k, v = kv[0], kv[1] |
|
||||||
} else { |
|
||||||
k, v = o, "true" |
|
||||||
} |
|
||||||
switch k { |
|
||||||
case "laddr": |
|
||||||
db.Laddr = v |
|
||||||
case "timeout": |
|
||||||
to, err := time.ParseDuration(v) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
db.Timeout = to |
|
||||||
default: |
|
||||||
return nil, errors.New("Unknown option: " + k) |
|
||||||
} |
|
||||||
} |
|
||||||
// Remove protocol part
|
|
||||||
pd = pd[1:] |
|
||||||
} |
|
||||||
// Parse database part of URI
|
|
||||||
dup := strings.SplitN(pd[0], "/", 3) |
|
||||||
if len(dup) != 3 { |
|
||||||
return nil, errors.New("Wrong database part of URI") |
|
||||||
} |
|
||||||
db.DbName = dup[0] |
|
||||||
db.User = dup[1] |
|
||||||
db.Passwd = dup[2] |
|
||||||
|
|
||||||
return db, nil |
|
||||||
} |
|
@ -1,50 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"regexp" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
type mysqlDriver struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
dsnPattern := regexp.MustCompile( |
|
||||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
|
||||||
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
|
|
||||||
`\/(?P<dbname>.*?)` + // /dbname
|
|
||||||
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1¶mN=valueN]
|
|
||||||
matches := dsnPattern.FindStringSubmatch(dataSourceName) |
|
||||||
//tlsConfigRegister := make(map[string]*tls.Config)
|
|
||||||
names := dsnPattern.SubexpNames() |
|
||||||
|
|
||||||
uri := &core.Uri{DbType: core.MYSQL} |
|
||||||
|
|
||||||
for i, match := range matches { |
|
||||||
switch names[i] { |
|
||||||
case "dbname": |
|
||||||
uri.DbName = match |
|
||||||
case "params": |
|
||||||
if len(match) > 0 { |
|
||||||
kvs := strings.Split(match, "&") |
|
||||||
for _, kv := range kvs { |
|
||||||
splits := strings.Split(kv, "=") |
|
||||||
if len(splits) == 2 { |
|
||||||
switch splits[0] { |
|
||||||
case "charset": |
|
||||||
uri.Charset = splits[1] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
return uri, nil |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"regexp" |
|
||||||
|
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
type oci8Driver struct { |
|
||||||
} |
|
||||||
|
|
||||||
//dataSourceName=user/password@ipv4:port/dbname
|
|
||||||
//dataSourceName=user/password@[ipv6]:port/dbname
|
|
||||||
func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
db := &core.Uri{DbType: core.ORACLE} |
|
||||||
dsnPattern := regexp.MustCompile( |
|
||||||
`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
|
|
||||||
`(?P<net>.*)` + // ip:port
|
|
||||||
`\/(?P<dbname>.*)`) // dbname
|
|
||||||
matches := dsnPattern.FindStringSubmatch(dataSourceName) |
|
||||||
names := dsnPattern.SubexpNames() |
|
||||||
for i, match := range matches { |
|
||||||
switch names[i] { |
|
||||||
case "dbname": |
|
||||||
db.DbName = match |
|
||||||
} |
|
||||||
} |
|
||||||
if db.DbName == "" { |
|
||||||
return nil, errors.New("dbname is empty") |
|
||||||
} |
|
||||||
return db, nil |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
type odbcDriver struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
kv := strings.Split(dataSourceName, ";") |
|
||||||
var dbName string |
|
||||||
|
|
||||||
for _, c := range kv { |
|
||||||
vv := strings.Split(strings.TrimSpace(c), "=") |
|
||||||
if len(vv) == 2 { |
|
||||||
switch strings.ToLower(vv[0]) { |
|
||||||
case "database": |
|
||||||
dbName = vv[1] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if dbName == "" { |
|
||||||
return nil, errors.New("no db name provided") |
|
||||||
} |
|
||||||
return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil |
|
||||||
} |
|
@ -1,119 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"net/url" |
|
||||||
"sort" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
type pqDriver struct { |
|
||||||
} |
|
||||||
|
|
||||||
type values map[string]string |
|
||||||
|
|
||||||
func (vs values) Set(k, v string) { |
|
||||||
vs[k] = v |
|
||||||
} |
|
||||||
|
|
||||||
func (vs values) Get(k string) (v string) { |
|
||||||
return vs[k] |
|
||||||
} |
|
||||||
|
|
||||||
func errorf(s string, args ...interface{}) { |
|
||||||
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) |
|
||||||
} |
|
||||||
|
|
||||||
func parseURL(connstr string) (string, error) { |
|
||||||
u, err := url.Parse(connstr) |
|
||||||
if err != nil { |
|
||||||
return "", err |
|
||||||
} |
|
||||||
|
|
||||||
if u.Scheme != "postgresql" && u.Scheme != "postgres" { |
|
||||||
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) |
|
||||||
} |
|
||||||
|
|
||||||
var kvs []string |
|
||||||
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) |
|
||||||
accrue := func(k, v string) { |
|
||||||
if v != "" { |
|
||||||
kvs = append(kvs, k+"="+escaper.Replace(v)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if u.User != nil { |
|
||||||
v := u.User.Username() |
|
||||||
accrue("user", v) |
|
||||||
|
|
||||||
v, _ = u.User.Password() |
|
||||||
accrue("password", v) |
|
||||||
} |
|
||||||
|
|
||||||
i := strings.Index(u.Host, ":") |
|
||||||
if i < 0 { |
|
||||||
accrue("host", u.Host) |
|
||||||
} else { |
|
||||||
accrue("host", u.Host[:i]) |
|
||||||
accrue("port", u.Host[i+1:]) |
|
||||||
} |
|
||||||
|
|
||||||
if u.Path != "" { |
|
||||||
accrue("dbname", u.Path[1:]) |
|
||||||
} |
|
||||||
|
|
||||||
q := u.Query() |
|
||||||
for k := range q { |
|
||||||
accrue(k, q.Get(k)) |
|
||||||
} |
|
||||||
|
|
||||||
sort.Strings(kvs) // Makes testing easier (not a performance concern)
|
|
||||||
return strings.Join(kvs, " "), nil |
|
||||||
} |
|
||||||
|
|
||||||
func parseOpts(name string, o values) { |
|
||||||
if len(name) == 0 { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
name = strings.TrimSpace(name) |
|
||||||
|
|
||||||
ps := strings.Split(name, " ") |
|
||||||
for _, p := range ps { |
|
||||||
kv := strings.Split(p, "=") |
|
||||||
if len(kv) < 2 { |
|
||||||
errorf("invalid option: %q", p) |
|
||||||
} |
|
||||||
o.Set(kv[0], kv[1]) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
db := &core.Uri{DbType: core.POSTGRES} |
|
||||||
o := make(values) |
|
||||||
var err error |
|
||||||
if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") { |
|
||||||
dataSourceName, err = parseURL(dataSourceName) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
parseOpts(dataSourceName, o) |
|
||||||
|
|
||||||
db.DbName = o.Get("dbname") |
|
||||||
if db.DbName == "" { |
|
||||||
return nil, errors.New("dbname is empty") |
|
||||||
} |
|
||||||
/*db.Schema = o.Get("schema") |
|
||||||
if len(db.Schema) == 0 { |
|
||||||
db.Schema = "public" |
|
||||||
}*/ |
|
||||||
return db, nil |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,84 @@ |
|||||||
|
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xorm |
||||||
|
|
||||||
|
// Incr provides a query string like "count = count + 1"
|
||||||
|
func (session *Session) Incr(column string, arg ...interface{}) *Session { |
||||||
|
session.Statement.Incr(column, arg...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Decr provides a query string like "count = count - 1"
|
||||||
|
func (session *Session) Decr(column string, arg ...interface{}) *Session { |
||||||
|
session.Statement.Decr(column, arg...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// SetExpr provides a query string like "column = {expression}"
|
||||||
|
func (session *Session) SetExpr(column string, expression string) *Session { |
||||||
|
session.Statement.SetExpr(column, expression) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Select provides some columns to special
|
||||||
|
func (session *Session) Select(str string) *Session { |
||||||
|
session.Statement.Select(str) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Cols provides some columns to special
|
||||||
|
func (session *Session) Cols(columns ...string) *Session { |
||||||
|
session.Statement.Cols(columns...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// AllCols ask all columns
|
||||||
|
func (session *Session) AllCols() *Session { |
||||||
|
session.Statement.AllCols() |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// MustCols specify some columns must use even if they are empty
|
||||||
|
func (session *Session) MustCols(columns ...string) *Session { |
||||||
|
session.Statement.MustCols(columns...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// UseBool automatically retrieve condition according struct, but
|
||||||
|
// if struct has bool field, it will ignore them. So use UseBool
|
||||||
|
// to tell system to do not ignore them.
|
||||||
|
// If no parameters, it will use all the bool field of struct, or
|
||||||
|
// it will use parameters's columns
|
||||||
|
func (session *Session) UseBool(columns ...string) *Session { |
||||||
|
session.Statement.UseBool(columns...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Distinct use for distinct columns. Caution: when you are using cache,
|
||||||
|
// distinct will not be cached because cache system need id,
|
||||||
|
// but distinct will not provide id
|
||||||
|
func (session *Session) Distinct(columns ...string) *Session { |
||||||
|
session.Statement.Distinct(columns...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Omit Only not use the parameters as select or update columns
|
||||||
|
func (session *Session) Omit(columns ...string) *Session { |
||||||
|
session.Statement.Omit(columns...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Nullable Set null when column is zero-value and nullable for update
|
||||||
|
func (session *Session) Nullable(columns ...string) *Session { |
||||||
|
session.Statement.Nullable(columns...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// NoAutoTime means do not automatically give created field and updated field
|
||||||
|
// the current time on the current session temporarily
|
||||||
|
func (session *Session) NoAutoTime() *Session { |
||||||
|
session.Statement.UseAutoTime = false |
||||||
|
return session |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xorm |
||||||
|
|
||||||
|
import "github.com/go-xorm/builder" |
||||||
|
|
||||||
|
// Sql provides raw sql input parameter. When you have a complex SQL statement
|
||||||
|
// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
|
||||||
|
//
|
||||||
|
// Deprecated: use SQL instead.
|
||||||
|
func (session *Session) Sql(query string, args ...interface{}) *Session { |
||||||
|
return session.SQL(query, args...) |
||||||
|
} |
||||||
|
|
||||||
|
// SQL provides raw sql input parameter. When you have a complex SQL statement
|
||||||
|
// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
|
||||||
|
func (session *Session) SQL(query interface{}, args ...interface{}) *Session { |
||||||
|
session.Statement.SQL(query, args...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Where provides custom query condition.
|
||||||
|
func (session *Session) Where(query interface{}, args ...interface{}) *Session { |
||||||
|
session.Statement.Where(query, args...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// And provides custom query condition.
|
||||||
|
func (session *Session) And(query interface{}, args ...interface{}) *Session { |
||||||
|
session.Statement.And(query, args...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Or provides custom query condition.
|
||||||
|
func (session *Session) Or(query interface{}, args ...interface{}) *Session { |
||||||
|
session.Statement.Or(query, args...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Id provides converting id as a query condition
|
||||||
|
//
|
||||||
|
// Deprecated: use ID instead
|
||||||
|
func (session *Session) Id(id interface{}) *Session { |
||||||
|
return session.ID(id) |
||||||
|
} |
||||||
|
|
||||||
|
// ID provides converting id as a query condition
|
||||||
|
func (session *Session) ID(id interface{}) *Session { |
||||||
|
session.Statement.ID(id) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// In provides a query string like "id in (1, 2, 3)"
|
||||||
|
func (session *Session) In(column string, args ...interface{}) *Session { |
||||||
|
session.Statement.In(column, args...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// NotIn provides a query string like "id in (1, 2, 3)"
|
||||||
|
func (session *Session) NotIn(column string, args ...interface{}) *Session { |
||||||
|
session.Statement.NotIn(column, args...) |
||||||
|
return session |
||||||
|
} |
||||||
|
|
||||||
|
// Conds returns session query conditions
|
||||||
|
func (session *Session) Conds() builder.Cond { |
||||||
|
return session.Statement.cond |
||||||
|
} |
@ -0,0 +1,673 @@ |
|||||||
|
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xorm |
||||||
|
|
||||||
|
import ( |
||||||
|
"database/sql" |
||||||
|
"database/sql/driver" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"reflect" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-xorm/core" |
||||||
|
) |
||||||
|
|
||||||
|
func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) { |
||||||
|
sdata := strings.TrimSpace(data) |
||||||
|
var x time.Time |
||||||
|
var err error |
||||||
|
|
||||||
|
if sdata == "0000-00-00 00:00:00" || |
||||||
|
sdata == "0001-01-01 00:00:00" { |
||||||
|
} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
|
||||||
|
// time stamp
|
||||||
|
sd, err := strconv.ParseInt(sdata, 10, 64) |
||||||
|
if err == nil { |
||||||
|
x = time.Unix(sd, 0) |
||||||
|
// !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion
|
||||||
|
if col.TimeZone == nil { |
||||||
|
x = x.In(session.Engine.TZLocation) |
||||||
|
} else { |
||||||
|
x = x.In(col.TimeZone) |
||||||
|
} |
||||||
|
session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} else { |
||||||
|
session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} |
||||||
|
} else if len(sdata) > 19 && strings.Contains(sdata, "-") { |
||||||
|
x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) |
||||||
|
session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
if err != nil { |
||||||
|
x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation) |
||||||
|
session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation) |
||||||
|
session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} |
||||||
|
|
||||||
|
} else if len(sdata) == 19 && strings.Contains(sdata, "-") { |
||||||
|
x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) |
||||||
|
session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { |
||||||
|
x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation) |
||||||
|
session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} else if col.SQLType.Name == core.Time { |
||||||
|
if strings.Contains(sdata, " ") { |
||||||
|
ssd := strings.Split(sdata, " ") |
||||||
|
sdata = ssd[1] |
||||||
|
} |
||||||
|
|
||||||
|
sdata = strings.TrimSpace(sdata) |
||||||
|
if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 { |
||||||
|
sdata = sdata[len(sdata)-8:] |
||||||
|
} |
||||||
|
|
||||||
|
st := fmt.Sprintf("2006-01-02 %v", sdata) |
||||||
|
x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation) |
||||||
|
session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) |
||||||
|
} else { |
||||||
|
outErr = fmt.Errorf("unsupported time format %v", sdata) |
||||||
|
return |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err) |
||||||
|
return |
||||||
|
} |
||||||
|
outTime = x |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) { |
||||||
|
return session.str2Time(col, string(data)) |
||||||
|
} |
||||||
|
|
||||||
|
// convert a db data([]byte) to a field value
|
||||||
|
func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error { |
||||||
|
if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { |
||||||
|
return structConvert.FromDB(data) |
||||||
|
} |
||||||
|
|
||||||
|
if structConvert, ok := fieldValue.Interface().(core.Conversion); ok { |
||||||
|
return structConvert.FromDB(data) |
||||||
|
} |
||||||
|
|
||||||
|
var v interface{} |
||||||
|
key := col.Name |
||||||
|
fieldType := fieldValue.Type() |
||||||
|
|
||||||
|
switch fieldType.Kind() { |
||||||
|
case reflect.Complex64, reflect.Complex128: |
||||||
|
x := reflect.New(fieldType) |
||||||
|
if len(data) > 0 { |
||||||
|
err := json.Unmarshal(data, x.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fieldValue.Set(x.Elem()) |
||||||
|
} |
||||||
|
case reflect.Slice, reflect.Array, reflect.Map: |
||||||
|
v = data |
||||||
|
t := fieldType.Elem() |
||||||
|
k := t.Kind() |
||||||
|
if col.SQLType.IsText() { |
||||||
|
x := reflect.New(fieldType) |
||||||
|
if len(data) > 0 { |
||||||
|
err := json.Unmarshal(data, x.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fieldValue.Set(x.Elem()) |
||||||
|
} |
||||||
|
} else if col.SQLType.IsBlob() { |
||||||
|
if k == reflect.Uint8 { |
||||||
|
fieldValue.Set(reflect.ValueOf(v)) |
||||||
|
} else { |
||||||
|
x := reflect.New(fieldType) |
||||||
|
if len(data) > 0 { |
||||||
|
err := json.Unmarshal(data, x.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fieldValue.Set(x.Elem()) |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
return ErrUnSupportedType |
||||||
|
} |
||||||
|
case reflect.String: |
||||||
|
fieldValue.SetString(string(data)) |
||||||
|
case reflect.Bool: |
||||||
|
d := string(data) |
||||||
|
v, err := strconv.ParseBool(d) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as bool: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(v)) |
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||||
|
sdata := string(data) |
||||||
|
var x int64 |
||||||
|
var err error |
||||||
|
// for mysql, when use bit, it returned \x01
|
||||||
|
if col.SQLType.Name == core.Bit && |
||||||
|
session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
|
||||||
|
if len(data) == 1 { |
||||||
|
x = int64(data[0]) |
||||||
|
} else { |
||||||
|
x = 0 |
||||||
|
} |
||||||
|
} else if strings.HasPrefix(sdata, "0x") { |
||||||
|
x, err = strconv.ParseInt(sdata, 16, 64) |
||||||
|
} else if strings.HasPrefix(sdata, "0") { |
||||||
|
x, err = strconv.ParseInt(sdata, 8, 64) |
||||||
|
} else if strings.EqualFold(sdata, "true") { |
||||||
|
x = 1 |
||||||
|
} else if strings.EqualFold(sdata, "false") { |
||||||
|
x = 0 |
||||||
|
} else { |
||||||
|
x, err = strconv.ParseInt(sdata, 10, 64) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.SetInt(x) |
||||||
|
case reflect.Float32, reflect.Float64: |
||||||
|
x, err := strconv.ParseFloat(string(data), 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as float64: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.SetFloat(x) |
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
||||||
|
x, err := strconv.ParseUint(string(data), 10, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.SetUint(x) |
||||||
|
//Currently only support Time type
|
||||||
|
case reflect.Struct: |
||||||
|
// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
|
||||||
|
if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { |
||||||
|
if err := nulVal.Scan(data); err != nil { |
||||||
|
return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error()) |
||||||
|
} |
||||||
|
} else { |
||||||
|
if fieldType.ConvertibleTo(core.TimeType) { |
||||||
|
x, err := session.byte2Time(col, data) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
v = x |
||||||
|
fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) |
||||||
|
} else if session.Statement.UseCascade { |
||||||
|
table, err := session.Engine.autoMapType(*fieldValue) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: current only support 1 primary key
|
||||||
|
if len(table.PrimaryKeys) > 1 { |
||||||
|
panic("unsupported composited primary key cascade") |
||||||
|
} |
||||||
|
var pk = make(core.PK, len(table.PrimaryKeys)) |
||||||
|
rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) |
||||||
|
pk[0], err = str2PK(string(data), rawValueType) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if !isPKZero(pk) { |
||||||
|
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
|
||||||
|
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
|
||||||
|
// property to be fetched lazily
|
||||||
|
structInter := reflect.New(fieldValue.Type()) |
||||||
|
newsession := session.Engine.NewSession() |
||||||
|
defer newsession.Close() |
||||||
|
has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if has { |
||||||
|
v = structInter.Elem().Interface() |
||||||
|
fieldValue.Set(reflect.ValueOf(v)) |
||||||
|
} else { |
||||||
|
return errors.New("cascade obj is not exist") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
case reflect.Ptr: |
||||||
|
// !nashtsai! TODO merge duplicated codes above
|
||||||
|
//typeStr := fieldType.String()
|
||||||
|
switch fieldType.Elem().Kind() { |
||||||
|
// case "*string":
|
||||||
|
case core.StringType.Kind(): |
||||||
|
x := string(data) |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*bool":
|
||||||
|
case core.BoolType.Kind(): |
||||||
|
d := string(data) |
||||||
|
v, err := strconv.ParseBool(d) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as bool: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType)) |
||||||
|
// case "*complex64":
|
||||||
|
case core.Complex64Type.Kind(): |
||||||
|
var x complex64 |
||||||
|
if len(data) > 0 { |
||||||
|
err := json.Unmarshal(data, &x) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
} |
||||||
|
// case "*complex128":
|
||||||
|
case core.Complex128Type.Kind(): |
||||||
|
var x complex128 |
||||||
|
if len(data) > 0 { |
||||||
|
err := json.Unmarshal(data, &x) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return err |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
} |
||||||
|
// case "*float64":
|
||||||
|
case core.Float64Type.Kind(): |
||||||
|
x, err := strconv.ParseFloat(string(data), 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as float64: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*float32":
|
||||||
|
case core.Float32Type.Kind(): |
||||||
|
var x float32 |
||||||
|
x1, err := strconv.ParseFloat(string(data), 32) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as float32: %s", key, err.Error()) |
||||||
|
} |
||||||
|
x = float32(x1) |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*uint64":
|
||||||
|
case core.Uint64Type.Kind(): |
||||||
|
var x uint64 |
||||||
|
x, err := strconv.ParseUint(string(data), 10, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*uint":
|
||||||
|
case core.UintType.Kind(): |
||||||
|
var x uint |
||||||
|
x1, err := strconv.ParseUint(string(data), 10, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
x = uint(x1) |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*uint32":
|
||||||
|
case core.Uint32Type.Kind(): |
||||||
|
var x uint32 |
||||||
|
x1, err := strconv.ParseUint(string(data), 10, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
x = uint32(x1) |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*uint8":
|
||||||
|
case core.Uint8Type.Kind(): |
||||||
|
var x uint8 |
||||||
|
x1, err := strconv.ParseUint(string(data), 10, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
x = uint8(x1) |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*uint16":
|
||||||
|
case core.Uint16Type.Kind(): |
||||||
|
var x uint16 |
||||||
|
x1, err := strconv.ParseUint(string(data), 10, 64) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
x = uint16(x1) |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*int64":
|
||||||
|
case core.Int64Type.Kind(): |
||||||
|
sdata := string(data) |
||||||
|
var x int64 |
||||||
|
var err error |
||||||
|
// for mysql, when use bit, it returned \x01
|
||||||
|
if col.SQLType.Name == core.Bit && |
||||||
|
strings.Contains(session.Engine.DriverName(), "mysql") { |
||||||
|
if len(data) == 1 { |
||||||
|
x = int64(data[0]) |
||||||
|
} else { |
||||||
|
x = 0 |
||||||
|
} |
||||||
|
} else if strings.HasPrefix(sdata, "0x") { |
||||||
|
x, err = strconv.ParseInt(sdata, 16, 64) |
||||||
|
} else if strings.HasPrefix(sdata, "0") { |
||||||
|
x, err = strconv.ParseInt(sdata, 8, 64) |
||||||
|
} else { |
||||||
|
x, err = strconv.ParseInt(sdata, 10, 64) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*int":
|
||||||
|
case core.IntType.Kind(): |
||||||
|
sdata := string(data) |
||||||
|
var x int |
||||||
|
var x1 int64 |
||||||
|
var err error |
||||||
|
// for mysql, when use bit, it returned \x01
|
||||||
|
if col.SQLType.Name == core.Bit && |
||||||
|
strings.Contains(session.Engine.DriverName(), "mysql") { |
||||||
|
if len(data) == 1 { |
||||||
|
x = int(data[0]) |
||||||
|
} else { |
||||||
|
x = 0 |
||||||
|
} |
||||||
|
} else if strings.HasPrefix(sdata, "0x") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 16, 64) |
||||||
|
x = int(x1) |
||||||
|
} else if strings.HasPrefix(sdata, "0") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 8, 64) |
||||||
|
x = int(x1) |
||||||
|
} else { |
||||||
|
x1, err = strconv.ParseInt(sdata, 10, 64) |
||||||
|
x = int(x1) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*int32":
|
||||||
|
case core.Int32Type.Kind(): |
||||||
|
sdata := string(data) |
||||||
|
var x int32 |
||||||
|
var x1 int64 |
||||||
|
var err error |
||||||
|
// for mysql, when use bit, it returned \x01
|
||||||
|
if col.SQLType.Name == core.Bit && |
||||||
|
session.Engine.dialect.DBType() == core.MYSQL { |
||||||
|
if len(data) == 1 { |
||||||
|
x = int32(data[0]) |
||||||
|
} else { |
||||||
|
x = 0 |
||||||
|
} |
||||||
|
} else if strings.HasPrefix(sdata, "0x") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 16, 64) |
||||||
|
x = int32(x1) |
||||||
|
} else if strings.HasPrefix(sdata, "0") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 8, 64) |
||||||
|
x = int32(x1) |
||||||
|
} else { |
||||||
|
x1, err = strconv.ParseInt(sdata, 10, 64) |
||||||
|
x = int32(x1) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*int8":
|
||||||
|
case core.Int8Type.Kind(): |
||||||
|
sdata := string(data) |
||||||
|
var x int8 |
||||||
|
var x1 int64 |
||||||
|
var err error |
||||||
|
// for mysql, when use bit, it returned \x01
|
||||||
|
if col.SQLType.Name == core.Bit && |
||||||
|
strings.Contains(session.Engine.DriverName(), "mysql") { |
||||||
|
if len(data) == 1 { |
||||||
|
x = int8(data[0]) |
||||||
|
} else { |
||||||
|
x = 0 |
||||||
|
} |
||||||
|
} else if strings.HasPrefix(sdata, "0x") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 16, 64) |
||||||
|
x = int8(x1) |
||||||
|
} else if strings.HasPrefix(sdata, "0") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 8, 64) |
||||||
|
x = int8(x1) |
||||||
|
} else { |
||||||
|
x1, err = strconv.ParseInt(sdata, 10, 64) |
||||||
|
x = int8(x1) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*int16":
|
||||||
|
case core.Int16Type.Kind(): |
||||||
|
sdata := string(data) |
||||||
|
var x int16 |
||||||
|
var x1 int64 |
||||||
|
var err error |
||||||
|
// for mysql, when use bit, it returned \x01
|
||||||
|
if col.SQLType.Name == core.Bit && |
||||||
|
strings.Contains(session.Engine.DriverName(), "mysql") { |
||||||
|
if len(data) == 1 { |
||||||
|
x = int16(data[0]) |
||||||
|
} else { |
||||||
|
x = 0 |
||||||
|
} |
||||||
|
} else if strings.HasPrefix(sdata, "0x") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 16, 64) |
||||||
|
x = int16(x1) |
||||||
|
} else if strings.HasPrefix(sdata, "0") { |
||||||
|
x1, err = strconv.ParseInt(sdata, 8, 64) |
||||||
|
x = int16(x1) |
||||||
|
} else { |
||||||
|
x1, err = strconv.ParseInt(sdata, 10, 64) |
||||||
|
x = int16(x1) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("arg %v as int: %s", key, err.Error()) |
||||||
|
} |
||||||
|
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) |
||||||
|
// case "*SomeStruct":
|
||||||
|
case reflect.Struct: |
||||||
|
switch fieldType { |
||||||
|
// case "*.time.Time":
|
||||||
|
case core.PtrTimeType: |
||||||
|
x, err := session.byte2Time(col, data) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
v = x |
||||||
|
fieldValue.Set(reflect.ValueOf(&x)) |
||||||
|
default: |
||||||
|
if session.Statement.UseCascade { |
||||||
|
structInter := reflect.New(fieldType.Elem()) |
||||||
|
table, err := session.Engine.autoMapType(structInter.Elem()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if len(table.PrimaryKeys) > 1 { |
||||||
|
panic("unsupported composited primary key cascade") |
||||||
|
} |
||||||
|
var pk = make(core.PK, len(table.PrimaryKeys)) |
||||||
|
rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) |
||||||
|
pk[0], err = str2PK(string(data), rawValueType) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
if !isPKZero(pk) { |
||||||
|
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
|
||||||
|
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
|
||||||
|
// property to be fetched lazily
|
||||||
|
newsession := session.Engine.NewSession() |
||||||
|
defer newsession.Close() |
||||||
|
has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if has { |
||||||
|
v = structInter.Interface() |
||||||
|
fieldValue.Set(reflect.ValueOf(v)) |
||||||
|
} else { |
||||||
|
return errors.New("cascade obj is not exist") |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) |
||||||
|
} |
||||||
|
} |
||||||
|
default: |
||||||
|
return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) |
||||||
|
} |
||||||
|
default: |
||||||
|
return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// convert a field value of a struct to interface for put into db
|
||||||
|
func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) { |
||||||
|
if fieldValue.CanAddr() { |
||||||
|
if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { |
||||||
|
data, err := fieldConvert.ToDB() |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
if col.SQLType.IsBlob() { |
||||||
|
return data, nil |
||||||
|
} |
||||||
|
return string(data), nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok { |
||||||
|
data, err := fieldConvert.ToDB() |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
if col.SQLType.IsBlob() { |
||||||
|
return data, nil |
||||||
|
} |
||||||
|
return string(data), nil |
||||||
|
} |
||||||
|
|
||||||
|
fieldType := fieldValue.Type() |
||||||
|
k := fieldType.Kind() |
||||||
|
if k == reflect.Ptr { |
||||||
|
if fieldValue.IsNil() { |
||||||
|
return nil, nil |
||||||
|
} else if !fieldValue.IsValid() { |
||||||
|
session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid") |
||||||
|
return nil, nil |
||||||
|
} else { |
||||||
|
// !nashtsai! deference pointer type to instance type
|
||||||
|
fieldValue = fieldValue.Elem() |
||||||
|
fieldType = fieldValue.Type() |
||||||
|
k = fieldType.Kind() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch k { |
||||||
|
case reflect.Bool: |
||||||
|
return fieldValue.Bool(), nil |
||||||
|
case reflect.String: |
||||||
|
return fieldValue.String(), nil |
||||||
|
case reflect.Struct: |
||||||
|
if fieldType.ConvertibleTo(core.TimeType) { |
||||||
|
t := fieldValue.Convert(core.TimeType).Interface().(time.Time) |
||||||
|
if session.Engine.dialect.DBType() == core.MSSQL { |
||||||
|
if t.IsZero() { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
} |
||||||
|
tf := session.Engine.FormatTime(col.SQLType.Name, t) |
||||||
|
return tf, nil |
||||||
|
} |
||||||
|
|
||||||
|
if !col.SQLType.IsJson() { |
||||||
|
// !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
|
||||||
|
if v, ok := fieldValue.Interface().(driver.Valuer); ok { |
||||||
|
return v.Value() |
||||||
|
} |
||||||
|
|
||||||
|
fieldTable, err := session.Engine.autoMapType(fieldValue) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if len(fieldTable.PrimaryKeys) == 1 { |
||||||
|
pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) |
||||||
|
return pkField.Interface(), nil |
||||||
|
} |
||||||
|
return 0, fmt.Errorf("no primary key for col %v", col.Name) |
||||||
|
} |
||||||
|
|
||||||
|
if col.SQLType.IsText() { |
||||||
|
bytes, err := json.Marshal(fieldValue.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
return string(bytes), nil |
||||||
|
} else if col.SQLType.IsBlob() { |
||||||
|
bytes, err := json.Marshal(fieldValue.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
return bytes, nil |
||||||
|
} |
||||||
|
return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) |
||||||
|
case reflect.Complex64, reflect.Complex128: |
||||||
|
bytes, err := json.Marshal(fieldValue.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
return string(bytes), nil |
||||||
|
case reflect.Array, reflect.Slice, reflect.Map: |
||||||
|
if !fieldValue.IsValid() { |
||||||
|
return fieldValue.Interface(), nil |
||||||
|
} |
||||||
|
|
||||||
|
if col.SQLType.IsText() { |
||||||
|
bytes, err := json.Marshal(fieldValue.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
return string(bytes), nil |
||||||
|
} else if col.SQLType.IsBlob() { |
||||||
|
var bytes []byte |
||||||
|
var err error |
||||||
|
if (k == reflect.Array || k == reflect.Slice) && |
||||||
|
(fieldValue.Type().Elem().Kind() == reflect.Uint8) { |
||||||
|
bytes = fieldValue.Bytes() |
||||||
|
} else { |
||||||
|
bytes, err = json.Marshal(fieldValue.Interface()) |
||||||
|
if err != nil { |
||||||
|
session.Engine.logger.Error(err) |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
} |
||||||
|
return bytes, nil |
||||||
|
} |
||||||
|
return nil, ErrUnSupportedType |
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
||||||
|
return int64(fieldValue.Uint()), nil |
||||||
|
default: |
||||||
|
return fieldValue.Interface(), nil |
||||||
|
} |
||||||
|
} |
@ -1,20 +0,0 @@ |
|||||||
// Copyright 2015 The Xorm Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package xorm |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/go-xorm/core" |
|
||||||
) |
|
||||||
|
|
||||||
// func init() {
|
|
||||||
// core.RegisterDriver("sqlite3", &sqlite3Driver{})
|
|
||||||
// }
|
|
||||||
|
|
||||||
type sqlite3Driver struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { |
|
||||||
return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil |
|
||||||
} |
|
@ -0,0 +1,281 @@ |
|||||||
|
// Copyright 2017 The Xorm Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xorm |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"reflect" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-xorm/core" |
||||||
|
) |
||||||
|
|
||||||
|
type tagContext struct { |
||||||
|
tagName string |
||||||
|
params []string |
||||||
|
preTag, nextTag string |
||||||
|
table *core.Table |
||||||
|
col *core.Column |
||||||
|
fieldValue reflect.Value |
||||||
|
isIndex bool |
||||||
|
isUnique bool |
||||||
|
indexNames map[string]int |
||||||
|
engine *Engine |
||||||
|
hasCacheTag bool |
||||||
|
hasNoCacheTag bool |
||||||
|
ignoreNext bool |
||||||
|
} |
||||||
|
|
||||||
|
// tagHandler describes tag handler for XORM
|
||||||
|
type tagHandler func(ctx *tagContext) error |
||||||
|
|
||||||
|
var ( |
||||||
|
// defaultTagHandlers enumerates all the default tag handler
|
||||||
|
defaultTagHandlers = map[string]tagHandler{ |
||||||
|
"<-": OnlyFromDBTagHandler, |
||||||
|
"->": OnlyToDBTagHandler, |
||||||
|
"PK": PKTagHandler, |
||||||
|
"NULL": NULLTagHandler, |
||||||
|
"NOT": IgnoreTagHandler, |
||||||
|
"AUTOINCR": AutoIncrTagHandler, |
||||||
|
"DEFAULT": DefaultTagHandler, |
||||||
|
"CREATED": CreatedTagHandler, |
||||||
|
"UPDATED": UpdatedTagHandler, |
||||||
|
"DELETED": DeletedTagHandler, |
||||||
|
"VERSION": VersionTagHandler, |
||||||
|
"UTC": UTCTagHandler, |
||||||
|
"LOCAL": LocalTagHandler, |
||||||
|
"NOTNULL": NotNullTagHandler, |
||||||
|
"INDEX": IndexTagHandler, |
||||||
|
"UNIQUE": UniqueTagHandler, |
||||||
|
"CACHE": CacheTagHandler, |
||||||
|
"NOCACHE": NoCacheTagHandler, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func init() { |
||||||
|
for k := range core.SqlTypes { |
||||||
|
defaultTagHandlers[k] = SQLTypeTagHandler |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// IgnoreTagHandler describes ignored tag handler
|
||||||
|
func IgnoreTagHandler(ctx *tagContext) error { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// OnlyFromDBTagHandler describes mapping direction tag handler
|
||||||
|
func OnlyFromDBTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.MapType = core.ONLYFROMDB |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// OnlyToDBTagHandler describes mapping direction tag handler
|
||||||
|
func OnlyToDBTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.MapType = core.ONLYTODB |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// PKTagHandler decribes primary key tag handler
|
||||||
|
func PKTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.IsPrimaryKey = true |
||||||
|
ctx.col.Nullable = false |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// NULLTagHandler describes null tag handler
|
||||||
|
func NULLTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT") |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// NotNullTagHandler describes notnull tag handler
|
||||||
|
func NotNullTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.Nullable = false |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// AutoIncrTagHandler describes autoincr tag handler
|
||||||
|
func AutoIncrTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.IsAutoIncrement = true |
||||||
|
/* |
||||||
|
if len(ctx.params) > 0 { |
||||||
|
autoStartInt, err := strconv.Atoi(ctx.params[0]) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
ctx.col.AutoIncrStart = autoStartInt |
||||||
|
} else { |
||||||
|
ctx.col.AutoIncrStart = 1 |
||||||
|
} |
||||||
|
*/ |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// DefaultTagHandler describes default tag handler
|
||||||
|
func DefaultTagHandler(ctx *tagContext) error { |
||||||
|
if len(ctx.params) > 0 { |
||||||
|
ctx.col.Default = ctx.params[0] |
||||||
|
} else { |
||||||
|
ctx.col.Default = ctx.nextTag |
||||||
|
ctx.ignoreNext = true |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// CreatedTagHandler describes created tag handler
|
||||||
|
func CreatedTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.IsCreated = true |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// VersionTagHandler describes version tag handler
|
||||||
|
func VersionTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.IsVersion = true |
||||||
|
ctx.col.Default = "1" |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// UTCTagHandler describes utc tag handler
|
||||||
|
func UTCTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.TimeZone = time.UTC |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// LocalTagHandler describes local tag handler
|
||||||
|
func LocalTagHandler(ctx *tagContext) error { |
||||||
|
if len(ctx.params) == 0 { |
||||||
|
ctx.col.TimeZone = time.Local |
||||||
|
} else { |
||||||
|
var err error |
||||||
|
ctx.col.TimeZone, err = time.LoadLocation(ctx.params[0]) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// UpdatedTagHandler describes updated tag handler
|
||||||
|
func UpdatedTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.IsUpdated = true |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// DeletedTagHandler describes deleted tag handler
|
||||||
|
func DeletedTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.IsDeleted = true |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// IndexTagHandler describes index tag handler
|
||||||
|
func IndexTagHandler(ctx *tagContext) error { |
||||||
|
if len(ctx.params) > 0 { |
||||||
|
ctx.indexNames[ctx.params[0]] = core.IndexType |
||||||
|
} else { |
||||||
|
ctx.isIndex = true |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// UniqueTagHandler describes unique tag handler
|
||||||
|
func UniqueTagHandler(ctx *tagContext) error { |
||||||
|
if len(ctx.params) > 0 { |
||||||
|
ctx.indexNames[ctx.params[0]] = core.UniqueType |
||||||
|
} else { |
||||||
|
ctx.isUnique = true |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// SQLTypeTagHandler describes SQL Type tag handler
|
||||||
|
func SQLTypeTagHandler(ctx *tagContext) error { |
||||||
|
ctx.col.SQLType = core.SQLType{Name: ctx.tagName} |
||||||
|
if len(ctx.params) > 0 { |
||||||
|
if ctx.tagName == core.Enum { |
||||||
|
ctx.col.EnumOptions = make(map[string]int) |
||||||
|
for k, v := range ctx.params { |
||||||
|
v = strings.TrimSpace(v) |
||||||
|
v = strings.Trim(v, "'") |
||||||
|
ctx.col.EnumOptions[v] = k |
||||||
|
} |
||||||
|
} else if ctx.tagName == core.Set { |
||||||
|
ctx.col.SetOptions = make(map[string]int) |
||||||
|
for k, v := range ctx.params { |
||||||
|
v = strings.TrimSpace(v) |
||||||
|
v = strings.Trim(v, "'") |
||||||
|
ctx.col.SetOptions[v] = k |
||||||
|
} |
||||||
|
} else { |
||||||
|
var err error |
||||||
|
if len(ctx.params) == 2 { |
||||||
|
ctx.col.Length, err = strconv.Atoi(ctx.params[0]) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
ctx.col.Length2, err = strconv.Atoi(ctx.params[1]) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} else if len(ctx.params) == 1 { |
||||||
|
ctx.col.Length, err = strconv.Atoi(ctx.params[0]) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// ExtendsTagHandler describes extends tag handler
|
||||||
|
func ExtendsTagHandler(ctx *tagContext) error { |
||||||
|
var fieldValue = ctx.fieldValue |
||||||
|
switch fieldValue.Kind() { |
||||||
|
case reflect.Ptr: |
||||||
|
f := fieldValue.Type().Elem() |
||||||
|
if f.Kind() == reflect.Struct { |
||||||
|
fieldPtr := fieldValue |
||||||
|
fieldValue = fieldValue.Elem() |
||||||
|
if !fieldValue.IsValid() || fieldPtr.IsNil() { |
||||||
|
fieldValue = reflect.New(f).Elem() |
||||||
|
} |
||||||
|
} |
||||||
|
fallthrough |
||||||
|
case reflect.Struct: |
||||||
|
parentTable, err := ctx.engine.mapType(fieldValue) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
for _, col := range parentTable.Columns() { |
||||||
|
col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName) |
||||||
|
ctx.table.AddColumn(col) |
||||||
|
for indexName, indexType := range col.Indexes { |
||||||
|
addIndex(indexName, ctx.table, col, indexType) |
||||||
|
} |
||||||
|
} |
||||||
|
default: |
||||||
|
//TODO: warning
|
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// CacheTagHandler describes cache tag handler
|
||||||
|
func CacheTagHandler(ctx *tagContext) error { |
||||||
|
if !ctx.hasCacheTag { |
||||||
|
ctx.hasCacheTag = true |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// NoCacheTagHandler describes nocache tag handler
|
||||||
|
func NoCacheTagHandler(ctx *tagContext) error { |
||||||
|
if !ctx.hasNoCacheTag { |
||||||
|
ctx.hasNoCacheTag = true |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
Loading…
Reference in new issue