forked from ClickHouse/clickhouse-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
helpers.go
138 lines (126 loc) · 3.17 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package clickhouse
import (
"database/sql/driver"
"fmt"
"strings"
"time"
)
// Truncate timezone
//
// clickhouse.Date(time.Date(2017, 1, 1, 0, 0, 0, 0, time.Local)) -> time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
type Date time.Time
func (date Date) Value() (driver.Value, error) {
return time.Date(time.Time(date).Year(), time.Time(date).Month(), time.Time(date).Day(), 0, 0, 0, 0, time.UTC), nil
}
// Truncate timezone
//
// clickhouse.DateTime(time.Date(2017, 1, 1, 0, 0, 0, 0, time.Local)) -> time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
type DateTime time.Time
func (datetime DateTime) Value() (driver.Value, error) {
return time.Date(
time.Time(datetime).Year(),
time.Time(datetime).Month(),
time.Time(datetime).Day(),
time.Time(datetime).Hour(),
time.Time(datetime).Minute(),
time.Time(datetime).Second(),
0,
time.UTC,
), nil
}
func isInsert(query string) bool {
if f := strings.Fields(query); len(f) > 2 {
return strings.EqualFold("INSERT", f[0]) && strings.EqualFold("INTO", f[1]) && strings.Index(strings.ToUpper(query), " SELECT ") == -1
}
return false
}
func quote(v driver.Value) string {
switch v.(type) {
case string, *string, time.Time, *time.Time:
return "'" + escape(v) + "'"
}
return fmt.Sprint(v)
}
func escape(v driver.Value) string {
switch value := v.(type) {
case string:
return strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(value)
case *string:
return strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(*value)
case time.Time:
return formatTime(value)
case *time.Time:
return formatTime(*value)
}
return fmt.Sprint(v)
}
func formatTime(value time.Time) string {
if (value.Hour() + value.Minute() + value.Second() + value.Nanosecond()) == 0 {
return value.Format("2006-01-02")
}
return value.Format("2006-01-02 15:04:05")
}
func toColumnType(ct string) (interface{}, error) {
// PODs
switch ct {
case "Date":
return Date{}, nil
case "DateTime":
return DateTime{}, nil
case "String":
return string(""), nil
case "Int8":
return int8(0), nil
case "Int16":
return int16(0), nil
case "Int32":
return int32(0), nil
case "Int64":
return int64(0), nil
case "UInt8":
return uint8(0), nil
case "UInt16":
return uint16(0), nil
case "UInt32":
return uint32(0), nil
case "UInt64":
return uint64(0), nil
case "Float32":
return float32(0), nil
case "Float64":
return float64(0), nil
}
// Specialised types
switch {
case strings.HasPrefix(ct, "FixedString"):
var arrLen int
if _, err := fmt.Sscanf(ct, "FixedString(%d)", &arrLen); err != nil {
return nil, err
}
return make([]byte, arrLen), nil
case strings.HasPrefix(ct, "Enum8"):
enum, err := parseEnum(ct)
if err != nil {
return nil, err
}
return enum8(enum), nil
case strings.HasPrefix(ct, "Enum16"):
enum, err := parseEnum(ct)
if err != nil {
return nil, err
}
return enum16(enum), nil
case strings.HasPrefix(ct, "Array"):
if len(ct) < 11 {
return nil, fmt.Errorf("invalid Array column type: %s", ct)
}
baseType, err := toColumnType(ct[6:][:len(ct)-7])
if err != nil {
return nil, fmt.Errorf("array: %v", err)
}
return array{
baseType: baseType,
}, nil
}
return nil, fmt.Errorf("unhandled type %v", ct)
}