Merge branch 'master' into master
This commit is contained in:
commit
02414bac40
|
@ -4,9 +4,7 @@
|
|||
|
||||
Xorm is a simple and powerful ORM for Go.
|
||||
|
||||
[](https://drone.gitea.com/xorm/xorm) [](https://gocover.io/xorm.io/xorm)
|
||||
[](https://goreportcard.com/report/xorm.io/xorm)
|
||||
[](https://discord.gg/HuR2CF3)
|
||||
[](https://drone.gitea.com/xorm/xorm) [](https://gocover.io/xorm.io/xorm) [](https://goreportcard.com/report/xorm.io/xorm) [](https://discord.gg/HuR2CF3)
|
||||
|
||||
## Notice
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
xorm 是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
|
||||
|
||||
[](https://drone.gitea.com/xorm/xorm) [](https://gocover.io/xorm.io/xorm)
|
||||
[](https://goreportcard.com/report/xorm.io/xorm)
|
||||
[](https://discord.gg/HuR2CF3)
|
||||
[](https://drone.gitea.com/xorm/xorm) [](https://gocover.io/xorm.io/xorm) [](https://goreportcard.com/report/xorm.io/xorm) [](https://discord.gg/HuR2CF3)
|
||||
|
||||
## Notice
|
||||
|
||||
|
|
70
engine.go
70
engine.go
|
@ -816,81 +816,11 @@ func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) {
|
|||
return session.IsTableExist(beanOrTableName)
|
||||
}
|
||||
|
||||
// IDOf get id from one struct
|
||||
func (engine *Engine) IDOf(bean interface{}) (schemas.PK, error) {
|
||||
return engine.IDOfV(reflect.ValueOf(bean))
|
||||
}
|
||||
|
||||
// TableName returns table name with schema prefix if has
|
||||
func (engine *Engine) TableName(bean interface{}, includeSchema ...bool) string {
|
||||
return dialects.FullTableName(engine.dialect, engine.GetTableMapper(), bean, includeSchema...)
|
||||
}
|
||||
|
||||
// IDOfV get id from one value of struct
|
||||
func (engine *Engine) IDOfV(rv reflect.Value) (schemas.PK, error) {
|
||||
return engine.idOfV(rv)
|
||||
}
|
||||
|
||||
func (engine *Engine) idOfV(rv reflect.Value) (schemas.PK, error) {
|
||||
v := reflect.Indirect(rv)
|
||||
table, err := engine.tagParser.ParseWithCache(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk := make([]interface{}, len(table.PrimaryKeys))
|
||||
for i, col := range table.PKColumns() {
|
||||
var err error
|
||||
|
||||
fieldName := col.FieldName
|
||||
for {
|
||||
parts := strings.SplitN(fieldName, ".", 2)
|
||||
if len(parts) == 1 {
|
||||
break
|
||||
}
|
||||
|
||||
v = v.FieldByName(parts[0])
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
return nil, ErrUnSupportedType
|
||||
}
|
||||
fieldName = parts[1]
|
||||
}
|
||||
|
||||
pkField := v.FieldByName(fieldName)
|
||||
switch pkField.Kind() {
|
||||
case reflect.String:
|
||||
pk[i], err = engine.idTypeAssertion(col, pkField.String())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
pk[i], err = engine.idTypeAssertion(col, strconv.FormatInt(pkField.Int(), 10))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
// id of uint will be converted to int64
|
||||
pk[i], err = engine.idTypeAssertion(col, strconv.FormatUint(pkField.Uint(), 10))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return schemas.PK(pk), nil
|
||||
}
|
||||
|
||||
func (engine *Engine) idTypeAssertion(col *schemas.Column, sid string) (interface{}, error) {
|
||||
if col.SQLType.IsNumeric() {
|
||||
n, err := strconv.ParseInt(sid, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n, nil
|
||||
} else if col.SQLType.IsText() {
|
||||
return sid, nil
|
||||
} else {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
}
|
||||
|
||||
// CreateIndexes create indexes
|
||||
func (engine *Engine) CreateIndexes(bean interface{}) error {
|
||||
session := engine.NewSession()
|
||||
|
|
|
@ -502,10 +502,48 @@ func TestFindAndCountOneFunc(t *testing.T) {
|
|||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
results = make([]FindAndCountStruct, 0, 1)
|
||||
cnt, err = testEngine.Where("msg = ?", true).Limit(1).FindAndCount(&results)
|
||||
cnt, err = testEngine.Where("1=1").Limit(1).FindAndCount(&results)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(results))
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
assert.EqualValues(t, 2, cnt)
|
||||
assert.EqualValues(t, FindAndCountStruct{
|
||||
Id: 1,
|
||||
Content: "111",
|
||||
Msg: false,
|
||||
}, results[0])
|
||||
|
||||
results = make([]FindAndCountStruct, 0, 1)
|
||||
cnt, err = testEngine.Where("1=1").Limit(1).FindAndCount(&results)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(results))
|
||||
assert.EqualValues(t, 2, cnt)
|
||||
assert.EqualValues(t, FindAndCountStruct{
|
||||
Id: 1,
|
||||
Content: "111",
|
||||
Msg: false,
|
||||
}, results[0])
|
||||
|
||||
results = make([]FindAndCountStruct, 0, 1)
|
||||
cnt, err = testEngine.Where("1=1").Limit(1, 1).FindAndCount(&results)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(results))
|
||||
assert.EqualValues(t, 2, cnt)
|
||||
assert.EqualValues(t, FindAndCountStruct{
|
||||
Id: 2,
|
||||
Content: "222",
|
||||
Msg: true,
|
||||
}, results[0])
|
||||
|
||||
results = make([]FindAndCountStruct, 0, 1)
|
||||
cnt, err = testEngine.Where("1=1").Limit(1, 1).FindAndCount(&results)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(results))
|
||||
assert.EqualValues(t, 2, cnt)
|
||||
assert.EqualValues(t, FindAndCountStruct{
|
||||
Id: 2,
|
||||
Content: "222",
|
||||
Msg: true,
|
||||
}, results[0])
|
||||
|
||||
results = make([]FindAndCountStruct, 0, 1)
|
||||
cnt, err = testEngine.Where("msg = ?", true).Select("id, content, msg").
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/internal/statements"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
"xorm.io/xorm/names"
|
||||
)
|
||||
|
@ -39,6 +40,14 @@ func TestUpdateMap(t *testing.T) {
|
|||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
cnt, err = testEngine.Table("update_table").ID(tb.Id).Update(map[string]interface{}{
|
||||
"name": "test2",
|
||||
"age": 36,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, statements.IsIDConditionWithNoTableErr(err))
|
||||
assert.EqualValues(t, 0, cnt)
|
||||
}
|
||||
|
||||
func TestUpdateLimit(t *testing.T) {
|
||||
|
@ -988,7 +997,7 @@ func TestUpdateMapContent(t *testing.T) {
|
|||
assert.EqualValues(t, false, c2.IsMan)
|
||||
assert.EqualValues(t, 2, c2.Gender)
|
||||
|
||||
cnt, err = testEngine.Table(testEngine.TableName(new(UpdateMapContent))).ID(c.Id).Update(map[string]interface{}{
|
||||
cnt, err = testEngine.Table(new(UpdateMapContent)).ID(c.Id).Update(map[string]interface{}{
|
||||
"age": 15,
|
||||
"is_man": true,
|
||||
"gender": 1,
|
||||
|
|
|
@ -20,6 +20,21 @@ var (
|
|||
uintType = reflect.TypeOf(uint64(0))
|
||||
)
|
||||
|
||||
// ErrIDConditionWithNoTable represents an error there is no reference table with an ID condition
|
||||
type ErrIDConditionWithNoTable struct {
|
||||
ID schemas.PK
|
||||
}
|
||||
|
||||
func (err ErrIDConditionWithNoTable) Error() string {
|
||||
return fmt.Sprintf("ID condition %#v need reference table", err.ID)
|
||||
}
|
||||
|
||||
// IsIDConditionWithNoTableErr return true if the err is ErrIDConditionWithNoTable
|
||||
func IsIDConditionWithNoTableErr(err error) bool {
|
||||
_, ok := err.(ErrIDConditionWithNoTable)
|
||||
return ok
|
||||
}
|
||||
|
||||
// ID generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?"
|
||||
func (statement *Statement) ID(id interface{}) *Statement {
|
||||
switch t := id.(type) {
|
||||
|
@ -58,11 +73,16 @@ func (statement *Statement) ID(id interface{}) *Statement {
|
|||
return statement
|
||||
}
|
||||
|
||||
// ProcessIDParam handles the process of id condition
|
||||
func (statement *Statement) ProcessIDParam() error {
|
||||
if statement.idParam == nil || statement.RefTable == nil {
|
||||
if statement.idParam == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if statement.RefTable == nil {
|
||||
return ErrIDConditionWithNoTable{statement.idParam}
|
||||
}
|
||||
|
||||
if len(statement.RefTable.PrimaryKeys) != len(statement.idParam) {
|
||||
return fmt.Errorf("ID condition is error, expect %d primarykeys, there are %d",
|
||||
len(statement.RefTable.PrimaryKeys),
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
package schemas
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -115,3 +117,17 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
|
|||
|
||||
return &fieldValue, nil
|
||||
}
|
||||
|
||||
// ConvertID converts id content to suitable type according column type
|
||||
func (col *Column) ConvertID(sid string) (interface{}, error) {
|
||||
if col.SQLType.IsNumeric() {
|
||||
n, err := strconv.ParseInt(sid, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n, nil
|
||||
} else if col.SQLType.IsText() {
|
||||
return sid, nil
|
||||
}
|
||||
return nil, errors.New("not supported")
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
package schemas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -28,6 +30,7 @@ type Table struct {
|
|||
Comment string
|
||||
}
|
||||
|
||||
// NewEmptyTable creates an empty table
|
||||
func NewEmptyTable() *Table {
|
||||
return NewTable("", nil)
|
||||
}
|
||||
|
@ -44,10 +47,12 @@ func NewTable(name string, t reflect.Type) *Table {
|
|||
}
|
||||
}
|
||||
|
||||
// Columns returns table's columns
|
||||
func (table *Table) Columns() []*Column {
|
||||
return table.columns
|
||||
}
|
||||
|
||||
// ColumnsSeq returns table's column names according sequence
|
||||
func (table *Table) ColumnsSeq() []string {
|
||||
return table.columnsSeq
|
||||
}
|
||||
|
@ -61,6 +66,7 @@ func (table *Table) columnsByName(name string) []*Column {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetColumn returns column according column name, if column not found, return nil
|
||||
func (table *Table) GetColumn(name string) *Column {
|
||||
cols := table.columnsByName(name)
|
||||
if cols != nil {
|
||||
|
@ -70,6 +76,7 @@ func (table *Table) GetColumn(name string) *Column {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetColumnIdx returns column according name and idx
|
||||
func (table *Table) GetColumnIdx(name string, idx int) *Column {
|
||||
cols := table.columnsByName(name)
|
||||
if cols != nil && idx < len(cols) {
|
||||
|
@ -144,3 +151,45 @@ func (table *Table) AddColumn(col *Column) {
|
|||
func (table *Table) AddIndex(index *Index) {
|
||||
table.Indexes[index.Name] = index
|
||||
}
|
||||
|
||||
// IDOfV get id from one value of struct
|
||||
func (table *Table) IDOfV(rv reflect.Value) (PK, error) {
|
||||
v := reflect.Indirect(rv)
|
||||
pk := make([]interface{}, len(table.PrimaryKeys))
|
||||
for i, col := range table.PKColumns() {
|
||||
var err error
|
||||
|
||||
fieldName := col.FieldName
|
||||
for {
|
||||
parts := strings.SplitN(fieldName, ".", 2)
|
||||
if len(parts) == 1 {
|
||||
break
|
||||
}
|
||||
|
||||
v = v.FieldByName(parts[0])
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("Unsupported read value of column %s from field %s", col.Name, col.FieldName)
|
||||
}
|
||||
fieldName = parts[1]
|
||||
}
|
||||
|
||||
pkField := v.FieldByName(fieldName)
|
||||
switch pkField.Kind() {
|
||||
case reflect.String:
|
||||
pk[i], err = col.ConvertID(pkField.String())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
pk[i], err = col.ConvertID(strconv.FormatInt(pkField.Int(), 10))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
// id of uint will be converted to int64
|
||||
pk[i], err = col.ConvertID(strconv.FormatUint(pkField.Uint(), 10))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return PK(pk), nil
|
||||
}
|
||||
|
|
|
@ -60,6 +60,12 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte
|
|||
if session.statement.OrderStr != "" {
|
||||
session.statement.OrderStr = ""
|
||||
}
|
||||
if session.statement.LimitN != nil {
|
||||
session.statement.LimitN = nil
|
||||
}
|
||||
if session.statement.Start > 0 {
|
||||
session.statement.Start = 0
|
||||
}
|
||||
|
||||
// session has stored the conditions so we use `unscoped` to avoid duplicated condition.
|
||||
return session.Unscoped().Count(reflect.New(sliceElementType).Interface())
|
||||
|
@ -320,7 +326,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
}
|
||||
var pk schemas.PK = make([]interface{}, len(table.PrimaryKeys))
|
||||
for i, col := range table.PKColumns() {
|
||||
pk[i], err = session.engine.idTypeAssertion(col, res[i])
|
||||
pk[i], err = col.ConvertID(res[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -370,7 +376,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
} else {
|
||||
session.engine.logger.Debugf("[cache] cache hit bean: %v, %v, %v", tableName, id, bean)
|
||||
|
||||
pk, err := session.engine.IDOf(bean)
|
||||
pk, err := table.IDOfV(reflect.ValueOf(bean))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -419,7 +425,6 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session.statement = statement
|
||||
|
||||
vs := reflect.Indirect(reflect.ValueOf(beans))
|
||||
|
@ -428,7 +433,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
|
|||
if rv.Kind() != reflect.Ptr {
|
||||
rv = rv.Addr()
|
||||
}
|
||||
id, err := session.engine.idOfV(rv)
|
||||
id, err := table.IDOfV(rv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue