From 5cdb6809450864cb3381fa1a40b04eec6a273062 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 22 Feb 2015 23:52:53 +0800 Subject: [PATCH] add cascade non-int64 primary key support & bug fixed #178 --- engine.go | 8 +- helpers.go | 37 +++++++++ mysql_dialect.go | 3 + session.go | 208 ++++++++++++++++++++++++++++++++++++++++++----- statement.go | 2 + 5 files changed, 236 insertions(+), 22 deletions(-) diff --git a/engine.go b/engine.go index 1120b8c3..0a97d663 100644 --- a/engine.go +++ b/engine.go @@ -990,8 +990,12 @@ func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) { } func (engine *Engine) IdOf(bean interface{}) core.PK { - table := engine.TableInfo(bean) - v := reflect.Indirect(reflect.ValueOf(bean)) + return engine.IdOfV(reflect.ValueOf(bean)) +} + +func (engine *Engine) IdOfV(rv reflect.Value) core.PK { + v := reflect.Indirect(rv) + table := engine.autoMapType(v) pk := make([]interface{}, len(table.PrimaryKeys)) for i, col := range table.PKColumns() { pkField := v.FieldByName(col.FieldName) diff --git a/helpers.go b/helpers.go index 355db543..353f509c 100644 --- a/helpers.go +++ b/helpers.go @@ -11,6 +11,43 @@ import ( "github.com/go-xorm/core" ) +func isZero(k interface{}) bool { + switch k.(type) { + case int: + return k.(int) == 0 + case int8: + return k.(int8) == 0 + case int16: + return k.(int16) == 0 + case int32: + return k.(int32) == 0 + case int64: + return k.(int64) == 0 + case uint: + return k.(uint) == 0 + case uint8: + return k.(uint8) == 0 + case uint16: + return k.(uint16) == 0 + case uint32: + return k.(uint32) == 0 + case uint64: + return k.(uint64) == 0 + case string: + return k.(string) == "" + } + return false +} + +func isPKZero(pk core.PK) bool { + for _, k := range pk { + if isZero(k) { + return true + } + } + return false +} + func indexNoCase(s, sep string) int { return strings.Index(strings.ToLower(s), strings.ToLower(sep)) } diff --git a/mysql_dialect.go b/mysql_dialect.go index 1a6019c6..602cd0ec 100644 --- a/mysql_dialect.go +++ b/mysql_dialect.go @@ -218,6 +218,9 @@ func (db *mysql) SqlType(c *core.Column) string { res += ")" case core.NVarchar: res = core.Varchar + case core.Uuid: + res = core.Varchar + c.Length = 40 default: res = t } diff --git a/session.go b/session.go index dd5ccb61..2431607e 100644 --- a/session.go +++ b/session.go @@ -648,11 +648,6 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, ErrCacheFailed } - // TODO: remove this after support multi pk cache - /*if len(session.Statement.RefTable.PrimaryKeys) != 1 { - return false, ErrCacheFailed - }*/ - for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } @@ -1775,18 +1770,45 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { - var x int64 - if rawValueType.Kind() == reflect.Int64 { - x = vv.Int() + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - if x != 0 { + var pk = make(core.PK, len(table.PrimaryKeys)) + switch rawValueType.Kind() { + case reflect.Int64: + pk[0] = vv.Int() + case reflect.Int: + pk[0] = int(vv.Int()) + case reflect.Int32: + pk[0] = int32(vv.Int()) + case reflect.Int16: + pk[0] = int16(vv.Int()) + case reflect.Int8: + pk[0] = int8(vv.Int()) + case reflect.Uint64: + pk[0] = vv.Uint() + case reflect.Uint: + pk[0] = uint(vv.Uint()) + case reflect.Uint32: + pk[0] = uint32(vv.Uint()) + case reflect.Uint16: + pk[0] = uint16(vv.Uint()) + case reflect.Uint8: + pk[0] = uint8(vv.Uint()) + case reflect.String: + pk[0] = vv.String() + default: + panic("unsupported primary key type cascade") + } + + if !isPKZero(pk) { // !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()) newsession := session.Engine.NewSession() defer newsession.Close() - has, err := newsession.Id(x).Get(structInter.Interface()) + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } @@ -2415,18 +2437,86 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - if x != 0 { + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + switch rawValueType.Kind() { + case reflect.Int64: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = x + case reflect.Int: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int(x) + case reflect.Int32: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int32(x) + case reflect.Int16: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int16(x) + case reflect.Int8: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int8(x) + case reflect.Uint64: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = x + case reflect.Uint: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint(x) + case reflect.Uint32: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint32(x) + case reflect.Uint16: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint16(x) + case reflect.Uint8: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint8(x) + case reflect.String: + pk[0] = string(data) + default: + panic("unsupported primary key type cascade") + } + + if !isPKZero(pk) { // !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()) newsession := session.Engine.NewSession() defer newsession.Close() - has, err := newsession.Id(x).Get(structInter.Interface()) + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } @@ -2690,17 +2780,95 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, structInter := reflect.New(fieldType.Elem()) table := session.Engine.autoMapType(structInter.Elem()) if table != nil { - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - if x != 0 { + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + switch rawValueType.Kind() { + case reflect.Int64: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = x + case reflect.Int: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = int(x) + case reflect.Int32: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = int32(x) + case reflect.Int16: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = int16(x) + case reflect.Int8: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = x + case reflect.Uint64: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = x + case reflect.Uint: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint(x) + case reflect.Uint32: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint32(x) + case reflect.Uint16: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint16(x) + case reflect.Uint8: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint8(x) + case reflect.String: + pk[0] = string(data) + default: + panic("unsupported primary key type cascade") + } + + if !isPKZero(pk) { // !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 newsession := session.Engine.NewSession() defer newsession.Close() - has, err := newsession.Id(x).Get(structInter.Interface()) + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } diff --git a/statement.go b/statement.go index 318a2a7b..75501f40 100644 --- a/statement.go +++ b/statement.go @@ -589,6 +589,8 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, continue } val = engine.FormatTime(col.SQLType.Name, t) + } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok { + continue } else { engine.autoMapType(fieldValue) if table, ok := engine.Tables[fieldValue.Type()]; ok {