Skip to content

Commit

Permalink
Merge d29902b into aa921cc
Browse files Browse the repository at this point in the history
  • Loading branch information
bgaifullin committed Jan 12, 2019
2 parents aa921cc + d29902b commit ab8cc94
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 9 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@ http://user:password@host:8123/clicks?read_timeout=10&write_timeout=20

* UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64
* Float32, Float64
* Decimal(P, S), Decimal32(S), Decimal64(S), Decimal128(S)
* String
* FixedString(N)
* Date
* DateTime
* Enum
* [Array(T) (one-dimensional)](https://clickhouse.yandex/reference_en.html#Array(T))
* [Nested(Name1 Type1, Name2 Type2, ...)](https://clickhouse.yandex/docs/en/data_types/nested_data_structures/nested/)

Notes:
database/sql does not allow to use big uint64 values.
It is recommended use type `UInt64` which is provided by driver for such kind of values.
type `[]byte` are used as raw string (without quoting)
for passing value of type `[]uint8` to driver as array - please use the wrapper `clickhouse.Array`
for passing decimal value please use the wrappers `clickhouse.Decimal*`

## Install
```
Expand Down
12 changes: 8 additions & 4 deletions clickhouse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ var ddls = []string{
a8 Array(UInt8),
d Date,
t DateTime,
e Enum8('one'=1, 'two'=2, 'three'=3)
e Enum8('one'=1, 'two'=2, 'three'=3),
d32 Decimal32(4),
d64 Decimal64(4),
d128 Decimal128(4),
d10 Decimal(10, 4)
) ENGINE = Memory`,
`INSERT INTO data VALUES
(-1, 1, 1.0, '1', '1', [1], [10], '2011-03-06', '2011-03-06 06:20:00', 'one'),
(-2, 2, 2.0, '2', '2', [2], [20], '2012-05-31', '2012-05-31 11:20:00', 'two'),
(-3, 3, 3.0, '3', '2', [3], [30], '2016-04-04', '2016-04-04 11:30:00', 'three')
(-1, 1, 1.0, '1', '1', [1], [10], '2011-03-06', '2011-03-06 06:20:00', 'one', '10', '100', '1000', '1'),
(-2, 2, 2.0, '2', '2', [2], [20], '2012-05-31', '2012-05-31 11:20:00', 'two', '30', '300', '2000', '2'),
(-3, 3, 3.0, '3', '2', [3], [30], '2016-04-04', '2016-04-04 11:30:00', 'three', '40', '400', '3000', '3')
`,
}

Expand Down
1 change: 1 addition & 0 deletions conn_go18_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (s *connSuite) TestColumnTypes() {
expected := []string{
"Int64", "UInt64", "Float64", "String", "String", "Array(Int16)", "Array(UInt8)", "Date", "DateTime",
"Enum8('one' = 1, 'two' = 2, 'three' = 3)",
"Decimal(9, 4)", "Decimal(18, 4)", "Decimal(38, 4)", "Decimal(10, 4)",
}
s.Require().Equal(len(expected), len(types))
for i, e := range expected {
Expand Down
12 changes: 11 additions & 1 deletion conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ func (s *connSuite) TestQuery() {
{"SELECT i64 AS num FROM data WHERE i64<?", []interface{}{-3}, [][]interface{}{}},
{"SELECT i64 AS num FROM data WHERE i64=?", []interface{}{nil}, [][]interface{}{}},
{"SELECT i64 AS num FROM data WHERE i64=?", []interface{}{-1}, [][]interface{}{{int64(-1)}}},
{"SELECT d32 AS num FROM data WHERE d32=?", []interface{}{Decimal32(10, 4)}, [][]interface{}{{"10.0000"}}},
{"SELECT d64 AS num FROM data WHERE d64=?", []interface{}{Decimal32(100, 4)}, [][]interface{}{{"100.0000"}}},
{"SELECT d128 AS num FROM data WHERE d128=?", []interface{}{Decimal32(1000, 4)}, [][]interface{}{{"1000.0000"}}},
{
"SELECT * FROM data WHERE u64=?",
[]interface{}{1},
[][]interface{}{{int64(-1), uint64(1), float64(1), "1", "1", []int16{1}, []uint8{10},
parseDate("2011-03-06"), parseDateTime("2011-03-06 06:20:00"), "one"}},
parseDate("2011-03-06"), parseDateTime("2011-03-06 06:20:00"), "one",
"10.0000", "100.0000", "1000.0000", "1.0000"}},
},
{
"SELECT i64, count() FROM data WHERE i64<0 GROUP BY i64 WITH TOTALS ORDER BY i64",
Expand Down Expand Up @@ -101,6 +105,12 @@ func (s *connSuite) TestExec() {
"SELECT u64 FROM data WHERE u64=?",
[]interface{}{UInt64(maxAllowedUInt64*2 + 1)},
},
{
"INSERT INTO data (d32, d64, d128) VALUES(?, ?, ?)",
"",
[]interface{}{Decimal32(50, 4), Decimal64(500, 4), Decimal128(5000, 4)},
},

{
"INSERT INTO data (d, t) VALUES (?, ?)",
"",
Expand Down
6 changes: 2 additions & 4 deletions dataparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"io"
"reflect"
"strconv"
"strings"
"time"
)

Expand Down Expand Up @@ -399,6 +398,8 @@ func newDataParser(t *TypeDesc, unquote bool) (DataParser, error) {
return &floatParser{32}, nil
case "Float64":
return &floatParser{64}, nil
case "Decimal":
return &stringParser{unquote: unquote}, nil
case "String", "Enum8", "Enum16":
return &stringParser{unquote: unquote}, nil
case "FixedString":
Expand Down Expand Up @@ -433,9 +434,6 @@ func newDataParser(t *TypeDesc, unquote bool) (DataParser, error) {
}
return &tupleParser{subParsers}, nil
default:
if strings.Contains(t.Name, "Decimal") {
return &stringParser{unquote: unquote}, nil
}
return nil, fmt.Errorf("type %s is not supported", t.Name)
}
}
6 changes: 6 additions & 0 deletions dataparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ func TestParseData(t *testing.T) {
inputdata: "-inf",
output: float32(math.Inf(-1)),
},
{
name: "decimal",
inputtype: "Decimal(9,4)",
inputdata: "123",
output: "123",
},
{
name: "date",
inputtype: "Date",
Expand Down
8 changes: 8 additions & 0 deletions typeparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ func TestParseTypeDesc(t *testing.T) {
Args: []*TypeDesc{{Name: "UTC"}},
},
},
{
name: "decimal",
input: "Decimal(9,4)",
output: &TypeDesc{
Name: "Decimal",
Args: []*TypeDesc{{Name: "9"}, {Name: "4"}},
},
},
{
name: "quoted escaped arg",
input: `DateTime('UTC\b\r\n\'\f\t\0')`,
Expand Down
30 changes: 30 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package clickhouse

import (
"database/sql/driver"
"fmt"
"strconv"
"time"
)
Expand All @@ -21,6 +22,24 @@ func UInt64(u uint64) driver.Valuer {
return bigUint64(u)
}

// Decimal32 converts value to Decimal32 of precision S.
// The value can be a number or a string. The S (scale) parameter specifies the number of decimal places.
func Decimal32(v interface{}, s int32) driver.Valuer {
return decimal{32, s, v}
}

// Decimal64 converts value to Decimal64 of precision S.
// The value can be a number or a string. The S (scale) parameter specifies the number of decimal places.
func Decimal64(v interface{}, s int32) driver.Valuer {
return decimal{64, s, v}
}

// Decimal128 converts value to Decimal128 of precision S.
// The value can be a number or a string. The S (scale) parameter specifies the number of decimal places.
func Decimal128(v interface{}, s int32) driver.Valuer {
return decimal{128, s, v}
}

type array struct {
v interface{}
}
Expand All @@ -43,3 +62,14 @@ type bigUint64 uint64
func (u bigUint64) Value() (driver.Value, error) {
return []byte(strconv.FormatUint(uint64(u), 10)), nil
}

type decimal struct {
p int32
s int32
v interface{}
}

// Value implements driver.Valuer
func (d decimal) Value() (driver.Value, error) {
return []byte(fmt.Sprintf("toDecimal%d(%v, %d)", d.p, d.v, d.s)), nil
}
16 changes: 16 additions & 0 deletions types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,19 @@ func TestUInt64(t *testing.T) {
assert.Equal(t, []byte("9223372036854775808"), dv)
}
}

func TestDecimal(t *testing.T) {
dv, err := Decimal32("1000", 4).Value()
if assert.NoError(t, err) {
assert.Equal(t, []byte("toDecimal32(1000, 4)"), dv)
}

dv, err = Decimal64(100, 1).Value()
if assert.NoError(t, err) {
assert.Equal(t, []byte("toDecimal64(100, 1)"), dv)
}
dv, err = Decimal128(100.01, 1).Value()
if assert.NoError(t, err) {
assert.Equal(t, []byte("toDecimal128(100.01, 1)"), dv)
}
}

0 comments on commit ab8cc94

Please sign in to comment.