-
Notifications
You must be signed in to change notification settings - Fork 2
/
types.go
228 lines (213 loc) · 5.71 KB
/
types.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package questdb
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)
// QuestDBType is string which represents a type in the QuestDb world
type QuestDBType string
var (
// boolean (true or false)
Boolean QuestDBType = "boolean"
// 8 bit signed integer (-128 to 127)
Byte QuestDBType = "byte"
// 16-bit signed integer (-32768 to 32767)
Short QuestDBType = "short"
// 16-bit unicode character
Char QuestDBType = "char"
// 32-bit signed integer (0x80000000 to 0x7fffffff)
Int QuestDBType = "int"
// 32-bit float (float32 - single precision IEEE 754)
Float QuestDBType = "float"
// variable length string (see QuestDB docs)
Symbol QuestDBType = "symbol"
// variable length string
String QuestDBType = "string"
// variable length JSON string
JSON QuestDBType = "json"
// type that implements EncoderDecoder interface
Encoded QuestDBType = "encoded"
// 64-bit signed integer (0x8000000000000000L to 0x7fffffffffffffffL)
Long QuestDBType = "long"
// 64-bit signed offset in milliseconds from Unix Epoch
Date QuestDBType = "date"
// 64-bit signed offset in microseconds from Unix Epoch
Timestamp QuestDBType = "timestamp"
// 64-bit float (float64 - double precision IEEE 754)
Double QuestDBType = "double"
// byte array
Binary QuestDBType = "binary"
// hyphenated uuid string
UUID QuestDBType = "uuid"
// 256-bit unsigned integer
// unsupported
Long256 QuestDBType = "long256"
// Geohash
// unsupported
Geohash QuestDBType = "geohash"
)
// serializeValue func takes a value interface{} and a QuestDBType and returns the
// serialized string of that value according to the provided QuestDBType.
func serializeValue(v interface{}, qdbType QuestDBType) (string, error) {
switch qdbType {
case Boolean:
switch val := v.(type) {
case bool:
return fmt.Sprintf("%t", val), nil
}
case Byte:
switch val := v.(type) {
case int8:
return fmt.Sprintf("%d", val), nil
}
case Short:
switch val := v.(type) {
case uint8, int8, int16:
return fmt.Sprintf("%di", val), nil
}
case Char:
switch val := v.(type) {
case rune:
return fmt.Sprintf("%c", val), nil
}
case Int:
switch val := v.(type) {
case uint8, int8, uint16, int16, int32:
return fmt.Sprintf("%di", val), nil
}
case Float:
switch val := v.(type) {
case float32:
return fmt.Sprintf("%f", val), nil
}
case Symbol:
switch val := v.(type) {
case string:
return quoteEscape(val, needsEscapeForSymbol, quoteSymbolFn), nil
}
case String:
switch val := v.(type) {
case string:
return quoteEscape(val, needsEscapeForStr, quoteStringFn), nil
}
case Long:
switch val := v.(type) {
case uint8, int8, uint16, int16, uint32, int32, int64, int:
return fmt.Sprintf("%di", val), nil
}
case Date:
switch val := v.(type) {
case int64:
return fmt.Sprintf("%d", val), nil
case time.Time:
return fmt.Sprintf("%d", val.UnixMilli()), nil
}
case Timestamp:
switch val := v.(type) {
case int64:
return fmt.Sprintf("%dt", val), nil
case time.Time:
return fmt.Sprintf("%dt", val.UnixMicro()), nil
}
case Double:
switch val := v.(type) {
case float32, float64:
return fmt.Sprintf("%f", val), nil
}
case Binary:
switch val := v.(type) {
case Bytes:
return fmt.Sprintf("\"%s\"", base64.StdEncoding.EncodeToString(val)), nil
case string:
return fmt.Sprintf("\"%s\"", base64.StdEncoding.EncodeToString([]byte(val))), nil
case []byte:
return fmt.Sprintf("\"%s\"", base64.StdEncoding.EncodeToString(val)), nil
}
case JSON:
by, err := json.Marshal(v)
if err != nil {
return "", fmt.Errorf("could not json marshal %T: %w", v, err)
}
return fmt.Sprintf("\"%s\"", base64.StdEncoding.EncodeToString(by)), nil
case UUID:
// sent in as a hyphenated uuid string
switch val := v.(type) {
case string:
return fmt.Sprintf("\"%s\"", val), nil
}
default:
return "", fmt.Errorf("type %T is not compatible with %s", v, qdbType)
}
return "", fmt.Errorf("type %T is not compatible with %s", v, qdbType)
}
// Quote and escape an ILP input value, returns new string that is properly quoted and escaped.
func quoteEscape(s string, needsEscape func(byte) bool, quoteFn func(*strings.Builder)) string {
var b strings.Builder
quoteFn(&b)
for i := 0; i < len(s); i++ {
if needsEscape(s[i]) {
b.WriteByte('\\')
}
b.WriteByte(s[i])
}
quoteFn(&b)
return b.String()
}
func needsEscapeForStr(c byte) bool {
return c == '\n' || c == '\r' || c == '\\' || c == '"'
}
func quoteStringFn(b *strings.Builder) {
b.WriteByte('"')
}
func needsEscapeForSymbol(c byte) bool {
return c == '\n' || c == '\r' || c == '\\' || c == ' ' || c == ',' || c == '='
}
func quoteSymbolFn(*strings.Builder) {
// no op, symbols are not quoted in ILP format
}
var supportedQDBTypes = []QuestDBType{
Boolean,
Byte,
Short,
Char,
Int,
Float,
Symbol,
String,
Long,
Date,
Timestamp,
Double,
Binary,
JSON,
UUID,
// Long256,
}
// TableNamer is an interface which has a single method, TableName, which
// returns a string representing the struct's table name in QuestDB.
type TableNamer interface {
TableName() string
}
// isValidAndSupportedQuestDBType func takes a str string and returns a bool representing
// whether or not str is a valid and supported QuestDBType.
func isValidAndSupportedQuestDBType(str QuestDBType) bool {
for _, kind := range supportedQDBTypes {
if str == kind {
return true
}
}
return false
}
// isSerializableType takes a v interface{} and returns a bool which represents
// whether or not v can be serialized into Influx line protocol message value.
func IsSerializableType(v interface{}) bool {
switch v.(type) {
case bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, int,
float32, float64, string, Bytes, time.Time:
return true
default:
return false
}
}