Merge branch 'master' into fix-missing-string-if-not-blob

This commit is contained in:
Andrew Thornton 2021-07-22 20:02:00 +08:00
commit d5142257f1
29 changed files with 728 additions and 685 deletions

View File

@ -6,7 +6,7 @@ GOFMT ?= gofmt -s
TAGS ?= TAGS ?=
SED_INPLACE := sed -i SED_INPLACE := sed -i
GO_DIRS := caches contexts integrations convert core dialects internal log migrate names schemas tags GO_DIRS := caches contexts integrations core dialects internal log migrate names schemas tags
GOFILES := $(wildcard *.go) GOFILES := $(wildcard *.go)
GOFILES += $(shell find $(GO_DIRS) -name "*.go" -type f) GOFILES += $(shell find $(GO_DIRS) -name "*.go" -type f)
INTEGRATION_PACKAGES := xorm.io/xorm/integrations INTEGRATION_PACKAGES := xorm.io/xorm/integrations

View File

@ -44,11 +44,13 @@ Drivers for Go's sql package which currently support database/sql includes:
* [SQLite](https://sqlite.org) * [SQLite](https://sqlite.org)
- [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
- [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) (windows unsupported)
* MsSql * MsSql
- [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
* Oracle * Oracle
- [github.com/godror/godror)](https://github.com/godror/godror) (experiment)
- [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
## Installation ## Installation

View File

@ -43,11 +43,13 @@ v1.0.0 相对于 v0.8.2 有以下不兼容的变更:
* [SQLite](https://sqlite.org) * [SQLite](https://sqlite.org)
- [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
- [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) (Windows试验性支持)
* MsSql * MsSql
- [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
* Oracle * Oracle
- [github.com/godror/godror)](https://github.com/godror/godror) (试验性支持)
- [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持) - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持)
## 安装 ## 安装

View File

@ -15,7 +15,7 @@ import (
"strconv" "strconv"
"time" "time"
"xorm.io/xorm/convert" "xorm.io/xorm/internal/convert"
) )
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
@ -36,347 +36,6 @@ func cloneBytes(b []byte) []byte {
return c return c
} }
func asString(src interface{}) string {
switch v := src.(type) {
case string:
return v
case []byte:
return string(v)
case *sql.NullString:
return v.String
case *sql.NullInt32:
return fmt.Sprintf("%d", v.Int32)
case *sql.NullInt64:
return fmt.Sprintf("%d", v.Int64)
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
case reflect.Float32:
return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
case reflect.Bool:
return strconv.FormatBool(rv.Bool())
}
return fmt.Sprintf("%v", src)
}
func asInt64(src interface{}) (int64, error) {
switch v := src.(type) {
case int:
return int64(v), nil
case int16:
return int64(v), nil
case int32:
return int64(v), nil
case int8:
return int64(v), nil
case int64:
return v, nil
case uint:
return int64(v), nil
case uint8:
return int64(v), nil
case uint16:
return int64(v), nil
case uint32:
return int64(v), nil
case uint64:
return int64(v), nil
case []byte:
return strconv.ParseInt(string(v), 10, 64)
case string:
return strconv.ParseInt(v, 10, 64)
case *sql.NullString:
return strconv.ParseInt(v.String, 10, 64)
case *sql.NullInt32:
return int64(v.Int32), nil
case *sql.NullInt64:
return int64(v.Int64), nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(rv.Uint()), nil
case reflect.Float64, reflect.Float32:
return int64(rv.Float()), nil
case reflect.String:
return strconv.ParseInt(rv.String(), 10, 64)
}
return 0, fmt.Errorf("unsupported value %T as int64", src)
}
func asUint64(src interface{}) (uint64, error) {
switch v := src.(type) {
case int:
return uint64(v), nil
case int16:
return uint64(v), nil
case int32:
return uint64(v), nil
case int8:
return uint64(v), nil
case int64:
return uint64(v), nil
case uint:
return uint64(v), nil
case uint8:
return uint64(v), nil
case uint16:
return uint64(v), nil
case uint32:
return uint64(v), nil
case uint64:
return v, nil
case []byte:
return strconv.ParseUint(string(v), 10, 64)
case string:
return strconv.ParseUint(v, 10, 64)
case *sql.NullString:
return strconv.ParseUint(v.String, 10, 64)
case *sql.NullInt32:
return uint64(v.Int32), nil
case *sql.NullInt64:
return uint64(v.Int64), nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return uint64(rv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return uint64(rv.Uint()), nil
case reflect.Float64, reflect.Float32:
return uint64(rv.Float()), nil
case reflect.String:
return strconv.ParseUint(rv.String(), 10, 64)
}
return 0, fmt.Errorf("unsupported value %T as uint64", src)
}
func asFloat64(src interface{}) (float64, error) {
switch v := src.(type) {
case int:
return float64(v), nil
case int16:
return float64(v), nil
case int32:
return float64(v), nil
case int8:
return float64(v), nil
case int64:
return float64(v), nil
case uint:
return float64(v), nil
case uint8:
return float64(v), nil
case uint16:
return float64(v), nil
case uint32:
return float64(v), nil
case uint64:
return float64(v), nil
case []byte:
return strconv.ParseFloat(string(v), 64)
case string:
return strconv.ParseFloat(v, 64)
case *sql.NullString:
return strconv.ParseFloat(v.String, 64)
case *sql.NullInt32:
return float64(v.Int32), nil
case *sql.NullInt64:
return float64(v.Int64), nil
case *sql.NullFloat64:
return v.Float64, nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(rv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(rv.Uint()), nil
case reflect.Float64, reflect.Float32:
return float64(rv.Float()), nil
case reflect.String:
return strconv.ParseFloat(rv.String(), 64)
}
return 0, fmt.Errorf("unsupported value %T as int64", src)
}
func asBigFloat(src interface{}) (*big.Float, error) {
res := big.NewFloat(0)
switch v := src.(type) {
case int:
res.SetInt64(int64(v))
return res, nil
case int16:
res.SetInt64(int64(v))
return res, nil
case int32:
res.SetInt64(int64(v))
return res, nil
case int8:
res.SetInt64(int64(v))
return res, nil
case int64:
res.SetInt64(int64(v))
return res, nil
case uint:
res.SetUint64(uint64(v))
return res, nil
case uint8:
res.SetUint64(uint64(v))
return res, nil
case uint16:
res.SetUint64(uint64(v))
return res, nil
case uint32:
res.SetUint64(uint64(v))
return res, nil
case uint64:
res.SetUint64(uint64(v))
return res, nil
case []byte:
res.SetString(string(v))
return res, nil
case string:
res.SetString(v)
return res, nil
case *sql.NullString:
if v.Valid {
res.SetString(v.String)
return res, nil
}
return nil, nil
case *sql.NullInt32:
if v.Valid {
res.SetInt64(int64(v.Int32))
return res, nil
}
return nil, nil
case *sql.NullInt64:
if v.Valid {
res.SetInt64(int64(v.Int64))
return res, nil
}
return nil, nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
res.SetInt64(rv.Int())
return res, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
res.SetUint64(rv.Uint())
return res, nil
case reflect.Float64, reflect.Float32:
res.SetFloat64(rv.Float())
return res, nil
case reflect.String:
res.SetString(rv.String())
return res, nil
}
return nil, fmt.Errorf("unsupported value %T as big.Float", src)
}
func asBytes(src interface{}) ([]byte, bool) {
switch t := src.(type) {
case []byte:
return t, true
case *sql.NullString:
if !t.Valid {
return nil, true
}
return []byte(t.String), true
case *sql.RawBytes:
return *t, true
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.AppendInt(nil, rv.Int(), 10), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.AppendUint(nil, rv.Uint(), 10), true
case reflect.Float32:
return strconv.AppendFloat(nil, rv.Float(), 'g', -1, 32), true
case reflect.Float64:
return strconv.AppendFloat(nil, rv.Float(), 'g', -1, 64), true
case reflect.Bool:
return strconv.AppendBool(nil, rv.Bool()), true
case reflect.String:
return []byte(rv.String()), true
}
return nil, false
}
func asTime(src interface{}, dbLoc *time.Location, uiLoc *time.Location) (*time.Time, error) {
switch t := src.(type) {
case string:
return convert.String2Time(t, dbLoc, uiLoc)
case *sql.NullString:
if !t.Valid {
return nil, nil
}
return convert.String2Time(t.String, dbLoc, uiLoc)
case []uint8:
if t == nil {
return nil, nil
}
return convert.String2Time(string(t), dbLoc, uiLoc)
case *sql.NullTime:
if !t.Valid {
return nil, nil
}
z, _ := t.Time.Zone()
if len(z) == 0 || t.Time.Year() == 0 || t.Time.Location().String() != dbLoc.String() {
tm := time.Date(t.Time.Year(), t.Time.Month(), t.Time.Day(), t.Time.Hour(),
t.Time.Minute(), t.Time.Second(), t.Time.Nanosecond(), dbLoc).In(uiLoc)
return &tm, nil
}
tm := t.Time.In(uiLoc)
return &tm, nil
case *time.Time:
z, _ := t.Zone()
if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbLoc.String() {
tm := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
t.Minute(), t.Second(), t.Nanosecond(), dbLoc).In(uiLoc)
return &tm, nil
}
tm := t.In(uiLoc)
return &tm, nil
case time.Time:
z, _ := t.Zone()
if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbLoc.String() {
tm := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
t.Minute(), t.Second(), t.Nanosecond(), dbLoc).In(uiLoc)
return &tm, nil
}
tm := t.In(uiLoc)
return &tm, nil
case int:
tm := time.Unix(int64(t), 0).In(uiLoc)
return &tm, nil
case int64:
tm := time.Unix(t, 0).In(uiLoc)
return &tm, nil
case *sql.NullInt64:
tm := time.Unix(t.Int64, 0).In(uiLoc)
return &tm, nil
}
return nil, fmt.Errorf("unsupported value %#v as time", src)
}
// convertAssign copies to dest the value in src, converting it if possible. // convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information. // An error is returned if the copy would result in loss of information.
// dest should be a pointer type. // dest should be a pointer type.
@ -585,7 +244,7 @@ func convertAssign(dest, src interface{}, originalLocation *time.Location, conve
} }
return nil return nil
} }
case *NullUint32: case *convert.NullUint32:
switch d := dest.(type) { switch d := dest.(type) {
case *uint8: case *uint8:
if s.Valid { if s.Valid {
@ -603,7 +262,7 @@ func convertAssign(dest, src interface{}, originalLocation *time.Location, conve
} }
return nil return nil
} }
case *NullUint64: case *convert.NullUint64:
switch d := dest.(type) { switch d := dest.(type) {
case *uint64: case *uint64:
if s.Valid { if s.Valid {
@ -628,11 +287,11 @@ func convertAssign(dest, src interface{}, originalLocation *time.Location, conve
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64: reflect.Float32, reflect.Float64:
*d = asString(src) *d = convert.AsString(src)
return nil return nil
} }
case *[]byte: case *[]byte:
if b, ok := asBytes(src); ok { if b, ok := convert.AsBytes(src); ok {
*d = b *d = b
return nil return nil
} }
@ -666,7 +325,7 @@ func convertAssignV(dv reflect.Value, src interface{}) error {
} }
return convertAssignV(dv.Elem(), src) return convertAssignV(dv.Elem(), src)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i64, err := asInt64(src) i64, err := convert.AsInt64(src)
if err != nil { if err != nil {
err = strconvErr(err) err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err) return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err)
@ -674,7 +333,7 @@ func convertAssignV(dv reflect.Value, src interface{}) error {
dv.SetInt(i64) dv.SetInt(i64)
return nil return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
u64, err := asUint64(src) u64, err := convert.AsUint64(src)
if err != nil { if err != nil {
err = strconvErr(err) err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err) return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err)
@ -682,7 +341,7 @@ func convertAssignV(dv reflect.Value, src interface{}) error {
dv.SetUint(u64) dv.SetUint(u64)
return nil return nil
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
f64, err := asFloat64(src) f64, err := convert.AsFloat64(src)
if err != nil { if err != nil {
err = strconvErr(err) err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err) return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err)
@ -690,17 +349,17 @@ func convertAssignV(dv reflect.Value, src interface{}) error {
dv.SetFloat(f64) dv.SetFloat(f64)
return nil return nil
case reflect.String: case reflect.String:
dv.SetString(asString(src)) dv.SetString(convert.AsString(src))
return nil return nil
case reflect.Bool: case reflect.Bool:
b, err := asBool(src) b, err := convert.AsBool(src)
if err != nil { if err != nil {
return err return err
} }
dv.SetBool(b) dv.SetBool(b)
return nil return nil
case reflect.Slice, reflect.Map, reflect.Struct, reflect.Array: case reflect.Slice, reflect.Map, reflect.Struct, reflect.Array:
data, ok := asBytes(src) data, ok := convert.AsBytes(src)
if !ok { if !ok {
return fmt.Errorf("onvertAssignV: src cannot be as bytes %#v", src) return fmt.Errorf("onvertAssignV: src cannot be as bytes %#v", src)
} }
@ -753,201 +412,3 @@ func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
} }
return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv) return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
} }
func asBool(src interface{}) (bool, error) {
switch v := src.(type) {
case bool:
return v, nil
case *bool:
return *v, nil
case *sql.NullBool:
return v.Bool, nil
case int64:
return v > 0, nil
case int:
return v > 0, nil
case int8:
return v > 0, nil
case int16:
return v > 0, nil
case int32:
return v > 0, nil
case []byte:
if len(v) == 0 {
return false, nil
}
if v[0] == 0x00 {
return false, nil
} else if v[0] == 0x01 {
return true, nil
}
return strconv.ParseBool(string(v))
case string:
return strconv.ParseBool(v)
case *sql.NullInt64:
return v.Int64 > 0, nil
case *sql.NullInt32:
return v.Int32 > 0, nil
default:
return false, fmt.Errorf("unknow type %T as bool", src)
}
}
// str2PK convert string value to primary key value according to tp
func str2PKValue(s string, tp reflect.Type) (reflect.Value, error) {
var err error
var result interface{}
var defReturn = reflect.Zero(tp)
switch tp.Kind() {
case reflect.Int:
result, err = strconv.Atoi(s)
if err != nil {
return defReturn, fmt.Errorf("convert %s as int: %s", s, err.Error())
}
case reflect.Int8:
x, err := strconv.Atoi(s)
if err != nil {
return defReturn, fmt.Errorf("convert %s as int8: %s", s, err.Error())
}
result = int8(x)
case reflect.Int16:
x, err := strconv.Atoi(s)
if err != nil {
return defReturn, fmt.Errorf("convert %s as int16: %s", s, err.Error())
}
result = int16(x)
case reflect.Int32:
x, err := strconv.Atoi(s)
if err != nil {
return defReturn, fmt.Errorf("convert %s as int32: %s", s, err.Error())
}
result = int32(x)
case reflect.Int64:
result, err = strconv.ParseInt(s, 10, 64)
if err != nil {
return defReturn, fmt.Errorf("convert %s as int64: %s", s, err.Error())
}
case reflect.Uint:
x, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return defReturn, fmt.Errorf("convert %s as uint: %s", s, err.Error())
}
result = uint(x)
case reflect.Uint8:
x, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return defReturn, fmt.Errorf("convert %s as uint8: %s", s, err.Error())
}
result = uint8(x)
case reflect.Uint16:
x, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return defReturn, fmt.Errorf("convert %s as uint16: %s", s, err.Error())
}
result = uint16(x)
case reflect.Uint32:
x, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return defReturn, fmt.Errorf("convert %s as uint32: %s", s, err.Error())
}
result = uint32(x)
case reflect.Uint64:
result, err = strconv.ParseUint(s, 10, 64)
if err != nil {
return defReturn, fmt.Errorf("convert %s as uint64: %s", s, err.Error())
}
case reflect.String:
result = s
default:
return defReturn, errors.New("unsupported convert type")
}
return reflect.ValueOf(result).Convert(tp), nil
}
func str2PK(s string, tp reflect.Type) (interface{}, error) {
v, err := str2PKValue(s, tp)
if err != nil {
return nil, err
}
return v.Interface(), nil
}
var (
_ sql.Scanner = &NullUint64{}
)
// NullUint64 represents an uint64 that may be null.
// NullUint64 implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullUint64 struct {
Uint64 uint64
Valid bool
}
// Scan implements the Scanner interface.
func (n *NullUint64) Scan(value interface{}) error {
if value == nil {
n.Uint64, n.Valid = 0, false
return nil
}
n.Valid = true
var err error
n.Uint64, err = asUint64(value)
return err
}
// Value implements the driver Valuer interface.
func (n NullUint64) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Uint64, nil
}
var (
_ sql.Scanner = &NullUint32{}
)
// NullUint32 represents an uint32 that may be null.
// NullUint32 implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullUint32 struct {
Uint32 uint32
Valid bool // Valid is true if Uint32 is not NULL
}
// Scan implements the Scanner interface.
func (n *NullUint32) Scan(value interface{}) error {
if value == nil {
n.Uint32, n.Valid = 0, false
return nil
}
n.Valid = true
i64, err := asUint64(value)
if err != nil {
return err
}
n.Uint32 = uint32(i64)
return nil
}
// Value implements the driver Valuer interface.
func (n NullUint32) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return int64(n.Uint32), nil
}
var (
_ sql.Scanner = &EmptyScanner{}
)
// EmptyScanner represents an empty scanner which will ignore the scan
type EmptyScanner struct{}
// Scan implements sql.Scanner
func (EmptyScanner) Scan(value interface{}) error {
return nil
}

