You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
2.6 KiB
118 lines
2.6 KiB
package clickhouse
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type dataReader interface {
|
|
Read() (record []string, err error)
|
|
}
|
|
|
|
func newTextRows(c *conn, body io.ReadCloser, location *time.Location, useDBLocation bool) (*textRows, error) {
|
|
tsvReader := newReader(body)
|
|
|
|
columns, err := tsvReader.Read()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newTextRows: failed to parse the list of columns: %w", err)
|
|
}
|
|
|
|
types, err := tsvReader.Read()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newTextRows: failed to parse the list of column types: %w", err)
|
|
}
|
|
for i := range types {
|
|
types[i], err = readUnquoted(strings.NewReader(types[i]), 0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newTextRows: failed to read the type '%s': %w", types[i], err)
|
|
}
|
|
}
|
|
|
|
parsers := make([]DataParser, len(types))
|
|
for i, typ := range types {
|
|
desc, err := ParseTypeDesc(typ)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newTextRows: failed to parse a description of the type '%s': %w", typ, err)
|
|
}
|
|
|
|
parsers[i], err = NewDataParser(desc, &DataParserOptions{
|
|
Location: location,
|
|
UseDBLocation: useDBLocation,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newTextRows: failed to create a data parser for the type '%s': %w", typ, err)
|
|
}
|
|
}
|
|
|
|
return &textRows{
|
|
c: c,
|
|
respBody: body,
|
|
tsv: tsvReader,
|
|
columns: columns,
|
|
types: types,
|
|
parsers: parsers,
|
|
}, nil
|
|
}
|
|
|
|
type textRows struct {
|
|
c *conn
|
|
respBody io.ReadCloser
|
|
tsv dataReader
|
|
columns []string
|
|
types []string
|
|
parsers []DataParser
|
|
}
|
|
|
|
func (r *textRows) Columns() []string {
|
|
return r.columns
|
|
}
|
|
|
|
func (r *textRows) Close() error {
|
|
r.c.cancel = nil
|
|
return r.respBody.Close()
|
|
}
|
|
|
|
func (r *textRows) Next(dest []driver.Value) error {
|
|
row, err := r.tsv.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// skip row before WITH TOTALS,
|
|
// not but do not skip an empty line if it is part of the result
|
|
if len(row) == 1 && row[0] == "" {
|
|
row, err = r.tsv.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for i, s := range row {
|
|
reader := strings.NewReader(s)
|
|
v, err := r.parsers[i].Parse(reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, _, err := reader.ReadRune(); err != io.EOF {
|
|
return fmt.Errorf("trailing data after parsing the value")
|
|
}
|
|
dest[i] = v
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ColumnTypeScanType implements the driver.RowsColumnTypeScanType
|
|
func (r *textRows) ColumnTypeScanType(index int) reflect.Type {
|
|
return r.parsers[index].Type()
|
|
}
|
|
|
|
// ColumnTypeDatabaseTypeName implements the driver.RowsColumnTypeDatabaseTypeName
|
|
func (r *textRows) ColumnTypeDatabaseTypeName(index int) string {
|
|
return r.types[index]
|
|
}
|