codeword/vendor/github.com/ClickHouse/clickhouse-go/lib/column/decimal.go
2024-08-27 22:09:10 +03:00

378 lines
9.4 KiB
Go

package column
import (
b "encoding/binary"
"errors"
"fmt"
"math"
"reflect"
"strconv"
"strings"
"github.com/ClickHouse/clickhouse-go/lib/binary"
)
// Table of powers of 10 for fast casting from floating types to decimal type
// representations.
var factors10 = []float64{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13,
1e14, 1e15, 1e16, 1e17, 1e18,
}
// Decimal represents Decimal(P, S) ClickHouse. Decimal is represented as
// integral. Also floating-point types are supported for query parameters.
//
// Since there is no support for int128 in Golang, decimals with precision 19
// through 38 are represented as 16 little-endian bytes.
type Decimal struct {
base
nobits int // its domain is {32, 64}
precision int
scale int
}
func (d *Decimal) Read(decoder *binary.Decoder, isNull bool) (interface{}, error) {
switch d.nobits {
case 32:
return decoder.Int32()
case 64:
return decoder.Int64()
case 128:
return decoder.Decimal128()
default:
return nil, errors.New("unachievable execution path")
}
}
func (d *Decimal) Write(encoder *binary.Encoder, v interface{}) error {
switch d.nobits {
case 32:
return d.write32(encoder, v)
case 64:
return d.write64(encoder, v)
case 128:
return d.write128(encoder, v)
default:
return errors.New("unachievable execution path")
}
}
func (d *Decimal) float2int32(floating float64) int32 {
fixed := int32(floating * factors10[d.scale])
return fixed
}
func (d *Decimal) float2int64(floating float64) int64 {
fixed := int64(floating * factors10[d.scale])
return fixed
}
func (d *Decimal) write32(encoder *binary.Encoder, v interface{}) error {
switch v := v.(type) {
case int8:
return encoder.Int32(int32(v))
case int16:
return encoder.Int32(int32(v))
case int32:
return encoder.Int32(int32(v))
case int64:
if v > math.MaxInt32 || v < math.MinInt32 {
return errors.New("overflow when narrowing type conversion from int64 to int32")
}
return encoder.Int32(int32(v))
case uint8:
return encoder.Int32(int32(v))
case uint16:
return encoder.Int32(int32(v))
case uint32:
if v > math.MaxInt32 {
return errors.New("overflow when narrowing type conversion from uint32 to int32")
}
return encoder.Int32(int32(v))
case uint64:
if v > math.MaxInt32 {
return errors.New("overflow when narrowing type conversion from uint64 to int32")
}
return encoder.Int32(int32(v))
case float32:
fixed := d.float2int32(float64(v))
return encoder.Int32(fixed)
case float64:
fixed := d.float2int32(float64(v))
return encoder.Int32(fixed)
// this relies on Nullable never sending nil values through
case *int8:
return encoder.Int32(int32(*v))
case *int16:
return encoder.Int32(int32(*v))
case *int32:
return encoder.Int32(int32(*v))
case *int64:
if *v > math.MaxInt32 || *v < math.MinInt32 {
return errors.New("overflow when narrowing type conversion from int64 to int32")
}
return encoder.Int32(int32(*v))
case *uint8:
return encoder.Int32(int32(*v))
case *uint16:
return encoder.Int32(int32(*v))
case *uint32:
if *v > math.MaxInt32 {
return errors.New("overflow when narrowing type conversion from uint34 to int32")
}
return encoder.Int32(int32(*v))
case *uint64:
if *v > math.MaxInt32 {
return errors.New("overflow when narrowing type conversion from uint64 to int32")
}
return encoder.Int32(int32(*v))
case *float32:
fixed := d.float2int32(float64(*v))
return encoder.Int32(fixed)
case *float64:
fixed := d.float2int32(float64(*v))
return encoder.Int32(fixed)
}
return &ErrUnexpectedType{
T: v,
Column: d,
}
}
func (d *Decimal) write64(encoder *binary.Encoder, v interface{}) error {
switch v := v.(type) {
case int:
return encoder.Int64(int64(v))
case int8:
return encoder.Int64(int64(v))
case int16:
return encoder.Int64(int64(v))
case int32:
return encoder.Int64(int64(v))
case int64:
return encoder.Int64(int64(v))
case uint8:
return encoder.Int64(int64(v))
case uint16:
return encoder.Int64(int64(v))
case uint32:
return encoder.Int64(int64(v))
case uint64:
if v > math.MaxInt64 {
return errors.New("overflow when narrowing type conversion from uint64 to int64")
}
return encoder.Int64(int64(v))
case float32:
fixed := d.float2int64(float64(v))
return encoder.Int64(fixed)
case float64:
fixed := d.float2int64(float64(v))
return encoder.Int64(fixed)
// this relies on Nullable never sending nil values through
case *int:
return encoder.Int64(int64(*v))
case *int8:
return encoder.Int64(int64(*v))
case *int16:
return encoder.Int64(int64(*v))
case *int32:
return encoder.Int64(int64(*v))
case *int64:
return encoder.Int64(int64(*v))
case *uint8:
return encoder.Int64(int64(*v))
case *uint16:
return encoder.Int64(int64(*v))
case *uint32:
return encoder.Int64(int64(*v))
case *uint64:
if *v > math.MaxInt64 {
return errors.New("overflow when narrowing type conversion from uint64 to int64")
}
return encoder.Int64(int64(*v))
case *float32:
fixed := d.float2int64(float64(*v))
return encoder.Int64(fixed)
case *float64:
fixed := d.float2int64(float64(*v))
return encoder.Int64(fixed)
}
return &ErrUnexpectedType{
T: v,
Column: d,
}
}
// Turns an int64 into 16 little-endian bytes.
func int64ToDecimal128(v int64) []byte {
bytes := make([]byte, 16)
b.LittleEndian.PutUint64(bytes[:8], uint64(v))
sign := 0
if v < 0 {
sign = -1
}
b.LittleEndian.PutUint64(bytes[8:], uint64(sign))
return bytes
}
// Turns a uint64 into 16 little-endian bytes.
func uint64ToDecimal128(v uint64) []byte {
bytes := make([]byte, 16)
b.LittleEndian.PutUint64(bytes[:8], uint64(v))
return bytes
}
func (d *Decimal) write128(encoder *binary.Encoder, v interface{}) error {
switch v := v.(type) {
case int:
return encoder.Decimal128(int64ToDecimal128(int64(v)))
case int8:
return encoder.Decimal128(int64ToDecimal128(int64(v)))
case int16:
return encoder.Decimal128(int64ToDecimal128(int64(v)))
case int32:
return encoder.Decimal128(int64ToDecimal128(int64(v)))
case int64:
return encoder.Decimal128(int64ToDecimal128(v))
case uint8:
return encoder.Decimal128(uint64ToDecimal128(uint64(v)))
case uint16:
return encoder.Decimal128(uint64ToDecimal128(uint64(v)))
case uint32:
return encoder.Decimal128(uint64ToDecimal128(uint64(v)))
case uint64:
return encoder.Decimal128(uint64ToDecimal128(v))
case float32:
fixed := d.float2int64(float64(v))
return encoder.Decimal128(int64ToDecimal128(fixed))
case float64:
fixed := d.float2int64(float64(v))
return encoder.Decimal128(int64ToDecimal128(fixed))
case []byte:
if len(v) != 16 {
return errors.New("expected 16 bytes")
}
return encoder.Decimal128(v)
// this relies on Nullable never sending nil values through
case *int:
return encoder.Decimal128(int64ToDecimal128(int64(*v)))
case *int8:
return encoder.Decimal128(int64ToDecimal128(int64(*v)))
case *int16:
return encoder.Decimal128(int64ToDecimal128(int64(*v)))
case *int32:
return encoder.Decimal128(int64ToDecimal128(int64(*v)))
case *int64:
return encoder.Decimal128(int64ToDecimal128(*v))
case *uint8:
return encoder.Decimal128(uint64ToDecimal128(uint64(*v)))
case *uint16:
return encoder.Decimal128(uint64ToDecimal128(uint64(*v)))
case *uint32:
return encoder.Decimal128(uint64ToDecimal128(uint64(*v)))
case *uint64:
return encoder.Decimal128(uint64ToDecimal128(*v))
case *float32:
fixed := d.float2int64(float64(*v))
return encoder.Decimal128(int64ToDecimal128(fixed))
case *float64:
fixed := d.float2int64(float64(*v))
return encoder.Decimal128(int64ToDecimal128(fixed))
case *[]byte:
if len(*v) != 16 {
return errors.New("expected 16 bytes")
}
return encoder.Decimal128(*v)
}
return &ErrUnexpectedType{
T: v,
Column: d,
}
}
func parseDecimal(name, chType string) (Column, error) {
switch {
case len(chType) < 12:
fallthrough
case !strings.HasPrefix(chType, "Decimal"):
fallthrough
case chType[7] != '(':
fallthrough
case chType[len(chType)-1] != ')':
return nil, fmt.Errorf("invalid Decimal format: '%s'", chType)
}
var params = strings.Split(chType[8:len(chType)-1], ",")
if len(params) != 2 {
return nil, fmt.Errorf("invalid Decimal format: '%s'", chType)
}
params[0] = strings.TrimSpace(params[0])
params[1] = strings.TrimSpace(params[1])
var err error
var decimal = &Decimal{
base: base{
name: name,
chType: chType,
},
}
if decimal.precision, err = strconv.Atoi(params[0]); err != nil {
return nil, fmt.Errorf("'%s' is not Decimal type: %s", chType, err)
} else if decimal.precision < 1 {
return nil, errors.New("wrong precision of Decimal type")
}
if decimal.scale, err = strconv.Atoi(params[1]); err != nil {
return nil, fmt.Errorf("'%s' is not Decimal type: %s", chType, err)
} else if decimal.scale < 0 || decimal.scale > decimal.precision {
return nil, errors.New("wrong scale of Decimal type")
}
switch {
case decimal.precision <= 9:
decimal.nobits = 32
decimal.valueOf = columnBaseTypes[int32(0)]
case decimal.precision <= 18:
decimal.nobits = 64
decimal.valueOf = columnBaseTypes[int64(0)]
case decimal.precision <= 38:
decimal.nobits = 128
decimal.valueOf = reflect.ValueOf([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
default:
return nil, errors.New("precision of Decimal exceeds max bound")
}
return decimal, nil
}
func (d *Decimal) GetPrecision() int {
return d.precision
}
func (d *Decimal) GetScale() int {
return d.scale
}