Skip to content

Commit f22c4e2

Browse files
author
Vladimir Minashkin
committed
perf(column): bulk-append fast paths for Array and LowCardinality
1 parent 9402dff commit f22c4e2

6 files changed

Lines changed: 549 additions & 15 deletions

File tree

lib/column/array.go

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package column
22

33
import (
44
"database/sql"
5+
"errors"
56
"fmt"
67
"reflect"
78
"strings"
@@ -101,6 +102,11 @@ func (col *Array) Row(i int, ptr bool) any {
101102
}
102103

103104
func (col *Array) Append(v any) (nulls []uint8, err error) {
105+
if col.depth == 1 {
106+
if handled, err := col.appendBulkPlain(v); handled || err != nil {
107+
return nil, err
108+
}
109+
}
104110
value := reflect.Indirect(reflect.ValueOf(v))
105111
if value.Kind() != reflect.Slice {
106112
return nil, &ColumnConverterError{
@@ -151,6 +157,19 @@ func (col *Array) appendRowDefault(v any) error {
151157

152158
func appendRowPlain[T any](col *Array, arr []T) error {
153159
col.appendOffset(0, uint64(len(arr)))
160+
_, err := col.values.Append(arr)
161+
if err == nil {
162+
return nil
163+
}
164+
165+
// Append rejects unknown slice types up-front via ColumnConverterError
166+
// without mutating column state. AppendRow has a reflection-based
167+
// conversion fallback that accepts e.g. []int into Array(Int64); preserve
168+
// that by retrying per-item.
169+
var convErr *ColumnConverterError
170+
if !errors.As(err, &convErr) {
171+
return err
172+
}
154173
for _, item := range arr {
155174
if err := col.values.AppendRow(item); err != nil {
156175
return err
@@ -161,14 +180,108 @@ func appendRowPlain[T any](col *Array, arr []T) error {
161180

162181
func appendNullableRowPlain[T any](col *Array, arr []*T) error {
163182
col.appendOffset(0, uint64(len(arr)))
183+
_, err := col.values.Append(arr)
184+
if err == nil {
185+
return nil
186+
}
187+
188+
var convErr *ColumnConverterError
189+
if !errors.As(err, &convErr) {
190+
return err
191+
}
192+
164193
for _, item := range arr {
165-
var err error
166194
if item == nil {
167-
err = col.values.AppendRow(nil)
168-
} else {
169-
err = col.values.AppendRow(item)
195+
if err := col.values.AppendRow(nil); err != nil {
196+
return err
197+
}
198+
continue
170199
}
171-
if err != nil {
200+
if err := col.values.AppendRow(item); err != nil {
201+
return err
202+
}
203+
}
204+
return nil
205+
206+
}
207+
208+
// appendBulkPlain is the bulk equivalent of appendRowPlain for depth-1 Array
209+
// columns. Flattens arr into a single inner Append call so the inner column's
210+
// growth/loop preamble runs once instead of N times.
211+
//
212+
// Mirrors appendRowPlain's retry semantics: inner Append rejects unknown slice
213+
// types up-front via ColumnConverterError without mutating its state, so on
214+
// that error we retry per-item with AppendRow from the flat slice.
215+
func appendBulkPlain[T any](col *Array, arr [][]T) error {
216+
if len(arr) == 0 {
217+
return nil
218+
}
219+
220+
total := 0
221+
for _, row := range arr {
222+
total += len(row)
223+
}
224+
225+
flat := make([]T, 0, total)
226+
for _, row := range arr {
227+
col.appendOffset(0, uint64(len(row)))
228+
flat = append(flat, row...)
229+
}
230+
231+
_, err := col.values.Append(flat)
232+
if err == nil {
233+
return nil
234+
}
235+
236+
var convErr *ColumnConverterError
237+
if !errors.As(err, &convErr) {
238+
return err
239+
}
240+
241+
for _, item := range flat {
242+
if err := col.values.AppendRow(item); err != nil {
243+
return err
244+
}
245+
}
246+
247+
return nil
248+
}
249+
250+
func appendNullableBulkPlain[T any](col *Array, arr [][]*T) error {
251+
if len(arr) == 0 {
252+
return nil
253+
}
254+
total := 0
255+
for _, row := range arr {
256+
total += len(row)
257+
}
258+
259+
flat := make([]*T, 0, total)
260+
for _, row := range arr {
261+
col.appendOffset(0, uint64(len(row)))
262+
flat = append(flat, row...)
263+
}
264+
265+
_, err := col.values.Append(flat)
266+
if err == nil {
267+
return nil
268+
}
269+
270+
var convErr *ColumnConverterError
271+
272+
if !errors.As(err, &convErr) {
273+
return err
274+
}
275+
276+
for _, item := range flat {
277+
if item == nil {
278+
if err := col.values.AppendRow(nil); err != nil {
279+
return err
280+
}
281+
continue
282+
}
283+
284+
if err := col.values.AppendRow(item); err != nil {
172285
return err
173286
}
174287
}

lib/column/array_gen.go

Lines changed: 139 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)