View File

@ -1,49 +0,0 @@
// Copyright 2021 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 convert
import (
"fmt"
"strconv"
"time"
"xorm.io/xorm/internal/utils"
)
// String2Time converts a string to time with original location
func String2Time(s string, originalLocation *time.Location, convertedLocation *time.Location) (*time.Time, error) {
if len(s) == 19 {
if s == utils.ZeroTime0 || s == utils.ZeroTime1 {
return &time.Time{}, nil
}
dt, err := time.ParseInLocation("2006-01-02 15:04:05", s, originalLocation)
if err != nil {
return nil, err
}
dt = dt.In(convertedLocation)
return &dt, nil
} else if len(s) == 20 && s[10] == 'T' && s[19] == 'Z' {
dt, err := time.ParseInLocation("2006-01-02T15:04:05", s[:19], originalLocation)
if err != nil {
return nil, err
}
dt = dt.In(convertedLocation)
return &dt, nil
} else if len(s) == 25 && s[10] == 'T' && s[19] == '+' && s[22] == ':' {
dt, err := time.Parse(time.RFC3339, s)
if err != nil {
return nil, err
}
dt = dt.In(convertedLocation)
return &dt, nil
} else {
i, err := strconv.ParseInt(s, 10, 64)
if err == nil {
tm := time.Unix(i, 0).In(convertedLocation)
return &tm, nil
}
}
return nil, fmt.Errorf("unsupported conversion from %s to time", s)
}

