sql/schema/
type.rs

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    // integer types
12    I16,
13    I32,
14    I64,
15    // float types
16    F32,
17    F64,
18    // arbitrary precision types
19    Decimal,
20    Numeric(u8, u8),
21    // byte types
22    Bytes,
23    // date types
24    Time,
25    Date,
26    DateTime,
27    NaiveDateTime,
28    Duration,
29    // json types
30    Json,
31    Jsonb,
32    // extension types
33    Uuid,
34    // string types
35    Text,
36    // Array types
37    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!(
78                "Encountered `ARRAY` type when reading data schema from database. ARRAY must be handled separately."
79            ),
80            s => Other(s.to_string()),
81        };
82        Ok(s)
83    }
84}
85
86impl ToSql for Type {
87    fn write_sql(&self, buf: &mut String, dialect: Dialect) {
88        use self::Type::*;
89        let s = match self {
90            Boolean => "boolean",
91            I16 => "smallint",
92            I32 => "integer",
93            I64 => "bigint",
94            Bytes => "bytea",
95            Time => "time without time zone",
96            Date => "date",
97            DateTime => "timestamptz",
98            NaiveDateTime => "timestamp without time zone",
99            Duration => "interval",
100            Json => "json",
101            Jsonb => "jsonb",
102            F32 => "real",
103            F64 => "double precision",
104            Decimal => "numeric",
105            Numeric(p, s) => {
106                return buf.push_str(&format!("numeric({}, {})", p, s));
107            }
108            Uuid => "uuid",
109            Text => "character varying",
110            Array(inner) => {
111                buf.push_sql(inner.as_ref(), dialect);
112                if dialect == Dialect::Postgres {
113                    buf.push_str("[]");
114                } else {
115                    buf.push_str(" ARRAY")
116                }
117                return;
118            }
119            Other(z) => {
120                // #[cfg(feature = "tracing")]
121                // tracing::warn!(z, "Unknown type. SQL may not be valid.");
122                // buf.push_str("/* Unknown type: ");
123                buf.push_str(z);
124                // buf.push_str(" */");
125                return;
126            }
127        };
128        buf.push_str(s);
129    }
130}
131
132#[cfg(test)]
133mod test {
134    use super::*;
135
136    #[test]
137    fn test_numeric() {
138        let s = "numeric";
139        let t = Type::from_str(s).unwrap();
140        assert_eq!(t, Type::Decimal);
141    }
142}