Skip to content

Commit

Permalink
Merge pull request #81 from mailru/develop
Browse files Browse the repository at this point in the history
Nullable fields, go 1.12
  • Loading branch information
DoubleDi committed Aug 7, 2019
2 parents 0c89049 + 2dea5bf commit bc658e5
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 6 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go:
- '1.9'
- '1.10'
- '1.11'
- '1.12'

services:
- docker
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func main() {
}

rows, err := connect.Query(`
SELECT
SELECT
country_code,
os_id,
browser_id,
Expand Down
26 changes: 25 additions & 1 deletion dataparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ type dateTimeParser struct {
location *time.Location
}

type nullableParser struct {
DataParser
}

func (p *nullableParser) Parse(s io.RuneScanner) (driver.Value, error) {
// Clickhouse returns `\N` string for `null` in tsv format.
// For checking this value we need to check first two runes in `io.RuneScanner`, but we can't reset `io.RuneScanner` after it.
// Copy io.RuneScanner to `bytes.Buffer` and use it twice (1st for casting to string and checking to null, 2nd for passing to original parser)
d := readRaw(s)

if bytes.Equal(d.Bytes(), []byte(`\N`)) {
return nil, nil
}

return p.DataParser.Parse(d)
}

func readNumber(s io.RuneScanner) (string, error) {
var builder bytes.Buffer

Expand Down Expand Up @@ -384,7 +401,14 @@ func newDataParser(t *TypeDesc, unquote bool, opt *DataParserOptions) (DataParse
case "Nothing":
return &nothingParser{}, nil
case "Nullable":
return nil, fmt.Errorf("Nullable types are not supported")
if len(t.Args) == 0 {
return nil, fmt.Errorf("Nullable should pass original type")
}
p, err := newDataParser(t.Args[0], unquote, opt)
if err != nil {
return nil, err
}
return &nullableParser{p}, nil
case "Date":
loc := time.UTC
if opt != nil && opt.Location != nil {
Expand Down
14 changes: 10 additions & 4 deletions dataparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@ func TestParseData(t *testing.T) {

testCases := []*testCase{
{
name: "nullable not supported",
inputtype: "Nullable(String)",
inputdata: "NULL",
failNewParser: true,
name: "nullable string",
inputtype: "Nullable(String)",
inputdata: `\N`,
output: nil,
},
{
name: "nullable int not null",
inputtype: "Nullable(UInt64)",
inputdata: "655",
output: uint64(655),
},
{
name: "string",
Expand Down
16 changes: 16 additions & 0 deletions tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ func readEscaped(s io.RuneScanner) (rune, error) {
}
}

func readRaw(s io.RuneScanner) *bytes.Buffer {
var data bytes.Buffer

for {
r := read(s)

if r == eof {
break
}

data.WriteRune(r)
}

return &data
}

func readQuoted(s io.RuneScanner) (*token, error) {
var data bytes.Buffer

Expand Down

0 comments on commit bc658e5

Please sign in to comment.