68
doc.go
View File

@ -14,23 +14,30 @@ Make sure you have installed Go 1.11+ and then:
Create Engine Create Engine
Firstly, we should new an engine for a database Firstly, we should create an engine for a database
engine, err := xorm.NewEngine(driverName, dataSourceName) engine, err := xorm.NewEngine(driverName, dataSourceName)
Method NewEngine's parameters is the same as sql.Open. It depends Method NewEngine's parameters are the same as sql.Open which depend drivers' implementation.
drivers' implementation. Generally, one engine for an application is enough. You can define it as a package variable.
Generally, one engine for an application is enough. You can set it as package variable.
Raw Methods Raw Methods
XORM also support raw SQL execution: XORM supports raw SQL execution:
1. query a SQL string, the returned results is []map[string][]byte 1. query with a SQL string, the returned results is []map[string][]byte
results, err := engine.Query("select * from user") results, err := engine.Query("select * from user")
2. execute a SQL string, the returned results 2. query with a SQL string, the returned results is []map[string]string
results, err := engine.QueryString("select * from user")
3. query with a SQL string, the returned results is []map[string]interface{}
results, err := engine.QueryInterface("select * from user")
4. execute with a SQL string, the returned results
affected, err := engine.Exec("update user set .... where ...") affected, err := engine.Exec("update user set .... where ...")
@ -77,7 +84,9 @@ There are 8 major ORM methods and many helpful methods to use to operate databas
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
err := engine.Iterate(...) err := engine.Iterate(new(User), func(i int, bean interface{}) error {
// do something
})
// SELECT * FROM user // SELECT * FROM user
rows, err := engine.Rows(...) rows, err := engine.Rows(...)
@ -120,7 +129,7 @@ another is Rows
Conditions Conditions
The above 8 methods could use with condition methods chainable. The above 8 methods could use with condition methods chainable.
Attention: the above 8 methods should be the last chainable method. Notice: the above 8 methods should be the last chainable method.
1. ID, In 1. ID, In
@ -179,6 +188,47 @@ Attention: the above 8 methods should be the last chainable method.
engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users) engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users)
//SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id
Builder
xorm could work with xorm.io/builder directly.
1. With Where
var cond = builder.Eq{"a":1, "b":2}
engine.Where(cond).Find(&users)
2. With In
var subQuery = builder.Select("name").From("group")
engine.In("group_name", subQuery).Find(&users)
3. With Join
var subQuery = builder.Select("name").From("group")
engine.Join("INNER", subQuery, "group.id = user.group_id").Find(&users)
4. With SetExprs
var subQuery = builder.Select("name").From("group")
engine.ID(1).SetExprs("name", subQuery).Update(new(User))
5. With SQL
var query = builder.Select("name").From("group")
results, err := engine.SQL(query).Find(&groups)
6. With Query
var query = builder.Select("name").From("group")
results, err := engine.Query(query)
results, err := engine.QueryString(query)
results, err := engine.QueryInterface(query)
7. With Exec
var query = builder.Insert("a, b").Into("table1").Select("b, c").From("table2")
results, err := engine.Exec(query)
More usage, please visit http://xorm.io/docs More usage, please visit http://xorm.io/docs
*/ */
package xorm package xorm

