Skip to content

Commit 284e081

Browse files
authored
feat: Rewriting the value parser (#149)
* feat: ttc server * dockerfile * hack * column type * fix * ttc.yaml * fix detail * clean up * fix * fix * fix * fix * fix * update * location * fix
1 parent f641030 commit 284e081

File tree

13 files changed

+701
-213
lines changed

13 files changed

+701
-213
lines changed

columntype.go

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
package godatabend
2+
3+
import (
4+
"database/sql/driver"
5+
"fmt"
6+
"reflect"
7+
"strconv"
8+
"time"
9+
)
10+
11+
type ColumnType interface {
12+
Desc() *TypeDesc
13+
DatabaseTypeName() string
14+
Nullable() (bool, bool)
15+
ScanType() reflect.Type
16+
Parse(s string) (driver.Value, error)
17+
Length() (int64, bool)
18+
PrecisionScale() (int64, int64, bool)
19+
}
20+
21+
type columnTypeDefault struct{}
22+
23+
func (columnTypeDefault) Length() (int64, bool) {
24+
return 0, false
25+
}
26+
27+
func (columnTypeDefault) PrecisionScale() (int64, int64, bool) {
28+
return 0, 0, false
29+
}
30+
31+
type unknownColumnType struct {
32+
columnTypeDefault
33+
dbType string
34+
desc *TypeDesc
35+
}
36+
37+
func (c unknownColumnType) ScanType() reflect.Type {
38+
return reflectTypeString
39+
}
40+
41+
func (c unknownColumnType) Nullable() (bool, bool) {
42+
return false, false
43+
}
44+
45+
func (c unknownColumnType) DatabaseTypeName() string {
46+
return c.dbType
47+
}
48+
49+
func (c unknownColumnType) Parse(s string) (driver.Value, error) {
50+
return s, nil
51+
}
52+
53+
func (c unknownColumnType) Desc() *TypeDesc {
54+
return c.desc
55+
}
56+
57+
type isNullable bool
58+
59+
func (b isNullable) Nullable() (bool, bool) {
60+
return bool(b), true
61+
}
62+
63+
func (b isNullable) wrapName(s string) string {
64+
if bool(b) {
65+
return s + " NULL"
66+
}
67+
return s
68+
}
69+
70+
func (b isNullable) checkNull(s string) bool {
71+
return bool(b) && s == "NULL"
72+
}
73+
74+
type simpleColumnType struct {
75+
dbType string
76+
scanType reflect.Type
77+
nullable bool
78+
parseNull bool
79+
}
80+
81+
func (*simpleColumnType) PrecisionScale() (int64, int64, bool) {
82+
return 0, 0, false
83+
}
84+
85+
func (c *simpleColumnType) DatabaseTypeName() string {
86+
return c.dbType
87+
}
88+
89+
func (c *simpleColumnType) Nullable() (bool, bool) {
90+
return c.nullable, true
91+
}
92+
93+
func (c *simpleColumnType) Desc() *TypeDesc {
94+
return &TypeDesc{Name: c.dbType, Nullable: c.nullable}
95+
}
96+
97+
func (*simpleColumnType) Length() (int64, bool) {
98+
return 0, false
99+
}
100+
101+
func (c *simpleColumnType) Parse(s string) (driver.Value, error) {
102+
if c.nullable && c.parseNull && s == "NULL" {
103+
return nil, nil
104+
}
105+
return s, nil
106+
}
107+
108+
func (c *simpleColumnType) ScanType() reflect.Type {
109+
return c.scanType
110+
}
111+
112+
type timestampColumnType struct {
113+
tz *time.Location
114+
columnTypeDefault
115+
isNullable
116+
}
117+
118+
func (c timestampColumnType) Parse(s string) (driver.Value, error) {
119+
if c.checkNull(s) {
120+
return nil, nil
121+
}
122+
return time.ParseInLocation("2006-01-02 15:04:05.999999", s, c.tz)
123+
}
124+
125+
func (c timestampColumnType) ScanType() reflect.Type {
126+
return reflectTypeTime
127+
}
128+
129+
func (c timestampColumnType) DatabaseTypeName() string {
130+
return c.wrapName("Timestamp")
131+
}
132+
133+
func (c timestampColumnType) Desc() *TypeDesc {
134+
return &TypeDesc{Name: "Timestamp", Nullable: bool(c.isNullable)}
135+
}
136+
137+
type dateColumnType struct {
138+
tz *time.Location
139+
columnTypeDefault
140+
isNullable
141+
}
142+
143+
func (c dateColumnType) Parse(s string) (driver.Value, error) {
144+
if c.checkNull(s) {
145+
return nil, nil
146+
}
147+
return time.ParseInLocation("2006-01-02", s, c.tz)
148+
}
149+
150+
func (c dateColumnType) ScanType() reflect.Type {
151+
return reflectTypeTime
152+
}
153+
154+
func (c dateColumnType) DatabaseTypeName() string {
155+
return c.wrapName("Date")
156+
}
157+
158+
func (c dateColumnType) Desc() *TypeDesc {
159+
return &TypeDesc{Name: "Date", Nullable: bool(c.isNullable)}
160+
}
161+
162+
type decimalColumnType struct {
163+
precision int64
164+
scale int64
165+
columnTypeDefault
166+
isNullable
167+
}
168+
169+
func (c *decimalColumnType) DatabaseTypeName() string {
170+
return c.wrapName(fmt.Sprintf("Decimal(%d, %d)", c.precision, c.scale))
171+
}
172+
173+
func (c *decimalColumnType) Desc() *TypeDesc {
174+
return &TypeDesc{Name: "Decimal", Nullable: bool(c.isNullable), Args: []*TypeDesc{{Name: strconv.Itoa(int(c.precision))}, {Name: strconv.Itoa(int(c.scale))}}}
175+
}
176+
177+
func (*decimalColumnType) Parse(s string) (driver.Value, error) {
178+
return s, nil
179+
}
180+
181+
func (c *decimalColumnType) PrecisionScale() (int64, int64, bool) {
182+
return c.precision, c.scale, true
183+
}
184+
185+
func (*decimalColumnType) ScanType() reflect.Type {
186+
return reflectTypeString
187+
}
188+
189+
func NewColumnType(dbType string, opts *ColumnTypeOptions) (ColumnType, error) {
190+
if opts == nil {
191+
opts = defaultColumnTypeOptions()
192+
}
193+
desc, err := ParseTypeDesc(dbType)
194+
if err != nil {
195+
return nil, err
196+
}
197+
desc = desc.Normalize()
198+
nullable := isNullable(desc.Nullable)
199+
parseNull := opts.formatNullAsStr
200+
switch desc.Name {
201+
case "String":
202+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeString, nullable: desc.Nullable, parseNull: false}, nil
203+
case "Boolean":
204+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeBool, nullable: desc.Nullable, parseNull: parseNull}, nil
205+
case "Int8":
206+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeInt8, nullable: desc.Nullable, parseNull: parseNull}, nil
207+
case "Int16":
208+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeInt16, nullable: desc.Nullable, parseNull: parseNull}, nil
209+
case "Int32":
210+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeInt32, nullable: desc.Nullable, parseNull: parseNull}, nil
211+
case "Int64":
212+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeInt64, nullable: desc.Nullable, parseNull: parseNull}, nil
213+
case "UInt8":
214+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeUInt8, nullable: desc.Nullable, parseNull: parseNull}, nil
215+
case "UInt16":
216+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeUInt16, nullable: desc.Nullable, parseNull: parseNull}, nil
217+
case "UInt32":
218+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeUInt32, nullable: desc.Nullable, parseNull: parseNull}, nil
219+
case "UInt64":
220+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeUInt64, nullable: desc.Nullable, parseNull: parseNull}, nil
221+
case "Float32":
222+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeFloat32, nullable: desc.Nullable, parseNull: parseNull}, nil
223+
case "Float64":
224+
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeFloat64, nullable: desc.Nullable, parseNull: parseNull}, nil
225+
case "Timestamp":
226+
return &timestampColumnType{isNullable: nullable, tz: opts.timezone}, nil
227+
case "Date":
228+
return &dateColumnType{isNullable: nullable, tz: opts.timezone}, nil
229+
case "Decimal":
230+
precision, err := strconv.ParseInt(desc.Args[0].Name, 10, 64)
231+
if err != nil {
232+
return nil, fmt.Errorf("malformed precision specified for Decimal: %v", err)
233+
}
234+
scale, err := strconv.ParseInt(desc.Args[1].Name, 10, 64)
235+
if err != nil {
236+
return nil, fmt.Errorf("malformed scale specified for Decimal: %v", err)
237+
}
238+
return &decimalColumnType{isNullable: nullable, precision: precision, scale: scale}, nil
239+
default:
240+
return unknownColumnType{dbType: dbType, desc: desc}, nil
241+
}
242+
}
243+
244+
type ColumnTypeOptions struct {
245+
formatNullAsStr bool
246+
timezone *time.Location
247+
}
248+
249+
func defaultColumnTypeOptions() *ColumnTypeOptions {
250+
return &ColumnTypeOptions{
251+
formatNullAsStr: false,
252+
timezone: time.UTC,
253+
}
254+
}
255+
256+
func (opt *ColumnTypeOptions) SetFormatNullAsStr(v bool) {
257+
opt.formatNullAsStr = v
258+
}
259+
260+
func (opt *ColumnTypeOptions) SetTimezone(v *time.Location) {
261+
opt.timezone = v
262+
}

0 commit comments

Comments
 (0)