aboutsummaryrefslogtreecommitdiff
path: root/select.go
diff options
context:
space:
mode:
Diffstat (limited to 'select.go')
-rw-r--r--select.go45
1 files changed, 36 insertions, 9 deletions
diff --git a/select.go b/select.go
index 6838f8d..0b86ab5 100644
--- a/select.go
+++ b/select.go
@@ -27,17 +27,44 @@ func (s Store[R]) Select(ctx context.Context, db Handle, query string, args ...a
var result []R
slots := make([]any, len(indexes))
for rows.Next() {
- var target R
- err = collectRow(rows, s.plan, reflect.ValueOf(&target).Elem(), slots, indexes)
+ var target *R
+ result, target = growRecordSlice(result)
+ err = collectRow(rows, s.plan, reflect.ValueOf(target).Elem(), slots, indexes)
if err != nil {
return nil, err
}
- result = append(result, target)
}
return result, newIOError(err, "Rows.Err", rows.Err())
}
+// Appends an empty R to the slice and returns a pointer to it, as well as the updated slice.
+// It is more efficient to write:
+//
+// var result []R
+// for rows.Next() {
+// var target *R
+// result, target = growRecordSlice(result)
+// doSomethingWith(rows, reflect.ValueOf(target).Elem())
+// }
+//
+// Instead of the more obvious:
+//
+// var result []R
+// for rows.Next() {
+// var target R
+// doSomethingWith(rows, reflect.ValueOf(&target).Elem())
+// result = append(result, target)
+// }
+//
+// In the second phrasing, `target` escapes to the heap because of `reflect.ValueOf(&target)`,
+// causing an additional allocation for `target` as well as a memcpy of `target` during `append()`.
+func growRecordSlice[R any](records []R) (newRecords []R, target *R) {
+ var zero R
+ newRecords = append(records, zero)
+ return newRecords, &newRecords[len(newRecords)-1]
+}
+
// SelectWhere is like [Store.Select], but you only provide the part of the SELECT query that comes after the WHERE.
// The initial part ("SELECT ... FROM ... WHERE") is autogenerated and prepended to partialQuery.
// This has two benefits:
@@ -63,12 +90,12 @@ func (s Store[R]) SelectWhere(ctx context.Context, db Handle, partialQuery strin
var result []R
slots := make([]any, len(indexes))
for rows.Next() {
- var target R
- err = collectRow(rows, s.plan, reflect.ValueOf(&target).Elem(), slots, indexes)
+ var target *R
+ result, target = growRecordSlice(result)
+ err = collectRow(rows, s.plan, reflect.ValueOf(target).Elem(), slots, indexes)
if err != nil {
return nil, err
}
- result = append(result, target)
}
return result, newIOError(err, "Rows.Err", rows.Err())
@@ -232,12 +259,12 @@ func (q PreparedSelectQuery[R]) Select(ctx context.Context, db Handle, args ...a
var result []R
slots := make([]any, len(indexes))
for rows.Next() {
- var target R
- err = collectRow(rows, q.store.plan, reflect.ValueOf(&target).Elem(), slots, indexes)
+ var target *R
+ result, target = growRecordSlice(result)
+ err = collectRow(rows, q.store.plan, reflect.ValueOf(target).Elem(), slots, indexes)
if err != nil {
return nil, err
}
- result = append(result, target)
}
return result, newIOError(err, "Rows.Err", rows.Err())