1
0
mirror of https://github.com/astaxie/beego.git synced 2024-06-02 09:23:27 +00:00

add support for sql.Null* types

Change instructions for sqlite3 tests to use in memory db for much faster
This commit is contained in:
Daniel Hobe 2014-02-27 19:53:35 -08:00 committed by asta.xie
parent 57afd3d979
commit 9f3af59250
4 changed files with 167 additions and 60 deletions

111
orm/db.go
View File

@ -103,15 +103,36 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
} else { } else {
switch fi.fieldType { switch fi.fieldType {
case TypeBooleanField: case TypeBooleanField:
value = field.Bool() if nb, ok := field.Interface().(sql.NullBool); ok {
case TypeCharField, TypeTextField: value = nil
value = field.String() if nb.Valid {
case TypeFloatField, TypeDecimalField: value = nb.Bool
vu := field.Interface() }
if _, ok := vu.(float32); ok {
value, _ = StrTo(ToStr(vu)).Float64()
} else { } else {
value = field.Float() value = field.Bool()
}
case TypeCharField, TypeTextField:
if ns, ok := field.Interface().(sql.NullString); ok {
value = nil
if ns.Valid {
value = ns.String
}
} else {
value = field.String()
}
case TypeFloatField, TypeDecimalField:
if nf, ok := field.Interface().(sql.NullFloat64); ok {
value = nil
if nf.Valid {
value = nf.Float64
}
} else {
vu := field.Interface()
if _, ok := vu.(float32); ok {
value, _ = StrTo(ToStr(vu)).Float64()
} else {
value = field.Float()
}
} }
case TypeDateField, TypeDateTimeField: case TypeDateField, TypeDateTimeField:
value = field.Interface() value = field.Interface()
@ -124,7 +145,14 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
case fi.fieldType&IsPostiveIntegerField > 0: case fi.fieldType&IsPostiveIntegerField > 0:
value = field.Uint() value = field.Uint()
case fi.fieldType&IsIntegerField > 0: case fi.fieldType&IsIntegerField > 0:
value = field.Int() if ni, ok := field.Interface().(sql.NullInt64); ok {
value = nil
if ni.Valid {
value = ni.Int64
}
} else {
value = field.Int()
}
case fi.fieldType&IsRelField > 0: case fi.fieldType&IsRelField > 0:
if field.IsNil() { if field.IsNil() {
value = nil value = nil
@ -1122,17 +1150,37 @@ setValue:
switch { switch {
case fieldType == TypeBooleanField: case fieldType == TypeBooleanField:
if isNative { if isNative {
if value == nil { if nb, ok := field.Interface().(sql.NullBool); ok {
value = false if value == nil {
nb.Valid = false
} else {
nb.Bool = value.(bool)
nb.Valid = true
}
field.Set(reflect.ValueOf(nb))
} else {
if value == nil {
value = false
}
field.SetBool(value.(bool))
} }
field.SetBool(value.(bool))
} }
case fieldType == TypeCharField || fieldType == TypeTextField: case fieldType == TypeCharField || fieldType == TypeTextField:
if isNative { if isNative {
if value == nil { if ns, ok := field.Interface().(sql.NullString); ok {
value = "" if value == nil {
ns.Valid = false
} else {
ns.String = value.(string)
ns.Valid = true
}
field.Set(reflect.ValueOf(ns))
} else {
if value == nil {
value = ""
}
field.SetString(value.(string))
} }
field.SetString(value.(string))
} }
case fieldType == TypeDateField || fieldType == TypeDateTimeField: case fieldType == TypeDateField || fieldType == TypeDateTimeField:
if isNative { if isNative {
@ -1151,18 +1199,39 @@ setValue:
} }
} else { } else {
if isNative { if isNative {
if value == nil { if ni, ok := field.Interface().(sql.NullInt64); ok {
value = int64(0) if value == nil {
ni.Valid = false
} else {
ni.Int64 = value.(int64)
ni.Valid = true
}
field.Set(reflect.ValueOf(ni))
} else {
if value == nil {
value = int64(0)
}
field.SetInt(value.(int64))
} }
field.SetInt(value.(int64))
} }
} }
case fieldType == TypeFloatField || fieldType == TypeDecimalField: case fieldType == TypeFloatField || fieldType == TypeDecimalField:
if isNative { if isNative {
if value == nil { if nf, ok := field.Interface().(sql.NullFloat64); ok {
value = float64(0) if value == nil {
nf.Valid = false
} else {
nf.Float64 = value.(float64)
nf.Valid = true
}
field.Set(reflect.ValueOf(nf))
} else {
if value == nil {
value = float64(0)
}
field.SetFloat(value.(float64))
} }
field.SetFloat(value.(float64))
} }
case fieldType&IsRelField > 0: case fieldType&IsRelField > 0:
if value != nil { if value != nil {

View File

@ -1,6 +1,7 @@
package orm package orm
import ( import (
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
@ -116,27 +117,31 @@ type Data struct {
} }
type DataNull struct { type DataNull struct {
Id int Id int
Boolean bool `orm:"null"` Boolean bool `orm:"null"`
Char string `orm:"null;size(50)"` Char string `orm:"null;size(50)"`
Text string `orm:"null;type(text)"` Text string `orm:"null;type(text)"`
Date time.Time `orm:"null;type(date)"` Date time.Time `orm:"null;type(date)"`
DateTime time.Time `orm:"null;column(datetime)""` DateTime time.Time `orm:"null;column(datetime)""`
Byte byte `orm:"null"` Byte byte `orm:"null"`
Rune rune `orm:"null"` Rune rune `orm:"null"`
Int int `orm:"null"` Int int `orm:"null"`
Int8 int8 `orm:"null"` Int8 int8 `orm:"null"`
Int16 int16 `orm:"null"` Int16 int16 `orm:"null"`
Int32 int32 `orm:"null"` Int32 int32 `orm:"null"`
Int64 int64 `orm:"null"` Int64 int64 `orm:"null"`
Uint uint `orm:"null"` Uint uint `orm:"null"`
Uint8 uint8 `orm:"null"` Uint8 uint8 `orm:"null"`
Uint16 uint16 `orm:"null"` Uint16 uint16 `orm:"null"`
Uint32 uint32 `orm:"null"` Uint32 uint32 `orm:"null"`
Uint64 uint64 `orm:"null"` Uint64 uint64 `orm:"null"`
Float32 float32 `orm:"null"` Float32 float32 `orm:"null"`
Float64 float64 `orm:"null"` Float64 float64 `orm:"null"`
Decimal float64 `orm:"digits(8);decimals(4);null"` Decimal float64 `orm:"digits(8);decimals(4);null"`
NullString sql.NullString `orm:"null"`
NullBool sql.NullBool `orm:"null"`
NullFloat64 sql.NullFloat64 `orm:"null"`
NullInt64 sql.NullInt64 `orm:"null"`
} }
// only for mysql // only for mysql
@ -303,9 +308,8 @@ go test -v github.com/astaxie/beego/orm
#### Sqlite3 #### Sqlite3
touch /path/to/orm_test.db
export ORM_DRIVER=sqlite3 export ORM_DRIVER=sqlite3
export ORM_SOURCE=/path/to/orm_test.db export ORM_SOURCE='file:memory_test?mode=memory'
go test -v github.com/astaxie/beego/orm go test -v github.com/astaxie/beego/orm

View File

@ -1,6 +1,7 @@
package orm package orm
import ( import (
"database/sql"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
@ -98,30 +99,29 @@ func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col
// return field type as type constant from reflect.Value // return field type as type constant from reflect.Value
func getFieldType(val reflect.Value) (ft int, err error) { func getFieldType(val reflect.Value) (ft int, err error) {
elm := reflect.Indirect(val) elm := reflect.Indirect(val)
switch elm.Kind() { switch elm.Interface().(type) {
case reflect.Int8: case int8:
ft = TypeBitField ft = TypeBitField
case reflect.Int16: case int16:
ft = TypeSmallIntegerField ft = TypeSmallIntegerField
case reflect.Int32, reflect.Int: case int32, int:
ft = TypeIntegerField ft = TypeIntegerField
case reflect.Int64: case int64, sql.NullInt64:
ft = TypeBigIntegerField ft = TypeBigIntegerField
case reflect.Uint8: case uint8:
ft = TypePositiveBitField ft = TypePositiveBitField
case reflect.Uint16: case uint16:
ft = TypePositiveSmallIntegerField ft = TypePositiveSmallIntegerField
case reflect.Uint32, reflect.Uint: case uint32, uint:
ft = TypePositiveIntegerField ft = TypePositiveIntegerField
case reflect.Uint64: case uint64:
ft = TypePositiveBigIntegerField ft = TypePositiveBigIntegerField
case reflect.Float32, reflect.Float64: case float32, float64, sql.NullFloat64:
ft = TypeFloatField ft = TypeFloatField
case reflect.Bool: case bool, sql.NullBool:
ft = TypeBooleanField ft = TypeBooleanField
case reflect.String: case string, sql.NullString:
ft = TypeCharField ft = TypeCharField
case reflect.Invalid:
default: default:
if elm.CanInterface() { if elm.CanInterface() {
if _, ok := elm.Interface().(time.Time); ok { if _, ok := elm.Interface().(time.Time); ok {

View File

@ -2,6 +2,7 @@ package orm
import ( import (
"bytes" "bytes"
"database/sql"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -258,12 +259,45 @@ func TestNullDataTypes(t *testing.T) {
err = dORM.Read(&d) err = dORM.Read(&d)
throwFail(t, err) throwFail(t, err)
throwFail(t, AssertIs(d.NullBool.Valid, false))
throwFail(t, AssertIs(d.NullString.Valid, false))
throwFail(t, AssertIs(d.NullInt64.Valid, false))
throwFail(t, AssertIs(d.NullFloat64.Valid, false))
_, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec() _, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec()
throwFail(t, err) throwFail(t, err)
d = DataNull{Id: 2} d = DataNull{Id: 2}
err = dORM.Read(&d) err = dORM.Read(&d)
throwFail(t, err) throwFail(t, err)
d = DataNull{
DateTime: time.Now(),
NullString: sql.NullString{"test", true},
NullBool: sql.NullBool{true, true},
NullInt64: sql.NullInt64{42, true},
NullFloat64: sql.NullFloat64{42.42, true},
}
id, err = dORM.Insert(&d)
throwFail(t, err)
throwFail(t, AssertIs(id, 3))
d = DataNull{Id: 3}
err = dORM.Read(&d)
throwFail(t, err)
throwFail(t, AssertIs(d.NullBool.Valid, true))
throwFail(t, AssertIs(d.NullBool.Bool, true))
throwFail(t, AssertIs(d.NullString.Valid, true))
throwFail(t, AssertIs(d.NullString.String, "test"))
throwFail(t, AssertIs(d.NullInt64.Valid, true))
throwFail(t, AssertIs(d.NullInt64.Int64, 42))
throwFail(t, AssertIs(d.NullFloat64.Valid, true))
throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42))
} }
func TestCRUD(t *testing.T) { func TestCRUD(t *testing.T) {
@ -1646,10 +1680,10 @@ func TestTransaction(t *testing.T) {
func TestReadOrCreate(t *testing.T) { func TestReadOrCreate(t *testing.T) {
u := &User{ u := &User{
UserName: "Kyle", UserName: "Kyle",
Email: "kylemcc@gmail.com", Email: "kylemcc@gmail.com",
Password: "other_pass", Password: "other_pass",
Status: 7, Status: 7,
IsStaff: false, IsStaff: false,
IsActive: true, IsActive: true,
} }