View File

@ -237,3 +237,31 @@ func (eg *EngineGroup) Slave() *Engine {
func (eg *EngineGroup) Slaves() []*Engine { func (eg *EngineGroup) Slaves() []*Engine {
return eg.slaves return eg.slaves
} }
// Query execcute a select SQL and return the result
func (eg *EngineGroup) Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) {
sess := eg.NewSession()
sess.isAutoClose = true
return sess.Query(sqlOrArgs...)
}
// QueryInterface execcute a select SQL and return the result
func (eg *EngineGroup) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) {
sess := eg.NewSession()
sess.isAutoClose = true
return sess.QueryInterface(sqlOrArgs...)
}
// QueryString execcute a select SQL and return the result
func (eg *EngineGroup) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) {
sess := eg.NewSession()
sess.isAutoClose = true
return sess.QueryString(sqlOrArgs...)
}
// Rows execcute a select SQL and return the result
func (eg *EngineGroup) Rows(bean interface{}) (*Rows, error) {
sess := eg.NewSession()
sess.isAutoClose = true
return sess.Rows(bean)
}

View File

@ -9,68 +9,18 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"strconv"
"testing" "testing"
"time" "time"
"xorm.io/xorm" "xorm.io/xorm"
"xorm.io/xorm/contexts" "xorm.io/xorm/contexts"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func convertInt(v interface{}) (int64, error) {
switch v.(type) {
case int:
return int64(v.(int)), nil
case int8:
return int64(v.(int8)), nil
case int16:
return int64(v.(int16)), nil
case int32:
return int64(v.(int32)), nil
case int64:
return v.(int64), nil
case []byte:
i, err := strconv.ParseInt(string(v.([]byte)), 10, 64)
if err != nil {
return 0, err
}
return i, nil
case string:
i, err := strconv.ParseInt(v.(string), 10, 64)
if err != nil {
return 0, err
}
return i, nil
}
return 0, fmt.Errorf("unsupported type: %v", v)
}
func convertFloat(v interface{}) (float64, error) {
switch v.(type) {
case float32:
return float64(v.(float32)), nil
case float64:
return v.(float64), nil
case string:
i, err := strconv.ParseFloat(v.(string), 64)
if err != nil {
return 0, err
}
return i, nil
case []byte:
i, err := strconv.ParseFloat(string(v.([]byte)), 64)
if err != nil {
return 0, err
}
return i, nil
}
return 0, fmt.Errorf("unsupported type: %v", v)
}
func TestGetVar(t *testing.T) { func TestGetVar(t *testing.T) {
assert.NoError(t, PrepareEngine()) assert.NoError(t, PrepareEngine())
@ -261,17 +211,17 @@ func TestGetVar(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, true, has) assert.Equal(t, true, has)
v1, err := convertInt(valuesSliceInter[0]) v1, err := convert.AsInt64(valuesSliceInter[0])
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, v1) assert.EqualValues(t, 1, v1)
assert.Equal(t, "hi", fmt.Sprintf("%s", valuesSliceInter[1])) assert.Equal(t, "hi", fmt.Sprintf("%s", valuesSliceInter[1]))
v3, err := convertInt(valuesSliceInter[2]) v3, err := convert.AsInt64(valuesSliceInter[2])
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 28, v3) assert.EqualValues(t, 28, v3)
v4, err := convertFloat(valuesSliceInter[3]) v4, err := convert.AsFloat64(valuesSliceInter[3])
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "1.5", fmt.Sprintf("%v", v4)) assert.Equal(t, "1.5", fmt.Sprintf("%v", v4))
} }

View File

@ -396,3 +396,25 @@ func TestJoinWithSubQuery(t *testing.T) {
assert.EqualValues(t, 1, len(querys)) assert.EqualValues(t, 1, len(querys))
assert.EqualValues(t, q, querys[0]) assert.EqualValues(t, q, querys[0])
} }
func TestQueryStringWithLimit(t *testing.T) {
assert.NoError(t, PrepareEngine())
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
t.SkipNow()
return
}
type QueryWithLimit struct {
Id int64 `xorm:"autoincr pk"`
Msg string `xorm:"varchar(255)"`
DepartId int64
Money float32
}
assert.NoError(t, testEngine.Sync2(new(QueryWithLimit)))
data, err := testEngine.Table("query_with_limit").Limit(20, 20).QueryString()
assert.NoError(t, err)
assert.EqualValues(t, 0, len(data))
}

View File

@ -13,7 +13,7 @@ import (
"testing" "testing"
"xorm.io/xorm" "xorm.io/xorm"
"xorm.io/xorm/convert" "xorm.io/xorm/internal/convert"
"xorm.io/xorm/internal/json" "xorm.io/xorm/internal/json"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"

51
internal/convert/bool.go Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2021 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 convert
import (
"database/sql"
"fmt"
"strconv"
)
// AsBool convert interface as bool
func AsBool(src interface{}) (bool, error) {
switch v := src.(type) {
case bool:
return v, nil
case *bool:
return *v, nil
case *sql.NullBool:
return v.Bool, nil
case int64:
return v > 0, nil
case int:
return v > 0, nil
case int8:
return v > 0, nil
case int16:
return v > 0, nil
case int32:
return v > 0, nil
case []byte:
if len(v) == 0 {
return false, nil
}
if v[0] == 0x00 {
return false, nil
} else if v[0] == 0x01 {
return true, nil
}
return strconv.ParseBool(string(v))
case string:
return strconv.ParseBool(v)
case *sql.NullInt64:
return v.Int64 > 0, nil
case *sql.NullInt32:
return v.Int32 > 0, nil
default:
return false, fmt.Errorf("unknow type %T as bool", src)
}
}

142
internal/convert/float.go Normal file
View File

@ -0,0 +1,142 @@
// Copyright 2021 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 convert
import (
"database/sql"
"fmt"
"math/big"
"reflect"
"strconv"
)
// AsFloat64 convets interface as float64
func AsFloat64(src interface{}) (float64, error) {
switch v := src.(type) {
case int:
return float64(v), nil
case int16:
return float64(v), nil
case int32:
return float64(v), nil
case int8:
return float64(v), nil
case int64:
return float64(v), nil
case uint:
return float64(v), nil
case uint8:
return float64(v), nil
case uint16:
return float64(v), nil
case uint32:
return float64(v), nil
case uint64:
return float64(v), nil
case []byte:
return strconv.ParseFloat(string(v), 64)
case string:
return strconv.ParseFloat(v, 64)
case *sql.NullString:
return strconv.ParseFloat(v.String, 64)
case *sql.NullInt32:
return float64(v.Int32), nil
case *sql.NullInt64:
return float64(v.Int64), nil
case *sql.NullFloat64:
return v.Float64, nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(rv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(rv.Uint()), nil
case reflect.Float64, reflect.Float32:
return float64(rv.Float()), nil
case reflect.String:
return strconv.ParseFloat(rv.String(), 64)
}
return 0, fmt.Errorf("unsupported value %T as int64", src)
}
// AsBigFloat converts interface as big.Float
func AsBigFloat(src interface{}) (*big.Float, error) {
res := big.NewFloat(0)
switch v := src.(type) {
case int:
res.SetInt64(int64(v))
return res, nil
case int16:
res.SetInt64(int64(v))
return res, nil
case int32:
res.SetInt64(int64(v))
return res, nil
case int8:
res.SetInt64(int64(v))
return res, nil
case int64:
res.SetInt64(int64(v))
return res, nil
case uint:
res.SetUint64(uint64(v))
return res, nil
case uint8:
res.SetUint64(uint64(v))
return res, nil
case uint16:
res.SetUint64(uint64(v))
return res, nil
case uint32:
res.SetUint64(uint64(v))
return res, nil
case uint64:
res.SetUint64(uint64(v))
return res, nil
case []byte:
res.SetString(string(v))
return res, nil
case string:
res.SetString(v)
return res, nil
case *sql.NullString:
if v.Valid {
res.SetString(v.String)
return res, nil
}
return nil, nil
case *sql.NullInt32:
if v.Valid {
res.SetInt64(int64(v.Int32))
return res, nil
}
return nil, nil
case *sql.NullInt64:
if v.Valid {
res.SetInt64(int64(v.Int64))
return res, nil
}
return nil, nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
res.SetInt64(rv.Int())
return res, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
res.SetUint64(rv.Uint())
return res, nil
case reflect.Float64, reflect.Float32:
res.SetFloat64(rv.Float())
return res, nil
case reflect.String:
res.SetString(rv.String())
return res, nil
}
return nil, fmt.Errorf("unsupported value %T as big.Float", src)
}

178
internal/convert/int.go Normal file
View File

@ -0,0 +1,178 @@
// Copyright 2021 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 convert
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"strconv"
)
// AsInt64 converts interface as int64
func AsInt64(src interface{}) (int64, error) {
switch v := src.(type) {
case int:
return int64(v), nil
case int16:
return int64(v), nil
case int32:
return int64(v), nil
case int8:
return int64(v), nil
case int64:
return v, nil
case uint:
return int64(v), nil
case uint8:
return int64(v), nil
case uint16:
return int64(v), nil
case uint32:
return int64(v), nil
case uint64:
return int64(v), nil
case []byte:
return strconv.ParseInt(string(v), 10, 64)
case string:
return strconv.ParseInt(v, 10, 64)
case *sql.NullString:
return strconv.ParseInt(v.String, 10, 64)
case *sql.NullInt32:
return int64(v.Int32), nil
case *sql.NullInt64:
return int64(v.Int64), nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(rv.Uint()), nil
case reflect.Float64, reflect.Float32:
return int64(rv.Float()), nil
case reflect.String:
return strconv.ParseInt(rv.String(), 10, 64)
}
return 0, fmt.Errorf("unsupported value %T as int64", src)
}
// AsUint64 converts interface as uint64
func AsUint64(src interface{}) (uint64, error) {
switch v := src.(type) {
case int:
return uint64(v), nil
case int16:
return uint64(v), nil
case int32:
return uint64(v), nil
case int8:
return uint64(v), nil
case int64:
return uint64(v), nil
case uint:
return uint64(v), nil
case uint8:
return uint64(v), nil
case uint16:
return uint64(v), nil
case uint32:
return uint64(v), nil
case uint64:
return v, nil
case []byte:
return strconv.ParseUint(string(v), 10, 64)
case string:
return strconv.ParseUint(v, 10, 64)
case *sql.NullString:
return strconv.ParseUint(v.String, 10, 64)
case *sql.NullInt32:
return uint64(v.Int32), nil
case *sql.NullInt64:
return uint64(v.Int64), nil
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return uint64(rv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return uint64(rv.Uint()), nil
case reflect.Float64, reflect.Float32:
return uint64(rv.Float()), nil
case reflect.String:
return strconv.ParseUint(rv.String(), 10, 64)
}
return 0, fmt.Errorf("unsupported value %T as uint64", src)
}
var (
_ sql.Scanner = &NullUint64{}
)
// NullUint64 represents an uint64 that may be null.
// NullUint64 implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullUint64 struct {
Uint64 uint64
Valid bool
}
// Scan implements the Scanner interface.
func (n *NullUint64) Scan(value interface{}) error {
if value == nil {
n.Uint64, n.Valid = 0, false
return nil
}
n.Valid = true
var err error
n.Uint64, err = AsUint64(value)
return err
}
// Value implements the driver Valuer interface.
func (n NullUint64) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Uint64, nil
}
var (
_ sql.Scanner = &NullUint32{}
)
// NullUint32 represents an uint32 that may be null.
// NullUint32 implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullUint32 struct {
Uint32 uint32
Valid bool // Valid is true if Uint32 is not NULL
}
// Scan implements the Scanner interface.
func (n *NullUint32) Scan(value interface{}) error {
if value == nil {
n.Uint32, n.Valid = 0, false
return nil
}
n.Valid = true
i64, err := AsUint64(value)
if err != nil {
return err
}
n.Uint32 = uint32(i64)
return nil
}
// Value implements the driver Valuer interface.
func (n NullUint32) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return int64(n.Uint32), nil
}

