Skip to content

Commit

Permalink
reflection-free AppendRow for plain arrays (#1130)
Browse files Browse the repository at this point in the history
  • Loading branch information
cergxx authored Oct 26, 2023
1 parent cdbdd90 commit 74f4e2d
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 9 deletions.
47 changes: 42 additions & 5 deletions lib/column/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ func (col *Array) Append(v any) (nulls []uint8, err error) {
}

func (col *Array) AppendRow(v any) error {
if col.depth == 1 {
// try to use reflection-free method.
return col.appendRowPlain(v)
}
return col.appendRowDefault(v)
}

func (col *Array) appendRowDefault(v any) error {
var elem reflect.Value
switch v := v.(type) {
case reflect.Value:
Expand All @@ -155,13 +163,35 @@ func (col *Array) AppendRow(v any) error {
return col.append(elem, 0)
}

func appendRowPlain[T any](col *Array, arr []T) error {
col.appendOffset(0, uint64(len(arr)))
for _, item := range arr {
if err := col.values.AppendRow(item); err != nil {
return err
}
}
return nil
}

func appendNullableRowPlain[T any](col *Array, arr []*T) error {
col.appendOffset(0, uint64(len(arr)))
for _, item := range arr {
var err error
if item == nil {
err = col.values.AppendRow(nil)
} else {
err = col.values.AppendRow(item)
}
if err != nil {
return err
}
}
return nil
}

func (col *Array) append(elem reflect.Value, level int) error {
if level < col.depth {
offset := uint64(elem.Len())
if ln := col.offsets[level].values.Rows(); ln != 0 {
offset += col.offsets[level].values.col.Row(ln - 1)
}
col.offsets[level].values.col.Append(offset)
col.appendOffset(level, uint64(elem.Len()))
for i := 0; i < elem.Len(); i++ {
if err := col.append(elem.Index(i), level+1); err != nil {
return err
Expand All @@ -175,6 +205,13 @@ func (col *Array) append(elem reflect.Value, level int) error {
return col.values.AppendRow(elem.Interface())
}

func (col *Array) appendOffset(level int, offset uint64) {
if ln := col.offsets[level].values.Rows(); ln != 0 {
offset += col.offsets[level].values.col.Row(ln - 1)
}
col.offsets[level].values.col.Append(offset)
}

func (col *Array) Decode(reader *proto.Reader, rows int) error {
for _, offset := range col.offsets {
if err := offset.values.col.DecodeColumn(reader, rows); err != nil {
Expand Down
161 changes: 161 additions & 0 deletions lib/column/array_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions lib/column/codegen/array.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to ClickHouse, Inc. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. ClickHouse, Inc. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// Code generated by make codegen DO NOT EDIT.
// source: lib/column/codegen/array.tpl

package column

import (
"database/sql"
"github.com/ClickHouse/ch-go/proto"
"github.com/google/uuid"
"github.com/paulmach/orb"
"github.com/shopspring/decimal"
"math/big"
"net"
"net/netip"
"time"
)

// appendRowPlain is a reflection-free realisation of append for plain arrays.
func (col *Array) appendRowPlain(v any) error {
switch tv := v.(type) {
{{- range . }}
case []{{ . }}:
return appendRowPlain(col, tv)
case []*{{ . }}:
return appendNullableRowPlain(col, tv)
{{- end }}
default:
return col.appendRowDefault(v)
}
}
28 changes: 24 additions & 4 deletions lib/column/codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ import (
var (
//go:embed column.tpl
columnSrc string
//go:embed array.tpl
arraySrc string
)
var (
types []_type
types []_type
supportedGoTypes []string
)

type _type struct {
Expand Down Expand Up @@ -65,6 +68,19 @@ func init() {
sort.Slice(types, func(i, j int) bool {
return sequenceKey(types[i].ChType) < sequenceKey(types[j].ChType)
})

for _, typ := range types {
supportedGoTypes = append(supportedGoTypes, typ.GoType)
}
supportedGoTypes = append(supportedGoTypes,
"string", "[]byte", "sql.NullString",
"int", "uint", "big.Int", "decimal.Decimal",
"bool", "sql.NullBool",
"time.Time", "sql.NullTime",
"uuid.UUID",
"netip.Addr", "net.IP", "proto.IPv6", "[16]byte",
"orb.MultiPolygon", "orb.Point", "orb.Polygon", "orb.Ring",
)
}
func write(name string, v any, t *template.Template) error {
out := new(bytes.Buffer)
Expand All @@ -87,10 +103,14 @@ func write(name string, v any, t *template.Template) error {
}

func main() {
for name, tpl := range map[string]*template.Template{
"column_gen": template.Must(template.New("column").Parse(columnSrc)),
for name, tpl := range map[string]struct {
template *template.Template
args any
}{
"column_gen": {template.Must(template.New("column").Parse(columnSrc)), types},
"array_gen": {template.Must(template.New("array").Parse(arraySrc)), supportedGoTypes},
} {
if err := write(name, types, tpl); err != nil {
if err := write(name, tpl.args, tpl.template); err != nil {
log.Fatal(err)
}
}
Expand Down

0 comments on commit 74f4e2d

Please sign in to comment.