-
Notifications
You must be signed in to change notification settings - Fork 2
/
serial.go
143 lines (116 loc) · 3.92 KB
/
serial.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
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trino
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
type UnsupportedArgError struct {
t string
}
func (e UnsupportedArgError) Error() string {
return fmt.Sprintf("trino: unsupported arg type: %s", e.t)
}
// Numeric is a string representation of a number, such as "10", "5.5" or in scientific form
// If another string format is used it will error to serialise
type Numeric string
// Serial converts any supported value to its equivalent string for as a Trino parameter
// See https://trino.io/docs/current/language/types.html
func Serial(v interface{}) (string, error) {
switch x := v.(type) {
case nil:
return "", UnsupportedArgError{"<nil>"}
// numbers convertible to int
case int8:
return strconv.Itoa(int(x)), nil
case int16:
return strconv.Itoa(int(x)), nil
case int32:
return strconv.Itoa(int(x)), nil
case int:
return strconv.Itoa(x), nil
case uint16:
return strconv.Itoa(int(x)), nil
case int64:
return strconv.FormatInt(x, 10), nil
case uint32:
return strconv.FormatUint(uint64(x), 10), nil
case uint:
return strconv.FormatUint(uint64(x), 10), nil
case uint64:
return strconv.FormatUint(x, 10), nil
// float32, float64 not supported because digit precision will easily cause large problems
case float32:
return "", UnsupportedArgError{"float32"}
case float64:
return "", UnsupportedArgError{"float64"}
case Numeric:
if _, err := strconv.ParseFloat(string(x), 64); err != nil {
return "", err
}
return string(x), nil
// note byte and uint are not supported, this is because byte is an alias for uint8
// if you were to use uint8 (as a number) it could be interpreted as a byte, so it is unsupported
// use string instead of byte and any other uint/int type for uint8
case byte:
return "", UnsupportedArgError{"byte/uint8"}
case bool:
return strconv.FormatBool(x), nil
case string:
return "'" + strings.Replace(x, "'", "''", -1) + "'", nil
// TODO - []byte should probably be matched to 'VARBINARY' in presto
case []byte:
return "", UnsupportedArgError{"[]byte"}
// time.Time and time.Duration not supported as time and date take several different formats in Trino
case time.Time:
return "", UnsupportedArgError{"time.Time"}
case time.Duration:
return "", UnsupportedArgError{"time.Duration"}
// TODO - json.RawMesssage should probably be matched to 'JSON' in Trino
case json.RawMessage:
return "", UnsupportedArgError{"json.RawMessage"}
}
if reflect.TypeOf(v).Kind() == reflect.Slice {
x := reflect.ValueOf(v)
if x.IsNil() {
return "", UnsupportedArgError{"[]<nil>"}
}
slice := make([]interface{}, x.Len())
for i := 0; i < x.Len(); i++ {
slice[i] = x.Index(i).Interface()
}
return serialSlice(slice)
}
if reflect.TypeOf(v).Kind() == reflect.Map {
// are Trino MAPs indifferent to order? Golang maps are, if Trino aren't then the two types can't be compatible
return "", UnsupportedArgError{"map"}
}
// TODO - consider the remaining types in https://trino.io/docs/current/language/types.html (Row, IP, ...)
return "", UnsupportedArgError{fmt.Sprintf("%T", v)}
}
func serialSlice(v []interface{}) (string, error) {
ss := make([]string, len(v))
for i, x := range v {
s, err := Serial(x)
if err != nil {
return "", err
}
ss[i] = s
}
return "ARRAY[" + strings.Join(ss, ", ") + "]", nil
}