-
Notifications
You must be signed in to change notification settings - Fork 509
/
ClickHouseDataType.java
393 lines (360 loc) · 14.6 KB
/
ClickHouseDataType.java
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
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
package com.clickhouse.client;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
/**
* Basic ClickHouse data types.
*
* <p>
* This list is based on the list of data type families returned by
* {@code SELECT * FROM system.data_type_families}
*
* <p>
* {@code LowCardinality} and {@code Nullable} are technically data types in
* ClickHouse, but for the sake of this driver, we treat these data types as
* modifiers for the underlying base data types.
*/
@SuppressWarnings("squid:S115")
public enum ClickHouseDataType {
IntervalYear(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalQuarter(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalMonth(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalWeek(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalDay(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalHour(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalMinute(Long.class, false, true, true, 8, 19, 0, 0, 0),
IntervalSecond(Long.class, false, true, true, 8, 19, 0, 0, 0),
UInt8(Short.class, false, true, false, 1, 3, 0, 0, 0, "INT1 UNSIGNED", "TINYINT UNSIGNED"),
UInt16(Integer.class, false, true, false, 2, 5, 0, 0, 0, "SMALLINT UNSIGNED"),
UInt32(Long.class, false, true, false, 4, 10, 0, 0, 0, "INT UNSIGNED", "INTEGER UNSIGNED", "MEDIUMINT UNSIGNED"),
UInt64(Long.class, false, true, false, 8, 20, 0, 0, 0, "BIGINT UNSIGNED"),
UInt128(BigInteger.class, false, true, false, 16, 39, 0, 0, 0),
UInt256(BigInteger.class, false, true, false, 32, 78, 0, 0, 0),
Int8(Byte.class, false, true, true, 1, 3, 0, 0, 0, "BYTE", "INT1", "INT1 SIGNED", "TINYINT", "TINYINT SIGNED"),
Int16(Short.class, false, true, true, 2, 5, 0, 0, 0, "SMALLINT", "SMALLINT SIGNED"),
Int32(Integer.class, false, true, true, 4, 10, 0, 0, 0, "INT", "INTEGER", "MEDIUMINT", "INT SIGNED",
"INTEGER SIGNED", "MEDIUMINT SIGNED"),
Int64(Long.class, false, true, true, 8, 19, 0, 0, 0, "BIGINT", "BIGINT SIGNED"),
Int128(BigInteger.class, false, true, true, 16, 39, 0, 0, 0),
Int256(BigInteger.class, false, true, true, 32, 77, 0, 0, 0),
Bool(Boolean.class, false, false, true, 1, 1, 0, 0, 0, "BOOLEAN"),
Date(LocalDate.class, false, false, false, 2, 10, 0, 0, 0),
Date32(LocalDate.class, false, false, false, 4, 10, 0, 0, 0),
DateTime(LocalDateTime.class, true, false, false, 0, 29, 0, 0, 9, "TIMESTAMP"),
DateTime32(LocalDateTime.class, true, false, false, 4, 19, 0, 0, 0),
DateTime64(LocalDateTime.class, true, false, false, 8, 29, 3, 0, 9),
Decimal(BigDecimal.class, true, false, true, 0, 76, 0, 0, 76, "DEC", "NUMERIC", "FIXED"),
Decimal32(BigDecimal.class, true, false, true, 4, 9, 9, 0, 9),
Decimal64(BigDecimal.class, true, false, true, 8, 18, 18, 0, 18),
Decimal128(BigDecimal.class, true, false, true, 16, 38, 38, 0, 38),
Decimal256(BigDecimal.class, true, false, true, 32, 76, 20, 0, 76),
UUID(UUID.class, false, true, false, 16, 69, 0, 0, 0),
@Deprecated
Enum(String.class, true, true, false, 1, 0, 0, 0, 0),
Enum8(String.class, true, true, false, 1, 0, 0, 0, 0), // "ENUM"),
Enum16(String.class, true, true, false, 2, 0, 0, 0, 0),
Float32(Float.class, false, true, true, 4, 12, 0, 0, 38, "FLOAT", "REAL", "SINGLE"),
Float64(Double.class, false, true, true, 16, 22, 0, 0, 308, "DOUBLE", "DOUBLE PRECISION"),
IPv4(Inet4Address.class, false, true, false, 4, 10, 0, 0, 0, "INET4"),
IPv6(Inet6Address.class, false, true, false, 16, 39, 0, 0, 0, "INET6"),
FixedString(String.class, true, true, false, 0, 0, 0, 0, 0, "BINARY"),
String(String.class, false, true, false, 0, 0, 0, 0, 0, "BINARY LARGE OBJECT", "BINARY VARYING", "BLOB", "BYTEA",
"CHAR", "CHARACTER", "CHARACTER LARGE OBJECT", "CHARACTER VARYING", "CHAR LARGE OBJECT", "CHAR VARYING",
"CLOB", "LONGBLOB", "LONGTEXT", "MEDIUMBLOB", "MEDIUMTEXT", "NATIONAL CHAR", "NATIONAL CHARACTER",
"NATIONAL CHARACTER LARGE OBJECT", "NATIONAL CHARACTER VARYING", "NATIONAL CHAR VARYING", "NCHAR",
"NCHAR LARGE OBJECT", "NCHAR VARYING", "NVARCHAR", "TEXT", "TINYBLOB", "TINYTEXT", "VARBINARY", "VARCHAR",
"VARCHAR2"),
AggregateFunction(String.class, true, true, false, 0, 0, 0, 0, 0), // implementation-defined intermediate state
SimpleAggregateFunction(String.class, true, true, false, 0, 0, 0, 0, 0),
Array(Object.class, true, true, false, 0, 0, 0, 0, 0),
Map(Map.class, true, true, false, 0, 0, 0, 0, 0),
Nested(Object.class, true, true, false, 0, 0, 0, 0, 0),
Tuple(List.class, true, true, false, 0, 0, 0, 0, 0),
Point(Object.class, false, true, true, 33, 0, 0, 0, 0), // same as Tuple(Float64, Float64)
Polygon(Object.class, false, true, true, 0, 0, 0, 0, 0), // same as Array(Ring)
MultiPolygon(Object.class, false, true, true, 0, 0, 0, 0, 0), // same as Array(Polygon)
Ring(Object.class, false, true, true, 0, 0, 0, 0, 0), // same as Array(Point)
Nothing(Object.class, false, true, false, 0, 0, 0, 0, 0);
/**
* Immutable set(sorted) for all aliases.
*/
public static final Set<String> allAliases;
/**
* Immutable mapping between name and type.
*/
public static final Map<String, ClickHouseDataType> name2type;
static {
Set<String> set = new TreeSet<>();
Map<String, ClickHouseDataType> map = new HashMap<>();
String errorMsg = "[%s] is used by type [%s]";
ClickHouseDataType used = null;
for (ClickHouseDataType t : ClickHouseDataType.values()) {
String name = t.name();
if (!t.isCaseSensitive()) {
name = name.toUpperCase();
}
used = map.put(name, t);
if (used != null) {
throw new IllegalStateException(java.lang.String.format(Locale.ROOT, errorMsg, name, used.name()));
}
// aliases are all case-insensitive
for (String alias : t.aliases) {
String aliasInUpperCase = alias.toUpperCase();
set.add(aliasInUpperCase);
used = map.put(aliasInUpperCase, t);
if (used != null) {
throw new IllegalStateException(java.lang.String.format(Locale.ROOT, errorMsg, alias, used.name()));
}
}
}
allAliases = Collections.unmodifiableSet(set);
name2type = Collections.unmodifiableMap(map);
}
/**
* Checks if the given type name is an alias or not.
*
* @param typeName type name
* @return true if the type name is an alias; false otherwise
*/
public static boolean isAlias(String typeName) {
return typeName != null && !typeName.isEmpty() && allAliases.contains(typeName.trim().toUpperCase());
}
public static List<String> match(String part) {
List<String> types = new LinkedList<>();
for (ClickHouseDataType t : values()) {
if (t.isCaseSensitive()) {
if (t.name().equals(part)) {
types.add(t.name());
break;
}
} else {
if (t.name().equalsIgnoreCase(part)) {
types.add(part);
break;
}
}
}
if (types.isEmpty()) {
part = part.toUpperCase();
String prefix = part + ' ';
for (String alias : allAliases) {
if (alias.length() == part.length() && alias.equals(part)) {
types.add(alias);
} else if (alias.length() > part.length() && alias.startsWith(prefix)) {
types.add(alias);
}
}
}
return types;
}
/**
* Converts given type name to corresponding data type.
*
* @param typeName non-empty type name
* @return data type
*/
public static ClickHouseDataType of(String typeName) {
if (typeName == null || (typeName = typeName.trim()).isEmpty()) {
throw new IllegalArgumentException("Non-empty typeName is required");
}
ClickHouseDataType type = name2type.get(typeName);
if (type == null) {
type = name2type.get(typeName.toUpperCase()); // case-insensitive or just an alias
}
if (type == null) {
throw new IllegalArgumentException("Unknown data type: " + typeName);
}
return type;
}
/**
* Converts given Java class to wrapper object(e.g. {@code int.class} to
* {@code Integer.class}) if applicable.
*
* @param javaClass Java class
* @return wrapper object
*/
public static Class<?> toObjectType(Class<?> javaClass) {
if (byte.class == javaClass || boolean.class == javaClass || Boolean.class == javaClass) {
javaClass = Byte.class;
} else if (short.class == javaClass) {
javaClass = Short.class;
} else if (int.class == javaClass || char.class == javaClass || Character.class == javaClass) {
javaClass = Integer.class;
} else if (long.class == javaClass) {
javaClass = Long.class;
} else if (float.class == javaClass) {
javaClass = Float.class;
} else if (double.class == javaClass) {
javaClass = Double.class;
} else if (javaClass == null) {
javaClass = Object.class;
}
return javaClass;
}
/**
* Converts given Java class to primitive types(e.g. {@code Integer.class} to
* {@code int.class}) if applicable.
*
* @param javaClass Java class
* @return primitive type
*/
public static Class<?> toPrimitiveType(Class<?> javaClass) {
if (Byte.class == javaClass || Boolean.class == javaClass || boolean.class == javaClass) {
javaClass = byte.class;
} else if (Short.class == javaClass) {
javaClass = short.class;
} else if (Integer.class == javaClass || Character.class == javaClass || char.class == javaClass) {
javaClass = int.class;
} else if (Long.class == javaClass) {
javaClass = long.class;
} else if (Float.class == javaClass) {
javaClass = float.class;
} else if (Double.class == javaClass) {
javaClass = double.class;
} else if (javaClass == null) {
javaClass = Object.class;
}
return javaClass;
}
private final Class<?> objectType;
private final Class<?> primitiveType;
private final boolean parameter;
private final boolean caseSensitive;
private final boolean signed;
private final List<String> aliases;
private final int byteLength;
private final int maxPrecision;
private final int defaultScale;
private final int minScale;
private final int maxScale;
ClickHouseDataType(Class<?> javaClass, boolean parameter, boolean caseSensitive, boolean signed, int byteLength,
int maxPrecision, int defaultScale, int minScale, int maxScale, String... aliases) {
this.objectType = toObjectType(javaClass);
this.primitiveType = toPrimitiveType(javaClass);
this.parameter = parameter;
this.caseSensitive = caseSensitive;
this.signed = signed;
this.byteLength = byteLength;
this.maxPrecision = maxPrecision;
this.defaultScale = defaultScale;
this.minScale = minScale;
this.maxScale = maxScale;
if (aliases == null || aliases.length == 0) {
this.aliases = Collections.emptyList();
} else {
this.aliases = Collections.unmodifiableList(Arrays.asList(aliases));
}
}
/**
* Gets Java class for this data type. Prefer wrapper objects to primitives(e.g.
* {@code Integer.class} instead of {@code int.class}).
*
* @return Java class
*/
public Class<?> getObjectClass() {
return objectType;
}
/**
* Gets Java class for this data type. Prefer primitives to wrapper objects(e.g.
* {@code int.class} instead of {@code Integer.class}).
*
* @return Java class
*/
public Class<?> getPrimitiveClass() {
return primitiveType;
}
/**
* Checks if this data type may have parameter(s).
*
* @return true if this data type may have parameter; false otherwise
*/
public boolean hasParameter() {
return parameter;
}
/**
* Checks if name of this data type is case sensistive or not.
*
* @return true if it's case sensitive; false otherwise
*/
public boolean isCaseSensitive() {
return caseSensitive;
}
/**
* Checks if this data type could be a nested structure.
*
* @return true if it could be a nested structure; false otherwise
*/
public boolean isNested() {
return this == AggregateFunction || this == Array || this == Map || this == Nested || this == Tuple;
}
/**
* Checks if this data type represents signed number.
*
* @return true if it's signed; false otherwise
*/
public boolean isSigned() {
return signed;
}
/**
* Gets immutable list of aliases for this data type.
*
* @return immutable list of aliases
*/
public List<String> getAliases() {
return aliases;
}
/**
* Gets byte length of this data type. Zero means unlimited.
*
* @return byte length of this data type
*/
public int getByteLength() {
return byteLength;
}
/**
* Gets maximum precision of this data type. Zero means unknown or not
* supported.
*
* @return maximum precision of this data type.
*/
public int getMaxPrecision() {
return maxPrecision;
}
/**
* Gets default scale of this data type. Zero means unknown or not supported.
*
* @return default scale of this data type.
*/
public int getDefaultScale() {
return defaultScale;
}
/**
* Gets minimum scale of this data type. Zero means unknown or not supported.
*
* @return minimum scale of this data type.
*/
public int getMinScale() {
return minScale;
}
/**
* Gets maximum scale of this data type. Zero means unknown or not supported.
*
* @return maximum scale of this data type.
*/
public int getMaxScale() {
return maxScale;
}
}