View File

@ -0,0 +1,19 @@
// Copyright 2021 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 convert
import "database/sql"
var (
_ sql.Scanner = &EmptyScanner{}
)
// EmptyScanner represents an empty scanner which will ignore the scan
type EmptyScanner struct{}
// Scan implements sql.Scanner
func (EmptyScanner) Scan(value interface{}) error {
return nil
}

View File

@ -0,0 +1,75 @@
// Copyright 2021 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 convert
import (
"database/sql"
"fmt"
"reflect"
"strconv"
)
// AsString converts interface as string
func AsString(src interface{}) string {
switch v := src.(type) {
case string:
return v
case []byte:
return string(v)
case *sql.NullString:
return v.String
case *sql.NullInt32:
return fmt.Sprintf("%d", v.Int32)
case *sql.NullInt64:
return fmt.Sprintf("%d", v.Int64)
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
case reflect.Float32:
return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
case reflect.Bool:
return strconv.FormatBool(rv.Bool())
}
return fmt.Sprintf("%v", src)
}
// AsBytes converts interface as bytes
func AsBytes(src interface{}) ([]byte, bool) {
switch t := src.(type) {
case []byte:
return t, true
case *sql.NullString:
if !t.Valid {
return nil, true
}
return []byte(t.String), true
case *sql.RawBytes:
return *t, true
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.AppendInt(nil, rv.Int(), 10), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.AppendUint(nil, rv.Uint(), 10), true
case reflect.Float32:
return strconv.AppendFloat(nil, rv.Float(), 'g', -1, 32), true
case reflect.Float64:
return strconv.AppendFloat(nil, rv.Float(), 'g', -1, 64), true
case reflect.Bool:
return strconv.AppendBool(nil, rv.Bool()), true
case reflect.String:
return []byte(rv.String()), true
}
return nil, false
}

108
internal/convert/time.go Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2021 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 convert
import (
"database/sql"
"fmt"
"strconv"
"time"
"xorm.io/xorm/internal/utils"
)
// String2Time converts a string to time with original location
func String2Time(s string, originalLocation *time.Location, convertedLocation *time.Location) (*time.Time, error) {
if len(s) == 19 {
if s == utils.ZeroTime0 || s == utils.ZeroTime1 {
return &time.Time{}, nil
}
dt, err := time.ParseInLocation("2006-01-02 15:04:05", s, originalLocation)
if err != nil {
return nil, err
}
dt = dt.In(convertedLocation)
return &dt, nil
} else if len(s) == 20 && s[10] == 'T' && s[19] == 'Z' {
dt, err := time.ParseInLocation("2006-01-02T15:04:05", s[:19], originalLocation)
if err != nil {
return nil, err
}
dt = dt.In(convertedLocation)
return &dt, nil
} else if len(s) == 25 && s[10] == 'T' && s[19] == '+' && s[22] == ':' {
dt, err := time.Parse(time.RFC3339, s)
if err != nil {
return nil, err
}
dt = dt.In(convertedLocation)
return &dt, nil
} else {
i, err := strconv.ParseInt(s, 10, 64)
if err == nil {
tm := time.Unix(i, 0).In(convertedLocation)
return &tm, nil
}
}
return nil, fmt.Errorf("unsupported conversion from %s to time", s)
}
// AsTime converts interface as time
func AsTime(src interface{}, dbLoc *time.Location, uiLoc *time.Location) (*time.Time, error) {
switch t := src.(type) {
case string:
return String2Time(t, dbLoc, uiLoc)
case *sql.NullString:
if !t.Valid {
return nil, nil
}
return String2Time(t.String, dbLoc, uiLoc)
case []uint8:
if t == nil {
return nil, nil
}
return String2Time(string(t), dbLoc, uiLoc)
case *sql.NullTime:
if !t.Valid {
return nil, nil
}
z, _ := t.Time.Zone()
if len(z) == 0 || t.Time.Year() == 0 || t.Time.Location().String() != dbLoc.String() {
tm := time.Date(t.Time.Year(), t.Time.Month(), t.Time.Day(), t.Time.Hour(),
t.Time.Minute(), t.Time.Second(), t.Time.Nanosecond(), dbLoc).In(uiLoc)
return &tm, nil
}
tm := t.Time.In(uiLoc)
return &tm, nil
case *time.Time:
z, _ := t.Zone()
if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbLoc.String() {
tm := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
t.Minute(), t.Second(), t.Nanosecond(), dbLoc).In(uiLoc)
return &tm, nil
}
tm := t.In(uiLoc)
return &tm, nil
case time.Time:
z, _ := t.Zone()
if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbLoc.String() {
tm := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
t.Minute(), t.Second(), t.Nanosecond(), dbLoc).In(uiLoc)
return &tm, nil
}
tm := t.In(uiLoc)
return &tm, nil
case int:
tm := time.Unix(int64(t), 0).In(uiLoc)
return &tm, nil
case int64:
tm := time.Unix(t, 0).In(uiLoc)
return &tm, nil
case *sql.NullInt64:
tm := time.Unix(t.Int64, 0).In(uiLoc)
return &tm, nil
}
return nil, fmt.Errorf("unsupported value %#v as time", src)
}

View File

