diff --git a/dialects/sqlite/sqlite.go b/dialects/sqlite/sqlite.go index ecc12fe7..abde9e64 100644 --- a/dialects/sqlite/sqlite.go +++ b/dialects/sqlite/sqlite.go @@ -3,7 +3,9 @@ package sqlite import ( "bytes" "database/sql" + "errors" "fmt" + "reflect" "github.com/jinzhu/gorm" "github.com/jinzhu/gorm/dialects/common/sqlbuilder" @@ -157,9 +159,90 @@ func (dialect *Dialect) Query(tx *gorm.DB) (err error) { _, err = builder.SQL.WriteTo(s) args = append(args, builder.Args) } + + rows, err := dialect.DB.Query(s.String(), args) + + if err == nil { + err = scanRows(rows, tx.Statement.Dest) + } + return } +func scanRows(rows *sql.Rows, values interface{}) (err error) { + var ( + isSlice bool + results = indirect(reflect.ValueOf(values)) + ) + columns, err := rows.Columns() + + if kind := results.Kind(); kind == reflect.Slice { + isSlice = true + resultType := results.Type().Elem() + results.Set(reflect.MakeSlice(resultType, 0, 0)) + } else if kind != reflect.Struct || kind != reflect.Map { + return errors.New("unsupported destination, should be slice or map or struct") + } + + for rows.Next() { + elem := results + if isSlice { + elem = reflect.New(results.Type().Elem()).Elem() + } + + dests, err := toScanMap(columns, elem) + + if err == nil { + err = rows.Scan(dests...) + } + + if err != nil { + return err + } + + if isSlice { + results.Set(reflect.Append(results, elem)) + } + } + + return +} + +func toScanMap(columns []string, elem reflect.Value) (results []interface{}, err error) { + results = make([]interface{}, len(columns)) + + switch elem.Kind() { + case reflect.Map: + for idx, column := range columns { + var value interface{} + elem.SetMapIndex(reflect.ValueOf(column), reflect.ValueOf(value)) + results[idx] = &value + } + case reflect.Struct: + fieldsMap := model.Parse(elem.Interface()).FieldsMap() + for idx, column := range columns { + if f, ok := fieldsMap[column]; ok { + results[idx] = f.Value.Addr().Interface() + } + } + case reflect.Ptr: + if elem.IsNil() { + elem.Set(reflect.New(elem.Type().Elem())) + } + return toScanMap(columns, elem) + default: + return nil, errors.New("unsupported destination") + } + return +} + +func indirect(reflectValue reflect.Value) reflect.Value { + for reflectValue.Kind() == reflect.Ptr { + reflectValue = reflectValue.Elem() + } + return reflectValue +} + // Update update func (dialect *Dialect) Update(tx *gorm.DB) (err error) { var ( diff --git a/model/model.go b/model/model.go index f8637058..36808b84 100644 --- a/model/model.go +++ b/model/model.go @@ -16,6 +16,37 @@ import ( // } var DefaultTableNameHandler func(tx *gorm.DB, tableName string) string +// Parse parse model +func Parse(value interface{}) *Model { + return &Model{ + ReflectValue: reflect.ValueOf(value), + Schema: schema.Parse(value), + } +} + +// Model model struct +type Model struct { + ReflectValue reflect.Value + Schema *schema.Schema +} + +// FieldsMap fields map +func (model *Model) FieldsMap() map[string]*Field { + fieldsMap := map[string]*Field{} + + for _, sf := range model.Schema.Fields { + obj := model.ReflectValue + for _, bn := range sf.BindNames { + obj = obj.FieldByName(bn) + } + field := &Field{Field: sf, Value: obj} + + fieldsMap[sf.DBName] = field + } + + return fieldsMap +} + // Field GORM model field type Field struct { *schema.Field diff --git a/model/utils.go b/model/utils.go index 700f7087..50b9c356 100644 --- a/model/utils.go +++ b/model/utils.go @@ -2,6 +2,7 @@ package model import "reflect" +// IsBlank check value is blank or not func IsBlank(value reflect.Value) bool { switch value.Kind() { case reflect.String: