Remove gorm dependency from Query() func

pull/2/head
Vinícius Garcia 2020-11-23 01:20:13 -03:00
parent 13bd087cee
commit 1ea81bb8e0
2 changed files with 65 additions and 19 deletions

View File

@ -60,24 +60,57 @@ func (c Client) Query(
query string, query string,
params ...interface{}, params ...interface{},
) error { ) error {
t := reflect.TypeOf(records) slicePtr := reflect.ValueOf(records)
if t.Kind() != reflect.Ptr { 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) return fmt.Errorf("kissorm: expected to receive a pointer to slice of structs, but got: %T", records)
} }
t = t.Elem() sliceType := slicePtrType.Elem()
if t.Kind() != reflect.Slice { slice := slicePtr.Elem()
return fmt.Errorf("kissorm: expected to receive a pointer to slice of structs, but got: %T", records) structType, isSliceOfPtrs, err := decodeAsSliceOfStructs(sliceType)
} if err != nil {
if t.Elem().Kind() != reflect.Struct { return err
return fmt.Errorf("kissorm: expected to receive a pointer to slice of structs, but got: %T", records)
} }
it := c.db.Raw(query, params...) if isSliceOfPtrs {
if it.Error != nil { // Truncate the slice so there is no risk
return it.Error // 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, // QueryOne queries one instance from the database,
@ -108,6 +141,9 @@ func (c Client) QueryOne(
defer rows.Close() defer rows.Close()
if !rows.Next() { if !rows.Next() {
if rows.Err() != nil {
return rows.Err()
}
return ErrRecordNotFound return ErrRecordNotFound
} }
@ -134,12 +170,6 @@ func (c Client) QueryChunks(
ctx context.Context, ctx context.Context,
parser ChunkParser, parser ChunkParser,
) error { ) 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) fnValue := reflect.ValueOf(parser.ForEachChunk)
chunkType, err := parseInputFunc(parser.ForEachChunk) chunkType, err := parseInputFunc(parser.ForEachChunk)
if err != nil { if err != nil {
@ -153,6 +183,12 @@ func (c Client) QueryChunks(
return err return err
} }
rows, err := c.db.DB().QueryContext(ctx, parser.Query, parser.Params...)
if err != nil {
return err
}
defer rows.Close()
var idx = 0 var idx = 0
for rows.Next() { for rows.Next() {
// Allocate new slice elements // 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 // If no rows were found or idx was reset to 0
// on the last iteration skip this last call to ForEachChunk: // on the last iteration skip this last call to ForEachChunk:
if idx > 0 { if idx > 0 {

View File

@ -37,6 +37,11 @@ func TestQuery(t *testing.T) {
var users []User var users []User
err := c.Query(ctx, &users, `SELECT * FROM users WHERE id=1;`) err := c.Query(ctx, &users, `SELECT * FROM users WHERE id=1;`)
assert.Equal(t, nil, err) 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) assert.Equal(t, []User{}, users)
}) })