Skip to content

Commit 0d0f71e

Browse files
committed
Merge branch 'master' of https://github.com/kshvakov/clickhouse
2 parents b86f51d + cd8ba92 commit 0d0f71e

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

lib/column/column.go

+2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ func Factory(name, chType string, timezone *time.Location) (Column, error) {
137137
return parseFixedString(name, chType)
138138
case strings.HasPrefix(chType, "Enum8"), strings.HasPrefix(chType, "Enum16"):
139139
return parseEnum(name, chType)
140+
case strings.HasPrefix(chType, "Decimal"):
141+
return parseDecimal(name, chType)
140142
}
141143
return nil, fmt.Errorf("column: unhandled type %v", chType)
142144
}

lib/column/decimal.go

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package column
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/kshvakov/clickhouse/lib/binary"
10+
)
11+
12+
// Table of powers of 10 for fast casting from floating types to decimal type
13+
// representations.
14+
var factors10 = []float64{
15+
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13,
16+
1e14, 1e15, 1e16, 1e17, 1e18,
17+
}
18+
19+
// Decimal represents Decimal(P, S) ClickHouse. Since there is support for
20+
// int128 in Golang, the implementation does not support to 128-bits decimals
21+
// as well. Decimal is represented as integral. Also floating-point types are
22+
// supported for query parameters.
23+
type Decimal struct {
24+
base
25+
nobits int // its domain is {32, 64}
26+
precision int
27+
scale int
28+
}
29+
30+
func (d *Decimal) Read(decoder *binary.Decoder) (interface{}, error) {
31+
switch d.nobits {
32+
case 32:
33+
return decoder.Int32()
34+
case 64:
35+
return decoder.Int64()
36+
default:
37+
return nil, errors.New("unachievable execution path")
38+
}
39+
}
40+
41+
func (d *Decimal) Write(encoder *binary.Encoder, v interface{}) error {
42+
switch d.nobits {
43+
case 32:
44+
return d.write32(encoder, v)
45+
case 64:
46+
return d.write64(encoder, v)
47+
default:
48+
return errors.New("unachievable execution path")
49+
}
50+
}
51+
52+
func (d *Decimal) float2int32(floating float64) int32 {
53+
fixed := int32(floating * factors10[d.scale])
54+
return fixed
55+
}
56+
57+
func (d *Decimal) float2int64(floating float64) int64 {
58+
fixed := int64(floating * factors10[d.scale])
59+
return fixed
60+
}
61+
62+
func (d *Decimal) write32(encoder *binary.Encoder, v interface{}) error {
63+
switch v := v.(type) {
64+
case int8:
65+
return encoder.Int32(int32(v))
66+
case int16:
67+
return encoder.Int32(int32(v))
68+
case int32:
69+
return encoder.Int32(int32(v))
70+
case int64:
71+
return errors.New("narrowing type conversion from int64 to int32")
72+
73+
case uint8:
74+
return encoder.Int32(int32(v))
75+
case uint16:
76+
return encoder.Int32(int32(v))
77+
case uint32:
78+
return errors.New("narrowing type conversion from uint32 to int32")
79+
case uint64:
80+
return errors.New("narrowing type conversion from uint64 to int32")
81+
82+
case float32:
83+
fixed := d.float2int32(float64(v))
84+
return encoder.Int32(fixed)
85+
case float64:
86+
fixed := d.float2int32(float64(v))
87+
return encoder.Int32(fixed)
88+
}
89+
90+
return &ErrUnexpectedType{
91+
T: v,
92+
Column: d,
93+
}
94+
return errors.New("narrowing type conversion from int64 to int32")
95+
}
96+
97+
func (d *Decimal) write64(encoder *binary.Encoder, v interface{}) error {
98+
switch v := v.(type) {
99+
case int:
100+
return encoder.Int64(int64(v))
101+
case int8:
102+
return encoder.Int64(int64(v))
103+
case int16:
104+
return encoder.Int64(int64(v))
105+
case int32:
106+
return encoder.Int64(int64(v))
107+
case int64:
108+
return encoder.Int64(int64(v))
109+
110+
case uint8:
111+
return encoder.Int64(int64(v))
112+
case uint16:
113+
return encoder.Int64(int64(v))
114+
case uint32:
115+
return encoder.Int64(int64(v))
116+
case uint64:
117+
return errors.New("narrowing type conversion from uint64 to int64")
118+
119+
case float32:
120+
fixed := d.float2int64(float64(v))
121+
return encoder.Int64(fixed)
122+
case float64:
123+
fixed := d.float2int64(float64(v))
124+
return encoder.Int64(fixed)
125+
}
126+
127+
return &ErrUnexpectedType{
128+
T: v,
129+
Column: d,
130+
}
131+
}
132+
133+
func parseDecimal(name, chType string) (Column, error) {
134+
switch {
135+
case len(chType) < 12:
136+
fallthrough
137+
case !strings.HasPrefix(chType, "Decimal"):
138+
fallthrough
139+
case chType[7] != '(':
140+
fallthrough
141+
case chType[len(chType)-1] != ')':
142+
return nil, fmt.Errorf("invalid Decimal format: '%s'", chType)
143+
}
144+
145+
var params = strings.Split(chType[8:len(chType)-1], ",")
146+
147+
if len(params) != 2 {
148+
return nil, fmt.Errorf("invalid Decimal format: '%s'", chType)
149+
}
150+
151+
params[0] = strings.TrimSpace(params[0])
152+
params[1] = strings.TrimSpace(params[1])
153+
154+
var err error
155+
var decimal = &Decimal{
156+
base: base{
157+
name: name,
158+
chType: chType,
159+
},
160+
}
161+
162+
if decimal.precision, err = strconv.Atoi(params[0]); err != nil {
163+
return nil, fmt.Errorf("'%s' is not Decimal type: %s", chType, err)
164+
} else if decimal.precision < 1 {
165+
return nil, errors.New("wrong precision of Decimal type")
166+
}
167+
168+
if decimal.scale, err = strconv.Atoi(params[1]); err != nil {
169+
return nil, fmt.Errorf("'%s' is not Decimal type: %s", chType, err)
170+
} else if decimal.scale < 0 || decimal.scale > decimal.precision {
171+
return nil, errors.New("wrong scale of Decimal type")
172+
}
173+
174+
switch {
175+
case decimal.precision <= 9:
176+
decimal.nobits = 32
177+
decimal.valueOf = baseTypes[int32(0)]
178+
case decimal.precision <= 18:
179+
decimal.nobits = 64
180+
decimal.valueOf = baseTypes[int64(0)]
181+
case decimal.precision <= 38:
182+
return nil, errors.New("Decimal128 is not supported")
183+
default:
184+
return nil, errors.New("precision of Decimal exceeds max bound")
185+
}
186+
187+
return decimal, nil
188+
}

0 commit comments

Comments
 (0)