mirror of
https://github.com/hanzoai/xorm.git
synced 2026-10-01 00:49:40 +00:00
fix(asDBCond): check string when driver.Valuer return string instead of pointer (#2541)
Description: the bug is due to `driver.Valuer()` return a non-pointer type value, no check this value and return true directly. [decimal](https://github.com/shopspring/decimal) is a widely used type in most business scenarios, so I think it's necessary to compatible with the [decimal](https://github.com/shopspring/decimal) What's changes: - check string value - be compatible with [decimal](https://github.com/shopspring/decimal) close #2411 Test Result: ` === RUN TestColumnsStringGeneration --- PASS: TestColumnsStringGeneration (0.00s) === RUN TestConvertSQLOrArgs --- PASS: TestConvertSQLOrArgs (0.00s) === RUN TestAsDbCondWhenFieldValueIsDriverValuer --- PASS: TestAsDbCondWhenFieldValueIsDriverValuer (0.00s) PASS ` Reviewed-on: https://gitea.com/xorm/xorm/pulls/2541 Reviewed-by: Lunny Xiao <[email protected]> Co-authored-by: Jinhan He <[email protected]> Co-committed-by: Jinhan He <[email protected]>
This commit is contained in:
@@ -436,6 +436,11 @@ func (statement *Statement) asDBCond(fieldValue reflect.Value, fieldType reflect
|
||||
val, _ := valNul.Value()
|
||||
if val == nil && !requiredField {
|
||||
return nil, false, nil
|
||||
} else if parsed, ok := val.(string); ok {
|
||||
// parsed == "0" is to be compatible with the github.com/shopspring/decimal
|
||||
if (parsed == "" || parsed == "0") && !requiredField {
|
||||
return nil, false, nil
|
||||
}
|
||||
}
|
||||
return val, true, nil
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package statements
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -207,3 +208,50 @@ func BenchmarkColumnsStringGeneration(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ZeroDecimal struct{}
|
||||
|
||||
func (ZeroDecimal) Value() (driver.Value, error) {
|
||||
return "0", nil
|
||||
}
|
||||
|
||||
type EmptyDecimal struct{}
|
||||
|
||||
func (EmptyDecimal) Value() (driver.Value, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func TestAsDbCondWhenFieldValueIsDriverValuer(t *testing.T) {
|
||||
statement, err := createTestStatement()
|
||||
assert.NoError(t, err)
|
||||
|
||||
zeroDecimal := ZeroDecimal{}
|
||||
val, ok, err := statement.asDBCond(
|
||||
reflect.ValueOf(zeroDecimal),
|
||||
reflect.TypeOf(zeroDecimal),
|
||||
&schemas.Column{
|
||||
Name: "zero_decimal",
|
||||
TableName: "test",
|
||||
},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
assert.Nil(t, val)
|
||||
assert.False(t, ok)
|
||||
assert.NoError(t, err)
|
||||
|
||||
emptyDecimal := EmptyDecimal{}
|
||||
val, ok, err = statement.asDBCond(
|
||||
reflect.ValueOf(emptyDecimal),
|
||||
reflect.TypeOf(emptyDecimal),
|
||||
&schemas.Column{
|
||||
Name: "empty_decimal",
|
||||
TableName: "test",
|
||||
},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
assert.Nil(t, val)
|
||||
assert.False(t, ok)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ package tests
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func TestCount(t *testing.T) {
|
||||
@@ -190,3 +192,38 @@ func TestCountWithLimit(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, cnt)
|
||||
}
|
||||
|
||||
func TestCountStructWithDecimal(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
type ProductCount struct {
|
||||
Uid int `xorm:"pk autoincr"`
|
||||
Price decimal.Decimal `xorm:"decimal(35,30)"`
|
||||
}
|
||||
assert.NoError(t, testEngine.Sync(new(ProductCount)))
|
||||
|
||||
session := testEngine.NewSession()
|
||||
defer session.Close()
|
||||
var err error
|
||||
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
|
||||
err = session.Begin()
|
||||
assert.NoError(t, err)
|
||||
_, err = session.Exec("SET IDENTITY_INSERT `product_count` ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
cnt, err := session.Insert(&ProductCount{Uid: 2, Price: decimal.NewFromFloat(0.8)})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
cnt, err = session.Insert(&ProductCount{Uid: 3, Price: decimal.NewFromFloat(1.5)})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
|
||||
err = session.Commit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Expected SQL: SELECT count(*) FROM `product_count` WHERE price>?
|
||||
// Wrong SQL: SELECT count(*) FROM `product_count` WHERE price>? AND `price`=?
|
||||
total, err := testEngine.Where(builder.Gt{"price": decimal.NewFromInt(1)}).Count(new(ProductCount))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/internal/utils"
|
||||
@@ -1306,3 +1307,39 @@ func TestFindInMaxID(t *testing.T) {
|
||||
err := testEngine.In("id", builder.Select("max(id)").From(testEngine.Quote(tableName))).Find(&res)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestFindStructWithDecimal(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
type ProductFind struct {
|
||||
Uid int `xorm:"pk autoincr"`
|
||||
Price decimal.Decimal `xorm:"decimal(35,30)"`
|
||||
}
|
||||
assert.NoError(t, testEngine.Sync(new(ProductFind)))
|
||||
|
||||
session := testEngine.NewSession()
|
||||
defer session.Close()
|
||||
var err error
|
||||
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
|
||||
err = session.Begin()
|
||||
assert.NoError(t, err)
|
||||
_, err = session.Exec("SET IDENTITY_INSERT `product_find` ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
cnt, err := session.Insert(&ProductFind{Uid: 2, Price: decimal.NewFromFloat(0.8)})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
cnt, err = session.Insert(&ProductFind{Uid: 3, Price: decimal.NewFromFloat(1.5)})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
|
||||
err = session.Commit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Expected SQL: SELECT `uid`, `price` FROM `product_find` WHERE price>?
|
||||
// Wrong SQL: SELECT `uid`, `price` FROM `product_find` WHERE price>? AND `price`=?
|
||||
products := make([]*ProductFind, 0)
|
||||
err = testEngine.Where(builder.Gt{"price": decimal.NewFromInt(1)}).Find(&products)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int(1), len(products))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/convert"
|
||||
@@ -1021,3 +1022,39 @@ func TestGetBytesVars(t *testing.T) {
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, gbv.Id, myID)
|
||||
}
|
||||
|
||||
func TestGetStructWithDecimal(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
type ProductGet struct {
|
||||
Uid int `xorm:"pk autoincr"`
|
||||
Price decimal.Decimal `xorm:"decimal(35,30)"`
|
||||
}
|
||||
assert.NoError(t, testEngine.Sync(new(ProductGet)))
|
||||
|
||||
session := testEngine.NewSession()
|
||||
defer session.Close()
|
||||
var err error
|
||||
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
|
||||
err = session.Begin()
|
||||
assert.NoError(t, err)
|
||||
_, err = session.Exec("SET IDENTITY_INSERT `product_get` ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
cnt, err := session.Insert(&ProductGet{Uid: 2, Price: decimal.NewFromFloat(0.8)})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
cnt, err = session.Insert(&ProductGet{Uid: 3, Price: decimal.NewFromFloat(1.5)})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
if testEngine.Dialect().URI().DBType == schemas.MSSQL {
|
||||
err = session.Commit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Expected SQL: SELECT `uid`, `price` FROM `product_get` WHERE price>? LIMIT 1
|
||||
// Wrong SQL: SELECT `uid`, `price` FROM `product_get` WHERE price>? AND `price`=? LIMIT 1
|
||||
product := new(ProductGet)
|
||||
exist, err := testEngine.Where(builder.Gt{"price": decimal.NewFromInt(1)}).Get(product)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exist)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user