Merge branch 'master' into lunny/default
This commit is contained in:
commit
2251f08742
16
.drone.yml
16
.drone.yml
|
|
@ -347,3 +347,19 @@ steps:
|
|||
image: golang:1.15
|
||||
commands:
|
||||
- make coverage
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: release-tag
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
steps:
|
||||
- name: release-tag-gitea
|
||||
pull: always
|
||||
image: plugins/gitea-release:latest
|
||||
settings:
|
||||
base_url: https://gitea.com
|
||||
title: '${DRONE_TAG} is released'
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
|
|
@ -3,6 +3,41 @@
|
|||
This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log.
|
||||
|
||||
## [1.1.2](https://gitea.com/xorm/xorm/releases/tag/1.1.2) - 2021-07-04
|
||||
|
||||
* BUILD
|
||||
* Add release tag (#1966)
|
||||
|
||||
## [1.1.1](https://gitea.com/xorm/xorm/releases/tag/1.1.1) - 2021-07-03
|
||||
|
||||
* BUGFIXES
|
||||
* Ignore comments when deciding when to replace question marks. #1954 (#1955)
|
||||
* Fix bug didn't reset statement on update (#1939)
|
||||
* Fix create table with struct missing columns (#1938)
|
||||
* Fix #929 (#1936)
|
||||
* Fix exist (#1921)
|
||||
* ENHANCEMENTS
|
||||
* Improve get field value of bean (#1961)
|
||||
* refactor splitTag function (#1960)
|
||||
* Fix #1663 (#1952)
|
||||
* fix pg GetColumns missing comment (#1949)
|
||||
* Support build flag jsoniter to replace default json (#1916)
|
||||
* refactor exprParam (#1825)
|
||||
* Add DBVersion (#1723)
|
||||
* TESTING
|
||||
* Add test to confirm #1247 resolved (#1951)
|
||||
* Add test for dump table with default value (#1950)
|
||||
* Test for #1486 (#1942)
|
||||
* Add sync tests to confirm #539 is gone (#1937)
|
||||
* test for unsigned int32 (#1923)
|
||||
* Add tests for array store (#1922)
|
||||
* BUILD
|
||||
* Remove mymysql from ci (#1928)
|
||||
* MISC
|
||||
* fix lint (#1953)
|
||||
* Compitable with cockroach (#1930)
|
||||
* Replace goracle with godror (#1914)
|
||||
|
||||
## [1.1.0](https://gitea.com/xorm/xorm/releases/tag/1.1.0) - 2021-05-14
|
||||
|
||||
* FEATURES
|
||||
|
|
|
|||
20
Makefile
20
Makefile
|
|
@ -138,7 +138,7 @@ test: go-check
|
|||
test-cockroach: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -db=postgres -schema='$(TEST_COCKROACH_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \
|
||||
-conn_str="postgres://$(TEST_COCKROACH_USERNAME):$(TEST_COCKROACH_PASSWORD)@$(TEST_COCKROACH_HOST)/$(TEST_COCKROACH_DBNAME)?sslmode=disable&experimental_serial_normalization=sql_sequence" \
|
||||
-ignore_update_limit=true -coverprofile=cockroach.$(TEST_COCKROACH_SCHEMA).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-ignore_update_limit=true -coverprofile=cockroach.$(TEST_COCKROACH_SCHEMA).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-cockroach\#%
|
||||
test-cockroach\#%: go-check
|
||||
|
|
@ -152,7 +152,7 @@ test-mssql: go-check
|
|||
-conn_str="server=$(TEST_MSSQL_HOST);user id=$(TEST_MSSQL_USERNAME);password=$(TEST_MSSQL_PASSWORD);database=$(TEST_MSSQL_DBNAME)" \
|
||||
-default_varchar=$(TEST_MSSQL_DEFAULT_VARCHAR) -default_char=$(TEST_MSSQL_DEFAULT_CHAR) \
|
||||
-do_nvarchar_override_test=$(TEST_MSSQL_DO_NVARCHAR_OVERRIDE_TEST) \
|
||||
-coverprofile=mssql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-coverprofile=mssql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PNONY: test-mssql\#%
|
||||
test-mssql\#%: go-check
|
||||
|
|
@ -166,7 +166,7 @@ test-mssql\#%: go-check
|
|||
test-mymysql: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mymysql -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \
|
||||
-conn_str="tcp:$(TEST_MYSQL_HOST)*$(TEST_MYSQL_DBNAME)/$(TEST_MYSQL_USERNAME)/$(TEST_MYSQL_PASSWORD)" \
|
||||
-coverprofile=mymysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-coverprofile=mymysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PNONY: test-mymysql\#%
|
||||
test-mymysql\#%: go-check
|
||||
|
|
@ -178,7 +178,7 @@ test-mymysql\#%: go-check
|
|||
test-mysql: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mysql -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \
|
||||
-conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)" \
|
||||
-coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-mysql\#%
|
||||
test-mysql\#%: go-check
|
||||
|
|
@ -190,7 +190,7 @@ test-mysql\#%: go-check
|
|||
test-postgres: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -db=postgres -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \
|
||||
-conn_str="postgres://$(TEST_PGSQL_USERNAME):$(TEST_PGSQL_PASSWORD)@$(TEST_PGSQL_HOST)/$(TEST_PGSQL_DBNAME)?sslmode=disable" \
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=postgres.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=postgres.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-postgres\#%
|
||||
test-postgres\#%: go-check
|
||||
|
|
@ -201,12 +201,12 @@ test-postgres\#%: go-check
|
|||
.PHONY: test-sqlite3
|
||||
test-sqlite3: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -cache=$(TEST_CACHE_ENABLE) -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" \
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite3.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite3.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-sqlite3-schema
|
||||
test-sqlite3-schema: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -schema=xorm -cache=$(TEST_CACHE_ENABLE) -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" \
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite3.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite3.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-sqlite3\#%
|
||||
test-sqlite3\#%: go-check
|
||||
|
|
@ -216,12 +216,12 @@ test-sqlite3\#%: go-check
|
|||
.PHONY: test-sqlite
|
||||
test-sqlite: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -cache=$(TEST_CACHE_ENABLE) -db=sqlite -conn_str="./test.db?cache=shared&mode=rwc" \
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-sqlite-schema
|
||||
test-sqlite-schema: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -schema=xorm -cache=$(TEST_CACHE_ENABLE) -db=sqlite -conn_str="./test.db?cache=shared&mode=rwc" \
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-sqlite\#%
|
||||
test-sqlite\#%: go-check
|
||||
|
|
@ -233,7 +233,7 @@ test-sqlite\#%: go-check
|
|||
test-tidb: go-check
|
||||
$(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mysql -cache=$(TEST_CACHE_ENABLE) -ignore_select_update=true \
|
||||
-conn_str="$(TEST_TIDB_USERNAME):$(TEST_TIDB_PASSWORD)@tcp($(TEST_TIDB_HOST))/$(TEST_TIDB_DBNAME)" \
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=tidb.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic
|
||||
-quote=$(TEST_QUOTE_POLICY) -coverprofile=tidb.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m
|
||||
|
||||
.PHONY: test-tidb\#%
|
||||
test-tidb\#%: go-check
|
||||
|
|
|
|||
19
README.md
19
README.md
|
|
@ -245,35 +245,38 @@ for rows.Next() {
|
|||
|
||||
```Go
|
||||
affected, err := engine.ID(1).Update(&user)
|
||||
// UPDATE user SET ... Where id = ?
|
||||
// UPDATE user SET ... WHERE id = ?
|
||||
|
||||
affected, err := engine.Update(&user, &User{Name:name})
|
||||
// UPDATE user SET ... Where name = ?
|
||||
// UPDATE user SET ... WHERE name = ?
|
||||
|
||||
var ids = []int64{1, 2, 3}
|
||||
affected, err := engine.In("id", ids).Update(&user)
|
||||
// UPDATE user SET ... Where id IN (?, ?, ?)
|
||||
// UPDATE user SET ... WHERE id IN (?, ?, ?)
|
||||
|
||||
// force update indicated columns by Cols
|
||||
affected, err := engine.ID(1).Cols("age").Update(&User{Name:name, Age: 12})
|
||||
// UPDATE user SET age = ?, updated=? Where id = ?
|
||||
// UPDATE user SET age = ?, updated=? WHERE id = ?
|
||||
|
||||
// force NOT update indicated columns by Omit
|
||||
affected, err := engine.ID(1).Omit("name").Update(&User{Name:name, Age: 12})
|
||||
// UPDATE user SET age = ?, updated=? Where id = ?
|
||||
// UPDATE user SET age = ?, updated=? WHERE id = ?
|
||||
|
||||
affected, err := engine.ID(1).AllCols().Update(&user)
|
||||
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
|
||||
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? WHERE id = ?
|
||||
```
|
||||
|
||||
* `Delete` delete one or more records, Delete MUST have condition
|
||||
|
||||
```Go
|
||||
affected, err := engine.Where(...).Delete(&user)
|
||||
// DELETE FROM user Where ...
|
||||
// DELETE FROM user WHERE ...
|
||||
|
||||
affected, err := engine.ID(2).Delete(&user)
|
||||
// DELETE FROM user Where id = ?
|
||||
// DELETE FROM user WHERE id = ?
|
||||
|
||||
affected, err := engine.Table("user").Where(...).Delete()
|
||||
// DELETE FROM user WHERE ...
|
||||
```
|
||||
|
||||
* `Count` count records
|
||||
|
|
|
|||
|
|
@ -271,6 +271,9 @@ affected, err := engine.Where(...).Delete(&user)
|
|||
|
||||
affected, err := engine.ID(2).Delete(&user)
|
||||
// DELETE FROM user Where id = ?
|
||||
|
||||
affected, err := engine.Table("user").Where(...).Delete()
|
||||
// DELETE FROM user WHERE ...
|
||||
```
|
||||
|
||||
* `Count` 获取记录条数
|
||||
|
|
|
|||
703
convert.go
703
convert.go
|
|
@ -5,12 +5,17 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/convert"
|
||||
)
|
||||
|
||||
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
|
||||
|
|
@ -37,6 +42,12 @@ func asString(src interface{}) 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() {
|
||||
|
|
@ -54,31 +65,326 @@ func asString(src interface{}) string {
|
|||
return fmt.Sprintf("%v", src)
|
||||
}
|
||||
|
||||
func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
|
||||
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 strconv.AppendInt(buf, rv.Int(), 10), true
|
||||
return rv.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return strconv.AppendUint(buf, rv.Uint(), 10), true
|
||||
case reflect.Float32:
|
||||
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
|
||||
case reflect.Float64:
|
||||
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
|
||||
case reflect.Bool:
|
||||
return strconv.AppendBool(buf, rv.Bool()), true
|
||||
return int64(rv.Uint()), nil
|
||||
case reflect.Float64, reflect.Float32:
|
||||
return int64(rv.Float()), nil
|
||||
case reflect.String:
|
||||
s := rv.String()
|
||||
return append(buf, s...), true
|
||||
return strconv.ParseInt(rv.String(), 10, 64)
|
||||
}
|
||||
return
|
||||
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.
|
||||
// An error is returned if the copy would result in loss of information.
|
||||
// dest should be a pointer type.
|
||||
func convertAssign(dest, src interface{}) error {
|
||||
func convertAssign(dest, src interface{}, originalLocation *time.Location, convertedLocation *time.Location) error {
|
||||
// Common cases, without reflect.
|
||||
switch s := src.(type) {
|
||||
case *interface{}:
|
||||
return convertAssign(dest, *s, originalLocation, convertedLocation)
|
||||
case string:
|
||||
switch d := dest.(type) {
|
||||
case *string:
|
||||
|
|
@ -115,7 +421,6 @@ func convertAssign(dest, src interface{}) error {
|
|||
*d = cloneBytes(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
case time.Time:
|
||||
switch d := dest.(type) {
|
||||
case *string:
|
||||
|
|
@ -143,6 +448,174 @@ func convertAssign(dest, src interface{}) error {
|
|||
*d = nil
|
||||
return nil
|
||||
}
|
||||
case *sql.NullString:
|
||||
switch d := dest.(type) {
|
||||
case *int:
|
||||
if s.Valid {
|
||||
*d, _ = strconv.Atoi(s.String)
|
||||
}
|
||||
return nil
|
||||
case *int64:
|
||||
if s.Valid {
|
||||
*d, _ = strconv.ParseInt(s.String, 10, 64)
|
||||
}
|
||||
return nil
|
||||
case *string:
|
||||
if s.Valid {
|
||||
*d = s.String
|
||||
}
|
||||
return nil
|
||||
case *time.Time:
|
||||
if s.Valid {
|
||||
var err error
|
||||
dt, err := convert.String2Time(s.String, originalLocation, convertedLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*d = *dt
|
||||
}
|
||||
return nil
|
||||
case *sql.NullTime:
|
||||
if s.Valid {
|
||||
var err error
|
||||
dt, err := convert.String2Time(s.String, originalLocation, convertedLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Valid = true
|
||||
d.Time = *dt
|
||||
}
|
||||
return nil
|
||||
case *big.Float:
|
||||
if s.Valid {
|
||||
if d == nil {
|
||||
d = big.NewFloat(0)
|
||||
}
|
||||
d.SetString(s.String)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sql.NullInt32:
|
||||
switch d := dest.(type) {
|
||||
case *int:
|
||||
if s.Valid {
|
||||
*d = int(s.Int32)
|
||||
}
|
||||
return nil
|
||||
case *int8:
|
||||
if s.Valid {
|
||||
*d = int8(s.Int32)
|
||||
}
|
||||
return nil
|
||||
case *int16:
|
||||
if s.Valid {
|
||||
*d = int16(s.Int32)
|
||||
}
|
||||
return nil
|
||||
case *int32:
|
||||
if s.Valid {
|
||||
*d = s.Int32
|
||||
}
|
||||
return nil
|
||||
case *int64:
|
||||
if s.Valid {
|
||||
*d = int64(s.Int32)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sql.NullInt64:
|
||||
switch d := dest.(type) {
|
||||
case *int:
|
||||
if s.Valid {
|
||||
*d = int(s.Int64)
|
||||
}
|
||||
return nil
|
||||
case *int8:
|
||||
if s.Valid {
|
||||
*d = int8(s.Int64)
|
||||
}
|
||||
return nil
|
||||
case *int16:
|
||||
if s.Valid {
|
||||
*d = int16(s.Int64)
|
||||
}
|
||||
return nil
|
||||
case *int32:
|
||||
if s.Valid {
|
||||
*d = int32(s.Int64)
|
||||
}
|
||||
return nil
|
||||
case *int64:
|
||||
if s.Valid {
|
||||
*d = s.Int64
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sql.NullFloat64:
|
||||
switch d := dest.(type) {
|
||||
case *int:
|
||||
if s.Valid {
|
||||
*d = int(s.Float64)
|
||||
}
|
||||
return nil
|
||||
case *float64:
|
||||
if s.Valid {
|
||||
*d = s.Float64
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sql.NullBool:
|
||||
switch d := dest.(type) {
|
||||
case *bool:
|
||||
if s.Valid {
|
||||
*d = s.Bool
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sql.NullTime:
|
||||
switch d := dest.(type) {
|
||||
case *time.Time:
|
||||
if s.Valid {
|
||||
*d = s.Time
|
||||
}
|
||||
return nil
|
||||
case *string:
|
||||
if s.Valid {
|
||||
*d = s.Time.In(convertedLocation).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *NullUint32:
|
||||
switch d := dest.(type) {
|
||||
case *uint8:
|
||||
if s.Valid {
|
||||
*d = uint8(s.Uint32)
|
||||
}
|
||||
return nil
|
||||
case *uint16:
|
||||
if s.Valid {
|
||||
*d = uint16(s.Uint32)
|
||||
}
|
||||
return nil
|
||||
case *uint:
|
||||
if s.Valid {
|
||||
*d = uint(s.Uint32)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *NullUint64:
|
||||
switch d := dest.(type) {
|
||||
case *uint64:
|
||||
if s.Valid {
|
||||
*d = s.Uint64
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sql.RawBytes:
|
||||
switch d := dest.(type) {
|
||||
case convert.Conversion:
|
||||
return d.FromDB(*s)
|
||||
}
|
||||
}
|
||||
|
||||
var sv reflect.Value
|
||||
|
|
@ -159,8 +632,7 @@ func convertAssign(dest, src interface{}) error {
|
|||
return nil
|
||||
}
|
||||
case *[]byte:
|
||||
sv = reflect.ValueOf(src)
|
||||
if b, ok := asBytes(nil, sv); ok {
|
||||
if b, ok := asBytes(src); ok {
|
||||
*d = b
|
||||
return nil
|
||||
}
|
||||
|
|
@ -178,78 +650,76 @@ func convertAssign(dest, src interface{}) error {
|
|||
return convertAssignV(reflect.ValueOf(dest), src)
|
||||
}
|
||||
|
||||
func convertAssignV(dpv reflect.Value, src interface{}) error {
|
||||
if dpv.Kind() != reflect.Ptr {
|
||||
return errors.New("destination not a pointer")
|
||||
}
|
||||
if dpv.IsNil() {
|
||||
return errNilPtr
|
||||
}
|
||||
|
||||
var sv = reflect.ValueOf(src)
|
||||
|
||||
dv := reflect.Indirect(dpv)
|
||||
if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
|
||||
switch b := src.(type) {
|
||||
case []byte:
|
||||
dv.Set(reflect.ValueOf(cloneBytes(b)))
|
||||
default:
|
||||
dv.Set(sv)
|
||||
}
|
||||
func convertAssignV(dv reflect.Value, src interface{}) error {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
|
||||
dv.Set(sv.Convert(dv.Type()))
|
||||
return nil
|
||||
if dv.Type().Implements(scannerType) {
|
||||
return dv.Interface().(sql.Scanner).Scan(src)
|
||||
}
|
||||
|
||||
switch dv.Kind() {
|
||||
case reflect.Ptr:
|
||||
if src == nil {
|
||||
dv.Set(reflect.Zero(dv.Type()))
|
||||
return nil
|
||||
if dv.IsNil() {
|
||||
dv.Set(reflect.New(dv.Type().Elem()))
|
||||
}
|
||||
|
||||
dv.Set(reflect.New(dv.Type().Elem()))
|
||||
return convertAssign(dv.Interface(), src)
|
||||
return convertAssignV(dv.Elem(), src)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
s := asString(src)
|
||||
i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
|
||||
i64, err := asInt64(src)
|
||||
if err != nil {
|
||||
err = strconvErr(err)
|
||||
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
|
||||
return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err)
|
||||
}
|
||||
dv.SetInt(i64)
|
||||
return nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
s := asString(src)
|
||||
u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
|
||||
u64, err := asUint64(src)
|
||||
if err != nil {
|
||||
err = strconvErr(err)
|
||||
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
|
||||
return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err)
|
||||
}
|
||||
dv.SetUint(u64)
|
||||
return nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
s := asString(src)
|
||||
f64, err := strconv.ParseFloat(s, dv.Type().Bits())
|
||||
f64, err := asFloat64(src)
|
||||
if err != nil {
|
||||
err = strconvErr(err)
|
||||
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
|
||||
return fmt.Errorf("converting driver.Value type %T to a %s: %v", src, dv.Kind(), err)
|
||||
}
|
||||
dv.SetFloat(f64)
|
||||
return nil
|
||||
case reflect.String:
|
||||
dv.SetString(asString(src))
|
||||
return nil
|
||||
case reflect.Bool:
|
||||
b, err := asBool(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dv.SetBool(b)
|
||||
return nil
|
||||
case reflect.Slice, reflect.Map, reflect.Struct, reflect.Array:
|
||||
data, ok := asBytes(src)
|
||||
if !ok {
|
||||
return fmt.Errorf("onvertAssignV: src cannot be as bytes %#v", src)
|
||||
}
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
if dv.Kind() != reflect.Ptr {
|
||||
dv = dv.Addr()
|
||||
}
|
||||
return json.Unmarshal(data, dv.Interface())
|
||||
default:
|
||||
return fmt.Errorf("convertAssignV: unsupported Scan, storing driver.Value type %T into type %T", src, dv.Interface())
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dpv.Interface())
|
||||
}
|
||||
|
||||
func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
|
||||
switch tp.Kind() {
|
||||
case reflect.Ptr:
|
||||
return asKind(vv.Elem(), tp.Elem())
|
||||
case reflect.Int64:
|
||||
return vv.Int(), nil
|
||||
case reflect.Int:
|
||||
|
|
@ -280,21 +750,47 @@ func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
|
|||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
|
||||
}
|
||||
|
||||
func asBool(bs []byte) (bool, error) {
|
||||
if len(bs) == 0 {
|
||||
return false, nil
|
||||
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)
|
||||
}
|
||||
if bs[0] == 0x00 {
|
||||
return false, nil
|
||||
} else if bs[0] == 0x01 {
|
||||
return true, nil
|
||||
}
|
||||
return strconv.ParseBool(string(bs))
|
||||
}
|
||||
|
||||
// str2PK convert string value to primary key value according to tp
|
||||
|
|
@ -376,3 +872,82 @@ func str2PK(s string, tp reflect.Type) (interface{}, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
// 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"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Interface2Interface converts interface of pointer as interface of value
|
||||
func Interface2Interface(userLocation *time.Location, v interface{}) (interface{}, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch vv := v.(type) {
|
||||
case *int64:
|
||||
return *vv, nil
|
||||
case *int8:
|
||||
return *vv, nil
|
||||
case *sql.NullString:
|
||||
return vv.String, nil
|
||||
case *sql.RawBytes:
|
||||
if len([]byte(*vv)) > 0 {
|
||||
return []byte(*vv), nil
|
||||
}
|
||||
return nil, nil
|
||||
case *sql.NullInt32:
|
||||
return vv.Int32, nil
|
||||
case *sql.NullInt64:
|
||||
return vv.Int64, nil
|
||||
case *sql.NullFloat64:
|
||||
return vv.Float64, nil
|
||||
case *sql.NullBool:
|
||||
if vv.Valid {
|
||||
return vv.Bool, nil
|
||||
}
|
||||
return nil, nil
|
||||
case *sql.NullTime:
|
||||
if vv.Valid {
|
||||
return vv.Time.In(userLocation).Format("2006-01-02 15:04:05"), nil
|
||||
}
|
||||
return "", nil
|
||||
default:
|
||||
return "", fmt.Errorf("convert assign string unsupported type: %#v", vv)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// 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)
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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 (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestString2Time(t *testing.T) {
|
||||
expectedLoc, err := time.LoadLocation("Asia/Shanghai")
|
||||
assert.NoError(t, err)
|
||||
|
||||
var kases = map[string]time.Time{
|
||||
"2021-06-06T22:58:20+08:00": time.Date(2021, 6, 6, 22, 58, 20, 0, expectedLoc),
|
||||
"2021-07-11 10:44:00": time.Date(2021, 7, 11, 18, 44, 0, 0, expectedLoc),
|
||||
"2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 04, 0, expectedLoc),
|
||||
}
|
||||
for layout, tm := range kases {
|
||||
t.Run(layout, func(t *testing.T) {
|
||||
target, err := String2Time(layout, time.UTC, expectedLoc)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, tm, *target)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -42,10 +42,12 @@ func (uri *URI) SetSchema(schema string) {
|
|||
type Dialect interface {
|
||||
Init(*URI) error
|
||||
URI() *URI
|
||||
SQLType(*schemas.Column) string
|
||||
FormatBytes(b []byte) string
|
||||
Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error)
|
||||
|
||||
SQLType(*schemas.Column) string
|
||||
Alias(string) string // return what a sql type's alias of
|
||||
ColumnTypeKind(string) int // database column type kind
|
||||
|
||||
IsReserved(string) bool
|
||||
Quoter() schemas.Quoter
|
||||
SetQuotePolicy(quotePolicy QuotePolicy)
|
||||
|
|
@ -80,6 +82,11 @@ type Base struct {
|
|||
quoter schemas.Quoter
|
||||
}
|
||||
|
||||
// Alias returned col itself
|
||||
func (db *Base) Alias(col string) string {
|
||||
return col
|
||||
}
|
||||
|
||||
// Quoter returns the current database Quoter
|
||||
func (db *Base) Quoter() schemas.Quoter {
|
||||
return db.quoter
|
||||
|
|
@ -96,11 +103,6 @@ func (db *Base) URI() *URI {
|
|||
return db.uri
|
||||
}
|
||||
|
||||
// FormatBytes formats bytes
|
||||
func (db *Base) FormatBytes(bs []byte) string {
|
||||
return fmt.Sprintf("0x%x", bs)
|
||||
}
|
||||
|
||||
// DropTableSQL returns drop table SQL
|
||||
func (db *Base) DropTableSQL(tableName string) (string, bool) {
|
||||
quote := db.dialect.Quoter().Quote
|
||||
|
|
@ -118,7 +120,7 @@ func (db *Base) HasRecords(queryer core.Queryer, ctx context.Context, query stri
|
|||
if rows.Next() {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
return false, rows.Err()
|
||||
}
|
||||
|
||||
// IsColumnExist returns true if the column of the table exist
|
||||
|
|
|
|||
|
|
@ -5,12 +5,24 @@
|
|||
package dialects
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/core"
|
||||
)
|
||||
|
||||
// ScanContext represents a context when Scan
|
||||
type ScanContext struct {
|
||||
DBLocation *time.Location
|
||||
UserLocation *time.Location
|
||||
}
|
||||
|
||||
// Driver represents a database driver
|
||||
type Driver interface {
|
||||
Parse(string, string) (*URI, error)
|
||||
GenScanResult(string) (interface{}, error) // according given column type generating a suitable scan interface
|
||||
Scan(*ScanContext, *core.Rows, []*sql.ColumnType, ...interface{}) error
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -59,3 +71,9 @@ func OpenDialect(driverName, connstr string) (Dialect, error) {
|
|||
|
||||
return dialect, nil
|
||||
}
|
||||
|
||||
type baseDriver struct{}
|
||||
|
||||
func (b *baseDriver) Scan(ctx *ScanContext, rows *core.Rows, types []*sql.ColumnType, v ...interface{}) error {
|
||||
return rows.Scan(v...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package dialects
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
|
@ -263,6 +264,9 @@ func (db *mssql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve
|
|||
|
||||
var version, level, edition string
|
||||
if !rows.Next() {
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return nil, errors.New("unknow version")
|
||||
}
|
||||
|
||||
|
|
@ -363,6 +367,19 @@ func (db *mssql) SQLType(c *schemas.Column) string {
|
|||
return res
|
||||
}
|
||||
|
||||
func (db *mssql) ColumnTypeKind(t string) int {
|
||||
switch strings.ToUpper(t) {
|
||||
case "DATE", "DATETIME", "DATETIME2", "TIME":
|
||||
return schemas.TIME_TYPE
|
||||
case "VARCHAR", "TEXT", "CHAR", "NVARCHAR", "NCHAR", "NTEXT":
|
||||
return schemas.TEXT_TYPE
|
||||
case "FLOAT", "REAL", "BIGINT", "DATETIMEOFFSET", "TINYINT", "SMALLINT", "INT":
|
||||
return schemas.NUMERIC_TYPE
|
||||
default:
|
||||
return schemas.UNKNOW_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
func (db *mssql) IsReserved(name string) bool {
|
||||
_, ok := mssqlReservedWords[strings.ToUpper(name)]
|
||||
return ok
|
||||
|
|
@ -495,6 +512,9 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName
|
|||
cols[col.Name] = col
|
||||
colSeq = append(colSeq, col.Name)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, nil, rows.Err()
|
||||
}
|
||||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
|
|
@ -519,6 +539,9 @@ func (db *mssql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schema
|
|||
table.Name = strings.Trim(name, "` ")
|
||||
tables = append(tables, table)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +565,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*schemas.Index, 0)
|
||||
indexes := make(map[string]*schemas.Index)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, colName, isUnique string
|
||||
|
|
@ -581,6 +604,9 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
|
|||
}
|
||||
index.AddColumn(colName)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return indexes, nil
|
||||
}
|
||||
|
||||
|
|
@ -624,6 +650,7 @@ func (db *mssql) Filters() []Filter {
|
|||
}
|
||||
|
||||
type odbcDriver struct {
|
||||
baseDriver
|
||||
}
|
||||
|
||||
func (p *odbcDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
|
|
@ -640,8 +667,7 @@ func (p *odbcDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
|||
for _, c := range kv {
|
||||
vv := strings.Split(strings.TrimSpace(c), "=")
|
||||
if len(vv) == 2 {
|
||||
switch strings.ToLower(vv[0]) {
|
||||
case "database":
|
||||
if strings.ToLower(vv[0]) == "database" {
|
||||
dbName = vv[1]
|
||||
}
|
||||
}
|
||||
|
|
@ -652,3 +678,26 @@ func (p *odbcDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
|||
}
|
||||
return &URI{DBName: dbName, DBType: schemas.MSSQL}, nil
|
||||
}
|
||||
|
||||
func (p *odbcDriver) GenScanResult(colType string) (interface{}, error) {
|
||||
switch colType {
|
||||
case "VARCHAR", "TEXT", "CHAR", "NVARCHAR", "NCHAR", "NTEXT":
|
||||
fallthrough
|
||||
case "DATE", "DATETIME", "DATETIME2", "TIME":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "FLOAT", "REAL":
|
||||
var s sql.NullFloat64
|
||||
return &s, nil
|
||||
case "BIGINT", "DATETIMEOFFSET":
|
||||
var s sql.NullInt64
|
||||
return &s, nil
|
||||
case "TINYINT", "SMALLINT", "INT":
|
||||
var s sql.NullInt32
|
||||
return &s, nil
|
||||
|
||||
default:
|
||||
var r sql.RawBytes
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package dialects
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
|
@ -188,6 +189,21 @@ func (db *mysql) Init(uri *URI) error {
|
|||
return db.Base.Init(db, uri)
|
||||
}
|
||||
|
||||
var (
|
||||
mysqlColAliases = map[string]string{
|
||||
"numeric": "decimal",
|
||||
}
|
||||
)
|
||||
|
||||
// Alias returns a alias of column
|
||||
func (db *mysql) Alias(col string) string {
|
||||
v, ok := mysqlColAliases[strings.ToLower(col)]
|
||||
if ok {
|
||||
return v
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
func (db *mysql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) {
|
||||
rows, err := queryer.QueryContext(ctx, "SELECT @@VERSION")
|
||||
if err != nil {
|
||||
|
|
@ -197,7 +213,10 @@ func (db *mysql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve
|
|||
|
||||
var version string
|
||||
if !rows.Next() {
|
||||
return nil, errors.New("Unknow version")
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return nil, errors.New("unknow version")
|
||||
}
|
||||
|
||||
if err := rows.Scan(&version); err != nil {
|
||||
|
|
@ -238,9 +257,6 @@ func (db *mysql) SetParams(params map[string]string) {
|
|||
fallthrough
|
||||
case "COMPRESSED":
|
||||
db.rowFormat = t
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -320,6 +336,21 @@ func (db *mysql) SQLType(c *schemas.Column) string {
|
|||
return res
|
||||
}
|
||||
|
||||
func (db *mysql) ColumnTypeKind(t string) int {
|
||||
switch strings.ToUpper(t) {
|
||||
case "DATETIME":
|
||||
return schemas.TIME_TYPE
|
||||
case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET":
|
||||
return schemas.TEXT_TYPE
|
||||
case "BIGINT", "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "FLOAT", "REAL", "DOUBLE PRECISION", "DECIMAL", "NUMERIC", "BIT":
|
||||
return schemas.NUMERIC_TYPE
|
||||
case "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB":
|
||||
return schemas.BLOB_TYPE
|
||||
default:
|
||||
return schemas.UNKNOW_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
func (db *mysql) IsReserved(name string) bool {
|
||||
_, ok := mysqlReservedWords[strings.ToUpper(name)]
|
||||
return ok
|
||||
|
|
@ -472,6 +503,9 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName
|
|||
cols[col.Name] = col
|
||||
colSeq = append(colSeq, col.Name)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, nil, rows.Err()
|
||||
}
|
||||
return colSeq, cols, nil
|
||||
}
|
||||
|
||||
|
|
@ -503,6 +537,9 @@ func (db *mysql) GetTables(queryer core.Queryer, ctx context.Context) ([]*schema
|
|||
table.StoreEngine = engine
|
||||
tables = append(tables, table)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +570,7 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*schemas.Index, 0)
|
||||
indexes := make(map[string]*schemas.Index)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, colName, nonUnique string
|
||||
|
|
@ -546,7 +583,7 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName
|
|||
continue
|
||||
}
|
||||
|
||||
if "YES" == nonUnique || nonUnique == "1" {
|
||||
if nonUnique == "YES" || nonUnique == "1" {
|
||||
indexType = schemas.IndexType
|
||||
} else {
|
||||
indexType = schemas.UniqueType
|
||||
|
|
@ -570,6 +607,9 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName
|
|||
}
|
||||
index.AddColumn(colName)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return indexes, nil
|
||||
}
|
||||
|
||||
|
|
@ -630,7 +670,77 @@ func (db *mysql) Filters() []Filter {
|
|||
return []Filter{}
|
||||
}
|
||||
|
||||
type mysqlDriver struct {
|
||||
baseDriver
|
||||
}
|
||||
|
||||
func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*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 := &URI{DBType: schemas.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 {
|
||||
if splits[0] == "charset" {
|
||||
uri.Charset = splits[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
func (p *mysqlDriver) GenScanResult(colType string) (interface{}, error) {
|
||||
switch colType {
|
||||
case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "ENUM", "SET":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "BIGINT":
|
||||
var s sql.NullInt64
|
||||
return &s, nil
|
||||
case "TINYINT", "SMALLINT", "MEDIUMINT", "INT":
|
||||
var s sql.NullInt32
|
||||
return &s, nil
|
||||
case "FLOAT", "REAL", "DOUBLE PRECISION", "DOUBLE":
|
||||
var s sql.NullFloat64
|
||||
return &s, nil
|
||||
case "DECIMAL", "NUMERIC":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "DATETIME", "TIMESTAMP":
|
||||
var s sql.NullTime
|
||||
return &s, nil
|
||||
case "BIT":
|
||||
var s sql.RawBytes
|
||||
return &s, nil
|
||||
case "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB":
|
||||
var r sql.RawBytes
|
||||
return &r, nil
|
||||
default:
|
||||
var r sql.RawBytes
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
|
||||
type mymysqlDriver struct {
|
||||
mysqlDriver
|
||||
}
|
||||
|
||||
func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
|
|
@ -681,41 +791,3 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
|||
|
||||
return uri, nil
|
||||
}
|
||||
|
||||
type mysqlDriver struct {
|
||||
}
|
||||
|
||||
func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*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 := &URI{DBType: schemas.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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package dialects
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
|
@ -524,6 +525,9 @@ func (db *oracle) Version(ctx context.Context, queryer core.Queryer) (*schemas.V
|
|||
|
||||
var version string
|
||||
if !rows.Next() {
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return nil, errors.New("unknow version")
|
||||
}
|
||||
|
||||
|
|
@ -567,6 +571,21 @@ func (db *oracle) SQLType(c *schemas.Column) string {
|
|||
return res
|
||||
}
|
||||
|
||||
func (db *oracle) ColumnTypeKind(t string) int {
|
||||
switch strings.ToUpper(t) {
|
||||
case "DATE":
|
||||
return schemas.TIME_TYPE
|
||||
case "CHAR", "NCHAR", "VARCHAR", "VARCHAR2", "NVARCHAR2", "LONG", "CLOB", "NCLOB":
|
||||
return schemas.TEXT_TYPE
|
||||
case "NUMBER":
|
||||
return schemas.NUMERIC_TYPE
|
||||
case "BLOB":
|
||||
return schemas.BLOB_TYPE
|
||||
default:
|
||||
return schemas.UNKNOW_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
func (db *oracle) AutoIncrStr() string {
|
||||
return "AUTO_INCREMENT"
|
||||
}
|
||||
|
|
@ -740,6 +759,9 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam
|
|||
cols[col.Name] = col
|
||||
colSeq = append(colSeq, col.Name)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, nil, rows.Err()
|
||||
}
|
||||
|
||||
return colSeq, cols, nil
|
||||
}
|
||||
|
|
@ -764,6 +786,9 @@ func (db *oracle) GetTables(queryer core.Queryer, ctx context.Context) ([]*schem
|
|||
|
||||
tables = append(tables, table)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
|
|
@ -778,7 +803,7 @@ func (db *oracle) GetIndexes(queryer core.Queryer, ctx context.Context, tableNam
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*schemas.Index, 0)
|
||||
indexes := make(map[string]*schemas.Index)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, colName, uniqueness string
|
||||
|
|
@ -813,6 +838,9 @@ func (db *oracle) GetIndexes(queryer core.Queryer, ctx context.Context, tableNam
|
|||
}
|
||||
index.AddColumn(colName)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return indexes, nil
|
||||
}
|
||||
|
||||
|
|
@ -823,9 +851,10 @@ func (db *oracle) Filters() []Filter {
|
|||
}
|
||||
|
||||
type godrorDriver struct {
|
||||
baseDriver
|
||||
}
|
||||
|
||||
func (cfg *godrorDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
func (g *godrorDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
db := &URI{DBType: schemas.ORACLE}
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
|
||||
|
|
@ -837,8 +866,7 @@ func (cfg *godrorDriver) Parse(driverName, dataSourceName string) (*URI, error)
|
|||
names := dsnPattern.SubexpNames()
|
||||
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
if names[i] == "dbname" {
|
||||
db.DBName = match
|
||||
}
|
||||
}
|
||||
|
|
@ -848,12 +876,33 @@ func (cfg *godrorDriver) Parse(driverName, dataSourceName string) (*URI, error)
|
|||
return db, nil
|
||||
}
|
||||
|
||||
func (g *godrorDriver) GenScanResult(colType string) (interface{}, error) {
|
||||
switch colType {
|
||||
case "CHAR", "NCHAR", "VARCHAR", "VARCHAR2", "NVARCHAR2", "LONG", "CLOB", "NCLOB":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "NUMBER":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "DATE":
|
||||
var s sql.NullTime
|
||||
return &s, nil
|
||||
case "BLOB":
|
||||
var r sql.RawBytes
|
||||
return &r, nil
|
||||
default:
|
||||
var r sql.RawBytes
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
|
||||
type oci8Driver struct {
|
||||
godrorDriver
|
||||
}
|
||||
|
||||
// dataSourceName=user/password@ipv4:port/dbname
|
||||
// dataSourceName=user/password@[ipv6]:port/dbname
|
||||
func (p *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
func (o *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
db := &URI{DBType: schemas.ORACLE}
|
||||
dsnPattern := regexp.MustCompile(
|
||||
`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
|
||||
|
|
@ -862,8 +911,7 @@ func (p *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) {
|
|||
matches := dsnPattern.FindStringSubmatch(dataSourceName)
|
||||
names := dsnPattern.SubexpNames()
|
||||
for i, match := range matches {
|
||||
switch names[i] {
|
||||
case "dbname":
|
||||
if names[i] == "dbname" {
|
||||
db.DBName = match
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package dialects
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
|
@ -777,12 +778,24 @@ var (
|
|||
var (
|
||||
// DefaultPostgresSchema default postgres schema
|
||||
DefaultPostgresSchema = "public"
|
||||
postgresColAliases = map[string]string{
|
||||
"numeric": "decimal",
|
||||
}
|
||||
)
|
||||
|
||||
type postgres struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// Alias returns a alias of column
|
||||
func (db *postgres) Alias(col string) string {
|
||||
v, ok := postgresColAliases[strings.ToLower(col)]
|
||||
if ok {
|
||||
return v
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
func (db *postgres) Init(uri *URI) error {
|
||||
db.quoter = postgresQuoter
|
||||
return db.Base.Init(db, uri)
|
||||
|
|
@ -797,7 +810,10 @@ func (db *postgres) Version(ctx context.Context, queryer core.Queryer) (*schemas
|
|||
|
||||
var version string
|
||||
if !rows.Next() {
|
||||
return nil, errors.New("Unknow version")
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return nil, errors.New("unknow version")
|
||||
}
|
||||
|
||||
if err := rows.Scan(&version); err != nil {
|
||||
|
|
@ -860,11 +876,6 @@ func (db *postgres) SetQuotePolicy(quotePolicy QuotePolicy) {
|
|||
}
|
||||
}
|
||||
|
||||
// FormatBytes formats bytes
|
||||
func (db *postgres) FormatBytes(bs []byte) string {
|
||||
return fmt.Sprintf("E'\\x%x'", bs)
|
||||
}
|
||||
|
||||
func (db *postgres) SQLType(c *schemas.Column) string {
|
||||
var res string
|
||||
switch t := c.SQLType.Name; t {
|
||||
|
|
@ -930,6 +941,21 @@ func (db *postgres) SQLType(c *schemas.Column) string {
|
|||
return res
|
||||
}
|
||||
|
||||
func (db *postgres) ColumnTypeKind(t string) int {
|
||||
switch strings.ToUpper(t) {
|
||||
case "DATETIME", "TIMESTAMP":
|
||||
return schemas.TIME_TYPE
|
||||
case "VARCHAR", "TEXT":
|
||||
return schemas.TEXT_TYPE
|
||||
case "BIGINT", "BIGSERIAL", "SMALLINT", "INT", "INT8", "INT4", "INTEGER", "SERIAL", "FLOAT", "FLOAT4", "REAL", "DOUBLE PRECISION":
|
||||
return schemas.NUMERIC_TYPE
|
||||
case "BOOL":
|
||||
return schemas.BOOL_TYPE
|
||||
default:
|
||||
return schemas.UNKNOW_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
func (db *postgres) IsReserved(name string) bool {
|
||||
_, ok := postgresReservedWords[strings.ToUpper(name)]
|
||||
return ok
|
||||
|
|
@ -1039,7 +1065,10 @@ func (db *postgres) IsColumnExist(queryer core.Queryer, ctx context.Context, tab
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
return rows.Next(), nil
|
||||
if rows.Next() {
|
||||
return true, nil
|
||||
}
|
||||
return false, rows.Err()
|
||||
}
|
||||
|
||||
func (db *postgres) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) {
|
||||
|
|
@ -1169,7 +1198,7 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A
|
|||
}
|
||||
}
|
||||
if _, ok := schemas.SqlTypes[col.SQLType.Name]; !ok {
|
||||
return nil, nil, fmt.Errorf("Unknown colType: %s - %s", dataType, col.SQLType.Name)
|
||||
return nil, nil, fmt.Errorf("unknown colType: %s - %s", dataType, col.SQLType.Name)
|
||||
}
|
||||
|
||||
col.Length = maxLen
|
||||
|
|
@ -1177,19 +1206,22 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A
|
|||
if !col.DefaultIsEmpty {
|
||||
if col.SQLType.IsText() {
|
||||
if strings.HasSuffix(col.Default, "::character varying") {
|
||||
col.Default = strings.TrimRight(col.Default, "::character varying")
|
||||
col.Default = strings.TrimSuffix(col.Default, "::character varying")
|
||||
} else if !strings.HasPrefix(col.Default, "'") {
|
||||
col.Default = "'" + col.Default + "'"
|
||||
}
|
||||
} else if col.SQLType.IsTime() {
|
||||
if strings.HasSuffix(col.Default, "::timestamp without time zone") {
|
||||
col.Default = strings.TrimRight(col.Default, "::timestamp without time zone")
|
||||
col.Default = strings.TrimSuffix(col.Default, "::timestamp without time zone")
|
||||
}
|
||||
}
|
||||
}
|
||||
cols[col.Name] = col
|
||||
colSeq = append(colSeq, col.Name)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, nil, rows.Err()
|
||||
}
|
||||
|
||||
return colSeq, cols, nil
|
||||
}
|
||||
|
|
@ -1220,6 +1252,9 @@ func (db *postgres) GetTables(queryer core.Queryer, ctx context.Context) ([]*sch
|
|||
table.Name = name
|
||||
tables = append(tables, table)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
|
|
@ -1236,7 +1271,7 @@ func getIndexColName(indexdef string) []string {
|
|||
|
||||
func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) {
|
||||
args := []interface{}{tableName}
|
||||
s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1")
|
||||
s := "SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1"
|
||||
if len(db.getSchema()) != 0 {
|
||||
args = append(args, db.getSchema())
|
||||
s = s + " AND schemaname=$2"
|
||||
|
|
@ -1248,7 +1283,7 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*schemas.Index, 0)
|
||||
indexes := make(map[string]*schemas.Index)
|
||||
for rows.Next() {
|
||||
var indexType int
|
||||
var indexName, indexdef string
|
||||
|
|
@ -1290,6 +1325,9 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN
|
|||
index.IsRegular = isRegular
|
||||
indexes[index.Name] = index
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return indexes, nil
|
||||
}
|
||||
|
||||
|
|
@ -1298,6 +1336,7 @@ func (db *postgres) Filters() []Filter {
|
|||
}
|
||||
|
||||
type pqDriver struct {
|
||||
baseDriver
|
||||
}
|
||||
|
||||
type values map[string]string
|
||||
|
|
@ -1374,6 +1413,32 @@ func (p *pqDriver) Parse(driverName, dataSourceName string) (*URI, error) {
|
|||
return db, nil
|
||||
}
|
||||
|
||||
func (p *pqDriver) GenScanResult(colType string) (interface{}, error) {
|
||||
switch colType {
|
||||
case "VARCHAR", "TEXT":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "BIGINT", "BIGSERIAL":
|
||||
var s sql.NullInt64
|
||||
return &s, nil
|
||||
case "SMALLINT", "INT", "INT8", "INT4", "INTEGER", "SERIAL":
|
||||
var s sql.NullInt32
|
||||
return &s, nil
|
||||
case "FLOAT", "FLOAT4", "REAL", "DOUBLE PRECISION":
|
||||
var s sql.NullFloat64
|
||||
return &s, nil
|
||||
case "DATETIME", "TIMESTAMP":
|
||||
var s sql.NullTime
|
||||
return &s, nil
|
||||
case "BOOL":
|
||||
var s sql.NullBool
|
||||
return &s, nil
|
||||
default:
|
||||
var r sql.RawBytes
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
|
||||
type pqDriverPgx struct {
|
||||
pqDriver
|
||||
}
|
||||
|
|
@ -1401,6 +1466,9 @@ func QueryDefaultPostgresSchema(ctx context.Context, queryer core.Queryer) (stri
|
|||
parts := strings.Split(defaultSchema, ",")
|
||||
return strings.TrimSpace(parts[len(parts)-1]), nil
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return "", rows.Err()
|
||||
}
|
||||
|
||||
return "", errors.New("No default schema")
|
||||
return "", errors.New("no default schema")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,9 +76,7 @@ func TestParsePgx(t *testing.T) {
|
|||
} else if err == nil && !reflect.DeepEqual(test.expected, uri.DBName) {
|
||||
t.Errorf("%q got: %#v want: %#v", test.in, uri.DBName, test.expected)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetIndexColName(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -169,7 +169,10 @@ func (db *sqlite3) Version(ctx context.Context, queryer core.Queryer) (*schemas.
|
|||
|
||||
var version string
|
||||
if !rows.Next() {
|
||||
return nil, errors.New("Unknow version")
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return nil, errors.New("unknow version")
|
||||
}
|
||||
|
||||
if err := rows.Scan(&version); err != nil {
|
||||
|
|
@ -233,8 +236,19 @@ func (db *sqlite3) SQLType(c *schemas.Column) string {
|
|||
}
|
||||
}
|
||||
|
||||
func (db *sqlite3) FormatBytes(bs []byte) string {
|
||||
return fmt.Sprintf("X'%x'", bs)
|
||||
func (db *sqlite3) ColumnTypeKind(t string) int {
|
||||
switch strings.ToUpper(t) {
|
||||
case "DATETIME":
|
||||
return schemas.TIME_TYPE
|
||||
case "TEXT":
|
||||
return schemas.TEXT_TYPE
|
||||
case "INTEGER", "REAL", "NUMERIC", "DECIMAL":
|
||||
return schemas.NUMERIC_TYPE
|
||||
case "BLOB":
|
||||
return schemas.BLOB_TYPE
|
||||
default:
|
||||
return schemas.UNKNOW_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
func (db *sqlite3) IsReserved(name string) bool {
|
||||
|
|
@ -404,12 +418,14 @@ func (db *sqlite3) GetColumns(queryer core.Queryer, ctx context.Context, tableNa
|
|||
defer rows.Close()
|
||||
|
||||
var name string
|
||||
for rows.Next() {
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, nil, rows.Err()
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
|
|
@ -472,6 +488,9 @@ func (db *sqlite3) GetTables(queryer core.Queryer, ctx context.Context) ([]*sche
|
|||
}
|
||||
tables = append(tables, table)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
|
|
@ -485,7 +504,7 @@ func (db *sqlite3) GetIndexes(queryer core.Queryer, ctx context.Context, tableNa
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
indexes := make(map[string]*schemas.Index, 0)
|
||||
indexes := make(map[string]*schemas.Index)
|
||||
for rows.Next() {
|
||||
var tmpSQL sql.NullString
|
||||
err = rows.Scan(&tmpSQL)
|
||||
|
|
@ -531,6 +550,9 @@ func (db *sqlite3) GetIndexes(queryer core.Queryer, ctx context.Context, tableNa
|
|||
index.IsRegular = isRegular
|
||||
indexes[index.Name] = index
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
|
||||
return indexes, nil
|
||||
}
|
||||
|
|
@ -540,6 +562,7 @@ func (db *sqlite3) Filters() []Filter {
|
|||
}
|
||||
|
||||
type sqlite3Driver struct {
|
||||
baseDriver
|
||||
}
|
||||
|
||||
func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*URI, error) {
|
||||
|
|
@ -549,3 +572,29 @@ func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*URI, error) {
|
|||
|
||||
return &URI{DBType: schemas.SQLITE, DBName: dataSourceName}, nil
|
||||
}
|
||||
|
||||
func (p *sqlite3Driver) GenScanResult(colType string) (interface{}, error) {
|
||||
switch colType {
|
||||
case "TEXT":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "INTEGER":
|
||||
var s sql.NullInt64
|
||||
return &s, nil
|
||||
case "DATETIME":
|
||||
var s sql.NullTime
|
||||
return &s, nil
|
||||
case "REAL":
|
||||
var s sql.NullFloat64
|
||||
return &s, nil
|
||||
case "NUMERIC", "DECIMAL":
|
||||
var s sql.NullString
|
||||
return &s, nil
|
||||
case "BLOB":
|
||||
var s sql.RawBytes
|
||||
return &s, nil
|
||||
default:
|
||||
var r sql.NullString
|
||||
return &r, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
226
engine.go
226
engine.go
|
|
@ -13,7 +13,6 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -21,7 +20,6 @@ import (
|
|||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/core"
|
||||
"xorm.io/xorm/dialects"
|
||||
"xorm.io/xorm/internal/json"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/log"
|
||||
"xorm.io/xorm/names"
|
||||
|
|
@ -35,6 +33,7 @@ type Engine struct {
|
|||
cacherMgr *caches.Manager
|
||||
defaultContext context.Context
|
||||
dialect dialects.Dialect
|
||||
driver dialects.Driver
|
||||
engineGroup *EngineGroup
|
||||
logger log.ContextLogger
|
||||
tagParser *tags.Parser
|
||||
|
|
@ -72,6 +71,7 @@ func newEngine(driverName, dataSourceName string, dialect dialects.Dialect, db *
|
|||
|
||||
engine := &Engine{
|
||||
dialect: dialect,
|
||||
driver: dialects.QueryDriver(driverName),
|
||||
TZLocation: time.Local,
|
||||
defaultContext: context.Background(),
|
||||
cacherMgr: cacherMgr,
|
||||
|
|
@ -444,95 +444,14 @@ func (engine *Engine) DumpTables(tables []*schemas.Table, w io.Writer, tp ...sch
|
|||
return engine.dumpTables(tables, w, tp...)
|
||||
}
|
||||
|
||||
func formatColumnValue(dstDialect dialects.Dialect, d interface{}, col *schemas.Column) string {
|
||||
if d == nil {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
if dq, ok := d.(bool); ok && (dstDialect.URI().DBType == schemas.SQLITE ||
|
||||
dstDialect.URI().DBType == schemas.MSSQL) {
|
||||
if dq {
|
||||
func formatBool(s string, dstDialect dialects.Dialect) string {
|
||||
if dstDialect.URI().DBType == schemas.MSSQL {
|
||||
switch s {
|
||||
case "true":
|
||||
return "1"
|
||||
case "false":
|
||||
return "0"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
if col.SQLType.IsText() {
|
||||
var v string
|
||||
switch reflect.TypeOf(d).Kind() {
|
||||
case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map:
|
||||
bytes, err := json.DefaultJSONHandler.Marshal(d)
|
||||
if err != nil {
|
||||
v = fmt.Sprintf("%s", d)
|
||||
} else {
|
||||
v = string(bytes)
|
||||
}
|
||||
default:
|
||||
v = fmt.Sprintf("%s", d)
|
||||
}
|
||||
|
||||
return "'" + strings.Replace(v, "'", "''", -1) + "'"
|
||||
} else if col.SQLType.IsTime() {
|
||||
if dstDialect.URI().DBType == schemas.MSSQL && col.SQLType.Name == schemas.DateTime {
|
||||
if t, ok := d.(time.Time); ok {
|
||||
return "'" + t.UTC().Format("2006-01-02 15:04:05") + "'"
|
||||
}
|
||||
}
|
||||
var v = fmt.Sprintf("%s", d)
|
||||
if strings.HasSuffix(v, " +0000 UTC") {
|
||||
return fmt.Sprintf("'%s'", v[0:len(v)-len(" +0000 UTC")])
|
||||
} else if strings.HasSuffix(v, " +0000 +0000") {
|
||||
return fmt.Sprintf("'%s'", v[0:len(v)-len(" +0000 +0000")])
|
||||
}
|
||||
return "'" + strings.Replace(v, "'", "''", -1) + "'"
|
||||
} else if col.SQLType.IsBlob() {
|
||||
if reflect.TypeOf(d).Kind() == reflect.Slice {
|
||||
return fmt.Sprintf("%s", dstDialect.FormatBytes(d.([]byte)))
|
||||
} else if reflect.TypeOf(d).Kind() == reflect.String {
|
||||
return fmt.Sprintf("'%s'", d.(string))
|
||||
}
|
||||
} else if col.SQLType.IsNumeric() {
|
||||
switch reflect.TypeOf(d).Kind() {
|
||||
case reflect.Slice:
|
||||
if col.SQLType.Name == schemas.Bool {
|
||||
return fmt.Sprintf("%v", strconv.FormatBool(d.([]byte)[0] != byte('0')))
|
||||
}
|
||||
return fmt.Sprintf("%s", string(d.([]byte)))
|
||||
case reflect.Int16, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
if col.SQLType.Name == schemas.Bool {
|
||||
v := reflect.ValueOf(d).Int() > 0
|
||||
if dstDialect.URI().DBType == schemas.SQLITE {
|
||||
if v {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
return fmt.Sprintf("%v", strconv.FormatBool(v))
|
||||
}
|
||||
return fmt.Sprintf("%d", d)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if col.SQLType.Name == schemas.Bool {
|
||||
v := reflect.ValueOf(d).Uint() > 0
|
||||
if dstDialect.URI().DBType == schemas.SQLITE {
|
||||
if v {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
return fmt.Sprintf("%v", strconv.FormatBool(v))
|
||||
}
|
||||
return fmt.Sprintf("%d", d)
|
||||
default:
|
||||
return fmt.Sprintf("%v", d)
|
||||
}
|
||||
}
|
||||
|
||||
s := fmt.Sprintf("%v", d)
|
||||
if strings.Contains(s, ":") || strings.Contains(s, "-") {
|
||||
if strings.HasSuffix(s, " +0000 UTC") {
|
||||
return fmt.Sprintf("'%s'", s[0:len(s)-len(" +0000 UTC")])
|
||||
}
|
||||
return fmt.Sprintf("'%s'", s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
@ -545,7 +464,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch
|
|||
} else {
|
||||
dstDialect = dialects.QueryDialect(tp[0])
|
||||
if dstDialect == nil {
|
||||
return errors.New("Unsupported database type")
|
||||
return fmt.Errorf("unsupported database type %v", tp[0])
|
||||
}
|
||||
|
||||
uri := engine.dialect.URI()
|
||||
|
|
@ -619,75 +538,78 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
if table.Type != nil {
|
||||
sess := engine.NewSession()
|
||||
defer sess.Close()
|
||||
for rows.Next() {
|
||||
beanValue := reflect.New(table.Type)
|
||||
bean := beanValue.Interface()
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scanResults, err := sess.row2Slice(rows, fields, bean)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataStruct := utils.ReflectValue(bean)
|
||||
_, err = sess.slice2Bean(scanResults, fields, bean, &dataStruct, table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.WriteString(w, "INSERT INTO "+dstDialect.Quoter().Quote(dstTableName)+" ("+destColNames+") VALUES (")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sess := engine.NewSession()
|
||||
defer sess.Close()
|
||||
for rows.Next() {
|
||||
_, err = io.WriteString(w, "INSERT INTO "+dstDialect.Quoter().Quote(dstTableName)+" ("+destColNames+") VALUES (")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var temp string
|
||||
for _, d := range dstCols {
|
||||
col := table.GetColumn(d)
|
||||
if col == nil {
|
||||
return errors.New("unknown column error")
|
||||
scanResults, err := sess.engine.scanStringInterface(rows, fields, types)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, scanResult := range scanResults {
|
||||
stp := schemas.SQLType{Name: types[i].DatabaseTypeName()}
|
||||
if stp.IsNumeric() {
|
||||
s := scanResult.(*sql.NullString)
|
||||
if s.Valid {
|
||||
if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err = io.WriteString(w, "NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if stp.IsBool() {
|
||||
s := scanResult.(*sql.NullString)
|
||||
if s.Valid {
|
||||
if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err = io.WriteString(w, "NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s := scanResult.(*sql.NullString)
|
||||
if s.Valid {
|
||||
if _, err = io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err = io.WriteString(w, "NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
field := dataStruct.FieldByIndex(col.FieldIndex)
|
||||
temp += "," + formatColumnValue(dstDialect, field.Interface(), col)
|
||||
}
|
||||
_, err = io.WriteString(w, temp[1:]+");\n")
|
||||
if err != nil {
|
||||
return err
|
||||
if i < len(scanResults)-1 {
|
||||
if _, err = io.WriteString(w, ","); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for rows.Next() {
|
||||
dest := make([]interface{}, len(cols))
|
||||
err = rows.ScanSlice(&dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.WriteString(w, "INSERT INTO "+dstDialect.Quoter().Quote(dstTableName)+" ("+destColNames+") VALUES (")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var temp string
|
||||
for i, d := range dest {
|
||||
col := table.GetColumn(cols[i])
|
||||
if col == nil {
|
||||
return errors.New("unknow column error")
|
||||
}
|
||||
|
||||
temp += "," + formatColumnValue(dstDialect, d, col)
|
||||
}
|
||||
_, err = io.WriteString(w, temp[1:]+");\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.WriteString(w, ");\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
// FIXME: Hack for postgres
|
||||
if dstDialect.URI().DBType == schemas.POSTGRES && table.AutoIncrColumn() != nil {
|
||||
|
|
@ -1202,10 +1124,10 @@ func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64
|
|||
}
|
||||
|
||||
// Delete records, bean's non-empty fields are conditions
|
||||
func (engine *Engine) Delete(bean interface{}) (int64, error) {
|
||||
func (engine *Engine) Delete(beans ...interface{}) (int64, error) {
|
||||
session := engine.NewSession()
|
||||
defer session.Close()
|
||||
return session.Delete(bean)
|
||||
return session.Delete(beans...)
|
||||
}
|
||||
|
||||
// Get retrieve one record from table, bean's non-empty fields
|
||||
|
|
|
|||
17
go.mod
17
go.mod
|
|
@ -3,14 +3,17 @@ module xorm.io/xorm
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/denisenkom/go-mssqldb v0.10.0
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/goccy/go-json v0.7.4
|
||||
github.com/json-iterator/go v1.1.11
|
||||
github.com/lib/pq v1.7.0
|
||||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/lib/pq v1.10.2
|
||||
github.com/mattn/go-sqlite3 v1.14.8
|
||||
github.com/shopspring/decimal v1.2.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ziutek/mymysql v1.5.4
|
||||
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
|
||||
xorm.io/builder v0.3.8
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
modernc.org/sqlite v1.11.2
|
||||
xorm.io/builder v0.3.9
|
||||
)
|
||||
|
|
|
|||
35
go.sum
35
go.sum
|
|
@ -5,12 +5,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8=
|
||||
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k=
|
||||
github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
|
|
@ -28,10 +34,14 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
|
||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
|
|
@ -45,10 +55,15 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
|
@ -99,28 +114,46 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0=
|
||||
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
|
||||
modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc=
|
||||
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA=
|
||||
modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY=
|
||||
modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk=
|
||||
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8=
|
||||
modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8=
|
||||
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E=
|
||||
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM=
|
||||
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
|
||||
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 h1:rgEUzE849tFlHSoeCrKyS9cZAljC+DY7MdMHKq6R6sY=
|
||||
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU=
|
||||
modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w=
|
||||
modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A=
|
||||
modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=
|
||||
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/tcl v1.5.0 h1:euZSUNfE0Fd4W8VqXI1Ly1v7fqDJoBuAV88Ea+SnaSs=
|
||||
modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc=
|
||||
modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI=
|
||||
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||
|
|
@ -128,3 +161,5 @@ modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
|
|||
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||
xorm.io/builder v0.3.8 h1:P/wPgRqa9kX5uE0aA1/ukJ23u9KH0aSRpHLwDKXigSE=
|
||||
xorm.io/builder v0.3.8/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
|
||||
xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
|
|
|
|||
|
|
@ -174,6 +174,18 @@ func TestDumpTables(t *testing.T) {
|
|||
assert.NoError(t, testEngine.(*xorm.Engine).DumpTablesToFile([]*schemas.Table{tb}, name, tp))
|
||||
})
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.DropTables(new(TestDumpTableStruct)))
|
||||
|
||||
importPath := fmt.Sprintf("dump_%v-table.sql", testEngine.Dialect().URI().DBType)
|
||||
t.Run("import_"+importPath, func(t *testing.T) {
|
||||
sess := testEngine.NewSession()
|
||||
defer sess.Close()
|
||||
assert.NoError(t, sess.Begin())
|
||||
_, err = sess.ImportFile(importPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, sess.Commit())
|
||||
})
|
||||
}
|
||||
|
||||
func TestDumpTables2(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
// 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 integrations
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func BenchmarkGetVars(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
assert.NoError(b, PrepareEngine())
|
||||
testEngine.ShowSQL(false)
|
||||
|
||||
type BenchmarkGetVars struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
assert.NoError(b, testEngine.Sync2(new(BenchmarkGetVars)))
|
||||
|
||||
var v = BenchmarkGetVars{
|
||||
Name: "myname",
|
||||
}
|
||||
_, err := testEngine.Insert(&v)
|
||||
assert.NoError(b, err)
|
||||
|
||||
b.StartTimer()
|
||||
var myname string
|
||||
for i := 0; i < b.N; i++ {
|
||||
has, err := testEngine.Cols("name").Table("benchmark_get_vars").Where("id=?", v.Id).Get(&myname)
|
||||
b.StopTimer()
|
||||
myname = ""
|
||||
assert.True(b, has)
|
||||
assert.NoError(b, err)
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetStruct(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
assert.NoError(b, PrepareEngine())
|
||||
testEngine.ShowSQL(false)
|
||||
|
||||
type BenchmarkGetStruct struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
assert.NoError(b, testEngine.Sync2(new(BenchmarkGetStruct)))
|
||||
|
||||
var v = BenchmarkGetStruct{
|
||||
Name: "myname",
|
||||
}
|
||||
_, err := testEngine.Insert(&v)
|
||||
assert.NoError(b, err)
|
||||
|
||||
b.StartTimer()
|
||||
var myname BenchmarkGetStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
has, err := testEngine.ID(v.Id).Get(&myname)
|
||||
b.StopTimer()
|
||||
myname.Id = 0
|
||||
myname.Name = ""
|
||||
assert.True(b, has)
|
||||
assert.NoError(b, err)
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFindStruct(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
assert.NoError(b, PrepareEngine())
|
||||
testEngine.ShowSQL(false)
|
||||
|
||||
type BenchmarkFindStruct struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
assert.NoError(b, testEngine.Sync2(new(BenchmarkFindStruct)))
|
||||
|
||||
var v = BenchmarkFindStruct{
|
||||
Name: "myname",
|
||||
}
|
||||
_, err := testEngine.Insert(&v)
|
||||
assert.NoError(b, err)
|
||||
|
||||
b.StartTimer()
|
||||
var mynames = make([]BenchmarkFindStruct, 0, 1)
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := testEngine.Find(&mynames)
|
||||
b.StopTimer()
|
||||
mynames = make([]BenchmarkFindStruct, 0, 1)
|
||||
assert.NoError(b, err)
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
|
@ -241,3 +241,28 @@ func TestUnscopeDelete(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
func TestDelete2(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type UserinfoDelete2 struct {
|
||||
Uid int64 `xorm:"id pk not null autoincr"`
|
||||
IsMan bool
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(UserinfoDelete2)))
|
||||
|
||||
user := UserinfoDelete2{}
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
cnt, err = testEngine.Table("userinfo_delete2").In("id", []int{1}).Delete()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
user2 := UserinfoDelete2{}
|
||||
has, err := testEngine.ID(1).Get(&user2)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,16 +406,16 @@ func TestFindMapPtrString(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestFindBit(t *testing.T) {
|
||||
type FindBitStruct struct {
|
||||
func TestFindBool(t *testing.T) {
|
||||
type FindBoolStruct struct {
|
||||
Id int64
|
||||
Msg bool `xorm:"bit"`
|
||||
Msg bool
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(FindBitStruct))
|
||||
assertSync(t, new(FindBoolStruct))
|
||||
|
||||
cnt, err := testEngine.Insert([]FindBitStruct{
|
||||
cnt, err := testEngine.Insert([]FindBoolStruct{
|
||||
{
|
||||
Msg: false,
|
||||
},
|
||||
|
|
@ -426,14 +426,13 @@ func TestFindBit(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, cnt)
|
||||
|
||||
var results = make([]FindBitStruct, 0, 2)
|
||||
var results = make([]FindBoolStruct, 0, 2)
|
||||
err = testEngine.Find(&results)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, len(results))
|
||||
}
|
||||
|
||||
func TestFindMark(t *testing.T) {
|
||||
|
||||
type Mark struct {
|
||||
Mark1 string `xorm:"VARCHAR(1)"`
|
||||
Mark2 string `xorm:"VARCHAR(1)"`
|
||||
|
|
@ -468,7 +467,7 @@ func TestFindAndCountOneFunc(t *testing.T) {
|
|||
type FindAndCountStruct struct {
|
||||
Id int64
|
||||
Content string
|
||||
Msg bool `xorm:"bit"`
|
||||
Msg bool
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -16,6 +17,7 @@ import (
|
|||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
@ -345,6 +347,29 @@ func TestGetSlice(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetMap(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type UserinfoMap struct {
|
||||
Uid int `xorm:"pk autoincr"`
|
||||
IsMan bool
|
||||
}
|
||||
|
||||
assertSync(t, new(UserinfoMap))
|
||||
|
||||
tableName := testEngine.Quote(testEngine.TableName("userinfo_map", true))
|
||||
_, err := testEngine.Exec(fmt.Sprintf("INSERT INTO %s (is_man) VALUES (NULL)", tableName))
|
||||
assert.NoError(t, err)
|
||||
|
||||
var valuesString = make(map[string]string)
|
||||
has, err := testEngine.Table("userinfo_map").Get(&valuesString)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, has)
|
||||
assert.Equal(t, 2, len(valuesString))
|
||||
assert.Equal(t, "1", valuesString["uid"])
|
||||
assert.Equal(t, "", valuesString["is_man"])
|
||||
}
|
||||
|
||||
func TestGetError(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
|
|
@ -766,3 +791,141 @@ func TestGetNil(t *testing.T) {
|
|||
assert.True(t, errors.Is(err, xorm.ErrObjectIsNil))
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
func TestGetBigFloat(t *testing.T) {
|
||||
type GetBigFloat struct {
|
||||
Id int64
|
||||
Money *big.Float `xorm:"numeric(22,2)"`
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(GetBigFloat))
|
||||
|
||||
{
|
||||
var gf = GetBigFloat{
|
||||
Money: big.NewFloat(999999.99),
|
||||
}
|
||||
_, err := testEngine.Insert(&gf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var m big.Float
|
||||
has, err := testEngine.Table("get_big_float").Cols("money").Where("id=?", gf.Id).Get(&m)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String())
|
||||
//fmt.Println(m.Cmp(gf.Money))
|
||||
//assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String())
|
||||
}
|
||||
|
||||
type GetBigFloat2 struct {
|
||||
Id int64
|
||||
Money *big.Float `xorm:"decimal(22,2)"`
|
||||
Money2 big.Float `xorm:"decimal(22,2)"`
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(GetBigFloat2))
|
||||
|
||||
{
|
||||
var gf2 = GetBigFloat2{
|
||||
Money: big.NewFloat(9999999.99),
|
||||
Money2: *big.NewFloat(99.99),
|
||||
}
|
||||
_, err := testEngine.Insert(&gf2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var m2 big.Float
|
||||
has, err := testEngine.Table("get_big_float2").Cols("money").Where("id=?", gf2.Id).Get(&m2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.True(t, m2.String() == gf2.Money.String(), "%v != %v", m2.String(), gf2.Money.String())
|
||||
//fmt.Println(m.Cmp(gf.Money))
|
||||
//assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String())
|
||||
|
||||
var gf3 GetBigFloat2
|
||||
has, err = testEngine.ID(gf2.Id).Get(&gf3)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.True(t, gf3.Money.String() == gf2.Money.String(), "%v != %v", gf3.Money.String(), gf2.Money.String())
|
||||
assert.True(t, gf3.Money2.String() == gf2.Money2.String(), "%v != %v", gf3.Money2.String(), gf2.Money2.String())
|
||||
|
||||
var gfs []GetBigFloat2
|
||||
err = testEngine.Find(&gfs)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(gfs))
|
||||
assert.True(t, gfs[0].Money.String() == gf2.Money.String(), "%v != %v", gfs[0].Money.String(), gf2.Money.String())
|
||||
assert.True(t, gfs[0].Money2.String() == gf2.Money2.String(), "%v != %v", gfs[0].Money2.String(), gf2.Money2.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDecimal(t *testing.T) {
|
||||
type GetDecimal struct {
|
||||
Id int64
|
||||
Money decimal.Decimal `xorm:"decimal(22,2)"`
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(GetDecimal))
|
||||
|
||||
{
|
||||
var gf = GetDecimal{
|
||||
Money: decimal.NewFromFloat(999999.99),
|
||||
}
|
||||
_, err := testEngine.Insert(&gf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var m decimal.Decimal
|
||||
has, err := testEngine.Table("get_decimal").Cols("money").Where("id=?", gf.Id).Get(&m)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String())
|
||||
//fmt.Println(m.Cmp(gf.Money))
|
||||
//assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String())
|
||||
}
|
||||
|
||||
type GetDecimal2 struct {
|
||||
Id int64
|
||||
Money *decimal.Decimal `xorm:"decimal(22,2)"`
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(GetDecimal2))
|
||||
|
||||
{
|
||||
v := decimal.NewFromFloat(999999.99)
|
||||
var gf = GetDecimal2{
|
||||
Money: &v,
|
||||
}
|
||||
_, err := testEngine.Insert(&gf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var m decimal.Decimal
|
||||
has, err := testEngine.Table("get_decimal2").Cols("money").Where("id=?", gf.Id).Get(&m)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String())
|
||||
//fmt.Println(m.Cmp(gf.Money))
|
||||
//assert.True(t, m.Cmp(gf.Money) == 0, "%v != %v", m.String(), gf.Money.String())
|
||||
}
|
||||
}
|
||||
func TestGetTime(t *testing.T) {
|
||||
type GetTimeStruct struct {
|
||||
Id int64
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(GetTimeStruct))
|
||||
|
||||
var gts = GetTimeStruct{
|
||||
CreateTime: time.Now().In(testEngine.GetTZLocation()),
|
||||
}
|
||||
_, err := testEngine.Insert(>s)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var gn time.Time
|
||||
has, err := testEngine.Table("get_time_struct").Cols(colMapper.Obj2Table("CreateTime")).Get(&gn)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, gts.CreateTime.Format(time.RFC3339), gn.Format(time.RFC3339))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,17 +168,17 @@ func TestInsertAutoIncr(t *testing.T) {
|
|||
assert.Greater(t, user.Uid, int64(0))
|
||||
}
|
||||
|
||||
type DefaultInsert struct {
|
||||
Id int64
|
||||
Status int `xorm:"default -1"`
|
||||
Name string
|
||||
Created time.Time `xorm:"created"`
|
||||
Updated time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
func TestInsertDefault(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type DefaultInsert struct {
|
||||
Id int64
|
||||
Status int `xorm:"default -1"`
|
||||
Name string
|
||||
Created time.Time `xorm:"created"`
|
||||
Updated time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
di := new(DefaultInsert)
|
||||
err := testEngine.Sync2(di)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -195,16 +195,16 @@ func TestInsertDefault(t *testing.T) {
|
|||
assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix())
|
||||
}
|
||||
|
||||
type DefaultInsert2 struct {
|
||||
Id int64
|
||||
Name string
|
||||
Url string `xorm:"text"`
|
||||
CheckTime time.Time `xorm:"not null default '2000-01-01 00:00:00' TIMESTAMP"`
|
||||
}
|
||||
|
||||
func TestInsertDefault2(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type DefaultInsert2 struct {
|
||||
Id int64
|
||||
Name string
|
||||
Url string `xorm:"text"`
|
||||
CheckTime time.Time `xorm:"not null default '2000-01-01 00:00:00'"`
|
||||
}
|
||||
|
||||
di := new(DefaultInsert2)
|
||||
err := testEngine.Sync2(di)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -1024,3 +1024,44 @@ func TestInsertIntSlice(t *testing.T) {
|
|||
assert.True(t, has)
|
||||
assert.EqualValues(t, v3, v4)
|
||||
}
|
||||
|
||||
func TestInsertDeleted(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type InsertDeletedStructNotRight struct {
|
||||
ID uint64 `xorm:"'ID' pk autoincr"`
|
||||
DeletedAt time.Time `xorm:"'DELETED_AT' deleted notnull"`
|
||||
}
|
||||
// notnull tag will be ignored
|
||||
err := testEngine.Sync2(new(InsertDeletedStructNotRight))
|
||||
assert.NoError(t, err)
|
||||
|
||||
type InsertDeletedStruct struct {
|
||||
ID uint64 `xorm:"'ID' pk autoincr"`
|
||||
DeletedAt time.Time `xorm:"'DELETED_AT' deleted"`
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(InsertDeletedStruct)))
|
||||
|
||||
var v InsertDeletedStruct
|
||||
_, err = testEngine.Insert(&v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var v2 InsertDeletedStruct
|
||||
has, err := testEngine.Get(&v2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
|
||||
_, err = testEngine.ID(v.ID).Delete(new(InsertDeletedStruct))
|
||||
assert.NoError(t, err)
|
||||
|
||||
var v3 InsertDeletedStruct
|
||||
has, err = testEngine.Get(&v3)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
|
||||
var v4 InsertDeletedStruct
|
||||
has, err = testEngine.Unscoped().Get(&v4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,16 @@ func TestUintId(t *testing.T) {
|
|||
err = testEngine.CreateTables(&UintId{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
tables, err := testEngine.DBMetas()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, 1, len(tables))
|
||||
cols := tables[0].PKColumns()
|
||||
assert.EqualValues(t, 1, len(cols))
|
||||
if testEngine.Dialect().URI().DBType == schemas.MYSQL {
|
||||
assert.EqualValues(t, "UNSIGNED INT", cols[0].SQLType.Name)
|
||||
}
|
||||
|
||||
cnt, err := testEngine.Insert(&UintId{Name: "test"})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func TestQueryString2(t *testing.T) {
|
|||
|
||||
type GetVar3 struct {
|
||||
Id int64 `xorm:"autoincr pk"`
|
||||
Msg bool `xorm:"bit"`
|
||||
Msg bool
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(GetVar3)))
|
||||
|
|
@ -107,6 +107,16 @@ func toFloat64(i interface{}) float64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
func toBool(i interface{}) bool {
|
||||
switch t := i.(type) {
|
||||
case int32:
|
||||
return t > 0
|
||||
case bool:
|
||||
return t
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestQueryInterface(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
|
|
@ -132,10 +142,10 @@ func TestQueryInterface(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records))
|
||||
assert.Equal(t, 5, len(records[0]))
|
||||
assert.EqualValues(t, 1, toInt64(records[0]["id"]))
|
||||
assert.Equal(t, "hi", toString(records[0]["msg"]))
|
||||
assert.EqualValues(t, 28, toInt64(records[0]["age"]))
|
||||
assert.EqualValues(t, 1.5, toFloat64(records[0]["money"]))
|
||||
assert.EqualValues(t, int64(1), records[0]["id"])
|
||||
assert.Equal(t, "hi", records[0]["msg"])
|
||||
assert.EqualValues(t, 28, records[0]["age"])
|
||||
assert.EqualValues(t, 1.5, records[0]["money"])
|
||||
}
|
||||
|
||||
func TestQueryNoParams(t *testing.T) {
|
||||
|
|
@ -192,7 +202,7 @@ func TestQueryStringNoParam(t *testing.T) {
|
|||
|
||||
type GetVar4 struct {
|
||||
Id int64 `xorm:"autoincr pk"`
|
||||
Msg bool `xorm:"bit"`
|
||||
Msg bool
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(GetVar4)))
|
||||
|
|
@ -229,7 +239,7 @@ func TestQuerySliceStringNoParam(t *testing.T) {
|
|||
|
||||
type GetVar6 struct {
|
||||
Id int64 `xorm:"autoincr pk"`
|
||||
Msg bool `xorm:"bit"`
|
||||
Msg bool
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(GetVar6)))
|
||||
|
|
@ -266,7 +276,7 @@ func TestQueryInterfaceNoParam(t *testing.T) {
|
|||
|
||||
type GetVar5 struct {
|
||||
Id int64 `xorm:"autoincr pk"`
|
||||
Msg bool `xorm:"bit"`
|
||||
Msg bool
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(GetVar5)))
|
||||
|
|
@ -280,14 +290,14 @@ func TestQueryInterfaceNoParam(t *testing.T) {
|
|||
records, err := testEngine.Table("get_var5").Limit(1).QueryInterface()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(records))
|
||||
assert.EqualValues(t, 1, toInt64(records[0]["id"]))
|
||||
assert.EqualValues(t, 0, toInt64(records[0]["msg"]))
|
||||
assert.EqualValues(t, 1, records[0]["id"])
|
||||
assert.False(t, toBool(records[0]["msg"]))
|
||||
|
||||
records, err = testEngine.Table("get_var5").Where(builder.Eq{"id": 1}).QueryInterface()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(records))
|
||||
assert.EqualValues(t, 1, toInt64(records[0]["id"]))
|
||||
assert.EqualValues(t, 0, toInt64(records[0]["msg"]))
|
||||
assert.EqualValues(t, 1, records[0]["id"])
|
||||
assert.False(t, toBool(records[0]["msg"]))
|
||||
}
|
||||
|
||||
func TestQueryWithBuilder(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package integrations
|
|||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -35,3 +36,32 @@ func TestExecAndQuery(t *testing.T) {
|
|||
assert.EqualValues(t, 1, id)
|
||||
assert.Equal(t, "user", string(results[0]["name"]))
|
||||
}
|
||||
|
||||
func TestExecTime(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type UserinfoExecTime struct {
|
||||
Uid int
|
||||
Name string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(UserinfoExecTime)))
|
||||
now := time.Now()
|
||||
res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_exec_time`", true)+" (uid, name, created) VALUES (?, ?, ?)", 1, "user", now)
|
||||
assert.NoError(t, err)
|
||||
cnt, err := res.RowsAffected()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
results, err := testEngine.QueryString("SELECT * FROM " + testEngine.TableName("`userinfo_exec_time`", true))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(results))
|
||||
assert.EqualValues(t, now.In(testEngine.GetTZLocation()).Format("2006-01-02 15:04:05"), results[0]["created"])
|
||||
|
||||
var uet UserinfoExecTime
|
||||
has, err := testEngine.Where("uid=?", 1).Get(&uet)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, now.In(testEngine.GetTZLocation()).Format("2006-01-02 15:04:05"), uet.Created.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -286,6 +286,19 @@ func TestSyncTable3(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSyncTable4(t *testing.T) {
|
||||
type SyncTable6 struct {
|
||||
Id int64
|
||||
Qty float64 `xorm:"numeric(36,2)"`
|
||||
}
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(SyncTable6)))
|
||||
|
||||
assert.NoError(t, testEngine.Sync2(new(SyncTable6)))
|
||||
}
|
||||
|
||||
func TestIsTableExist(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
|
|
|
|||
|
|
@ -1313,7 +1313,6 @@ func TestUpdateIgnoreOnlyFromDBFields(t *testing.T) {
|
|||
assert.EqualValues(t, true, has)
|
||||
assert.EqualValues(t, "", record.OnlyFromDBField)
|
||||
return &record
|
||||
|
||||
}
|
||||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(TestOnlyFromDBField))
|
||||
|
|
@ -1396,15 +1395,22 @@ func TestNilFromDB(t *testing.T) {
|
|||
assert.NoError(t, PrepareEngine())
|
||||
assertSync(t, new(TestTable1))
|
||||
|
||||
cnt, err := testEngine.Insert(&TestTable1{
|
||||
var tt0 = TestTable1{
|
||||
Field1: &TestFieldType1{
|
||||
cb: []byte("string"),
|
||||
},
|
||||
UpdateTime: time.Now(),
|
||||
})
|
||||
}
|
||||
cnt, err := testEngine.Insert(&tt0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var tt1 TestTable1
|
||||
has, err := testEngine.ID(tt0.Id).Get(&tt1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, "string", string(tt1.Field1.cb))
|
||||
|
||||
cnt, err = testEngine.Update(TestTable1{
|
||||
UpdateTime: time.Now().Add(time.Second),
|
||||
}, TestTable1{
|
||||
|
|
@ -1418,4 +1424,37 @@ func TestNilFromDB(t *testing.T) {
|
|||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var tt = TestTable1{
|
||||
UpdateTime: time.Now(),
|
||||
Field1: &TestFieldType1{
|
||||
cb: nil,
|
||||
},
|
||||
}
|
||||
cnt, err = testEngine.Insert(&tt)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var tt2 TestTable1
|
||||
has, err = testEngine.ID(tt.Id).Get(&tt2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.Nil(t, tt2.Field1)
|
||||
|
||||
var tt3 = TestTable1{
|
||||
UpdateTime: time.Now(),
|
||||
Field1: &TestFieldType1{
|
||||
cb: []byte{},
|
||||
},
|
||||
}
|
||||
cnt, err = testEngine.Insert(&tt3)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var tt4 TestTable1
|
||||
has, err = testEngine.ID(tt3.Id).Get(&tt4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.NotNil(t, tt4.Field1)
|
||||
assert.NotNil(t, tt4.Field1.cb)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,9 +53,18 @@ func TestTimeUserTimeDiffLoc(t *testing.T) {
|
|||
assert.NoError(t, PrepareEngine())
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
assert.NoError(t, err)
|
||||
oldTZLoc := testEngine.GetTZLocation()
|
||||
defer func() {
|
||||
testEngine.SetTZLocation(oldTZLoc)
|
||||
}()
|
||||
testEngine.SetTZLocation(loc)
|
||||
|
||||
dbLoc, err := time.LoadLocation("America/New_York")
|
||||
assert.NoError(t, err)
|
||||
oldDBLoc := testEngine.GetTZDatabase()
|
||||
defer func() {
|
||||
testEngine.SetTZDatabase(oldDBLoc)
|
||||
}()
|
||||
testEngine.SetTZDatabase(dbLoc)
|
||||
|
||||
type TimeUser2 struct {
|
||||
|
|
@ -118,9 +127,18 @@ func TestTimeUserCreatedDiffLoc(t *testing.T) {
|
|||
assert.NoError(t, PrepareEngine())
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
assert.NoError(t, err)
|
||||
oldTZLoc := testEngine.GetTZLocation()
|
||||
defer func() {
|
||||
testEngine.SetTZLocation(oldTZLoc)
|
||||
}()
|
||||
testEngine.SetTZLocation(loc)
|
||||
|
||||
dbLoc, err := time.LoadLocation("America/New_York")
|
||||
assert.NoError(t, err)
|
||||
oldDBLoc := testEngine.GetTZDatabase()
|
||||
defer func() {
|
||||
testEngine.SetTZDatabase(oldDBLoc)
|
||||
}()
|
||||
testEngine.SetTZDatabase(dbLoc)
|
||||
|
||||
type UserCreated2 struct {
|
||||
|
|
@ -204,9 +222,18 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) {
|
|||
assert.NoError(t, PrepareEngine())
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
assert.NoError(t, err)
|
||||
oldTZLoc := testEngine.GetTZLocation()
|
||||
defer func() {
|
||||
testEngine.SetTZLocation(oldTZLoc)
|
||||
}()
|
||||
testEngine.SetTZLocation(loc)
|
||||
|
||||
dbLoc, err := time.LoadLocation("America/New_York")
|
||||
assert.NoError(t, err)
|
||||
oldDBLoc := testEngine.GetTZDatabase()
|
||||
defer func() {
|
||||
testEngine.SetTZDatabase(oldDBLoc)
|
||||
}()
|
||||
testEngine.SetTZDatabase(dbLoc)
|
||||
|
||||
type UserUpdated2 struct {
|
||||
|
|
@ -311,9 +338,18 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) {
|
|||
assert.NoError(t, PrepareEngine())
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
assert.NoError(t, err)
|
||||
oldTZLoc := testEngine.GetTZLocation()
|
||||
defer func() {
|
||||
testEngine.SetTZLocation(oldTZLoc)
|
||||
}()
|
||||
testEngine.SetTZLocation(loc)
|
||||
|
||||
dbLoc, err := time.LoadLocation("America/New_York")
|
||||
assert.NoError(t, err)
|
||||
oldDBLoc := testEngine.GetTZDatabase()
|
||||
defer func() {
|
||||
testEngine.SetTZDatabase(oldDBLoc)
|
||||
}()
|
||||
testEngine.SetTZDatabase(dbLoc)
|
||||
|
||||
type UserDeleted2 struct {
|
||||
|
|
@ -435,9 +471,18 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) {
|
|||
assert.NoError(t, PrepareEngine())
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
assert.NoError(t, err)
|
||||
oldTZLoc := testEngine.GetTZLocation()
|
||||
defer func() {
|
||||
testEngine.SetTZLocation(oldTZLoc)
|
||||
}()
|
||||
testEngine.SetTZLocation(loc)
|
||||
|
||||
dbLoc, err := time.LoadLocation("America/New_York")
|
||||
assert.NoError(t, err)
|
||||
oldDBLoc := testEngine.GetTZDatabase()
|
||||
defer func() {
|
||||
testEngine.SetTZDatabase(oldDBLoc)
|
||||
}()
|
||||
testEngine.SetTZDatabase(dbLoc)
|
||||
|
||||
type UserDeleted4 struct {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ package integrations
|
|||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -42,15 +41,22 @@ func (m *CustomStruct) Scan(value interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if s, ok := value.([]byte); ok {
|
||||
seps := strings.Split(string(s), "/")
|
||||
var s string
|
||||
switch t := value.(type) {
|
||||
case string:
|
||||
s = t
|
||||
case []byte:
|
||||
s = string(t)
|
||||
}
|
||||
if len(s) > 0 {
|
||||
seps := strings.Split(s, "/")
|
||||
m.Year, _ = strconv.Atoi(seps[0])
|
||||
m.Month, _ = strconv.Atoi(seps[1])
|
||||
m.Day, _ = strconv.Atoi(seps[2])
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("scan data not fit []byte")
|
||||
return fmt.Errorf("scan data %#v not fit []byte", value)
|
||||
}
|
||||
|
||||
func (m CustomStruct) Value() (driver.Value, error) {
|
||||
|
|
|
|||
|
|
@ -176,13 +176,39 @@ func (s *SliceType) ToDB() ([]byte, error) {
|
|||
return json.DefaultJSONHandler.Marshal(s)
|
||||
}
|
||||
|
||||
type Nullable struct {
|
||||
Data string
|
||||
}
|
||||
|
||||
func (s *Nullable) FromDB(data []byte) error {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
*s = Nullable{
|
||||
Data: string(data),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Nullable) ToDB() ([]byte, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []byte(s.Data), nil
|
||||
}
|
||||
|
||||
type ConvStruct struct {
|
||||
Conv ConvString
|
||||
Conv2 *ConvString
|
||||
Cfg1 ConvConfig
|
||||
Cfg2 *ConvConfig `xorm:"TEXT"`
|
||||
Cfg3 convert.Conversion `xorm:"BLOB"`
|
||||
Slice SliceType
|
||||
Conv ConvString
|
||||
Conv2 *ConvString
|
||||
Cfg1 ConvConfig
|
||||
Cfg2 *ConvConfig `xorm:"TEXT"`
|
||||
Cfg3 convert.Conversion `xorm:"BLOB"`
|
||||
Slice SliceType
|
||||
Nullable1 *Nullable `xorm:"null"`
|
||||
Nullable2 *Nullable `xorm:"null"`
|
||||
}
|
||||
|
||||
func (c *ConvStruct) BeforeSet(name string, cell xorm.Cell) {
|
||||
|
|
@ -205,8 +231,10 @@ func TestConversion(t *testing.T) {
|
|||
c.Cfg2 = &ConvConfig{"xx", 2}
|
||||
c.Cfg3 = &ConvConfig{"zz", 3}
|
||||
c.Slice = []*ConvConfig{{"yy", 4}, {"ff", 5}}
|
||||
c.Nullable1 = &Nullable{Data: "test"}
|
||||
c.Nullable2 = nil
|
||||
|
||||
_, err := testEngine.Insert(c)
|
||||
_, err := testEngine.Nullable("nullable2").Insert(c)
|
||||
assert.NoError(t, err)
|
||||
|
||||
c1 := new(ConvStruct)
|
||||
|
|
@ -248,6 +276,9 @@ func TestConversion(t *testing.T) {
|
|||
assert.EqualValues(t, 2, len(c2.Slice))
|
||||
assert.EqualValues(t, *c.Slice[0], *c2.Slice[0])
|
||||
assert.EqualValues(t, *c.Slice[1], *c2.Slice[1])
|
||||
assert.NotNil(t, c1.Nullable1)
|
||||
assert.Equal(t, c1.Nullable1.Data, "test")
|
||||
assert.Nil(t, c1.Nullable2)
|
||||
}
|
||||
|
||||
type MyInt int
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ type Interface interface {
|
|||
CreateUniques(bean interface{}) error
|
||||
Decr(column string, arg ...interface{}) *Session
|
||||
Desc(...string) *Session
|
||||
Delete(interface{}) (int64, error)
|
||||
Delete(...interface{}) (int64, error)
|
||||
Distinct(columns ...string) *Session
|
||||
DropIndexes(bean interface{}) error
|
||||
Exec(sqlOrArgs ...interface{}) (sql.Result, error)
|
||||
|
|
@ -51,6 +51,7 @@ type Interface interface {
|
|||
MustCols(columns ...string) *Session
|
||||
NoAutoCondition(...bool) *Session
|
||||
NotIn(string, ...interface{}) *Session
|
||||
Nullable(...string) *Session
|
||||
Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session
|
||||
Omit(columns ...string) *Session
|
||||
OrderBy(order string) *Session
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
// +build gojson
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
gojson "github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
func init() {
|
||||
DefaultJSONHandler = GOjson{}
|
||||
}
|
||||
|
||||
// GOjson implements JSONInterface via gojson
|
||||
type GOjson struct{}
|
||||
|
||||
// Marshal implements JSONInterface
|
||||
func (GOjson) Marshal(v interface{}) ([]byte, error) {
|
||||
return gojson.Marshal(v)
|
||||
}
|
||||
|
||||
// Unmarshal implements JSONInterface
|
||||
func (GOjson) Unmarshal(data []byte, v interface{}) error {
|
||||
return gojson.Unmarshal(data, v)
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ func (statement *Statement) writeInsertOutput(buf *strings.Builder, table *schem
|
|||
if _, err := buf.WriteString(" OUTPUT Inserted."); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := buf.WriteString(table.AutoIncrement); err != nil {
|
||||
if err := statement.dialect.Quoter().QuoteTo(buf, table.AutoIncrement); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB
|
|||
fmt.Fprint(&buf, " LIMIT ", *pLimitN)
|
||||
}
|
||||
} else if dialect.URI().DBType == schemas.ORACLE {
|
||||
if statement.Start != 0 || pLimitN != nil {
|
||||
if statement.Start != 0 && pLimitN != nil {
|
||||
oldString := buf.String()
|
||||
buf.Reset()
|
||||
rawColStr := columnStr
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -662,10 +663,6 @@ func (statement *Statement) GenIndexSQL() []string {
|
|||
return sqls
|
||||
}
|
||||
|
||||
func uniqueName(tableName, uqeName string) string {
|
||||
return fmt.Sprintf("UQE_%v_%v", tableName, uqeName)
|
||||
}
|
||||
|
||||
// GenUniqueSQL generates unique SQL
|
||||
func (statement *Statement) GenUniqueSQL() []string {
|
||||
var sqls []string
|
||||
|
|
@ -693,6 +690,138 @@ func (statement *Statement) GenDelIndexSQL() []string {
|
|||
return sqls
|
||||
}
|
||||
|
||||
func (statement *Statement) asDBCond(fieldValue reflect.Value, fieldType reflect.Type, col *schemas.Column, allUseBool, requiredField bool) (interface{}, bool, error) {
|
||||
switch fieldType.Kind() {
|
||||
case reflect.Ptr:
|
||||
if fieldValue.IsNil() {
|
||||
return nil, true, nil
|
||||
}
|
||||
return statement.asDBCond(fieldValue.Elem(), fieldType.Elem(), col, allUseBool, requiredField)
|
||||
case reflect.Bool:
|
||||
if allUseBool || requiredField {
|
||||
return fieldValue.Interface(), true, nil
|
||||
}
|
||||
// if a bool in a struct, it will not be as a condition because it default is false,
|
||||
// please use Where() instead
|
||||
return nil, false, nil
|
||||
case reflect.String:
|
||||
if !requiredField && fieldValue.String() == "" {
|
||||
return nil, false, nil
|
||||
}
|
||||
// for MyString, should convert to string or panic
|
||||
if fieldType.String() != reflect.String.String() {
|
||||
return fieldValue.String(), true, nil
|
||||
}
|
||||
return fieldValue.Interface(), true, nil
|
||||
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
|
||||
if !requiredField && fieldValue.Int() == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return fieldValue.Interface(), true, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if !requiredField && fieldValue.Float() == 0.0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return fieldValue.Interface(), true, nil
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
|
||||
if !requiredField && fieldValue.Uint() == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return fieldValue.Interface(), true, nil
|
||||
case reflect.Struct:
|
||||
if fieldType.ConvertibleTo(schemas.TimeType) {
|
||||
t := fieldValue.Convert(schemas.TimeType).Interface().(time.Time)
|
||||
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
|
||||
return nil, false, nil
|
||||
}
|
||||
return dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t), true, nil
|
||||
} else if fieldType.ConvertibleTo(schemas.BigFloatType) {
|
||||
t := fieldValue.Convert(schemas.BigFloatType).Interface().(big.Float)
|
||||
v := t.String()
|
||||
if v == "0" {
|
||||
return nil, false, nil
|
||||
}
|
||||
return t.String(), true, nil
|
||||
} else if _, ok := reflect.New(fieldType).Interface().(convert.Conversion); ok {
|
||||
return nil, false, nil
|
||||
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
|
||||
val, _ := valNul.Value()
|
||||
if val == nil && !requiredField {
|
||||
return nil, false, nil
|
||||
}
|
||||
return val, true, nil
|
||||
} else {
|
||||
if col.IsJSON {
|
||||
if col.SQLType.IsText() {
|
||||
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return string(bytes), true, nil
|
||||
} else if col.SQLType.IsBlob() {
|
||||
var bytes []byte
|
||||
var err error
|
||||
bytes, err = json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return bytes, true, nil
|
||||
}
|
||||
} else {
|
||||
table, err := statement.tagParser.ParseWithCache(fieldValue)
|
||||
if err != nil {
|
||||
return fieldValue.Interface(), true, nil
|
||||
}
|
||||
|
||||
if len(table.PrimaryKeys) == 1 {
|
||||
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
|
||||
// fix non-int pk issues
|
||||
//if pkField.Int() != 0 {
|
||||
if pkField.IsValid() && !utils.IsZero(pkField.Interface()) {
|
||||
return pkField.Interface(), true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
return nil, false, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys)
|
||||
}
|
||||
}
|
||||
case reflect.Array:
|
||||
return nil, false, nil
|
||||
case reflect.Slice, reflect.Map:
|
||||
if fieldValue == reflect.Zero(fieldType) {
|
||||
return nil, false, nil
|
||||
}
|
||||
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if col.SQLType.IsText() {
|
||||
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return string(bytes), true, nil
|
||||
} else if col.SQLType.IsBlob() {
|
||||
var bytes []byte
|
||||
var err error
|
||||
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
|
||||
fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
if fieldValue.Len() > 0 {
|
||||
return fieldValue.Bytes(), true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
bytes, err = json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return bytes, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
return fieldValue.Interface(), true, nil
|
||||
}
|
||||
|
||||
func (statement *Statement) buildConds2(table *schemas.Table, bean interface{},
|
||||
includeVersion bool, includeUpdated bool, includeNil bool,
|
||||
includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
|
||||
|
|
@ -747,9 +876,7 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{},
|
|||
continue
|
||||
}
|
||||
|
||||
fieldType := reflect.TypeOf(fieldValue.Interface())
|
||||
requiredField := useAllCols
|
||||
|
||||
if b, ok := getFlagForColumn(mustColumnMap, col); ok {
|
||||
if b {
|
||||
requiredField = true
|
||||
|
|
@ -758,6 +885,7 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{},
|
|||
}
|
||||
}
|
||||
|
||||
fieldType := reflect.TypeOf(fieldValue.Interface())
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
if fieldValue.IsNil() {
|
||||
if includeNil {
|
||||
|
|
@ -774,131 +902,12 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{},
|
|||
}
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
switch fieldType.Kind() {
|
||||
case reflect.Bool:
|
||||
if allUseBool || requiredField {
|
||||
val = fieldValue.Interface()
|
||||
} else {
|
||||
// if a bool in a struct, it will not be as a condition because it default is false,
|
||||
// please use Where() instead
|
||||
continue
|
||||
}
|
||||
case reflect.String:
|
||||
if !requiredField && fieldValue.String() == "" {
|
||||
continue
|
||||
}
|
||||
// for MyString, should convert to string or panic
|
||||
if fieldType.String() != reflect.String.String() {
|
||||
val = fieldValue.String()
|
||||
} else {
|
||||
val = fieldValue.Interface()
|
||||
}
|
||||
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
|
||||
if !requiredField && fieldValue.Int() == 0 {
|
||||
continue
|
||||
}
|
||||
val = fieldValue.Interface()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if !requiredField && fieldValue.Float() == 0.0 {
|
||||
continue
|
||||
}
|
||||
val = fieldValue.Interface()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
|
||||
if !requiredField && fieldValue.Uint() == 0 {
|
||||
continue
|
||||
}
|
||||
val = fieldValue.Interface()
|
||||
case reflect.Struct:
|
||||
if fieldType.ConvertibleTo(schemas.TimeType) {
|
||||
t := fieldValue.Convert(schemas.TimeType).Interface().(time.Time)
|
||||
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
|
||||
continue
|
||||
}
|
||||
val = dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t)
|
||||
} else if _, ok := reflect.New(fieldType).Interface().(convert.Conversion); ok {
|
||||
continue
|
||||
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
|
||||
val, _ = valNul.Value()
|
||||
if val == nil && !requiredField {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if col.IsJSON {
|
||||
if col.SQLType.IsText() {
|
||||
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val = string(bytes)
|
||||
} else if col.SQLType.IsBlob() {
|
||||
var bytes []byte
|
||||
var err error
|
||||
bytes, err = json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val = bytes
|
||||
}
|
||||
} else {
|
||||
table, err := statement.tagParser.ParseWithCache(fieldValue)
|
||||
if err != nil {
|
||||
val = fieldValue.Interface()
|
||||
} else {
|
||||
if len(table.PrimaryKeys) == 1 {
|
||||
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
|
||||
// fix non-int pk issues
|
||||
//if pkField.Int() != 0 {
|
||||
if pkField.IsValid() && !utils.IsZero(pkField.Interface()) {
|
||||
val = pkField.Interface()
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
//TODO: how to handler?
|
||||
return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Array:
|
||||
val, ok, err := statement.asDBCond(fieldValue, fieldType, col, allUseBool, requiredField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
case reflect.Slice, reflect.Map:
|
||||
if fieldValue == reflect.Zero(fieldType) {
|
||||
continue
|
||||
}
|
||||
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if col.SQLType.IsText() {
|
||||
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val = string(bytes)
|
||||
} else if col.SQLType.IsBlob() {
|
||||
var bytes []byte
|
||||
var err error
|
||||
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
|
||||
fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
if fieldValue.Len() > 0 {
|
||||
val = fieldValue.Bytes()
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
bytes, err = json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val = bytes
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
val = fieldValue.Interface()
|
||||
}
|
||||
|
||||
conds = append(conds, builder.Eq{colName: val})
|
||||
|
|
@ -942,16 +951,29 @@ func (statement *Statement) quoteColumnStr(columnStr string) string {
|
|||
|
||||
// ConvertSQLOrArgs converts sql or args
|
||||
func (statement *Statement) ConvertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) {
|
||||
sql, args, err := convertSQLOrArgs(sqlOrArgs...)
|
||||
sql, args, err := statement.convertSQLOrArgs(sqlOrArgs...)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return statement.ReplaceQuote(sql), args, nil
|
||||
}
|
||||
|
||||
func convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) {
|
||||
func (statement *Statement) convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) {
|
||||
switch sqlOrArgs[0].(type) {
|
||||
case string:
|
||||
if len(sqlOrArgs) > 1 {
|
||||
var newArgs = make([]interface{}, 0, len(sqlOrArgs)-1)
|
||||
for _, arg := range sqlOrArgs[1:] {
|
||||
if v, ok := arg.(*time.Time); ok {
|
||||
newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05"))
|
||||
} else if v, ok := arg.(time.Time); ok {
|
||||
newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05"))
|
||||
} else {
|
||||
newArgs = append(newArgs, arg)
|
||||
}
|
||||
}
|
||||
return sqlOrArgs[0].(string), newArgs, nil
|
||||
}
|
||||
return sqlOrArgs[0].(string), sqlOrArgs[1:], nil
|
||||
case *builder.Builder:
|
||||
return sqlOrArgs[0].(*builder.Builder).ToSQL()
|
||||
|
|
@ -978,7 +1000,7 @@ func (statement *Statement) joinColumns(cols []*schemas.Column, includeTableName
|
|||
|
||||
// CondDeleted returns the conditions whether a record is soft deleted.
|
||||
func (statement *Statement) CondDeleted(col *schemas.Column) builder.Cond {
|
||||
var colName = col.Name
|
||||
var colName = statement.quote(col.Name)
|
||||
if statement.JoinStr != "" {
|
||||
var prefix string
|
||||
if statement.TableAlias != "" {
|
||||
|
|
|
|||
|
|
@ -127,8 +127,9 @@ func (statement *Statement) BuildUpdates(tableValue reflect.Value,
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
val = data
|
||||
if data != nil {
|
||||
val = data
|
||||
}
|
||||
goto APPEND
|
||||
}
|
||||
}
|
||||
|
|
@ -138,8 +139,9 @@ func (statement *Statement) BuildUpdates(tableValue reflect.Value,
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
val = data
|
||||
if data != nil {
|
||||
val = data
|
||||
}
|
||||
goto APPEND
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
|
|
@ -19,9 +20,10 @@ import (
|
|||
|
||||
var (
|
||||
nullFloatType = reflect.TypeOf(sql.NullFloat64{})
|
||||
bigFloatType = reflect.TypeOf(big.Float{})
|
||||
)
|
||||
|
||||
// Value2Interface convert a field value of a struct to interface for puting into database
|
||||
// Value2Interface convert a field value of a struct to interface for putting into database
|
||||
func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue reflect.Value) (interface{}, error) {
|
||||
if fieldValue.CanAddr() {
|
||||
if fieldConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
||||
|
|
@ -29,6 +31,12 @@ func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue refl
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data == nil {
|
||||
if col.Nullable {
|
||||
return nil, nil
|
||||
}
|
||||
data = []byte{}
|
||||
}
|
||||
if col.SQLType.IsBlob() {
|
||||
return data, nil
|
||||
}
|
||||
|
|
@ -43,12 +51,15 @@ func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue refl
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data == nil {
|
||||
if col.Nullable {
|
||||
return nil, nil
|
||||
}
|
||||
data = []byte{}
|
||||
}
|
||||
if col.SQLType.IsBlob() {
|
||||
return data, nil
|
||||
}
|
||||
if nil == data {
|
||||
return nil, nil
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
}
|
||||
|
|
@ -84,6 +95,9 @@ func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue refl
|
|||
return nil, nil
|
||||
}
|
||||
return t.Float64, nil
|
||||
} else if fieldType.ConvertibleTo(bigFloatType) {
|
||||
t := fieldValue.Convert(bigFloatType).Interface().(big.Float)
|
||||
return t.String(), nil
|
||||
}
|
||||
|
||||
if !col.IsJSON {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func IndexNoCase(s, sep string) int {
|
|||
return strings.Index(strings.ToLower(s), strings.ToLower(sep))
|
||||
}
|
||||
|
||||
// SplitNoCase split a string by a seperator with no care of capitalize
|
||||
// SplitNoCase split a string by a separator with no care of capitalize
|
||||
func SplitNoCase(s, sep string) []string {
|
||||
idx := IndexNoCase(s, sep)
|
||||
if idx < 0 {
|
||||
|
|
@ -22,7 +22,7 @@ func SplitNoCase(s, sep string) []string {
|
|||
return strings.Split(s, s[idx:idx+len(sep)])
|
||||
}
|
||||
|
||||
// SplitNNoCase split n by a seperator with no care of capitalize
|
||||
// SplitNNoCase split n by a separator with no care of capitalize
|
||||
func SplitNNoCase(s, sep string, n int) []string {
|
||||
idx := IndexNoCase(s, sep)
|
||||
if idx < 0 {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func (m SameMapper) Table2Obj(t string) string {
|
|||
return t
|
||||
}
|
||||
|
||||
// SnakeMapper implements IMapper and provides name transaltion between
|
||||
// SnakeMapper implements IMapper and provides name translation between
|
||||
// struct and database table
|
||||
type SnakeMapper struct {
|
||||
}
|
||||
|
|
|
|||
41
rows.go
41
rows.go
|
|
@ -5,7 +5,6 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
|
@ -17,10 +16,9 @@ import (
|
|||
|
||||
// Rows rows wrapper a rows to
|
||||
type Rows struct {
|
||||
session *Session
|
||||
rows *core.Rows
|
||||
beanType reflect.Type
|
||||
lastError error
|
||||
session *Session
|
||||
rows *core.Rows
|
||||
beanType reflect.Type
|
||||
}
|
||||
|
||||
func newRows(session *Session, bean interface{}) (*Rows, error) {
|
||||
|
|
@ -62,15 +60,6 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
|
|||
// !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
|
||||
// See https://gitea.com/xorm/xorm/issues/179
|
||||
if col := table.DeletedColumn(); col != nil && !session.statement.GetUnscoped() { // tag "deleted" is enabled
|
||||
var colName = session.engine.Quote(col.Name)
|
||||
if addedTableName {
|
||||
var nm = session.statement.TableName()
|
||||
if len(session.statement.TableAlias) > 0 {
|
||||
nm = session.statement.TableAlias
|
||||
}
|
||||
colName = session.engine.Quote(nm) + "." + colName
|
||||
}
|
||||
|
||||
autoCond = session.statement.CondDeleted(col)
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +75,6 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
|
|||
|
||||
rows.rows, err = rows.session.queryRows(sqlStr, args...)
|
||||
if err != nil {
|
||||
rows.lastError = err
|
||||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -96,25 +84,18 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
|
|||
|
||||
// Next move cursor to next record, return false if end has reached
|
||||
func (rows *Rows) Next() bool {
|
||||
if rows.lastError == nil && rows.rows != nil {
|
||||
hasNext := rows.rows.Next()
|
||||
if !hasNext {
|
||||
rows.lastError = sql.ErrNoRows
|
||||
}
|
||||
return hasNext
|
||||
}
|
||||
return false
|
||||
return rows.rows.Next()
|
||||
}
|
||||
|
||||
// Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close.
|
||||
func (rows *Rows) Err() error {
|
||||
return rows.lastError
|
||||
return rows.rows.Err()
|
||||
}
|
||||
|
||||
// Scan row record to bean properties
|
||||
func (rows *Rows) Scan(bean interface{}) error {
|
||||
if rows.lastError != nil {
|
||||
return rows.lastError
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType {
|
||||
|
|
@ -129,8 +110,12 @@ func (rows *Rows) Scan(bean interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
types, err := rows.rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scanResults, err := rows.session.row2Slice(rows.rows, fields, bean)
|
||||
scanResults, err := rows.session.row2Slice(rows.rows, fields, types, bean)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -154,5 +139,5 @@ func (rows *Rows) Close() error {
|
|||
return rows.rows.Close()
|
||||
}
|
||||
|
||||
return rows.lastError
|
||||
return rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,320 @@
|
|||
// 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 xorm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/convert"
|
||||
"xorm.io/xorm/core"
|
||||
"xorm.io/xorm/dialects"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// genScanResultsByBeanNullabale generates scan result
|
||||
func genScanResultsByBeanNullable(bean interface{}) (interface{}, bool, error) {
|
||||
switch t := bean.(type) {
|
||||
case *interface{}:
|
||||
return t, false, nil
|
||||
case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString, *sql.RawBytes:
|
||||
return t, false, nil
|
||||
case *time.Time:
|
||||
return &sql.NullString{}, true, nil
|
||||
case *sql.NullTime:
|
||||
return &sql.NullString{}, true, nil
|
||||
case *string:
|
||||
return &sql.NullString{}, true, nil
|
||||
case *int, *int8, *int16, *int32:
|
||||
return &sql.NullInt32{}, true, nil
|
||||
case *int64:
|
||||
return &sql.NullInt64{}, true, nil
|
||||
case *uint, *uint8, *uint16, *uint32:
|
||||
return &NullUint32{}, true, nil
|
||||
case *uint64:
|
||||
return &NullUint64{}, true, nil
|
||||
case *float32, *float64:
|
||||
return &sql.NullFloat64{}, true, nil
|
||||
case *bool:
|
||||
return &sql.NullBool{}, true, nil
|
||||
case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString,
|
||||
time.Time,
|
||||
string,
|
||||
int, int8, int16, int32, int64,
|
||||
uint, uint8, uint16, uint32, uint64,
|
||||
float32, float64,
|
||||
bool:
|
||||
return nil, false, fmt.Errorf("unsupported scan type: %t", t)
|
||||
case convert.Conversion:
|
||||
return &sql.RawBytes{}, true, nil
|
||||
}
|
||||
|
||||
tp := reflect.TypeOf(bean).Elem()
|
||||
switch tp.Kind() {
|
||||
case reflect.String:
|
||||
return &sql.NullString{}, true, nil
|
||||
case reflect.Int64:
|
||||
return &sql.NullInt64{}, true, nil
|
||||
case reflect.Int32, reflect.Int, reflect.Int16, reflect.Int8:
|
||||
return &sql.NullInt32{}, true, nil
|
||||
case reflect.Uint64:
|
||||
return &NullUint64{}, true, nil
|
||||
case reflect.Uint32, reflect.Uint, reflect.Uint16, reflect.Uint8:
|
||||
return &NullUint32{}, true, nil
|
||||
default:
|
||||
return nil, false, fmt.Errorf("unsupported type: %#v", bean)
|
||||
}
|
||||
}
|
||||
|
||||
func genScanResultsByBean(bean interface{}) (interface{}, bool, error) {
|
||||
switch t := bean.(type) {
|
||||
case *interface{}:
|
||||
return t, false, nil
|
||||
case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString,
|
||||
*sql.RawBytes,
|
||||
*string,
|
||||
*int, *int8, *int16, *int32, *int64,
|
||||
*uint, *uint8, *uint16, *uint32, *uint64,
|
||||
*float32, *float64,
|
||||
*bool:
|
||||
return t, false, nil
|
||||
case *time.Time, *sql.NullTime:
|
||||
return &sql.NullString{}, true, nil
|
||||
case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString,
|
||||
time.Time,
|
||||
string,
|
||||
int, int8, int16, int32, int64,
|
||||
uint, uint8, uint16, uint32, uint64,
|
||||
bool:
|
||||
return nil, false, fmt.Errorf("unsupported scan type: %t", t)
|
||||
case convert.Conversion:
|
||||
return &sql.RawBytes{}, true, nil
|
||||
}
|
||||
|
||||
tp := reflect.TypeOf(bean).Elem()
|
||||
switch tp.Kind() {
|
||||
case reflect.String:
|
||||
return new(string), true, nil
|
||||
case reflect.Int64:
|
||||
return new(int64), true, nil
|
||||
case reflect.Int32:
|
||||
return new(int32), true, nil
|
||||
case reflect.Int:
|
||||
return new(int32), true, nil
|
||||
case reflect.Int16:
|
||||
return new(int32), true, nil
|
||||
case reflect.Int8:
|
||||
return new(int32), true, nil
|
||||
case reflect.Uint64:
|
||||
return new(uint64), true, nil
|
||||
case reflect.Uint32:
|
||||
return new(uint32), true, nil
|
||||
case reflect.Uint:
|
||||
return new(uint), true, nil
|
||||
case reflect.Uint16:
|
||||
return new(uint16), true, nil
|
||||
case reflect.Uint8:
|
||||
return new(uint8), true, nil
|
||||
case reflect.Float32:
|
||||
return new(float32), true, nil
|
||||
case reflect.Float64:
|
||||
return new(float64), true, nil
|
||||
default:
|
||||
return nil, false, fmt.Errorf("unsupported type: %#v", bean)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]string, error) {
|
||||
var scanResults = make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var s sql.NullString
|
||||
scanResults[i] = &s
|
||||
}
|
||||
|
||||
if err := rows.Scan(scanResults...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]string, len(fields))
|
||||
for i, key := range fields {
|
||||
s := scanResults[i].(*sql.NullString)
|
||||
if s.String == "" {
|
||||
result[key] = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if schemas.TIME_TYPE == engine.dialect.ColumnTypeKind(types[i].DatabaseTypeName()) {
|
||||
t, err := convert.String2Time(s.String, engine.DatabaseTZ, engine.TZLocation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[key] = t.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
result[key] = s.String
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func row2mapBytes(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string][]byte, error) {
|
||||
var scanResults = make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var s sql.NullString
|
||||
scanResults[i] = &s
|
||||
}
|
||||
|
||||
if err := rows.Scan(scanResults...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string][]byte, len(fields))
|
||||
for ii, key := range fields {
|
||||
s := scanResults[ii].(*sql.NullString)
|
||||
result[key] = []byte(s.String)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (engine *Engine) scanStringInterface(rows *core.Rows, fields []string, types []*sql.ColumnType) ([]interface{}, error) {
|
||||
var scanResults = make([]interface{}, len(types))
|
||||
for i := 0; i < len(types); i++ {
|
||||
var s sql.NullString
|
||||
scanResults[i] = &s
|
||||
}
|
||||
|
||||
if err := engine.scan(rows, fields, types, scanResults...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scanResults, nil
|
||||
}
|
||||
|
||||
// scan is a wrap of driver.Scan but will automatically change the input values according requirements
|
||||
func (engine *Engine) scan(rows *core.Rows, fields []string, types []*sql.ColumnType, vv ...interface{}) error {
|
||||
var scanResults = make([]interface{}, 0, len(types))
|
||||
var replaces = make([]bool, 0, len(types))
|
||||
var err error
|
||||
for _, v := range vv {
|
||||
var replaced bool
|
||||
var scanResult interface{}
|
||||
switch t := v.(type) {
|
||||
case *big.Float, *time.Time, *sql.NullTime:
|
||||
scanResult = &sql.NullString{}
|
||||
replaced = true
|
||||
case sql.Scanner:
|
||||
scanResult = t
|
||||
case convert.Conversion:
|
||||
scanResult = &sql.RawBytes{}
|
||||
replaced = true
|
||||
default:
|
||||
nullable, ok := types[0].Nullable()
|
||||
if !ok || nullable {
|
||||
scanResult, replaced, err = genScanResultsByBeanNullable(v)
|
||||
} else {
|
||||
scanResult, replaced, err = genScanResultsByBean(v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
scanResults = append(scanResults, scanResult)
|
||||
replaces = append(replaces, replaced)
|
||||
}
|
||||
|
||||
if err = engine.driver.Scan(&dialects.ScanContext{
|
||||
DBLocation: engine.DatabaseTZ,
|
||||
UserLocation: engine.TZLocation,
|
||||
}, rows, types, scanResults...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, replaced := range replaces {
|
||||
if replaced {
|
||||
if err = convertAssign(vv[i], scanResults[i], engine.DatabaseTZ, engine.TZLocation); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (engine *Engine) scanInterfaces(rows *core.Rows, fields []string, types []*sql.ColumnType) ([]interface{}, error) {
|
||||
var scanResultContainers = make([]interface{}, len(types))
|
||||
for i := 0; i < len(types); i++ {
|
||||
scanResult, err := engine.driver.GenScanResult(types[i].DatabaseTypeName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scanResultContainers[i] = scanResult
|
||||
}
|
||||
if err := engine.scan(rows, fields, types, scanResultContainers...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scanResultContainers, nil
|
||||
}
|
||||
|
||||
func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fields []string) ([]string, error) {
|
||||
scanResults, err := engine.scanStringInterface(rows, fields, types)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results = make([]string, 0, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
results = append(results, scanResults[i].(*sql.NullString).String)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) {
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for rows.Next() {
|
||||
result, err := row2mapBytes(rows, types, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultsSlice = append(resultsSlice, result)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
|
||||
return resultsSlice, nil
|
||||
}
|
||||
|
||||
func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]interface{}, error) {
|
||||
var resultsMap = make(map[string]interface{}, len(fields))
|
||||
var scanResultContainers = make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
scanResult, err := engine.driver.GenScanResult(types[i].DatabaseTypeName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scanResultContainers[i] = scanResult
|
||||
}
|
||||
if err := engine.scan(rows, fields, types, scanResultContainers...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for ii, key := range fields {
|
||||
res, err := convert.Interface2Interface(engine.TZLocation, scanResultContainers[ii])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultsMap[key] = res
|
||||
}
|
||||
return resultsMap, nil
|
||||
}
|
||||
|
|
@ -58,7 +58,6 @@ func TestGetColumnIdx(t *testing.T) {
|
|||
func BenchmarkGetColumnWithToLower(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range testsGetColumn {
|
||||
|
||||
if _, ok := table.columnsMap[strings.ToLower(test.name)]; !ok {
|
||||
b.Errorf("Column not found:%s", test.name)
|
||||
}
|
||||
|
|
@ -69,7 +68,6 @@ func BenchmarkGetColumnWithToLower(b *testing.B) {
|
|||
func BenchmarkGetColumnIdxWithToLower(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range testsGetColumn {
|
||||
|
||||
if c, ok := table.columnsMap[strings.ToLower(test.name)]; ok {
|
||||
if test.idx < len(c) {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
package schemas
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -38,6 +39,7 @@ const (
|
|||
TIME_TYPE
|
||||
NUMERIC_TYPE
|
||||
ARRAY_TYPE
|
||||
BOOL_TYPE
|
||||
)
|
||||
|
||||
// IsType reutrns ture if the column type is the same as the parameter
|
||||
|
|
@ -63,6 +65,11 @@ func (s *SQLType) IsTime() bool {
|
|||
return s.IsType(TIME_TYPE)
|
||||
}
|
||||
|
||||
// IsBool returns true if column is a boolean type
|
||||
func (s *SQLType) IsBool() bool {
|
||||
return s.IsType(BOOL_TYPE)
|
||||
}
|
||||
|
||||
// IsNumeric returns true if column is a numeric type
|
||||
func (s *SQLType) IsNumeric() bool {
|
||||
return s.IsType(NUMERIC_TYPE)
|
||||
|
|
@ -208,7 +215,8 @@ var (
|
|||
Bytea: BLOB_TYPE,
|
||||
UniqueIdentifier: BLOB_TYPE,
|
||||
|
||||
Bool: NUMERIC_TYPE,
|
||||
Bool: BOOL_TYPE,
|
||||
Boolean: BOOL_TYPE,
|
||||
|
||||
Serial: NUMERIC_TYPE,
|
||||
BigSerial: NUMERIC_TYPE,
|
||||
|
|
@ -240,6 +248,7 @@ var (
|
|||
intDefault int
|
||||
uintDefault uint
|
||||
timeDefault time.Time
|
||||
bigFloatDefault big.Float
|
||||
)
|
||||
|
||||
// enumerates all types
|
||||
|
|
@ -267,7 +276,8 @@ var (
|
|||
ByteType = reflect.TypeOf(byteDefault)
|
||||
BytesType = reflect.SliceOf(ByteType)
|
||||
|
||||
TimeType = reflect.TypeOf(timeDefault)
|
||||
TimeType = reflect.TypeOf(timeDefault)
|
||||
BigFloatType = reflect.TypeOf(bigFloatDefault)
|
||||
)
|
||||
|
||||
// enumerates all types
|
||||
|
|
@ -365,3 +375,9 @@ func SQLType2Type(st SQLType) reflect.Type {
|
|||
return reflect.TypeOf("")
|
||||
}
|
||||
}
|
||||
|
||||
// SQLTypeName returns sql type name
|
||||
func SQLTypeName(tp string) string {
|
||||
fields := strings.Split(tp, "(")
|
||||
return fields[0]
|
||||
}
|
||||
|
|
|
|||
620
session.go
620
session.go
|
|
@ -16,7 +16,6 @@ import (
|
|||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/convert"
|
||||
|
|
@ -365,31 +364,30 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt,
|
|||
return
|
||||
}
|
||||
|
||||
func (session *Session) getField(dataStruct *reflect.Value, key string, table *schemas.Table, idx int) (*reflect.Value, error) {
|
||||
var col *schemas.Column
|
||||
if col = table.GetColumnIdx(key, idx); col == nil {
|
||||
return nil, ErrFieldIsNotExist{key, table.Name}
|
||||
func (session *Session) getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) {
|
||||
var col = table.GetColumnIdx(colName, idx)
|
||||
if col == nil {
|
||||
return nil, nil, ErrFieldIsNotExist{colName, table.Name}
|
||||
}
|
||||
|
||||
fieldValue, err := col.ValueOfV(dataStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if fieldValue == nil {
|
||||
return nil, ErrFieldIsNotValid{key, table.Name}
|
||||
return nil, nil, ErrFieldIsNotValid{colName, table.Name}
|
||||
}
|
||||
|
||||
if !fieldValue.IsValid() || !fieldValue.CanSet() {
|
||||
return nil, ErrFieldIsNotValid{key, table.Name}
|
||||
return nil, nil, ErrFieldIsNotValid{colName, table.Name}
|
||||
}
|
||||
|
||||
return fieldValue, nil
|
||||
return col, fieldValue, nil
|
||||
}
|
||||
|
||||
// Cell cell is a result of one column field
|
||||
type Cell *interface{}
|
||||
|
||||
func (session *Session) rows2Beans(rows *core.Rows, fields []string,
|
||||
func (session *Session) rows2Beans(rows *core.Rows, fields []string, types []*sql.ColumnType,
|
||||
table *schemas.Table, newElemFunc func([]string) reflect.Value,
|
||||
sliceValueSetFunc func(*reflect.Value, schemas.PK) error) error {
|
||||
for rows.Next() {
|
||||
|
|
@ -398,7 +396,7 @@ func (session *Session) rows2Beans(rows *core.Rows, fields []string,
|
|||
dataStruct := newValue.Elem()
|
||||
|
||||
// handle beforeClosures
|
||||
scanResults, err := session.row2Slice(rows, fields, bean)
|
||||
scanResults, err := session.row2Slice(rows, fields, types, bean)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -414,10 +412,10 @@ func (session *Session) rows2Beans(rows *core.Rows, fields []string,
|
|||
bean: bean,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interface{}) ([]interface{}, error) {
|
||||
func (session *Session) row2Slice(rows *core.Rows, fields []string, types []*sql.ColumnType, bean interface{}) ([]interface{}, error) {
|
||||
for _, closure := range session.beforeClosures {
|
||||
closure(bean)
|
||||
}
|
||||
|
|
@ -427,7 +425,7 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interfa
|
|||
var cell interface{}
|
||||
scanResults[i] = &cell
|
||||
}
|
||||
if err := rows.Scan(scanResults...); err != nil {
|
||||
if err := session.engine.scan(rows, fields, types, scanResults...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -436,6 +434,187 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interfa
|
|||
return scanResults, nil
|
||||
}
|
||||
|
||||
func (session *Session) setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error {
|
||||
bs, ok := asBytes(scanResult)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported database data type: %#v", scanResult)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.String {
|
||||
fieldValue.SetString(string(bs))
|
||||
return nil
|
||||
}
|
||||
|
||||
if fieldValue.CanAddr() {
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
x := reflect.New(fieldType)
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflect.Value,
|
||||
scanResult interface{}, table *schemas.Table) error {
|
||||
v, ok := scanResult.(*interface{})
|
||||
if ok {
|
||||
scanResult = *v
|
||||
}
|
||||
if scanResult == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fieldValue.CanAddr() {
|
||||
if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
||||
data, ok := asBytes(scanResult)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot convert %#v as bytes", scanResult)
|
||||
}
|
||||
return structConvert.FromDB(data)
|
||||
}
|
||||
}
|
||||
|
||||
if structConvert, ok := fieldValue.Interface().(convert.Conversion); ok {
|
||||
data, ok := asBytes(scanResult)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot convert %#v as bytes", scanResult)
|
||||
}
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
|
||||
fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
|
||||
return fieldValue.Interface().(convert.Conversion).FromDB(data)
|
||||
}
|
||||
return structConvert.FromDB(data)
|
||||
}
|
||||
|
||||
vv := reflect.ValueOf(scanResult)
|
||||
fieldType := fieldValue.Type()
|
||||
|
||||
if col.IsJSON {
|
||||
return session.setJSON(fieldValue, fieldType, scanResult)
|
||||
}
|
||||
|
||||
switch fieldType.Kind() {
|
||||
case reflect.Ptr:
|
||||
var e reflect.Value
|
||||
if fieldValue.IsNil() {
|
||||
e = reflect.New(fieldType.Elem()).Elem()
|
||||
} else {
|
||||
e = fieldValue.Elem()
|
||||
}
|
||||
if err := session.convertBeanField(col, &e, scanResult, table); err != nil {
|
||||
return err
|
||||
}
|
||||
if fieldValue.IsNil() {
|
||||
fieldValue.Set(e.Addr())
|
||||
}
|
||||
return nil
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return session.setJSON(fieldValue, fieldType, scanResult)
|
||||
case reflect.Slice, reflect.Array:
|
||||
bs, ok := asBytes(scanResult)
|
||||
if ok && fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
if col.SQLType.IsText() {
|
||||
x := reflect.New(fieldType)
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
} else {
|
||||
if fieldValue.Len() > 0 {
|
||||
for i := 0; i < fieldValue.Len(); i++ {
|
||||
if i < vv.Len() {
|
||||
fieldValue.Index(i).Set(vv.Index(i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < vv.Len(); i++ {
|
||||
fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
if fieldType.ConvertibleTo(schemas.BigFloatType) {
|
||||
v, err := asBigFloat(scanResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(v).Elem().Convert(fieldType))
|
||||
return nil
|
||||
}
|
||||
|
||||
if fieldType.ConvertibleTo(schemas.TimeType) {
|
||||
dbTZ := session.engine.DatabaseTZ
|
||||
if col.TimeZone != nil {
|
||||
dbTZ = col.TimeZone
|
||||
}
|
||||
|
||||
t, err := asTime(scanResult, dbTZ, session.engine.TZLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fieldValue.Set(reflect.ValueOf(*t).Convert(fieldType))
|
||||
return nil
|
||||
} else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
|
||||
err := nulVal.Scan(scanResult)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
session.engine.logger.Errorf("sql.Sanner error: %v", err)
|
||||
} else if session.statement.UseCascade {
|
||||
table, err := session.engine.tagParser.ParseWithCache(*fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(table.PrimaryKeys) != 1 {
|
||||
return errors.New("unsupported non or composited primary key cascade")
|
||||
}
|
||||
var pk = make(schemas.PK, len(table.PrimaryKeys))
|
||||
pk[0], err = asKind(vv, reflect.TypeOf(scanResult))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !pk.IsZero() {
|
||||
// !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())
|
||||
has, err := session.ID(pk).NoCascade().get(structInter.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if has {
|
||||
fieldValue.Set(structInter.Elem())
|
||||
} else {
|
||||
return errors.New("cascade obj is not exist")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} // switch fieldType.Kind()
|
||||
|
||||
return convertAssignV(fieldValue.Addr(), scanResult)
|
||||
}
|
||||
|
||||
func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *schemas.Table) (schemas.PK, error) {
|
||||
defer func() {
|
||||
executeAfterSet(bean, fields, scanResults)
|
||||
|
|
@ -445,10 +624,11 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b
|
|||
|
||||
var tempMap = make(map[string]int)
|
||||
var pk schemas.PK
|
||||
for ii, key := range fields {
|
||||
for i, colName := range fields {
|
||||
var idx int
|
||||
var lKey = strings.ToLower(colName)
|
||||
var ok bool
|
||||
var lKey = strings.ToLower(key)
|
||||
|
||||
if idx, ok = tempMap[lKey]; !ok {
|
||||
idx = 0
|
||||
} else {
|
||||
|
|
@ -456,9 +636,9 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b
|
|||
}
|
||||
tempMap[lKey] = idx
|
||||
|
||||
fieldValue, err := session.getField(dataStruct, key, table, idx)
|
||||
col, fieldValue, err := session.getField(dataStruct, table, colName, idx)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "is not valid") {
|
||||
if _, ok := err.(ErrFieldIsNotValid); !ok {
|
||||
session.engine.logger.Warnf("%v", err)
|
||||
}
|
||||
continue
|
||||
|
|
@ -466,408 +646,12 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b
|
|||
if fieldValue == nil {
|
||||
continue
|
||||
}
|
||||
rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii]))
|
||||
|
||||
// if row is null then ignore
|
||||
if rawValue.Interface() == nil {
|
||||
continue
|
||||
if err := session.convertBeanField(col, fieldValue, scanResults[i], table); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fieldValue.CanAddr() {
|
||||
if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
||||
if data, err := value2Bytes(&rawValue); err == nil {
|
||||
if err := structConvert.FromDB(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := fieldValue.Interface().(convert.Conversion); ok {
|
||||
if data, err := value2Bytes(&rawValue); err == nil {
|
||||
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
|
||||
fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
|
||||
}
|
||||
fieldValue.Interface().(convert.Conversion).FromDB(data)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
rawValueType := reflect.TypeOf(rawValue.Interface())
|
||||
vv := reflect.ValueOf(rawValue.Interface())
|
||||
col := table.GetColumnIdx(key, idx)
|
||||
if col.IsPrimaryKey {
|
||||
pk = append(pk, rawValue.Interface())
|
||||
}
|
||||
fieldType := fieldValue.Type()
|
||||
hasAssigned := false
|
||||
|
||||
if col.IsJSON {
|
||||
var bs []byte
|
||||
if rawValueType.Kind() == reflect.String {
|
||||
bs = []byte(vv.String())
|
||||
} else if rawValueType.ConvertibleTo(schemas.BytesType) {
|
||||
bs = vv.Bytes()
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind())
|
||||
}
|
||||
|
||||
hasAssigned = true
|
||||
|
||||
if len(bs) > 0 {
|
||||
if fieldType.Kind() == reflect.String {
|
||||
fieldValue.SetString(string(bs))
|
||||
continue
|
||||
}
|
||||
if fieldValue.CanAddr() {
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
x := reflect.New(fieldType)
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
switch fieldType.Kind() {
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
// TODO: reimplement this
|
||||
var bs []byte
|
||||
if rawValueType.Kind() == reflect.String {
|
||||
bs = []byte(vv.String())
|
||||
} else if rawValueType.ConvertibleTo(schemas.BytesType) {
|
||||
bs = vv.Bytes()
|
||||
}
|
||||
|
||||
hasAssigned = true
|
||||
if len(bs) > 0 {
|
||||
if fieldValue.CanAddr() {
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
x := reflect.New(fieldType)
|
||||
err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
switch rawValueType.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
switch rawValueType.Elem().Kind() {
|
||||
case reflect.Uint8:
|
||||
if fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
hasAssigned = true
|
||||
if col.SQLType.IsText() {
|
||||
x := reflect.New(fieldType)
|
||||
err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
} else {
|
||||
if fieldValue.Len() > 0 {
|
||||
for i := 0; i < fieldValue.Len(); i++ {
|
||||
if i < vv.Len() {
|
||||
fieldValue.Index(i).Set(vv.Index(i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < vv.Len(); i++ {
|
||||
fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
if rawValueType.Kind() == reflect.String {
|
||||
hasAssigned = true
|
||||
fieldValue.SetString(vv.String())
|
||||
}
|
||||
case reflect.Bool:
|
||||
if rawValueType.Kind() == reflect.Bool {
|
||||
hasAssigned = true
|
||||
fieldValue.SetBool(vv.Bool())
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch rawValueType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
hasAssigned = true
|
||||
fieldValue.SetInt(vv.Int())
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch rawValueType.Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
hasAssigned = true
|
||||
fieldValue.SetFloat(vv.Float())
|
||||
}
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
switch rawValueType.Kind() {
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
hasAssigned = true
|
||||
fieldValue.SetUint(vv.Uint())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
hasAssigned = true
|
||||
fieldValue.SetUint(uint64(vv.Int()))
|
||||
}
|
||||
case reflect.Struct:
|
||||
if fieldType.ConvertibleTo(schemas.TimeType) {
|
||||
dbTZ := session.engine.DatabaseTZ
|
||||
if col.TimeZone != nil {
|
||||
dbTZ = col.TimeZone
|
||||
}
|
||||
|
||||
if rawValueType == schemas.TimeType {
|
||||
hasAssigned = true
|
||||
|
||||
t := vv.Convert(schemas.TimeType).Interface().(time.Time)
|
||||
|
||||
z, _ := t.Zone()
|
||||
// set new location if database don't save timezone or give an incorrect timezone
|
||||
if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location
|
||||
session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
|
||||
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
|
||||
t.Minute(), t.Second(), t.Nanosecond(), dbTZ)
|
||||
}
|
||||
|
||||
t = t.In(session.engine.TZLocation)
|
||||
fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
|
||||
} else if rawValueType == schemas.IntType || rawValueType == schemas.Int64Type ||
|
||||
rawValueType == schemas.Int32Type {
|
||||
hasAssigned = true
|
||||
|
||||
t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation)
|
||||
fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
|
||||
} else {
|
||||
if d, ok := vv.Interface().([]uint8); ok {
|
||||
hasAssigned = true
|
||||
t, err := session.byte2Time(col, d)
|
||||
if err != nil {
|
||||
session.engine.logger.Errorf("byte2Time error: %v", err)
|
||||
hasAssigned = false
|
||||
} else {
|
||||
fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
|
||||
}
|
||||
} else if d, ok := vv.Interface().(string); ok {
|
||||
hasAssigned = true
|
||||
t, err := session.str2Time(col, d)
|
||||
if err != nil {
|
||||
session.engine.logger.Errorf("byte2Time error: %v", err)
|
||||
hasAssigned = false
|
||||
} else {
|
||||
fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface())
|
||||
}
|
||||
}
|
||||
} else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
|
||||
// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
|
||||
hasAssigned = true
|
||||
if err := nulVal.Scan(vv.Interface()); err != nil {
|
||||
session.engine.logger.Errorf("sql.Sanner error: %v", err)
|
||||
hasAssigned = false
|
||||
}
|
||||
} else if col.IsJSON {
|
||||
if rawValueType.Kind() == reflect.String {
|
||||
hasAssigned = true
|
||||
x := reflect.New(fieldType)
|
||||
if len([]byte(vv.String())) > 0 {
|
||||
err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
} else if rawValueType.Kind() == reflect.Slice {
|
||||
hasAssigned = true
|
||||
x := reflect.New(fieldType)
|
||||
if len(vv.Bytes()) > 0 {
|
||||
err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
}
|
||||
} else if session.statement.UseCascade {
|
||||
table, err := session.engine.tagParser.ParseWithCache(*fieldValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hasAssigned = true
|
||||
if len(table.PrimaryKeys) != 1 {
|
||||
return nil, errors.New("unsupported non or composited primary key cascade")
|
||||
}
|
||||
var pk = make(schemas.PK, len(table.PrimaryKeys))
|
||||
pk[0], err = asKind(vv, rawValueType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !pk.IsZero() {
|
||||
// !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())
|
||||
has, err := session.ID(pk).NoCascade().get(structInter.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
fieldValue.Set(structInter.Elem())
|
||||
} else {
|
||||
return nil, errors.New("cascade obj is not exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
// !nashtsai! TODO merge duplicated codes above
|
||||
switch fieldType {
|
||||
// following types case matching ptr's native type, therefore assign ptr directly
|
||||
case schemas.PtrStringType:
|
||||
if rawValueType.Kind() == reflect.String {
|
||||
x := vv.String()
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrBoolType:
|
||||
if rawValueType.Kind() == reflect.Bool {
|
||||
x := vv.Bool()
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrTimeType:
|
||||
if rawValueType == schemas.PtrTimeType {
|
||||
hasAssigned = true
|
||||
var x = rawValue.Interface().(time.Time)
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrFloat64Type:
|
||||
if rawValueType.Kind() == reflect.Float64 {
|
||||
x := vv.Float()
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrUint64Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = uint64(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrInt64Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
x := vv.Int()
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrFloat32Type:
|
||||
if rawValueType.Kind() == reflect.Float64 {
|
||||
var x = float32(vv.Float())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrIntType:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = int(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrInt32Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = int32(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrInt8Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = int8(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrInt16Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = int16(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrUintType:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = uint(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.PtrUint32Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = uint32(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.Uint8Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = uint8(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.Uint16Type:
|
||||
if rawValueType.Kind() == reflect.Int64 {
|
||||
var x = uint16(vv.Int())
|
||||
hasAssigned = true
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
case schemas.Complex64Type:
|
||||
var x complex64
|
||||
if len([]byte(vv.String())) > 0 {
|
||||
err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
hasAssigned = true
|
||||
case schemas.Complex128Type:
|
||||
var x complex128
|
||||
if len([]byte(vv.String())) > 0 {
|
||||
err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x))
|
||||
}
|
||||
hasAssigned = true
|
||||
} // switch fieldType
|
||||
} // switch fieldType.Kind()
|
||||
|
||||
// !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
|
||||
if !hasAssigned {
|
||||
data, err := value2Bytes(&rawValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = session.bytes2Value(col, fieldValue, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pk = append(pk, scanResults[i])
|
||||
}
|
||||
}
|
||||
return pk, nil
|
||||
|
|
|
|||
|
|
@ -1,521 +0,0 @@
|
|||
// 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/convert"
|
||||
"xorm.io/xorm/internal/json"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func (session *Session) str2Time(col *schemas.Column, data string) (outTime time.Time, outErr error) {
|
||||
sdata := strings.TrimSpace(data)
|
||||
var x time.Time
|
||||
var err error
|
||||
|
||||
var parseLoc = session.engine.DatabaseTZ
|
||||
if col.TimeZone != nil {
|
||||
parseLoc = col.TimeZone
|
||||
}
|
||||
|
||||
if sdata == utils.ZeroTime0 || sdata == utils.ZeroTime1 {
|
||||
} 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)
|
||||
}
|
||||
} else if len(sdata) > 19 && strings.Contains(sdata, "-") {
|
||||
x, err = time.ParseInLocation(time.RFC3339Nano, sdata, parseLoc)
|
||||
session.engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.Name, x, sdata)
|
||||
if err != nil {
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, parseLoc)
|
||||
}
|
||||
if err != nil {
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, parseLoc)
|
||||
}
|
||||
} else if len(sdata) == 19 && strings.Contains(sdata, "-") {
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, parseLoc)
|
||||
} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
|
||||
x, err = time.ParseInLocation("2006-01-02", sdata, parseLoc)
|
||||
} else if col.SQLType.Name == schemas.Time {
|
||||
if strings.Contains(sdata, " ") {
|
||||
ssd := strings.Split(sdata, " ")
|
||||
sdata = ssd[1]
|
||||
}
|
||||
|
||||
sdata = strings.TrimSpace(sdata)
|
||||
if session.engine.dialect.URI().DBType == schemas.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, parseLoc)
|
||||
} 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.In(session.engine.TZLocation)
|
||||
return
|
||||
}
|
||||
|
||||
func (session *Session) byte2Time(col *schemas.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 *schemas.Column, fieldValue *reflect.Value, data []byte) error {
|
||||
if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
||||
return structConvert.FromDB(data)
|
||||
}
|
||||
|
||||
if structConvert, ok := fieldValue.Interface().(convert.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.DefaultJSONHandler.Unmarshal(data, x.Interface())
|
||||
if err != nil {
|
||||
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.DefaultJSONHandler.Unmarshal(data, x.Interface())
|
||||
if err != nil {
|
||||
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.DefaultJSONHandler.Unmarshal(data, x.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(x.Elem())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ErrUnSupportedType
|
||||
}
|
||||
case reflect.String:
|
||||
fieldValue.SetString(string(data))
|
||||
case reflect.Bool:
|
||||
v, err := asBool(data)
|
||||
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 == schemas.Bit &&
|
||||
session.engine.dialect.URI().DBType == schemas.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(schemas.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.tagParser.ParseWithCache(*fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: current only support 1 primary key
|
||||
if len(table.PrimaryKeys) > 1 {
|
||||
return errors.New("unsupported composited primary key cascade")
|
||||
}
|
||||
|
||||
var pk = make(schemas.PK, len(table.PrimaryKeys))
|
||||
rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
|
||||
pk[0], err = str2PK(string(data), rawValueType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !pk.IsZero() {
|
||||
// !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())
|
||||
has, err := session.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 schemas.StringType.Kind():
|
||||
x := string(data)
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
// case "*bool":
|
||||
case schemas.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 schemas.Complex64Type.Kind():
|
||||
var x complex64
|
||||
if len(data) > 0 {
|
||||
err := json.DefaultJSONHandler.Unmarshal(data, &x)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
}
|
||||
// case "*complex128":
|
||||
case schemas.Complex128Type.Kind():
|
||||
var x complex128
|
||||
if len(data) > 0 {
|
||||
err := json.DefaultJSONHandler.Unmarshal(data, &x)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
|
||||
}
|
||||
// case "*float64":
|
||||
case schemas.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 schemas.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 schemas.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 schemas.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 schemas.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 schemas.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 schemas.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 schemas.Int64Type.Kind():
|
||||
sdata := string(data)
|
||||
var x int64
|
||||
var err error
|
||||
// for mysql, when use bit, it returned \x01
|
||||
if col.SQLType.Name == schemas.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 schemas.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 == schemas.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 schemas.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 == schemas.Bit &&
|
||||
session.engine.dialect.URI().DBType == schemas.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 schemas.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 == schemas.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 schemas.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 == schemas.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 schemas.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.tagParser.ParseWithCache(structInter.Elem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(table.PrimaryKeys) > 1 {
|
||||
return errors.New("unsupported composited primary key cascade")
|
||||
}
|
||||
|
||||
var pk = make(schemas.PK, len(table.PrimaryKeys))
|
||||
rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
|
||||
pk[0], err = str2PK(string(data), rawValueType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !pk.IsZero() {
|
||||
// !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
|
||||
has, err := session.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
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri
|
|||
}
|
||||
|
||||
// Delete records, bean's non-empty fields are conditions
|
||||
func (session *Session) Delete(bean interface{}) (int64, error) {
|
||||
func (session *Session) Delete(beans ...interface{}) (int64, error) {
|
||||
if session.isAutoClose {
|
||||
defer session.Close()
|
||||
}
|
||||
|
|
@ -92,20 +92,32 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
return 0, session.statement.LastError
|
||||
}
|
||||
|
||||
if err := session.statement.SetRefBean(bean); err != nil {
|
||||
return 0, err
|
||||
var (
|
||||
condSQL string
|
||||
condArgs []interface{}
|
||||
err error
|
||||
bean interface{}
|
||||
)
|
||||
if len(beans) > 0 {
|
||||
bean = beans[0]
|
||||
if err = session.statement.SetRefBean(bean); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
executeBeforeClosures(session, bean)
|
||||
|
||||
if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok {
|
||||
processor.BeforeDelete()
|
||||
}
|
||||
|
||||
condSQL, condArgs, err = session.statement.GenConds(bean)
|
||||
} else {
|
||||
condSQL, condArgs, err = session.statement.GenCondSQL(session.statement.Conds())
|
||||
}
|
||||
|
||||
executeBeforeClosures(session, bean)
|
||||
|
||||
if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok {
|
||||
processor.BeforeDelete()
|
||||
}
|
||||
|
||||
condSQL, condArgs, err := session.statement.GenConds(bean)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pLimitN := session.statement.LimitN
|
||||
if len(condSQL) == 0 && (pLimitN == nil || *pLimitN == 0) {
|
||||
return 0, ErrNeedDeletedCond
|
||||
|
|
@ -156,7 +168,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
|
||||
var realSQL string
|
||||
argsForCache := make([]interface{}, 0, len(condArgs)*2)
|
||||
if session.statement.GetUnscoped() || table.DeletedColumn() == nil { // tag "deleted" is disabled
|
||||
if session.statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled
|
||||
realSQL = deleteSQL
|
||||
copy(argsForCache, condArgs)
|
||||
argsForCache = append(condArgs, argsForCache...)
|
||||
|
|
@ -220,27 +232,29 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
|
|||
return 0, err
|
||||
}
|
||||
|
||||
// handle after delete processors
|
||||
if session.isAutoCommit {
|
||||
for _, closure := range session.afterClosures {
|
||||
closure(bean)
|
||||
}
|
||||
if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
|
||||
processor.AfterDelete()
|
||||
}
|
||||
} else {
|
||||
lenAfterClosures := len(session.afterClosures)
|
||||
if lenAfterClosures > 0 {
|
||||
if value, has := session.afterDeleteBeans[bean]; has && value != nil {
|
||||
*value = append(*value, session.afterClosures...)
|
||||
} else {
|
||||
afterClosures := make([]func(interface{}), lenAfterClosures)
|
||||
copy(afterClosures, session.afterClosures)
|
||||
session.afterDeleteBeans[bean] = &afterClosures
|
||||
if bean != nil {
|
||||
// handle after delete processors
|
||||
if session.isAutoCommit {
|
||||
for _, closure := range session.afterClosures {
|
||||
closure(bean)
|
||||
}
|
||||
if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
|
||||
processor.AfterDelete()
|
||||
}
|
||||
} else {
|
||||
if _, ok := interface{}(bean).(AfterDeleteProcessor); ok {
|
||||
session.afterDeleteBeans[bean] = nil
|
||||
lenAfterClosures := len(session.afterClosures)
|
||||
if lenAfterClosures > 0 && len(beans) > 0 {
|
||||
if value, has := session.afterDeleteBeans[beans[0]]; has && value != nil {
|
||||
*value = append(*value, session.afterClosures...)
|
||||
} else {
|
||||
afterClosures := make([]func(interface{}), lenAfterClosures)
|
||||
copy(afterClosures, session.afterClosures)
|
||||
session.afterDeleteBeans[bean] = &afterClosures
|
||||
}
|
||||
} else {
|
||||
if _, ok := interface{}(bean).(AfterDeleteProcessor); ok {
|
||||
session.afterDeleteBeans[bean] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,5 +25,8 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) {
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
return rows.Next(), nil
|
||||
if rows.Next() {
|
||||
return true, nil
|
||||
}
|
||||
return false, rows.Err()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,6 +172,11 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
|
|||
return err
|
||||
}
|
||||
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var newElemFunc func(fields []string) reflect.Value
|
||||
elemType := containerValue.Type().Elem()
|
||||
var isPointer bool
|
||||
|
|
@ -241,7 +246,7 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = session.rows2Beans(rows, fields, tb, newElemFunc, containerValueSetFunc)
|
||||
err = session.rows2Beans(rows, fields, types, tb, newElemFunc, containerValueSetFunc)
|
||||
rows.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -270,13 +275,13 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect
|
|||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func convertPKToValue(table *schemas.Table, dst interface{}, pk schemas.PK) error {
|
||||
cols := table.PKColumns()
|
||||
if len(cols) == 1 {
|
||||
return convertAssign(dst, pk[0])
|
||||
return convertAssign(dst, pk[0], nil, nil)
|
||||
}
|
||||
|
||||
dst = pk
|
||||
|
|
@ -337,6 +342,9 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
|
||||
ids = append(ids, pk)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
session.engine.logger.Debugf("[cache] cache sql: %v, %v, %v, %v, %v", ids, tableName, sqlStr, newsql, args)
|
||||
err = caches.PutCacheSql(cacher, ids, tableName, newsql, args)
|
||||
|
|
|
|||
303
session_get.go
303
session_get.go
|
|
@ -6,12 +6,17 @@ package xorm
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/caches"
|
||||
"xorm.io/xorm/convert"
|
||||
"xorm.io/xorm/core"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
|
@ -30,6 +35,19 @@ func (session *Session) Get(bean interface{}) (bool, error) {
|
|||
return session.get(bean)
|
||||
}
|
||||
|
||||
func isPtrOfTime(v interface{}) bool {
|
||||
if _, ok := v.(*time.Time); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
el := reflect.ValueOf(v).Elem()
|
||||
if el.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
|
||||
return el.Type().ConvertibleTo(schemas.TimeType)
|
||||
}
|
||||
|
||||
func (session *Session) get(bean interface{}) (bool, error) {
|
||||
defer session.resetStatement()
|
||||
|
||||
|
|
@ -46,7 +64,7 @@ func (session *Session) get(bean interface{}) (bool, error) {
|
|||
return false, ErrObjectIsNil
|
||||
}
|
||||
|
||||
if beanValue.Elem().Kind() == reflect.Struct {
|
||||
if beanValue.Elem().Kind() == reflect.Struct && !isPtrOfTime(bean) {
|
||||
if err := session.statement.SetRefBean(bean); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -108,6 +126,31 @@ func (session *Session) get(bean interface{}) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
var (
|
||||
valuerTypePlaceHolder driver.Valuer
|
||||
valuerType = reflect.TypeOf(&valuerTypePlaceHolder).Elem()
|
||||
|
||||
scannerTypePlaceHolder sql.Scanner
|
||||
scannerType = reflect.TypeOf(&scannerTypePlaceHolder).Elem()
|
||||
|
||||
conversionTypePlaceHolder convert.Conversion
|
||||
conversionType = reflect.TypeOf(&conversionTypePlaceHolder).Elem()
|
||||
)
|
||||
|
||||
func isScannableStruct(bean interface{}, typeLen int) bool {
|
||||
switch bean.(type) {
|
||||
case *time.Time:
|
||||
return false
|
||||
case sql.Scanner:
|
||||
return false
|
||||
case convert.Conversion:
|
||||
return typeLen > 1
|
||||
case *big.Float:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
|
||||
rows, err := session.queryRows(sqlStr, args...)
|
||||
if err != nil {
|
||||
|
|
@ -116,163 +159,128 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table,
|
|||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
if rows.Err() != nil {
|
||||
return false, rows.Err()
|
||||
}
|
||||
return false, nil
|
||||
return false, rows.Err()
|
||||
}
|
||||
|
||||
switch bean.(type) {
|
||||
case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString:
|
||||
return true, rows.Scan(&bean)
|
||||
case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString:
|
||||
return true, rows.Scan(bean)
|
||||
case *string:
|
||||
var res sql.NullString
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*string)) = res.String
|
||||
}
|
||||
return true, nil
|
||||
case *int:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*int)) = int(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *int8:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*int8)) = int8(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *int16:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*int16)) = int16(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *int32:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*int32)) = int32(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *int64:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*int64)) = int64(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *uint:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*uint)) = uint(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *uint8:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*uint8)) = uint8(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *uint16:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*uint16)) = uint16(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *uint32:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*uint32)) = uint32(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *uint64:
|
||||
var res sql.NullInt64
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*uint64)) = uint64(res.Int64)
|
||||
}
|
||||
return true, nil
|
||||
case *bool:
|
||||
var res sql.NullBool
|
||||
if err := rows.Scan(&res); err != nil {
|
||||
return true, err
|
||||
}
|
||||
if res.Valid {
|
||||
*(bean.(*bool)) = res.Bool
|
||||
}
|
||||
return true, nil
|
||||
// WARN: Alougth rows return true, but we may also return error.
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
switch beanKind {
|
||||
case reflect.Struct:
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
// WARN: Alougth rows return true, but get fields failed
|
||||
return true, err
|
||||
if !isScannableStruct(bean, len(types)) {
|
||||
break
|
||||
}
|
||||
|
||||
scanResults, err := session.row2Slice(rows, fields, bean)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// close it before convert data
|
||||
rows.Close()
|
||||
|
||||
dataStruct := utils.ReflectValue(bean)
|
||||
_, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, session.executeProcessors()
|
||||
return session.getStruct(rows, types, fields, table, bean)
|
||||
case reflect.Slice:
|
||||
err = rows.ScanSlice(bean)
|
||||
return session.getSlice(rows, types, fields, bean)
|
||||
case reflect.Map:
|
||||
err = rows.ScanMap(bean)
|
||||
case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
err = rows.Scan(bean)
|
||||
default:
|
||||
err = rows.Scan(bean)
|
||||
return session.getMap(rows, types, fields, bean)
|
||||
}
|
||||
|
||||
return session.getVars(rows, types, fields, bean)
|
||||
}
|
||||
|
||||
func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) (bool, error) {
|
||||
switch t := bean.(type) {
|
||||
case *[]string:
|
||||
res, err := session.engine.scanStringInterface(rows, fields, types)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
var needAppend = len(*t) == 0 // both support slice is empty or has been initlized
|
||||
for i, r := range res {
|
||||
if needAppend {
|
||||
*t = append(*t, r.(*sql.NullString).String)
|
||||
} else {
|
||||
(*t)[i] = r.(*sql.NullString).String
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case *[]interface{}:
|
||||
scanResults, err := session.engine.scanInterfaces(rows, fields, types)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
var needAppend = len(*t) == 0
|
||||
for ii := range fields {
|
||||
s, err := convert.Interface2Interface(session.engine.DatabaseTZ, scanResults[ii])
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
if needAppend {
|
||||
*t = append(*t, s)
|
||||
} else {
|
||||
(*t)[ii] = s
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
return true, fmt.Errorf("unspoorted slice type: %t", t)
|
||||
}
|
||||
}
|
||||
|
||||
func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) (bool, error) {
|
||||
switch t := bean.(type) {
|
||||
case *map[string]string:
|
||||
scanResults, err := session.engine.scanStringInterface(rows, fields, types)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
for ii, key := range fields {
|
||||
(*t)[key] = scanResults[ii].(*sql.NullString).String
|
||||
}
|
||||
return true, nil
|
||||
case *map[string]interface{}:
|
||||
scanResults, err := session.engine.scanInterfaces(rows, fields, types)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
for ii, key := range fields {
|
||||
s, err := convert.Interface2Interface(session.engine.DatabaseTZ, scanResults[ii])
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
(*t)[key] = s
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
return true, fmt.Errorf("unspoorted map type: %t", t)
|
||||
}
|
||||
}
|
||||
|
||||
func (session *Session) getVars(rows *core.Rows, types []*sql.ColumnType, fields []string, beans ...interface{}) (bool, error) {
|
||||
if len(beans) != len(types) {
|
||||
return false, fmt.Errorf("expected columns %d, but only %d variables", len(types), len(beans))
|
||||
}
|
||||
|
||||
err := session.engine.scan(rows, fields, types, beans...)
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (session *Session) getStruct(rows *core.Rows, types []*sql.ColumnType, fields []string, table *schemas.Table, bean interface{}) (bool, error) {
|
||||
scanResults, err := session.row2Slice(rows, fields, types, bean)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// close it before convert data
|
||||
rows.Close()
|
||||
|
||||
dataStruct := utils.ReflectValue(bean)
|
||||
_, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
return true, session.executeProcessors()
|
||||
}
|
||||
|
||||
func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
|
||||
// if has no reftable, then don't use cache currently
|
||||
if !session.canCache() {
|
||||
|
|
@ -304,9 +312,12 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
|
|||
if rows.Next() {
|
||||
err = rows.ScanSlice(&res)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
} else {
|
||||
if rows.Err() != nil {
|
||||
return false, rows.Err()
|
||||
}
|
||||
return false, ErrCacheFailed
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
|
@ -324,7 +325,6 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
copy(afterClosures, session.afterClosures)
|
||||
session.afterInsertBeans[bean] = &afterClosures
|
||||
}
|
||||
|
||||
} else {
|
||||
if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
|
||||
session.afterInsertBeans[bean] = nil
|
||||
|
|
@ -414,7 +414,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
return 1, nil
|
||||
}
|
||||
|
||||
return 1, convertAssignV(aiValue.Addr(), id)
|
||||
return 1, convertAssignV(*aiValue, id)
|
||||
}
|
||||
|
||||
res, err := session.exec(sqlStr, args...)
|
||||
|
|
@ -454,7 +454,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
if err := convertAssignV(aiValue.Addr(), id); err != nil {
|
||||
if err := convertAssignV(*aiValue, id); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
|
@ -497,6 +497,16 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac
|
|||
}
|
||||
|
||||
if col.IsDeleted {
|
||||
colNames = append(colNames, col.Name)
|
||||
if !col.Nullable {
|
||||
if col.SQLType.IsNumeric() {
|
||||
args = append(args, 0)
|
||||
} else {
|
||||
args = append(args, time.Time{}.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
} else {
|
||||
args = append(args, nil)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
|
|||
}
|
||||
i++
|
||||
}
|
||||
return err
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
// BufferSize sets the buffersize for iterate
|
||||
|
|
|
|||
167
session_query.go
167
session_query.go
|
|
@ -5,13 +5,7 @@
|
|||
package xorm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm/core"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// Query runs a raw sql and return records as []map[string][]byte
|
||||
|
|
@ -28,137 +22,50 @@ func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, er
|
|||
return session.queryBytes(sqlStr, args...)
|
||||
}
|
||||
|
||||
func value2String(rawValue *reflect.Value) (str string, err error) {
|
||||
aa := reflect.TypeOf((*rawValue).Interface())
|
||||
vv := reflect.ValueOf((*rawValue).Interface())
|
||||
switch aa.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
str = strconv.FormatInt(vv.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
str = strconv.FormatUint(vv.Uint(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
str = strconv.FormatFloat(vv.Float(), 'f', -1, 64)
|
||||
case reflect.String:
|
||||
str = vv.String()
|
||||
case reflect.Array, reflect.Slice:
|
||||
switch aa.Elem().Kind() {
|
||||
case reflect.Uint8:
|
||||
data := rawValue.Interface().([]byte)
|
||||
str = string(data)
|
||||
if str == "\x00" {
|
||||
str = "0"
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
|
||||
}
|
||||
// time type
|
||||
case reflect.Struct:
|
||||
if aa.ConvertibleTo(schemas.TimeType) {
|
||||
str = vv.Convert(schemas.TimeType).Interface().(time.Time).Format(time.RFC3339Nano)
|
||||
} else {
|
||||
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
|
||||
}
|
||||
case reflect.Bool:
|
||||
str = strconv.FormatBool(vv.Bool())
|
||||
case reflect.Complex128, reflect.Complex64:
|
||||
str = fmt.Sprintf("%v", vv.Complex())
|
||||
/* TODO: unsupported types below
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.Uintptr:
|
||||
case reflect.UnsafePointer:
|
||||
case reflect.Chan, reflect.Func, reflect.Interface:
|
||||
*/
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) {
|
||||
result := make(map[string]string)
|
||||
scanResultContainers := make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var scanResultContainer interface{}
|
||||
scanResultContainers[i] = &scanResultContainer
|
||||
}
|
||||
if err := rows.Scan(scanResultContainers...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for ii, key := range fields {
|
||||
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
|
||||
// if row is null then as empty string
|
||||
if rawValue.Interface() == nil {
|
||||
result[key] = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if data, err := value2String(&rawValue); err == nil {
|
||||
result[key] = data
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func row2sliceStr(rows *core.Rows, fields []string) (results []string, err error) {
|
||||
result := make([]string, 0, len(fields))
|
||||
scanResultContainers := make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var scanResultContainer interface{}
|
||||
scanResultContainers[i] = &scanResultContainer
|
||||
}
|
||||
if err := rows.Scan(scanResultContainers...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(fields); i++ {
|
||||
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[i]))
|
||||
// if row is null then as empty string
|
||||
if rawValue.Interface() == nil {
|
||||
result = append(result, "")
|
||||
continue
|
||||
}
|
||||
|
||||
if data, err := value2String(&rawValue); err == nil {
|
||||
result = append(result, data)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) {
|
||||
func (session *Session) rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) {
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
result, err := row2mapStr(rows, fields)
|
||||
result, err := session.engine.row2mapStr(rows, types, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultsSlice = append(resultsSlice, result)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
|
||||
return resultsSlice, nil
|
||||
}
|
||||
|
||||
func rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) {
|
||||
func (session *Session) rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) {
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
record, err := row2sliceStr(rows, fields)
|
||||
record, err := session.engine.row2sliceStr(rows, types, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultsSlice = append(resultsSlice, record)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
|
||||
return resultsSlice, nil
|
||||
}
|
||||
|
|
@ -180,7 +87,7 @@ func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]stri
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
return rows2Strings(rows)
|
||||
return session.rows2Strings(rows)
|
||||
}
|
||||
|
||||
// QuerySliceString runs a raw sql and return records as [][]string
|
||||
|
|
@ -200,38 +107,28 @@ func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string,
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
return rows2SliceString(rows)
|
||||
return session.rows2SliceString(rows)
|
||||
}
|
||||
|
||||
func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) {
|
||||
resultsMap = make(map[string]interface{}, len(fields))
|
||||
scanResultContainers := make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var scanResultContainer interface{}
|
||||
scanResultContainers[i] = &scanResultContainer
|
||||
}
|
||||
if err := rows.Scan(scanResultContainers...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for ii, key := range fields {
|
||||
resultsMap[key] = reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])).Interface()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) {
|
||||
func (session *Session) rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) {
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
types, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for rows.Next() {
|
||||
result, err := row2mapInterface(rows, fields)
|
||||
result, err := session.engine.row2mapInterface(rows, types, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultsSlice = append(resultsSlice, result)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return nil, rows.Err()
|
||||
}
|
||||
|
||||
return resultsSlice, nil
|
||||
}
|
||||
|
|
@ -253,5 +150,5 @@ func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]i
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
return rows2Interfaces(rows)
|
||||
return session.rows2Interfaces(rows)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package xorm
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"reflect"
|
||||
|
||||
"xorm.io/xorm/core"
|
||||
)
|
||||
|
|
@ -71,58 +70,6 @@ func (session *Session) queryRow(sqlStr string, args ...interface{}) *core.Row {
|
|||
return core.NewRow(session.queryRows(sqlStr, args...))
|
||||
}
|
||||
|
||||
func value2Bytes(rawValue *reflect.Value) ([]byte, error) {
|
||||
str, err := value2String(rawValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(str), nil
|
||||
}
|
||||
|
||||
func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) {
|
||||
result := make(map[string][]byte)
|
||||
scanResultContainers := make([]interface{}, len(fields))
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var scanResultContainer interface{}
|
||||
scanResultContainers[i] = &scanResultContainer
|
||||
}
|
||||
if err := rows.Scan(scanResultContainers...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for ii, key := range fields {
|
||||
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
|
||||
//if row is null then ignore
|
||||
if rawValue.Interface() == nil {
|
||||
result[key] = []byte{}
|
||||
continue
|
||||
}
|
||||
|
||||
if data, err := value2Bytes(&rawValue); err == nil {
|
||||
result[key] = data
|
||||
} else {
|
||||
return nil, err // !nashtsai! REVIEW, should return err or just error log?
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) {
|
||||
fields, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for rows.Next() {
|
||||
result, err := row2map(rows, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultsSlice = append(resultsSlice, result)
|
||||
}
|
||||
|
||||
return resultsSlice, nil
|
||||
}
|
||||
|
||||
func (session *Session) queryBytes(sqlStr string, args ...interface{}) ([]map[string][]byte, error) {
|
||||
rows, err := session.queryRows(sqlStr, args...)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -336,8 +336,10 @@ func (session *Session) Sync2(beans ...interface{}) error {
|
|||
}
|
||||
} else {
|
||||
if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') {
|
||||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s",
|
||||
tbNameWithSchema, col.Name, curType, expectedType)
|
||||
if !strings.EqualFold(schemas.SQLTypeName(curType), engine.dialect.Alias(schemas.SQLTypeName(expectedType))) {
|
||||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s",
|
||||
tbNameWithSchema, col.Name, curType, expectedType)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if expectedType == schemas.Varchar {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri
|
|||
|
||||
ids = append(ids, pk)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
session.engine.logger.Debugf("[cache] find updated id: %v", ids)
|
||||
} /*else {
|
||||
session.engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args)
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ func addIndex(indexName string, table *schemas.Table, col *schemas.Column, index
|
|||
}
|
||||
}
|
||||
|
||||
// ErrIgnoreField represents an error to ignore field
|
||||
var ErrIgnoreField = errors.New("field will be ignored")
|
||||
|
||||
func (parser *Parser) parseFieldWithNoTag(fieldIndex int, field reflect.StructField, fieldValue reflect.Value) (*schemas.Column, error) {
|
||||
|
|
@ -296,5 +297,11 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) {
|
|||
table.AddColumn(col)
|
||||
} // end for
|
||||
|
||||
deletedColumn := table.DeletedColumn()
|
||||
// check columns
|
||||
if deletedColumn != nil {
|
||||
deletedColumn.Nullable = true
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ func UpdatedTagHandler(ctx *Context) error {
|
|||
// DeletedTagHandler describes deleted tag handler
|
||||
func DeletedTagHandler(ctx *Context) error {
|
||||
ctx.col.IsDeleted = true
|
||||
ctx.col.Nullable = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue