1use crate::to_sql::{Dialect, ToSql};
2use anyhow::Result;
3use std::str::FromStr;
4
5use crate::util::SqlExtension;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum Type {
10 Boolean,
11 I16,
13 I32,
14 I64,
15 F32,
17 F64,
18 Decimal,
20 Numeric(u8, u8),
21 Bytes,
23 Time,
25 Date,
26 DateTime,
27 NaiveDateTime,
28 Duration,
29 Json,
31 Jsonb,
32 Uuid,
34 Text,
36 Array(Box<Type>),
38 Other(String),
39}
40
41impl Type {
42 pub fn lossy_eq(&self, other: &Type) -> bool {
43 use Type::*;
44 match (self, other) {
45 (Other(_), _) => true,
46 (a, b) => a == b,
47 }
48 }
49}
50
51impl FromStr for Type {
52 type Err = anyhow::Error;
53
54 fn from_str(s: &str) -> Result<Self> {
55 use Type::*;
56 let s = match s {
57 "numeric" => Decimal,
58 "bigint" => I64,
59 "int8" => I64,
60 "double precision" => F64,
61 "real" => F32,
62 "bool" => Boolean,
63 "boolean" => Boolean,
64 "date" => Date,
65 "bytea" => Bytes,
66 "timestamp with time zone" => DateTime,
67 "timestamp without time zone" => NaiveDateTime,
68 "interval" => Duration,
69 "json" => Json,
70 "jsonb" => Jsonb,
71 "uuid" => Uuid,
72 "smallint" => I16,
73 "text" => Text,
74 "character varying" => Text,
75 "varchar" => Text,
76 "integer" => I32,
77 "ARRAY" => panic!("Encountered `ARRAY` type when reading data schema from database. ARRAY must be handled separately."),
78 s => Other(s.to_string()),
79 };
80 Ok(s)
81 }
82}
83
84impl ToSql for Type {
85 fn write_sql(&self, buf: &mut String, dialect: Dialect) {
86 use self::Type::*;
87 let s = match self {
88 Boolean => "boolean",
89 I16 => "smallint",
90 I32 => "integer",
91 I64 => "bigint",
92 Bytes => "bytea",
93 Time => "time without time zone",
94 Date => "date",
95 DateTime => "timestamptz",
96 NaiveDateTime => "timestamp without time zone",
97 Duration => "interval",
98 Json => "json",
99 Jsonb => "jsonb",
100 F32 => "real",
101 F64 => "double precision",
102 Decimal => "numeric",
103 Numeric(p, s) => {
104 return buf.push_str(&format!("numeric({}, {})", p, s));
105 }
106 Uuid => "uuid",
107 Text => "character varying",
108 Array(inner) => {
109 buf.push_sql(inner.as_ref(), dialect);
110 if dialect == Dialect::Postgres {
111 buf.push_str("[]");
112 } else {
113 buf.push_str(" ARRAY")
114 }
115 return;
116 }
117 Other(z) => {
118 #[cfg(feature = "tracing")]
119 tracing::warn!(z, "Unknown type. SQL may not be valid.");
120 buf.push_str("/* Unknown type: ");
121 buf.push_str(z);
122 buf.push_str(" */");
123 return;
124 }
125 };
126 buf.push_str(s);
127 }
128}
129
130#[cfg(test)]
131mod test {
132 use super::*;
133
134 #[test]
135 fn test_numeric() {
136 let s = "numeric";
137 let t = Type::from_str(s).unwrap();
138 assert_eq!(t, Type::Decimal);
139 }
140}