1use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub enum SqlType {
19 Bool,
21
22 Int32,
24
25 Int64,
27
28 Uint32,
30
31 Uint64,
33
34 Float32,
36
37 Float64,
39
40 Numeric {
42 precision: Option<u8>,
43 scale: Option<u8>,
44 },
45
46 Varchar,
48
49 Varbinary,
51
52 Date,
54
55 Time,
57
58 Datetime,
60
61 Timestamp,
63
64 Interval,
66
67 Array(Box<SqlType>),
69
70 Struct(Vec<StructField>),
72
73 Json,
75
76 Range(Box<SqlType>),
78
79 Uuid,
81
82 Unknown,
84
85 Any,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
91pub struct StructField {
92 pub name: Option<String>,
93 pub data_type: SqlType,
94}
95
96impl SqlType {
97 pub fn is_numeric(&self) -> bool {
99 matches!(
100 self,
101 SqlType::Int32
102 | SqlType::Int64
103 | SqlType::Uint32
104 | SqlType::Uint64
105 | SqlType::Float32
106 | SqlType::Float64
107 | SqlType::Numeric { .. }
108 )
109 }
110
111 pub fn is_integer(&self) -> bool {
113 matches!(
114 self,
115 SqlType::Int32 | SqlType::Int64 | SqlType::Uint32 | SqlType::Uint64
116 )
117 }
118
119 pub fn is_signed_integer(&self) -> bool {
121 matches!(self, SqlType::Int32 | SqlType::Int64)
122 }
123
124 pub fn is_unsigned_integer(&self) -> bool {
126 matches!(self, SqlType::Uint32 | SqlType::Uint64)
127 }
128
129 pub fn is_floating_point(&self) -> bool {
131 matches!(self, SqlType::Float32 | SqlType::Float64)
132 }
133
134 pub fn is_string(&self) -> bool {
136 matches!(self, SqlType::Varchar)
137 }
138
139 pub fn is_datetime(&self) -> bool {
141 matches!(
142 self,
143 SqlType::Date | SqlType::Time | SqlType::Datetime | SqlType::Timestamp
144 )
145 }
146
147 pub fn is_comparable_with(&self, other: &SqlType) -> bool {
149 match (self, other) {
150 (a, b) if a == b => true,
152
153 (a, b) if a.is_numeric() && b.is_numeric() => true,
155
156 (a, b) if a.is_datetime() && b.is_datetime() => true,
158
159 (SqlType::Unknown, _) | (_, SqlType::Unknown) => true,
161 (SqlType::Any, _) | (_, SqlType::Any) => true,
162
163 _ => false,
164 }
165 }
166
167 pub fn can_coerce_to(&self, target: &SqlType) -> bool {
169 match (self, target) {
170 (a, b) if a == b => true,
172
173 (SqlType::Int32, SqlType::Int64) => true,
175 (SqlType::Uint32, SqlType::Uint64) => true,
176 (SqlType::Uint32, SqlType::Int64) => true,
177
178 (SqlType::Int32, SqlType::Float32) => true,
180 (SqlType::Int32, SqlType::Float64) => true,
181 (SqlType::Int64, SqlType::Float64) => true,
182 (SqlType::Uint32, SqlType::Float32) => true,
183 (SqlType::Uint32, SqlType::Float64) => true,
184 (SqlType::Uint64, SqlType::Float64) => true,
185
186 (SqlType::Float32, SqlType::Float64) => true,
188
189 (SqlType::Int32, SqlType::Numeric { .. }) => true,
191 (SqlType::Int64, SqlType::Numeric { .. }) => true,
192 (SqlType::Uint32, SqlType::Numeric { .. }) => true,
193 (SqlType::Uint64, SqlType::Numeric { .. }) => true,
194
195 (SqlType::Float32, SqlType::Numeric { .. }) => true,
197 (SqlType::Float64, SqlType::Numeric { .. }) => true,
198
199 (SqlType::Date, SqlType::Datetime) => true,
201 (SqlType::Date, SqlType::Timestamp) => true,
202
203 (SqlType::Datetime, SqlType::Timestamp) => true,
205
206 (SqlType::Unknown, _) => true,
208
209 (_, SqlType::Any) => true,
211
212 (SqlType::Array(a), SqlType::Array(b)) => a.can_coerce_to(b),
214
215 _ => false,
216 }
217 }
218
219 pub fn common_supertype(&self, other: &SqlType) -> Option<SqlType> {
221 match (self, other) {
222 (a, b) if a == b => Some(a.clone()),
224
225 (SqlType::Int32, SqlType::Int64) | (SqlType::Int64, SqlType::Int32) => {
227 Some(SqlType::Int64)
228 }
229 (SqlType::Uint32, SqlType::Uint64) | (SqlType::Uint64, SqlType::Uint32) => {
230 Some(SqlType::Uint64)
231 }
232 (SqlType::Int32, SqlType::Uint32) | (SqlType::Uint32, SqlType::Int32) => {
233 Some(SqlType::Int64) }
235 (SqlType::Int32, SqlType::Uint64) | (SqlType::Uint64, SqlType::Int32) => {
236 Some(SqlType::Float64) }
238 (SqlType::Int64, SqlType::Uint64) | (SqlType::Uint64, SqlType::Int64) => {
239 Some(SqlType::Float64) }
241 (SqlType::Uint32, SqlType::Int64) | (SqlType::Int64, SqlType::Uint32) => {
242 Some(SqlType::Int64)
243 }
244
245 (SqlType::Float32, SqlType::Float64) | (SqlType::Float64, SqlType::Float32) => {
247 Some(SqlType::Float64)
248 }
249
250 (SqlType::Int32, SqlType::Float32) | (SqlType::Float32, SqlType::Int32) => {
252 Some(SqlType::Float32)
253 }
254 (SqlType::Int32, SqlType::Float64) | (SqlType::Float64, SqlType::Int32) => {
255 Some(SqlType::Float64)
256 }
257 (SqlType::Int64, SqlType::Float32) | (SqlType::Float32, SqlType::Int64) => {
258 Some(SqlType::Float64)
259 }
260 (SqlType::Int64, SqlType::Float64) | (SqlType::Float64, SqlType::Int64) => {
261 Some(SqlType::Float64)
262 }
263 (SqlType::Uint32, SqlType::Float32) | (SqlType::Float32, SqlType::Uint32) => {
264 Some(SqlType::Float32)
265 }
266 (SqlType::Uint32, SqlType::Float64) | (SqlType::Float64, SqlType::Uint32) => {
267 Some(SqlType::Float64)
268 }
269 (SqlType::Uint64, SqlType::Float32) | (SqlType::Float32, SqlType::Uint64) => {
270 Some(SqlType::Float64)
271 }
272 (SqlType::Uint64, SqlType::Float64) | (SqlType::Float64, SqlType::Uint64) => {
273 Some(SqlType::Float64)
274 }
275
276 (t, SqlType::Numeric { .. }) | (SqlType::Numeric { .. }, t)
278 if t.is_integer() || t.is_floating_point() =>
279 {
280 Some(SqlType::Numeric {
281 precision: None,
282 scale: None,
283 })
284 }
285
286 (SqlType::Date, SqlType::Datetime) | (SqlType::Datetime, SqlType::Date) => {
288 Some(SqlType::Datetime)
289 }
290 (SqlType::Date, SqlType::Timestamp) | (SqlType::Timestamp, SqlType::Date) => {
291 Some(SqlType::Timestamp)
292 }
293 (SqlType::Datetime, SqlType::Timestamp) | (SqlType::Timestamp, SqlType::Datetime) => {
294 Some(SqlType::Timestamp)
295 }
296
297 (SqlType::Unknown, other) | (other, SqlType::Unknown) => Some(other.clone()),
299
300 (SqlType::Any, _) | (_, SqlType::Any) => Some(SqlType::Any),
302
303 (SqlType::Array(a), SqlType::Array(b)) => {
305 a.common_supertype(b).map(|t| SqlType::Array(Box::new(t)))
306 }
307
308 _ => None,
309 }
310 }
311
312 pub fn element_type(&self) -> Option<&SqlType> {
314 match self {
315 SqlType::Array(elem) => Some(elem),
316 _ => None,
317 }
318 }
319
320 pub fn struct_fields(&self) -> Option<&[StructField]> {
322 match self {
323 SqlType::Struct(fields) => Some(fields),
324 _ => None,
325 }
326 }
327}
328
329impl fmt::Display for SqlType {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 match self {
332 SqlType::Bool => write!(f, "BOOLEAN"),
333 SqlType::Int32 => write!(f, "INTEGER"),
334 SqlType::Int64 => write!(f, "BIGINT"),
335 SqlType::Uint32 => write!(f, "UINTEGER"),
336 SqlType::Uint64 => write!(f, "UBIGINT"),
337 SqlType::Float32 => write!(f, "REAL"),
338 SqlType::Float64 => write!(f, "DOUBLE PRECISION"),
339 SqlType::Numeric { precision, scale } => {
340 write!(f, "NUMERIC")?;
341 if let Some(p) = precision {
342 write!(f, "({}", p)?;
343 if let Some(s) = scale {
344 write!(f, ", {}", s)?;
345 }
346 write!(f, ")")?;
347 }
348 Ok(())
349 }
350 SqlType::Varchar => write!(f, "VARCHAR"),
351 SqlType::Varbinary => write!(f, "VARBINARY"),
352 SqlType::Date => write!(f, "DATE"),
353 SqlType::Time => write!(f, "TIME"),
354 SqlType::Datetime => write!(f, "DATETIME"),
355 SqlType::Timestamp => write!(f, "TIMESTAMP"),
356 SqlType::Interval => write!(f, "INTERVAL"),
357 SqlType::Array(elem) => write!(f, "ARRAY<{}>", elem),
358 SqlType::Struct(fields) => {
359 write!(f, "STRUCT<")?;
360 for (i, field) in fields.iter().enumerate() {
361 if i > 0 {
362 write!(f, ", ")?;
363 }
364 if let Some(name) = &field.name {
365 write!(f, "{} ", name)?;
366 }
367 write!(f, "{}", field.data_type)?;
368 }
369 write!(f, ">")
370 }
371 SqlType::Json => write!(f, "JSON"),
372 SqlType::Range(elem) => write!(f, "RANGE<{}>", elem),
373 SqlType::Uuid => write!(f, "UUID"),
374 SqlType::Unknown => write!(f, "UNKNOWN"),
375 SqlType::Any => write!(f, "ANY"),
376 }
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn test_numeric_classification() {
386 assert!(SqlType::Int32.is_numeric());
387 assert!(SqlType::Int64.is_numeric());
388 assert!(SqlType::Uint32.is_numeric());
389 assert!(SqlType::Uint64.is_numeric());
390 assert!(SqlType::Float32.is_numeric());
391 assert!(SqlType::Float64.is_numeric());
392 assert!(SqlType::Numeric {
393 precision: None,
394 scale: None
395 }
396 .is_numeric());
397 assert!(!SqlType::Varchar.is_numeric());
398 }
399
400 #[test]
401 fn test_coercion() {
402 assert!(SqlType::Int32.can_coerce_to(&SqlType::Int64));
404 assert!(SqlType::Uint32.can_coerce_to(&SqlType::Uint64));
405 assert!(SqlType::Uint32.can_coerce_to(&SqlType::Int64));
406
407 assert!(SqlType::Int32.can_coerce_to(&SqlType::Float32));
409 assert!(SqlType::Int32.can_coerce_to(&SqlType::Float64));
410 assert!(SqlType::Int64.can_coerce_to(&SqlType::Float64));
411
412 assert!(SqlType::Float32.can_coerce_to(&SqlType::Float64));
414
415 assert!(SqlType::Int64.can_coerce_to(&SqlType::Numeric {
417 precision: None,
418 scale: None
419 }));
420 assert!(SqlType::Float32.can_coerce_to(&SqlType::Numeric {
421 precision: None,
422 scale: None
423 }));
424
425 assert!(!SqlType::Varchar.can_coerce_to(&SqlType::Int64));
427 assert!(!SqlType::Int64.can_coerce_to(&SqlType::Int32)); }
429
430 #[test]
431 fn test_common_supertype() {
432 assert_eq!(
434 SqlType::Int32.common_supertype(&SqlType::Int64),
435 Some(SqlType::Int64)
436 );
437 assert_eq!(
438 SqlType::Uint32.common_supertype(&SqlType::Uint64),
439 Some(SqlType::Uint64)
440 );
441 assert_eq!(
442 SqlType::Int32.common_supertype(&SqlType::Uint32),
443 Some(SqlType::Int64) );
445
446 assert_eq!(
448 SqlType::Float32.common_supertype(&SqlType::Float64),
449 Some(SqlType::Float64)
450 );
451
452 assert_eq!(
454 SqlType::Int64.common_supertype(&SqlType::Float64),
455 Some(SqlType::Float64)
456 );
457 assert_eq!(
458 SqlType::Int32.common_supertype(&SqlType::Float32),
459 Some(SqlType::Float32)
460 );
461
462 assert_eq!(
464 SqlType::Date.common_supertype(&SqlType::Timestamp),
465 Some(SqlType::Timestamp)
466 );
467
468 assert_eq!(SqlType::Varchar.common_supertype(&SqlType::Int64), None);
470 }
471
472 #[test]
473 fn test_is_integer() {
474 assert!(SqlType::Int32.is_integer());
475 assert!(SqlType::Int64.is_integer());
476 assert!(SqlType::Uint32.is_integer());
477 assert!(SqlType::Uint64.is_integer());
478 assert!(!SqlType::Float32.is_integer());
479 assert!(!SqlType::Float64.is_integer());
480 assert!(!SqlType::Numeric {
481 precision: None,
482 scale: None
483 }
484 .is_integer());
485 assert!(!SqlType::Varchar.is_integer());
486 }
487
488 #[test]
489 fn test_is_signed_unsigned_integer() {
490 assert!(SqlType::Int32.is_signed_integer());
492 assert!(SqlType::Int64.is_signed_integer());
493 assert!(!SqlType::Uint32.is_signed_integer());
494 assert!(!SqlType::Uint64.is_signed_integer());
495
496 assert!(SqlType::Uint32.is_unsigned_integer());
498 assert!(SqlType::Uint64.is_unsigned_integer());
499 assert!(!SqlType::Int32.is_unsigned_integer());
500 assert!(!SqlType::Int64.is_unsigned_integer());
501 }
502
503 #[test]
504 fn test_is_floating_point() {
505 assert!(SqlType::Float32.is_floating_point());
506 assert!(SqlType::Float64.is_floating_point());
507 assert!(!SqlType::Int32.is_floating_point());
508 assert!(!SqlType::Int64.is_floating_point());
509 assert!(!SqlType::Numeric {
510 precision: None,
511 scale: None
512 }
513 .is_floating_point());
514 assert!(!SqlType::Varchar.is_floating_point());
515 }
516
517 #[test]
518 fn test_is_string() {
519 assert!(SqlType::Varchar.is_string());
520 assert!(!SqlType::Int64.is_string());
521 assert!(!SqlType::Varbinary.is_string());
522 assert!(!SqlType::Json.is_string());
523 }
524
525 #[test]
526 fn test_is_datetime() {
527 assert!(SqlType::Date.is_datetime());
528 assert!(SqlType::Time.is_datetime());
529 assert!(SqlType::Datetime.is_datetime());
530 assert!(SqlType::Timestamp.is_datetime());
531 assert!(!SqlType::Int64.is_datetime());
532 assert!(!SqlType::Varchar.is_datetime());
533 assert!(!SqlType::Interval.is_datetime());
534 }
535
536 #[test]
537 fn test_is_comparable_with() {
538 assert!(SqlType::Int64.is_comparable_with(&SqlType::Int64));
540 assert!(SqlType::Varchar.is_comparable_with(&SqlType::Varchar));
541
542 assert!(SqlType::Int64.is_comparable_with(&SqlType::Float64));
544 assert!(SqlType::Float64.is_comparable_with(&SqlType::Numeric {
545 precision: None,
546 scale: None
547 }));
548
549 assert!(SqlType::Date.is_comparable_with(&SqlType::Timestamp));
551 assert!(SqlType::Time.is_comparable_with(&SqlType::Datetime));
552
553 assert!(SqlType::Unknown.is_comparable_with(&SqlType::Int64));
555 assert!(SqlType::Int64.is_comparable_with(&SqlType::Unknown));
556 assert!(SqlType::Any.is_comparable_with(&SqlType::Varchar));
557
558 assert!(!SqlType::Varchar.is_comparable_with(&SqlType::Int64));
560 assert!(!SqlType::Bool.is_comparable_with(&SqlType::Float64));
561 }
562
563 #[test]
564 fn test_element_type() {
565 let array_int = SqlType::Array(Box::new(SqlType::Int64));
566 assert_eq!(array_int.element_type(), Some(&SqlType::Int64));
567
568 let nested = SqlType::Array(Box::new(SqlType::Array(Box::new(SqlType::Varchar))));
569 assert_eq!(
570 nested.element_type(),
571 Some(&SqlType::Array(Box::new(SqlType::Varchar)))
572 );
573
574 assert_eq!(SqlType::Int64.element_type(), None);
575 assert_eq!(SqlType::Varchar.element_type(), None);
576 }
577
578 #[test]
579 fn test_struct_fields() {
580 let struct_type = SqlType::Struct(vec![
581 StructField {
582 name: Some("a".to_string()),
583 data_type: SqlType::Int64,
584 },
585 StructField {
586 name: Some("b".to_string()),
587 data_type: SqlType::Varchar,
588 },
589 ]);
590 let fields = struct_type.struct_fields().unwrap();
591 assert_eq!(fields.len(), 2);
592 assert_eq!(fields[0].name, Some("a".to_string()));
593 assert_eq!(fields[0].data_type, SqlType::Int64);
594
595 assert_eq!(SqlType::Int64.struct_fields(), None);
596 }
597
598 #[test]
599 fn test_display() {
600 assert_eq!(format!("{}", SqlType::Bool), "BOOLEAN");
601 assert_eq!(format!("{}", SqlType::Int32), "INTEGER");
602 assert_eq!(format!("{}", SqlType::Int64), "BIGINT");
603 assert_eq!(format!("{}", SqlType::Uint32), "UINTEGER");
604 assert_eq!(format!("{}", SqlType::Uint64), "UBIGINT");
605 assert_eq!(format!("{}", SqlType::Float32), "REAL");
606 assert_eq!(format!("{}", SqlType::Float64), "DOUBLE PRECISION");
607 assert_eq!(
608 format!(
609 "{}",
610 SqlType::Numeric {
611 precision: None,
612 scale: None
613 }
614 ),
615 "NUMERIC"
616 );
617 assert_eq!(
618 format!(
619 "{}",
620 SqlType::Numeric {
621 precision: Some(10),
622 scale: None
623 }
624 ),
625 "NUMERIC(10)"
626 );
627 assert_eq!(
628 format!(
629 "{}",
630 SqlType::Numeric {
631 precision: Some(10),
632 scale: Some(2)
633 }
634 ),
635 "NUMERIC(10, 2)"
636 );
637 assert_eq!(format!("{}", SqlType::Varchar), "VARCHAR");
638 assert_eq!(format!("{}", SqlType::Varbinary), "VARBINARY");
639 assert_eq!(
640 format!("{}", SqlType::Array(Box::new(SqlType::Int64))),
641 "ARRAY<BIGINT>"
642 );
643 assert_eq!(format!("{}", SqlType::Uuid), "UUID");
644 assert_eq!(format!("{}", SqlType::Unknown), "UNKNOWN");
645 assert_eq!(format!("{}", SqlType::Any), "ANY");
646 }
647}