From 1ea81bb8e0121b718fbf4fee95408222e3841d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Garcia?= Date: Mon, 23 Nov 2020 01:20:13 -0300 Subject: [PATCH] Remove gorm dependency from Query() func --- kiss_orm.go | 79 ++++++++++++++++++++++++++++++++++++------------ kiss_orm_test.go | 5 +++ 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/kiss_orm.go b/kiss_orm.go index aa96bcc..748bfae 100644 --- a/kiss_orm.go +++ b/kiss_orm.go @@ -60,24 +60,57 @@ func (c Client) Query( query string, params ...interface{}, ) error { - t := reflect.TypeOf(records) - if t.Kind() != reflect.Ptr { + slicePtr := reflect.ValueOf(records) + slicePtrType := slicePtr.Type() + if slicePtrType.Kind() != reflect.Ptr { return fmt.Errorf("kissorm: expected to receive a pointer to slice of structs, but got: %T", records) } - t = t.Elem() - if t.Kind() != reflect.Slice { - return fmt.Errorf("kissorm: expected to receive a pointer to slice of structs, but got: %T", records) - } - if t.Elem().Kind() != reflect.Struct { - return fmt.Errorf("kissorm: expected to receive a pointer to slice of structs, but got: %T", records) + sliceType := slicePtrType.Elem() + slice := slicePtr.Elem() + structType, isSliceOfPtrs, err := decodeAsSliceOfStructs(sliceType) + if err != nil { + return err } - it := c.db.Raw(query, params...) - if it.Error != nil { - return it.Error + if isSliceOfPtrs { + // Truncate the slice so there is no risk + // of overwritting records that were already saved + // on the slice: + slice = slice.Slice(0, 0) } - it = it.Scan(records) - return it.Error + + rows, err := c.db.DB().QueryContext(ctx, query, params...) + if err != nil { + return err + } + defer rows.Close() + + for idx := 0; rows.Next(); idx++ { + // Allocate new slice elements + // only if they are not already allocated: + if slice.Len() <= idx { + var elemValue reflect.Value + elemValue = reflect.New(structType) + if !isSliceOfPtrs { + elemValue = elemValue.Elem() + } + slice = reflect.Append(slice, elemValue) + } + + err = scanRows(rows, slice.Index(idx).Addr().Interface()) + if err != nil { + return err + } + } + + if rows.Err() != nil { + return rows.Err() + } + + // Update the original slice passed by reference: + slicePtr.Elem().Set(slice) + + return nil } // QueryOne queries one instance from the database, @@ -108,6 +141,9 @@ func (c Client) QueryOne( defer rows.Close() if !rows.Next() { + if rows.Err() != nil { + return rows.Err() + } return ErrRecordNotFound } @@ -134,12 +170,6 @@ func (c Client) QueryChunks( ctx context.Context, parser ChunkParser, ) error { - rows, err := c.db.DB().QueryContext(ctx, parser.Query, parser.Params...) - if err != nil { - return err - } - defer rows.Close() - fnValue := reflect.ValueOf(parser.ForEachChunk) chunkType, err := parseInputFunc(parser.ForEachChunk) if err != nil { @@ -153,6 +183,12 @@ func (c Client) QueryChunks( return err } + rows, err := c.db.DB().QueryContext(ctx, parser.Query, parser.Params...) + if err != nil { + return err + } + defer rows.Close() + var idx = 0 for rows.Next() { // Allocate new slice elements @@ -186,6 +222,11 @@ func (c Client) QueryChunks( } } + // If Next() returned false because of an error: + if rows.Err() != nil { + return rows.Err() + } + // If no rows were found or idx was reset to 0 // on the last iteration skip this last call to ForEachChunk: if idx > 0 { diff --git a/kiss_orm_test.go b/kiss_orm_test.go index fc3b9c3..500b970 100644 --- a/kiss_orm_test.go +++ b/kiss_orm_test.go @@ -37,6 +37,11 @@ func TestQuery(t *testing.T) { var users []User err := c.Query(ctx, &users, `SELECT * FROM users WHERE id=1;`) assert.Equal(t, nil, err) + assert.Equal(t, []User(nil), users) + + users = []User{} + err = c.Query(ctx, &users, `SELECT * FROM users WHERE id=1;`) + assert.Equal(t, nil, err) assert.Equal(t, []User{}, users) })