forked from ydb-platform/ariga-atlas
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconvert.go
More file actions
313 lines (277 loc) · 7.54 KB
/
convert.go
File metadata and controls
313 lines (277 loc) · 7.54 KB
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// Copyright 2021-present The Atlas Authors. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
//go:build !ent
package ydb
import (
"errors"
"fmt"
"strconv"
"strings"
"ariga.io/atlas/sql/schema"
)
// FormatType converts a schema.Type to its YDB string representation.
func FormatType(typ schema.Type) (string, error) {
var (
formatted string
err error
)
switch t := typ.(type) {
case *OptionalType:
formatted = t.T
case *schema.BoolType:
formatted = TypeBool
case *schema.IntegerType:
formatted, err = formatIntegerType(t)
case *schema.FloatType:
formatted, err = formatFloatType(t)
case *schema.DecimalType:
formatted, err = formatDecimalType(t)
case *SerialType:
formatted = t.T
case *schema.BinaryType:
formatted = TypeString
case *schema.StringType:
formatted = TypeUtf8
case *schema.JSONType:
formatted, err = formatJSONType(t)
case *YsonType:
formatted = t.T
case *schema.UUIDType:
formatted = TypeUUID
case *schema.TimeType:
formatted, err = formatTimeType(t)
case *schema.EnumType:
err = errors.New("ydb: Enum can't be used as column data types for tables")
case *schema.UnsupportedType:
err = fmt.Errorf("ydb: unsupported type: %q", t.T)
default:
err = fmt.Errorf("ydb: unknown schema type: %T", t)
}
if err != nil {
return "", err
}
return formatted, nil
}
func formatIntegerType(intType *schema.IntegerType) (string, error) {
switch typ := strings.ToLower(intType.T); typ {
case TypeInt8:
if intType.Unsigned {
return TypeUint8, nil
}
return TypeInt8, nil
case TypeInt16:
if intType.Unsigned {
return TypeUint16, nil
}
return TypeInt16, nil
case TypeInt32:
if intType.Unsigned {
return TypeUint32, nil
}
return TypeInt32, nil
case TypeInt64:
if intType.Unsigned {
return TypeUint64, nil
}
return TypeInt64, nil
case TypeUint8, TypeUint16, TypeUint32, TypeUint64:
return typ, nil
default:
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", intType.T)
}
}
func formatFloatType(floatType *schema.FloatType) (string, error) {
switch typ := strings.ToLower(floatType.T); typ {
case TypeFloat, TypeDouble:
return typ, nil
default:
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", floatType.T)
}
}
func formatDecimalType(decType *schema.DecimalType) (string, error) {
if decType.Precision < 1 || decType.Precision > 35 {
return "", fmt.Errorf("ydb: DECIMAL precision must be in [1, 35] range, but was %q", decType.Precision)
}
if decType.Scale < 0 || decType.Scale > decType.Precision {
return "", fmt.Errorf("ydb: DECIMAL scale must be in [1, precision] range, but was %q", decType.Precision)
}
return fmt.Sprintf("%s(%d,%d)", TypeDecimal, decType.Precision, decType.Scale), nil
}
func formatJSONType(jsonType *schema.JSONType) (string, error) {
typ := strings.ToLower(jsonType.T)
switch typ {
case TypeJSONDocument, TypeJSON:
return typ, nil
default:
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", jsonType.T)
}
}
func formatTimeType(timeType *schema.TimeType) (string, error) {
switch typ := strings.ToLower(timeType.T); typ {
case TypeDate,
TypeDate32,
TypeDateTime,
TypeDateTime64,
TypeTimestamp,
TypeTimestamp64,
TypeInterval,
TypeInterval64,
TypeTzDate,
TypeTzDate32,
TypeTzDateTime,
TypeTzDateTime64,
TypeTzTimestamp,
TypeTzTimestamp64:
return typ, nil
default:
return "", fmt.Errorf("ydb: unsupported object identifier type: %q", timeType.T)
}
}
// ParseType returns the schema.Type value represented by the given raw type.
// The raw value is expected to follow the format of input for the CREATE TABLE statement.
func ParseType(typ string) (schema.Type, error) {
colDesc, err := parseColumn(strings.ToLower(typ))
if err != nil {
return nil, err
}
return columnType(colDesc)
}
// Nullability in YDB/YQL:
//
// YQL implements nullable types by wrapping them in Optional<T> containers.
// However, DDL statements do not support declaring arbitrary container types.
// Therefore, we assume that nested constructs (e.g. Optional<Optional<T>>) cannot
// occur when parsing types from DDL schemas.
type columnDecscriptor struct {
strT string
nullable bool
precision int64
scale int64
parts []string
}
func parseColumn(typ string) (*columnDecscriptor, error) {
if typ == "" {
return nil, errors.New("ydb: unexpected empty column type")
}
var (
err error
colDesc *columnDecscriptor
)
colDesc, typ = parseOptionalType(typ)
parts := strings.FieldsFunc(typ, func(r rune) bool {
return r == '(' || r == ')' || r == ' ' || r == ','
})
colDesc.strT = strings.ToLower(parts[0])
colDesc.parts = parts
switch colDesc.strT {
case TypeDecimal:
err = parseDecimalType(parts, colDesc)
case TypeFloat:
colDesc.precision = 24
case TypeDouble:
colDesc.precision = 53
}
if err != nil {
return nil, err
}
return colDesc, nil
}
func parseOptionalType(typ string) (*columnDecscriptor, string) {
colDesc := &columnDecscriptor{}
if strings.HasPrefix(typ, "optional<") {
colDesc.nullable = true
typ = strings.TrimPrefix(typ, "optional<")
typ = strings.TrimSuffix(typ, ">")
}
return colDesc, typ
}
func parseDecimalType(parts []string, colDesc *columnDecscriptor) error {
if len(parts) < 3 {
return errors.New("ydb: decimal should specify precision and scale")
}
precision, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil || precision < 1 || precision > 35 {
return fmt.Errorf("ydb: DECIMAL precision must be in range [1, 35], but was %q", parts[1])
}
scale, err := strconv.ParseInt(parts[2], 10, 64)
if err != nil || scale < 0 || scale > precision {
return fmt.Errorf("ydb: DECIMAL scale must be in range [1, precision], but was %q", parts[1])
}
colDesc.precision = precision
colDesc.scale = scale
return nil
}
func columnType(colDesc *columnDecscriptor) (schema.Type, error) {
var typ schema.Type
if colDesc.nullable {
colDesc.nullable = false
innerType, err := columnType(colDesc)
if err != nil {
return nil, err
}
innerTypeStr, err := FormatType(innerType)
if err != nil {
return nil, err
}
return &OptionalType{
T: fmt.Sprintf("optional<%s>", innerTypeStr),
InnerType: innerType,
}, nil
}
switch strT := colDesc.strT; strT {
case TypeBool:
typ = &schema.BoolType{T: strT}
case TypeInt8, TypeInt16, TypeInt32, TypeInt64:
typ = &schema.IntegerType{
T: strT,
Unsigned: false,
}
case TypeUint8, TypeUint16, TypeUint32, TypeUint64:
typ = &schema.IntegerType{
T: strT,
Unsigned: true,
}
case TypeFloat, TypeDouble:
typ = &schema.FloatType{
T: strT,
Precision: int(colDesc.precision),
}
case TypeDecimal:
typ = &schema.DecimalType{
T: strT,
Precision: int(colDesc.precision),
Scale: int(colDesc.scale),
}
case TypeSmallSerial, TypeSerial2, TypeSerial, TypeSerial4, TypeSerial8, TypeBigSerial:
typ = &SerialType{T: strT}
case TypeString:
typ = &schema.BinaryType{T: strT}
case TypeUtf8:
typ = &schema.StringType{T: strT}
case TypeJSON, TypeJSONDocument:
typ = &schema.JSONType{T: strT}
case TypeYson:
typ = &YsonType{T: strT}
case TypeUUID:
typ = &schema.UUIDType{T: strT}
case TypeDate,
TypeDate32,
TypeDateTime,
TypeDateTime64,
TypeTimestamp,
TypeTimestamp64,
TypeInterval,
TypeInterval64,
TypeTzDate,
TypeTzDate32,
TypeTzDateTime,
TypeTzDateTime64,
TypeTzTimestamp,
TypeTzTimestamp64:
typ = &schema.TimeType{T: strT}
default:
typ = &schema.UnsupportedType{T: strT}
}
return typ, nil
}