diff --git a/engine.go b/engine.go index 54ed387c..a27b6bdf 100644 --- a/engine.go +++ b/engine.go @@ -217,10 +217,15 @@ func (engine *Engine) NoCascade() *Session { } // MapCacher Set a table use a special cacher -func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { +func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error { v := rValue(bean) - tb := engine.autoMapType(v) + tb, err := engine.autoMapType(v) + if err != nil { + return err + } + tb.Cacher = cacher + return nil } // NewDB provides an interface to operate database directly @@ -776,7 +781,7 @@ func (engine *Engine) Having(conditions string) *Session { return session.Having(conditions) } -func (engine *Engine) autoMapType(v reflect.Value) *core.Table { +func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) { t := v.Type() engine.mutex.Lock() defer engine.mutex.Unlock() @@ -785,24 +790,23 @@ func (engine *Engine) autoMapType(v reflect.Value) *core.Table { var err error table, err = engine.mapType(v) if err != nil { - engine.logger.Error(err) - } else { - engine.Tables[t] = table - if engine.Cacher != nil { - if v.CanAddr() { - engine.GobRegister(v.Addr().Interface()) - } else { - engine.GobRegister(v.Interface()) - } + return nil, err + } + + engine.Tables[t] = table + if engine.Cacher != nil { + if v.CanAddr() { + engine.GobRegister(v.Addr().Interface()) + } else { + engine.GobRegister(v.Interface()) } } } - return table + return table, nil } // GobRegister register one struct to gob for cache use func (engine *Engine) GobRegister(v interface{}) *Engine { - //fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v) gob.Register(v) return engine } @@ -813,10 +817,19 @@ type Table struct { Name string } +// IsValid if table is valid +func (t *Table) IsValid() bool { + return t.Table != nil && len(t.Name) > 0 +} + // TableInfo get table info according to bean's content func (engine *Engine) TableInfo(bean interface{}) *Table { v := rValue(bean) - return &Table{engine.autoMapType(v), engine.tbName(v)} + tb, err := engine.autoMapType(v) + if err != nil { + engine.logger.Error(err) + } + return &Table{tb, engine.tbName(v)} } func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { @@ -1066,8 +1079,21 @@ func (engine *Engine) IdOfV(rv reflect.Value) core.PK { // IDOfV get id from one value of struct func (engine *Engine) IDOfV(rv reflect.Value) core.PK { + pk, err := engine.idOfV(rv) + if err != nil { + engine.logger.Error(err) + return nil + } + return pk +} + +func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) { v := reflect.Indirect(rv) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + if err != nil { + return nil, err + } + pk := make([]interface{}, len(table.PrimaryKeys)) for i, col := range table.PKColumns() { pkField := v.FieldByName(col.FieldName) @@ -1080,7 +1106,7 @@ func (engine *Engine) IDOfV(rv reflect.Value) core.PK { pk[i] = pkField.Uint() } } - return core.PK(pk) + return core.PK(pk), nil } // CreateIndexes create indexes @@ -1101,13 +1127,6 @@ func (engine *Engine) getCacher2(table *core.Table) core.Cacher { return table.Cacher } -func (engine *Engine) getCacher(v reflect.Value) core.Cacher { - if table := engine.autoMapType(v); table != nil { - return table.Cacher - } - return engine.Cacher -} - // ClearCacheBean if enabled cache, clear the cache bean func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { v := rValue(bean) @@ -1116,7 +1135,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { return errors.New("error params") } tableName := engine.tbName(v) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + if err != nil { + return err + } cacher := table.Cacher if cacher == nil { cacher = engine.Cacher @@ -1137,7 +1159,11 @@ func (engine *Engine) ClearCache(beans ...interface{}) error { return errors.New("error params") } tableName := engine.tbName(v) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + if err != nil { + return err + } + cacher := table.Cacher if cacher == nil { cacher = engine.Cacher @@ -1157,7 +1183,11 @@ func (engine *Engine) Sync(beans ...interface{}) error { for _, bean := range beans { v := rValue(bean) tableName := engine.tbName(v) - table := engine.autoMapType(v) + table, err := engine.autoMapType(v) + fmt.Println(v, table, err) + if err != nil { + return err + } s := engine.NewSession() defer s.Close() diff --git a/session.go b/session.go index 186daeaa..2e9eb9d8 100644 --- a/session.go +++ b/session.go @@ -606,40 +606,39 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i } } } else if session.Statement.UseCascade { - table := session.Engine.autoMapType(*fieldValue) - if table != nil { - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - panic("unsupported non or composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - var err error - pk[0], err = asKind(vv, rawValueType) + table, err := session.Engine.autoMapType(*fieldValue) + if err != nil { + return nil, err + } + + hasAssigned = true + if len(table.PrimaryKeys) != 1 { + panic("unsupported non or composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + pk[0], err = asKind(vv, rawValueType) + if err != nil { + return nil, err + } + + 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(pk).NoCascade().Get(structInter.Interface()) if err != nil { return nil, err } - - 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(pk).NoCascade().Get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - //v := structInter.Elem().Interface() - //fieldValue.Set(reflect.ValueOf(v)) - fieldValue.Set(structInter.Elem()) - } else { - return nil, errors.New("cascade obj is not exist") - } + if has { + //v := structInter.Elem().Interface() + //fieldValue.Set(reflect.ValueOf(v)) + fieldValue.Set(structInter.Elem()) + } else { + return nil, errors.New("cascade obj is not exist") } - } else { - session.Engine.logger.Error("unsupported struct type in Scan: ", fieldValue.Type().String()) } } case reflect.Ptr: diff --git a/session_convert.go b/session_convert.go index 36ab465f..7ef57b5f 100644 --- a/session_convert.go +++ b/session_convert.go @@ -208,40 +208,39 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, v = x fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) } else if session.Statement.UseCascade { - table := session.Engine.autoMapType(*fieldValue) - if table != nil { - // TODO: current only support 1 primary key - if len(table.PrimaryKeys) > 1 { - panic("unsupported composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - var err error - pk[0], err = str2PK(string(data), rawValueType) + table, err := session.Engine.autoMapType(*fieldValue) + if err != nil { + return err + } + + // TODO: current only support 1 primary key + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + pk[0], err = str2PK(string(data), rawValueType) + if err != nil { + return err + } + + 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(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } - - 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(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") - } + if has { + v = structInter.Elem().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()) } } } @@ -493,35 +492,36 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, default: if session.Statement.UseCascade { structInter := reflect.New(fieldType.Elem()) - table := session.Engine.autoMapType(structInter.Elem()) - if table != nil { - if len(table.PrimaryKeys) > 1 { - panic("unsupported composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - var err error - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - pk[0], err = str2PK(string(data), rawValueType) + table, err := session.Engine.autoMapType(structInter.Elem()) + if err != nil { + return err + } + + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + pk[0], err = str2PK(string(data), rawValueType) + if err != nil { + return err + } + + 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(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } - - 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(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") - } + if has { + v = structInter.Interface() + fieldValue.Set(reflect.ValueOf(v)) + } else { + return errors.New("cascade obj is not exist") } } } else { @@ -603,7 +603,10 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val return v.Value() } - fieldTable := session.Engine.autoMapType(fieldValue) + fieldTable, err := session.Engine.autoMapType(fieldValue) + if err != nil { + return nil, err + } if len(fieldTable.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) return pkField.Interface(), nil diff --git a/session_find.go b/session_find.go index 748e319f..61862edd 100644 --- a/session_find.go +++ b/session_find.go @@ -234,7 +234,11 @@ func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Va if elemType.Kind() == reflect.Struct { var newValue = newElemFunc(fields) dataStruct := rValue(newValue.Interface()) - return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, containerValueSetFunc) + tb, err := session.Engine.autoMapType(dataStruct) + if err != nil { + return err + } + return session.rows2Beans(rawRows, fields, len(fields), tb, newElemFunc, containerValueSetFunc) } for rawRows.Next() { @@ -407,7 +411,10 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in if rv.Kind() != reflect.Ptr { rv = rv.Addr() } - id := session.Engine.IdOfV(rv) + id, err := session.Engine.idOfV(rv) + if err != nil { + return err + } sid, err := id.ToString() if err != nil { return err diff --git a/statement.go b/statement.go index 71e9a7fe..d411f739 100644 --- a/statement.go +++ b/statement.go @@ -207,9 +207,14 @@ func (statement *Statement) NotIn(column string, args ...interface{}) *Statement return statement } -func (statement *Statement) setRefValue(v reflect.Value) { - statement.RefTable = statement.Engine.autoMapType(reflect.Indirect(v)) +func (statement *Statement) setRefValue(v reflect.Value) error { + var err error + statement.RefTable, err = statement.Engine.autoMapType(reflect.Indirect(v)) + if err != nil { + return err + } statement.tableName = statement.Engine.tbName(v) + return nil } // Table tempororily set table name, the parameter could be a string or a pointer of struct @@ -219,7 +224,12 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { if t.Kind() == reflect.String { statement.AltTableName = tableNameOrBean.(string) } else if t.Kind() == reflect.Struct { - statement.RefTable = statement.Engine.autoMapType(v) + var err error + statement.RefTable, err = statement.Engine.autoMapType(v) + if err != nil { + statement.Engine.logger.Error(err) + return statement + } statement.AltTableName = statement.Engine.tbName(v) } return statement