@ -247,6 +247,9 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB
top = fmt.Sprintf("TOP %d ", LimitNValue) top = fmt.Sprintf("TOP %d ", LimitNValue)
} }
if statement.Start > 0 { if statement.Start > 0 {
if statement.RefTable == nil {
return "", nil, errors.New("Unsupported query limit without reference table")
}
var column string var column string
if len(statement.RefTable.PKColumns()) == 0 { if len(statement.RefTable.PKColumns()) == 0 {
for _, index := range statement.RefTable.Indexes { for _, index := range statement.RefTable.Indexes {

View File

@ -15,8 +15,8 @@ import (
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm/contexts" "xorm.io/xorm/contexts"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects" "xorm.io/xorm/dialects"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/internal/json" "xorm.io/xorm/internal/json"
"xorm.io/xorm/internal/utils" "xorm.io/xorm/internal/utils"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"

View File

@ -11,8 +11,8 @@ import (
"reflect" "reflect"
"time" "time"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects" "xorm.io/xorm/dialects"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/internal/json" "xorm.io/xorm/internal/json"
"xorm.io/xorm/internal/utils" "xorm.io/xorm/internal/utils"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"

View File

@ -12,8 +12,8 @@ import (
"reflect" "reflect"
"time" "time"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects" "xorm.io/xorm/dialects"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/internal/json" "xorm.io/xorm/internal/json"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )

10
scan.go
View File

@ -11,9 +11,9 @@ import (
"reflect" "reflect"
"time" "time"
"xorm.io/xorm/convert"
"xorm.io/xorm/core" "xorm.io/xorm/core"
"xorm.io/xorm/dialects" "xorm.io/xorm/dialects"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )
@ -35,9 +35,9 @@ func genScanResultsByBeanNullable(bean interface{}) (interface{}, bool, error) {
case *int64: case *int64:
return &sql.NullInt64{}, true, nil return &sql.NullInt64{}, true, nil
case *uint, *uint8, *uint16, *uint32: case *uint, *uint8, *uint16, *uint32:
return &NullUint32{}, true, nil return &convert.NullUint32{}, true, nil
case *uint64: case *uint64:
return &NullUint64{}, true, nil return &convert.NullUint64{}, true, nil
case *float32, *float64: case *float32, *float64:
return &sql.NullFloat64{}, true, nil return &sql.NullFloat64{}, true, nil
case *bool: case *bool:
@ -63,9 +63,9 @@ func genScanResultsByBeanNullable(bean interface{}) (interface{}, bool, error) {
case reflect.Int32, reflect.Int, reflect.Int16, reflect.Int8: case reflect.Int32, reflect.Int, reflect.Int16, reflect.Int8:
return &sql.NullInt32{}, true, nil return &sql.NullInt32{}, true, nil
case reflect.Uint64: case reflect.Uint64:
return &NullUint64{}, true, nil return &convert.NullUint64{}, true, nil
case reflect.Uint32, reflect.Uint, reflect.Uint16, reflect.Uint8: case reflect.Uint32, reflect.Uint, reflect.Uint16, reflect.Uint8:
return &NullUint32{}, true, nil return &convert.NullUint32{}, true, nil
default: default:
return nil, false, fmt.Errorf("unsupported type: %#v", bean) return nil, false, fmt.Errorf("unsupported type: %#v", bean)
} }

View File

@ -18,8 +18,8 @@ import (
"strings" "strings"
"xorm.io/xorm/contexts" "xorm.io/xorm/contexts"
"xorm.io/xorm/convert"
"xorm.io/xorm/core" "xorm.io/xorm/core"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/internal/json" "xorm.io/xorm/internal/json"
"xorm.io/xorm/internal/statements" "xorm.io/xorm/internal/statements"
"xorm.io/xorm/log" "xorm.io/xorm/log"
@ -435,7 +435,7 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, types []*sql
} }
func (session *Session) setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error { func (session *Session) setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error {
bs, ok := asBytes(scanResult) bs, ok := convert.AsBytes(scanResult)
if !ok { if !ok {
return fmt.Errorf("unsupported database data type: %#v", scanResult) return fmt.Errorf("unsupported database data type: %#v", scanResult)
} }
@ -476,7 +476,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
if fieldValue.CanAddr() { if fieldValue.CanAddr() {
if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
data, ok := asBytes(scanResult) data, ok := convert.AsBytes(scanResult)
if !ok { if !ok {
return fmt.Errorf("cannot convert %#v as bytes", scanResult) return fmt.Errorf("cannot convert %#v as bytes", scanResult)
} }
@ -485,7 +485,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
} }
if structConvert, ok := fieldValue.Interface().(convert.Conversion); ok { if structConvert, ok := fieldValue.Interface().(convert.Conversion); ok {
data, ok := asBytes(scanResult) data, ok := convert.AsBytes(scanResult)
if !ok { if !ok {
return fmt.Errorf("cannot convert %#v as bytes", scanResult) return fmt.Errorf("cannot convert %#v as bytes", scanResult)
} }
@ -525,7 +525,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
case reflect.Complex64, reflect.Complex128: case reflect.Complex64, reflect.Complex128:
return session.setJSON(fieldValue, fieldType, scanResult) return session.setJSON(fieldValue, fieldType, scanResult)
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
bs, ok := asBytes(scanResult) bs, ok := convert.AsBytes(scanResult)
if ok && fieldType.Elem().Kind() == reflect.Uint8 { if ok && fieldType.Elem().Kind() == reflect.Uint8 {
if col.SQLType.IsText() { if col.SQLType.IsText() {
x := reflect.New(fieldType) x := reflect.New(fieldType)
@ -551,7 +551,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
} }
case reflect.Struct: case reflect.Struct:
if fieldType.ConvertibleTo(schemas.BigFloatType) { if fieldType.ConvertibleTo(schemas.BigFloatType) {
v, err := asBigFloat(scanResult) v, err := convert.AsBigFloat(scanResult)
if err != nil { if err != nil {
return err return err
} }
@ -565,7 +565,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec
dbTZ = col.TimeZone dbTZ = col.TimeZone
} }
t, err := asTime(scanResult, dbTZ, session.engine.TZLocation) t, err := convert.AsTime(scanResult, dbTZ, session.engine.TZLocation)
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,7 +6,6 @@ package xorm
import ( import (
"errors" "errors"
"fmt"
"reflect" "reflect"
"xorm.io/builder" "xorm.io/builder"
@ -476,12 +475,13 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
} else if sliceValue.Kind() == reflect.Map { } else if sliceValue.Kind() == reflect.Map {
var key = ids[j] var key = ids[j]
keyType := sliceValue.Type().Key() keyType := sliceValue.Type().Key()
keyValue := reflect.New(keyType)
var ikey interface{} var ikey interface{}
if len(key) == 1 { if len(key) == 1 {
ikey, err = str2PK(fmt.Sprintf("%v", key[0]), keyType) if err := convertAssignV(keyValue, key[0]); err != nil {
if err != nil {
return err return err
} }
ikey = keyValue.Elem().Interface()
} else { } else {
if keyType.Kind() != reflect.Slice { if keyType.Kind() != reflect.Slice {
return errors.New("table have multiple primary keys, key is not schemas.PK or slice") return errors.New("table have multiple primary keys, key is not schemas.PK or slice")

View File

@ -15,8 +15,8 @@ import (
"time" "time"
"xorm.io/xorm/caches" "xorm.io/xorm/caches"
"xorm.io/xorm/convert"
"xorm.io/xorm/core" "xorm.io/xorm/core"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/internal/utils" "xorm.io/xorm/internal/utils"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )

View File

@ -6,6 +6,7 @@ package xorm
import ( import (
"database/sql" "database/sql"
"strings"
"xorm.io/xorm/core" "xorm.io/xorm/core"
) )
@ -32,7 +33,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row
if session.isAutoCommit { if session.isAutoCommit {
var db *core.DB var db *core.DB
if session.sessionType == groupSession { if session.sessionType == groupSession && strings.EqualFold(sqlStr[:6], "select") {
db = session.engine.engineGroup.Slave().DB() db = session.engine.engineGroup.Slave().DB()
} else { } else {
db = session.DB() db = session.DB()

View File

@ -14,8 +14,8 @@ import (
"unicode" "unicode"
"xorm.io/xorm/caches" "xorm.io/xorm/caches"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects" "xorm.io/xorm/dialects"
"xorm.io/xorm/internal/convert"
"xorm.io/xorm/names" "xorm.io/xorm/names"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )