1#include "duckdb/common/types.hpp"
2
3#include "duckdb/common/exception.hpp"
4#include "duckdb/common/serializer.hpp"
5#include "duckdb/common/string_util.hpp"
6#include "duckdb/common/types/string_type.hpp"
7
8#include <cmath>
9
10using namespace std;
11
12namespace duckdb {
13
14const SQLType SQLType::SQLNULL = SQLType(SQLTypeId::SQLNULL);
15const SQLType SQLType::BOOLEAN = SQLType(SQLTypeId::BOOLEAN);
16const SQLType SQLType::TINYINT = SQLType(SQLTypeId::TINYINT);
17const SQLType SQLType::SMALLINT = SQLType(SQLTypeId::SMALLINT);
18const SQLType SQLType::INTEGER = SQLType(SQLTypeId::INTEGER);
19const SQLType SQLType::BIGINT = SQLType(SQLTypeId::BIGINT);
20const SQLType SQLType::FLOAT = SQLType(SQLTypeId::FLOAT);
21const SQLType SQLType::DOUBLE = SQLType(SQLTypeId::DOUBLE);
22const SQLType SQLType::DATE = SQLType(SQLTypeId::DATE);
23const SQLType SQLType::TIMESTAMP = SQLType(SQLTypeId::TIMESTAMP);
24const SQLType SQLType::TIME = SQLType(SQLTypeId::TIME);
25
26const SQLType SQLType::VARCHAR = SQLType(SQLTypeId::VARCHAR);
27const SQLType SQLType::VARBINARY = SQLType(SQLTypeId::VARBINARY);
28
29const SQLType SQLType::BLOB = SQLType(SQLTypeId::BLOB);
30
31// TODO these are incomplete and should maybe not exist as such
32const SQLType SQLType::STRUCT = SQLType(SQLTypeId::STRUCT);
33const SQLType SQLType::LIST = SQLType(SQLTypeId::LIST);
34
35const SQLType SQLType::ANY = SQLType(SQLTypeId::ANY);
36
37const vector<SQLType> SQLType::NUMERIC = {
38 SQLType::TINYINT, SQLType::SMALLINT, SQLType::INTEGER, SQLType::BIGINT,
39 SQLType::FLOAT, SQLType::DOUBLE, SQLType(SQLTypeId::DECIMAL)};
40
41const vector<SQLType> SQLType::INTEGRAL = {SQLType::TINYINT, SQLType::SMALLINT, SQLType::INTEGER, SQLType::BIGINT};
42
43const vector<SQLType> SQLType::ALL_TYPES = {
44 SQLType::BOOLEAN, SQLType::TINYINT, SQLType::SMALLINT, SQLType::INTEGER, SQLType::BIGINT,
45 SQLType::DATE, SQLType::TIMESTAMP, SQLType::DOUBLE, SQLType::FLOAT, SQLType(SQLTypeId::DECIMAL),
46 SQLType::VARCHAR, SQLType::BLOB};
47// TODO add LIST/STRUCT here
48
49const TypeId ROW_TYPE = TypeId::INT64;
50
51string TypeIdToString(TypeId type) {
52 switch (type) {
53 case TypeId::BOOL:
54 return "BOOL";
55 case TypeId::INT8:
56 return "INT8";
57 case TypeId::INT16:
58 return "INT16";
59 case TypeId::INT32:
60 return "INT32";
61 case TypeId::INT64:
62 return "INT64";
63 case TypeId::HASH:
64 return "HASH";
65 case TypeId::POINTER:
66 return "POINTER";
67 case TypeId::FLOAT:
68 return "FLOAT";
69 case TypeId::DOUBLE:
70 return "DOUBLE";
71 case TypeId::VARCHAR:
72 return "VARCHAR";
73 case TypeId::VARBINARY:
74 return "VARBINARY";
75 case TypeId::STRUCT:
76 return "STRUCT<?>";
77 case TypeId::LIST:
78 return "LIST<?>";
79 default:
80 throw ConversionException("Invalid TypeId %d", type);
81 }
82}
83
84idx_t GetTypeIdSize(TypeId type) {
85 switch (type) {
86 case TypeId::BOOL:
87 return sizeof(bool);
88 case TypeId::INT8:
89 return sizeof(int8_t);
90 case TypeId::INT16:
91 return sizeof(int16_t);
92 case TypeId::INT32:
93 return sizeof(int32_t);
94 case TypeId::INT64:
95 return sizeof(int64_t);
96 case TypeId::FLOAT:
97 return sizeof(float);
98 case TypeId::DOUBLE:
99 return sizeof(double);
100 case TypeId::HASH:
101 return sizeof(hash_t);
102 case TypeId::POINTER:
103 return sizeof(uintptr_t);
104 case TypeId::VARCHAR:
105 return sizeof(string_t);
106 case TypeId::STRUCT:
107 return 0; // no own payload
108 case TypeId::LIST:
109 return 16; // offset + len
110 case TypeId::VARBINARY:
111 return sizeof(blob_t);
112 default:
113 throw ConversionException("Invalid TypeId %d", type);
114 }
115}
116
117SQLType SQLTypeFromInternalType(TypeId type) {
118 switch (type) {
119 case TypeId::BOOL:
120 return SQLType(SQLTypeId::BOOLEAN);
121 case TypeId::INT8:
122 return SQLType::TINYINT;
123 case TypeId::INT16:
124 return SQLType::SMALLINT;
125 case TypeId::INT32:
126 return SQLType::INTEGER;
127 case TypeId::INT64:
128 return SQLType::BIGINT;
129 case TypeId::FLOAT:
130 return SQLType::FLOAT;
131 case TypeId::DOUBLE:
132 return SQLType::DOUBLE;
133 case TypeId::VARCHAR:
134 return SQLType::VARCHAR;
135 case TypeId::VARBINARY:
136 return SQLType(SQLTypeId::VARBINARY);
137 case TypeId::STRUCT:
138 return SQLType(SQLTypeId::STRUCT); // TODO we do not know the child types here
139 case TypeId::LIST:
140 return SQLType(SQLTypeId::LIST);
141 default:
142 throw ConversionException("Invalid TypeId %d", type);
143 }
144}
145
146bool TypeIsConstantSize(TypeId type) {
147 return (type >= TypeId::BOOL && type <= TypeId::DOUBLE) ||
148 (type >= TypeId::FIXED_SIZE_BINARY && type <= TypeId::DECIMAL) || type == TypeId::HASH ||
149 type == TypeId::POINTER;
150}
151bool TypeIsIntegral(TypeId type) {
152 return (type >= TypeId::UINT8 && type <= TypeId::INT64) || type == TypeId::HASH || type == TypeId::POINTER;
153}
154bool TypeIsNumeric(TypeId type) {
155 return type >= TypeId::UINT8 && type <= TypeId::DOUBLE;
156}
157bool TypeIsInteger(TypeId type) {
158 return type >= TypeId::UINT8 && type <= TypeId::INT64;
159}
160
161void SQLType::Serialize(Serializer &serializer) {
162 serializer.Write(id);
163 serializer.Write(width);
164 serializer.Write(scale);
165 serializer.WriteString(collation);
166}
167
168SQLType SQLType::Deserialize(Deserializer &source) {
169 auto id = source.Read<SQLTypeId>();
170 auto width = source.Read<uint16_t>();
171 auto scale = source.Read<uint8_t>();
172 auto collation = source.Read<string>();
173 return SQLType(id, width, scale, collation);
174}
175
176string SQLTypeIdToString(SQLTypeId id) {
177 switch (id) {
178 case SQLTypeId::BOOLEAN:
179 return "BOOLEAN";
180 case SQLTypeId::TINYINT:
181 return "TINYINT";
182 case SQLTypeId::SMALLINT:
183 return "SMALLINT";
184 case SQLTypeId::INTEGER:
185 return "INTEGER";
186 case SQLTypeId::BIGINT:
187 return "BIGINT";
188 case SQLTypeId::DATE:
189 return "DATE";
190 case SQLTypeId::TIME:
191 return "TIME";
192 case SQLTypeId::TIMESTAMP:
193 return "TIMESTAMP";
194 case SQLTypeId::FLOAT:
195 return "FLOAT";
196 case SQLTypeId::DOUBLE:
197 return "DOUBLE";
198 case SQLTypeId::DECIMAL:
199 return "DECIMAL";
200 case SQLTypeId::VARCHAR:
201 return "VARCHAR";
202 case SQLTypeId::BLOB:
203 return "BLOB";
204 case SQLTypeId::VARBINARY:
205 return "VARBINARY";
206 case SQLTypeId::CHAR:
207 return "CHAR";
208 case SQLTypeId::SQLNULL:
209 return "NULL";
210 case SQLTypeId::ANY:
211 return "ANY";
212 case SQLTypeId::STRUCT:
213 return "STRUCT<?>";
214 case SQLTypeId::LIST:
215 return "LIST<?>";
216 default:
217 return "INVALID";
218 }
219}
220
221string SQLTypeToString(SQLType type) {
222 // FIXME: display width/scale
223 switch (type.id) {
224 case SQLTypeId::STRUCT: {
225 string ret = "STRUCT<";
226 for (size_t i = 0; i < type.child_type.size(); i++) {
227 ret += type.child_type[i].first + ": " + SQLTypeToString(type.child_type[i].second);
228 if (i < type.child_type.size() - 1) {
229 ret += ", ";
230 }
231 }
232 ret += ">";
233 return ret;
234 }
235 case SQLTypeId::LIST: {
236 if (type.child_type.size() == 0) {
237 return "LIST<?>";
238 }
239 if (type.child_type.size() != 1) {
240 throw Exception("List needs a single child element");
241 }
242 return "LIST<" + SQLTypeToString(type.child_type[0].second) + ">";
243 }
244 default:
245 return SQLTypeIdToString(type.id);
246 }
247}
248
249SQLType TransformStringToSQLType(string str) {
250 auto lower_str = StringUtil::Lower(str);
251 // Transform column type
252 if (lower_str == "int" || lower_str == "int4" || lower_str == "signed" || lower_str == "integer" ||
253 lower_str == "integral" || lower_str == "int32") {
254 return SQLType::INTEGER;
255 } else if (lower_str == "varchar" || lower_str == "bpchar" || lower_str == "text" || lower_str == "string" ||
256 lower_str == "char") {
257 return SQLType::VARCHAR;
258 } else if (lower_str == "bytea" || lower_str == "blob") {
259 return SQLType::BLOB;
260 } else if (lower_str == "int8" || lower_str == "bigint" || lower_str == "int64" || lower_str == "long") {
261 return SQLType::BIGINT;
262 } else if (lower_str == "int2" || lower_str == "smallint" || lower_str == "short" || lower_str == "int16") {
263 return SQLType::SMALLINT;
264 } else if (lower_str == "timestamp" || lower_str == "datetime") {
265 return SQLType::TIMESTAMP;
266 } else if (lower_str == "bool" || lower_str == "boolean" || lower_str == "logical") {
267 return SQLType(SQLTypeId::BOOLEAN);
268 } else if (lower_str == "real" || lower_str == "float4" || lower_str == "float") {
269 return SQLType::FLOAT;
270 } else if (lower_str == "double" || lower_str == "numeric" || lower_str == "float8") {
271 return SQLType::DOUBLE;
272 } else if (lower_str == "tinyint" || lower_str == "int1") {
273 return SQLType::TINYINT;
274 } else if (lower_str == "varbinary") {
275 return SQLType(SQLTypeId::VARBINARY);
276 } else if (lower_str == "date") {
277 return SQLType::DATE;
278 } else if (lower_str == "time") {
279 return SQLType::TIME;
280 } else {
281 throw NotImplementedException("DataType %s not supported yet...\n", str.c_str());
282 }
283}
284
285bool SQLType::IsIntegral() const {
286 switch (id) {
287 case SQLTypeId::TINYINT:
288 case SQLTypeId::SMALLINT:
289 case SQLTypeId::INTEGER:
290 case SQLTypeId::BIGINT:
291 return true;
292 default:
293 return false;
294 }
295}
296
297bool SQLType::IsNumeric() const {
298 switch (id) {
299 case SQLTypeId::TINYINT:
300 case SQLTypeId::SMALLINT:
301 case SQLTypeId::INTEGER:
302 case SQLTypeId::BIGINT:
303 case SQLTypeId::FLOAT:
304 case SQLTypeId::DOUBLE:
305 case SQLTypeId::DECIMAL:
306 return true;
307 default:
308 return false;
309 }
310}
311
312bool SQLType::IsMoreGenericThan(SQLType &other) const {
313 if (other.id == id) {
314 return false;
315 }
316
317 if (other.id == SQLTypeId::SQLNULL) {
318 return true;
319 }
320
321 switch (id) {
322 case SQLTypeId::SMALLINT:
323 switch (other.id) {
324 case SQLTypeId::TINYINT:
325 return true;
326 default:
327 return false;
328 }
329 case SQLTypeId::INTEGER:
330 switch (other.id) {
331 case SQLTypeId::TINYINT:
332 case SQLTypeId::SMALLINT:
333 return true;
334 default:
335 return false;
336 }
337 case SQLTypeId::BIGINT:
338 switch (other.id) {
339 case SQLTypeId::TINYINT:
340 case SQLTypeId::SMALLINT:
341 case SQLTypeId::INTEGER:
342 return true;
343 default:
344 return false;
345 }
346 case SQLTypeId::DOUBLE:
347 switch (other.id) {
348 case SQLTypeId::TINYINT:
349 case SQLTypeId::SMALLINT:
350 case SQLTypeId::INTEGER:
351 case SQLTypeId::BIGINT:
352 return true;
353 default:
354 return false;
355 }
356 return false;
357 case SQLTypeId::DATE:
358 return false;
359 case SQLTypeId::TIMESTAMP:
360 switch (other.id) {
361 case SQLTypeId::TIME:
362 case SQLTypeId::DATE:
363 return true;
364 default:
365 return false;
366 }
367 case SQLTypeId::VARCHAR:
368 return true;
369 default:
370 return false;
371 }
372
373 return true;
374}
375
376TypeId GetInternalType(SQLType type) {
377 switch (type.id) {
378 case SQLTypeId::BOOLEAN:
379 return TypeId::BOOL;
380 case SQLTypeId::TINYINT:
381 return TypeId::INT8;
382 case SQLTypeId::SMALLINT:
383 return TypeId::INT16;
384 case SQLTypeId::SQLNULL:
385 case SQLTypeId::DATE:
386 case SQLTypeId::TIME:
387 case SQLTypeId::INTEGER:
388 return TypeId::INT32;
389 case SQLTypeId::BIGINT:
390 case SQLTypeId::TIMESTAMP:
391 return TypeId::INT64;
392 case SQLTypeId::FLOAT:
393 return TypeId::FLOAT;
394 case SQLTypeId::DOUBLE:
395 return TypeId::DOUBLE;
396 case SQLTypeId::DECIMAL:
397 // FIXME: for now
398 return TypeId::DOUBLE;
399 case SQLTypeId::VARCHAR:
400 case SQLTypeId::CHAR:
401 case SQLTypeId::BLOB:
402 return TypeId::VARCHAR;
403 case SQLTypeId::VARBINARY:
404 return TypeId::VARBINARY;
405 case SQLTypeId::STRUCT:
406 return TypeId::STRUCT;
407 case SQLTypeId::LIST:
408 return TypeId::LIST;
409 case SQLTypeId::ANY:
410 return TypeId::INVALID;
411 default:
412 throw ConversionException("Invalid SQLType %s", SQLTypeToString(type).c_str());
413 }
414}
415
416SQLType MaxSQLType(SQLType left, SQLType right) {
417 if (left.id < right.id) {
418 return right;
419 } else if (right.id < left.id) {
420 return left;
421 } else if (left.width > right.width || left.collation > right.collation) {
422 return left;
423 } else {
424 return right;
425 }
426}
427
428bool ApproxEqual(float ldecimal, float rdecimal) {
429 float epsilon = fabs(rdecimal) * 0.01;
430 return fabs(ldecimal - rdecimal) <= epsilon;
431}
432
433bool ApproxEqual(double ldecimal, double rdecimal) {
434 double epsilon = fabs(rdecimal) * 0.01;
435 return fabs(ldecimal - rdecimal) <= epsilon;
436}
437
438} // namespace duckdb
439