Skip to main content

zql_cli/util/
convert.rs

1use itertools::all;
2use odbc_api::{DataType, Nullability};
3use std::borrow::Cow;
4use std::num::NonZeroUsize;
5
6pub fn format_type(dtype: &DataType) -> String {
7    match dtype {
8        DataType::Unknown => String::from("UNKNOWN"),
9        DataType::Char { length } => format_varchar("CHAR", length),
10        DataType::WChar { length } => format_varchar("WCHAR", length),
11        DataType::Numeric { precision, scale } => format_numeric("NUMERIC", *precision, *scale),
12        DataType::Decimal { precision, scale } => format_numeric("DECIMAL", *precision, *scale),
13        DataType::Integer => String::from("INTEGER"),
14        DataType::SmallInt => String::from("SMALLINT"),
15        DataType::Float { precision } => format_float("FLOAT", *precision),
16        DataType::Real => String::from("REAL"),
17        DataType::Double => String::from("DOUBLE"),
18        DataType::Varchar { length } => format_varchar("VARCHAR", length),
19        DataType::WVarchar { length } => format_varchar("WVARCHAR", length),
20        DataType::LongVarchar { length } => format_varchar("LONGVARCHAR", length),
21        DataType::WLongVarchar { length } => format_varchar("WLONGVARCHAR", length),
22        DataType::LongVarbinary { length } => format_varchar("LONGVARBINARY", length),
23        DataType::Date => String::from("DATE"),
24        DataType::Time { precision } => format_time("TIME", *precision),
25        DataType::Timestamp { precision } => format_time("TIMESTAMP", *precision),
26        DataType::BigInt => String::from("BIGINT"),
27        DataType::TinyInt => String::from("TINYINT"),
28        DataType::Bit => String::from("BIT"),
29        DataType::Varbinary { length } => format_varchar("VARBINARY", length),
30        DataType::Binary { length } => format_varchar("BINARY", length),
31        DataType::Other { .. } => String::from("OTHER"),
32    }
33}
34
35fn format_varchar(name: &str, length: &Option<NonZeroUsize>) -> String {
36    if let Some(length) = length {
37        format!("{name}({length})")
38    } else {
39        format!("{name}")
40    }
41}
42
43fn format_numeric(name: &str, precision: usize, scale: i16) -> String {
44    format!("{name}({precision}, {scale})")
45}
46
47fn format_float(name: &str, precision: usize) -> String {
48    format!("{name}({precision})")
49}
50
51fn format_time(name: &str, precision: i16) -> String {
52    format!("{name}({precision})")
53}
54
55pub fn format_null(null: &Nullability) -> &'static str {
56    match null {
57        Nullability::Unknown => "UNKNOWN",
58        Nullability::Nullable => "NULL",
59        Nullability::NoNulls => "NOT NULL",
60    }
61}
62
63pub fn parse_type(dtype: Cow<str>, dsize: usize, dprec: usize) -> Cow<str> {
64    let dtype = dtype.to_uppercase();
65    if all(dtype.chars(), char::is_uppercase) {
66        let dtype = match dtype.as_str() {
67            "CHAR" | "VARCHAR" | "LONGVARCHAR" |
68            "WCHAR" | "WVARCHAR" | "WLONGVARCHAR" |
69            "NCHAR" | "NVARCHAR" | "NLONGVARCHAR" |
70            "BINARY" | "VARBINARY" | "LONGVARBINARY" |
71            "TEXT" | "FLOAT" | "TIME" | "TIMESTAMP" if dsize != 0 => {
72                format!("{}({})", dtype, dsize)
73            }
74            "NUMERIC" | "DECIMAL" if dsize != 0 => {
75                format!("{}({}, {})", dtype, dsize, dprec)
76            }
77            _ => {
78                dtype
79            }
80        };
81        Cow::Owned(dtype)
82    } else {
83        Cow::Owned(dtype)
84    }
85}
86
87pub fn parse_null(value: Cow<str>) -> Nullability {
88    match value.as_ref() {
89        "0" | "YES" => Nullability::Nullable,
90        "1" | "NO" => Nullability::NoNulls,
91        _ => Nullability::Unknown,
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use crate::cow_str;
98    use crate::util::convert::{format_null, format_type, parse_null, parse_type};
99    use odbc_api::sys::SqlDataType;
100    use odbc_api::DataType::*;
101    use odbc_api::Nullability;
102    use pretty_assertions::assert_eq;
103    use std::borrow::Cow;
104    use std::num::NonZeroUsize;
105
106    #[test]
107    fn test_sized_varchar_data_types_are_formatted() {
108        assert_eq!(format_type(&Char { length: NonZeroUsize::new(20) }), "CHAR(20)");
109        assert_eq!(format_type(&WChar { length: NonZeroUsize::new(20) }), "WCHAR(20)");
110        assert_eq!(format_type(&Varchar { length: NonZeroUsize::new(20) }), "VARCHAR(20)");
111        assert_eq!(format_type(&WVarchar { length: NonZeroUsize::new(20) }), "WVARCHAR(20)");
112        assert_eq!(format_type(&LongVarchar { length: NonZeroUsize::new(20) }), "LONGVARCHAR(20)");
113        assert_eq!(format_type(&WLongVarchar { length: NonZeroUsize::new(20) }), "WLONGVARCHAR(20)");
114        assert_eq!(format_type(&Binary { length: NonZeroUsize::new(20) }), "BINARY(20)");
115        assert_eq!(format_type(&Varbinary { length: NonZeroUsize::new(20) }), "VARBINARY(20)");
116        assert_eq!(format_type(&LongVarbinary { length: NonZeroUsize::new(20) }), "LONGVARBINARY(20)");
117    }
118
119    #[test]
120    fn test_unsized_varchar_data_types_are_formatted() {
121        assert_eq!(format_type(&Char { length: None }), "CHAR");
122        assert_eq!(format_type(&WChar { length: None }), "WCHAR");
123        assert_eq!(format_type(&Varchar { length: None }), "VARCHAR");
124        assert_eq!(format_type(&WVarchar { length: None }), "WVARCHAR");
125        assert_eq!(format_type(&LongVarchar { length: None }), "LONGVARCHAR");
126        assert_eq!(format_type(&WLongVarchar { length: None }), "WLONGVARCHAR");
127        assert_eq!(format_type(&Binary { length: None }), "BINARY");
128        assert_eq!(format_type(&Varbinary { length: None }), "VARBINARY");
129        assert_eq!(format_type(&LongVarbinary { length: None }), "LONGVARBINARY");
130    }
131
132    #[test]
133    fn test_numeric_and_decimal_data_types_are_formatted() {
134        assert_eq!(format_type(&Numeric { precision: 10, scale: 2 }), "NUMERIC(10, 2)");
135        assert_eq!(format_type(&Decimal { precision: 10, scale: 2 }), "DECIMAL(10, 2)");
136    }
137
138    #[test]
139    fn test_float_and_time_data_types_are_formatted() {
140        assert_eq!(format_type(&Float { precision: 10 }), "FLOAT(10)");
141        assert_eq!(format_type(&Time { precision: 10 }), "TIME(10)");
142        assert_eq!(format_type(&Timestamp { precision: 10 }), "TIMESTAMP(10)");
143    }
144
145    #[test]
146    fn test_integer_and_other_data_types_are_formatted() {
147        assert_eq!(format_type(&Unknown), "UNKNOWN");
148        assert_eq!(format_type(&Integer), "INTEGER");
149        assert_eq!(format_type(&SmallInt), "SMALLINT");
150        assert_eq!(format_type(&Real), "REAL");
151        assert_eq!(format_type(&Double), "DOUBLE");
152        assert_eq!(format_type(&Date), "DATE");
153        assert_eq!(format_type(&BigInt), "BIGINT");
154        assert_eq!(format_type(&TinyInt), "TINYINT");
155        assert_eq!(format_type(&Bit), "BIT");
156        assert_eq!(format_type(&Other { data_type: SqlDataType::UNKNOWN_TYPE, column_size: None, decimal_digits: 0 }), "OTHER");
157    }
158
159    #[test]
160    fn test_nullability_types_are_formatted() {
161        assert_eq!(format_null(&Nullability::Unknown), "UNKNOWN");
162        assert_eq!(format_null(&Nullability::Nullable), "NULL");
163        assert_eq!(format_null(&Nullability::NoNulls), "NOT NULL");
164    }
165
166    #[test]
167    fn test_sized_varchar_data_types_are_parsed() {
168        assert_eq!(parse_type(cow_str!("CHAR(10)"), 15, 0), "CHAR(10)");
169        assert_eq!(parse_type(cow_str!("WCHAR(10)"), 15, 0), "WCHAR(10)");
170        assert_eq!(parse_type(cow_str!("NCHAR(10)"), 15, 0), "NCHAR(10)");
171        assert_eq!(parse_type(cow_str!("VARCHAR(10)"), 15, 0), "VARCHAR(10)");
172        assert_eq!(parse_type(cow_str!("WVARCHAR(10)"), 15, 0), "WVARCHAR(10)");
173        assert_eq!(parse_type(cow_str!("NVARCHAR(10)"), 15, 0), "NVARCHAR(10)");
174        assert_eq!(parse_type(cow_str!("LONGVARCHAR(10)"), 15, 0), "LONGVARCHAR(10)");
175        assert_eq!(parse_type(cow_str!("WLONGVARCHAR(10)"), 15, 0), "WLONGVARCHAR(10)");
176        assert_eq!(parse_type(cow_str!("NLONGVARCHAR(10)"), 15, 0), "NLONGVARCHAR(10)");
177        assert_eq!(parse_type(cow_str!("BINARY(10)"), 15, 0), "BINARY(10)");
178        assert_eq!(parse_type(cow_str!("VARBINARY(10)"), 15, 0), "VARBINARY(10)");
179        assert_eq!(parse_type(cow_str!("LONGVARBINARY(10)"), 15, 0), "LONGVARBINARY(10)");
180        assert_eq!(parse_type(cow_str!("TEXT(10)"), 15, 0), "TEXT(10)");
181    }
182
183    #[test]
184    fn test_unsized_varchar_data_types_are_parsed() {
185        assert_eq!(parse_type(cow_str!("CHAR"), 15, 0), "CHAR(15)");
186        assert_eq!(parse_type(cow_str!("WCHAR"), 15, 0), "WCHAR(15)");
187        assert_eq!(parse_type(cow_str!("NCHAR"), 15, 0), "NCHAR(15)");
188        assert_eq!(parse_type(cow_str!("VARCHAR"), 15, 0), "VARCHAR(15)");
189        assert_eq!(parse_type(cow_str!("WVARCHAR"), 15, 0), "WVARCHAR(15)");
190        assert_eq!(parse_type(cow_str!("NVARCHAR"), 15, 0), "NVARCHAR(15)");
191        assert_eq!(parse_type(cow_str!("LONGVARCHAR"), 15, 0), "LONGVARCHAR(15)");
192        assert_eq!(parse_type(cow_str!("WLONGVARCHAR"), 15, 0), "WLONGVARCHAR(15)");
193        assert_eq!(parse_type(cow_str!("NLONGVARCHAR"), 15, 0), "NLONGVARCHAR(15)");
194        assert_eq!(parse_type(cow_str!("BINARY"), 15, 0), "BINARY(15)");
195        assert_eq!(parse_type(cow_str!("VARBINARY"), 15, 0), "VARBINARY(15)");
196        assert_eq!(parse_type(cow_str!("LONGVARBINARY"), 15, 0), "LONGVARBINARY(15)");
197        assert_eq!(parse_type(cow_str!("TEXT"), 15, 0), "TEXT(15)");
198    }
199
200    #[test]
201    fn test_unknown_varchar_data_types_are_parsed() {
202        assert_eq!(parse_type(cow_str!("CHAR"), 0, 0), "CHAR");
203        assert_eq!(parse_type(cow_str!("WCHAR"), 0, 0), "WCHAR");
204        assert_eq!(parse_type(cow_str!("NCHAR"), 0, 0), "NCHAR");
205        assert_eq!(parse_type(cow_str!("VARCHAR"), 0, 0), "VARCHAR");
206        assert_eq!(parse_type(cow_str!("WVARCHAR"), 0, 0), "WVARCHAR");
207        assert_eq!(parse_type(cow_str!("NVARCHAR"), 0, 0), "NVARCHAR");
208        assert_eq!(parse_type(cow_str!("LONGVARCHAR"), 0, 0), "LONGVARCHAR");
209        assert_eq!(parse_type(cow_str!("WLONGVARCHAR"), 0, 0), "WLONGVARCHAR");
210        assert_eq!(parse_type(cow_str!("NLONGVARCHAR"), 0, 0), "NLONGVARCHAR");
211        assert_eq!(parse_type(cow_str!("BINARY"), 0, 0), "BINARY");
212        assert_eq!(parse_type(cow_str!("VARBINARY"), 0, 0), "VARBINARY");
213        assert_eq!(parse_type(cow_str!("LONGVARBINARY"), 0, 0), "LONGVARBINARY");
214        assert_eq!(parse_type(cow_str!("TEXT"), 0, 0), "TEXT");
215    }
216
217    #[test]
218    fn test_sized_numeric_and_decimal_data_types_are_parsed() {
219        assert_eq!(parse_type(cow_str!("NUMERIC(10, 2)"), 15, 3), "NUMERIC(10, 2)");
220        assert_eq!(parse_type(cow_str!("DECIMAL(10, 2)"), 15, 3), "DECIMAL(10, 2)");
221    }
222
223    #[test]
224    fn test_unsized_numeric_and_decimal_data_types_are_parsed() {
225        assert_eq!(parse_type(cow_str!("NUMERIC"), 15, 3), "NUMERIC(15, 3)");
226        assert_eq!(parse_type(cow_str!("DECIMAL"), 15, 3), "DECIMAL(15, 3)");
227    }
228
229    #[test]
230    fn test_unknown_numeric_and_decimal_data_types_are_parsed() {
231        assert_eq!(parse_type(cow_str!("NUMERIC"), 0, 3), "NUMERIC");
232        assert_eq!(parse_type(cow_str!("DECIMAL"), 0, 3), "DECIMAL");
233    }
234
235    #[test]
236    fn test_sized_float_and_time_data_types_are_parsed() {
237        assert_eq!(parse_type(cow_str!("FLOAT(10)"), 15, 0), "FLOAT(10)");
238        assert_eq!(parse_type(cow_str!("TIME(10)"), 15, 0), "TIME(10)");
239        assert_eq!(parse_type(cow_str!("TIMESTAMP(10)"), 15, 0), "TIMESTAMP(10)");
240    }
241
242    #[test]
243    fn test_unsized_float_and_time_data_types_are_parsed() {
244        assert_eq!(parse_type(cow_str!("FLOAT"), 15, 0), "FLOAT(15)");
245        assert_eq!(parse_type(cow_str!("TIME"), 15, 0), "TIME(15)");
246        assert_eq!(parse_type(cow_str!("TIMESTAMP"), 15, 0), "TIMESTAMP(15)");
247    }
248
249    #[test]
250    fn test_unknown_float_and_time_data_types_are_parsed() {
251        assert_eq!(parse_type(cow_str!("FLOAT"), 0, 0), "FLOAT");
252        assert_eq!(parse_type(cow_str!("TIME"), 0, 0), "TIME");
253        assert_eq!(parse_type(cow_str!("TIMESTAMP"), 0, 0), "TIMESTAMP");
254    }
255
256    #[test]
257    fn test_sized_integer_and_other_data_types_are_parsed() {
258        assert_eq!(parse_type(cow_str!("UNKNOWN(10)"), 15, 0), "UNKNOWN(10)");
259        assert_eq!(parse_type(cow_str!("INTEGER(10)"), 15, 0), "INTEGER(10)");
260        assert_eq!(parse_type(cow_str!("SMALLINT(10)"), 15, 0), "SMALLINT(10)");
261        assert_eq!(parse_type(cow_str!("REAL(10)"), 15, 0), "REAL(10)");
262        assert_eq!(parse_type(cow_str!("DOUBLE(10)"), 15, 0), "DOUBLE(10)");
263        assert_eq!(parse_type(cow_str!("DATE(10)"), 15, 0), "DATE(10)");
264        assert_eq!(parse_type(cow_str!("BIGINT(10)"), 15, 0), "BIGINT(10)");
265        assert_eq!(parse_type(cow_str!("TINYINT(10)"), 15, 0), "TINYINT(10)");
266        assert_eq!(parse_type(cow_str!("BIT(10)"), 15, 0), "BIT(10)");
267        assert_eq!(parse_type(cow_str!("OTHER(10)"), 15, 0), "OTHER(10)");
268    }
269
270    #[test]
271    fn test_unsized_integer_and_other_data_types_are_parsed() {
272        assert_eq!(parse_type(cow_str!("UNKNOWN"), 15, 0), "UNKNOWN");
273        assert_eq!(parse_type(cow_str!("INTEGER"), 15, 0), "INTEGER");
274        assert_eq!(parse_type(cow_str!("SMALLINT"), 15, 0), "SMALLINT");
275        assert_eq!(parse_type(cow_str!("REAL"), 15, 0), "REAL");
276        assert_eq!(parse_type(cow_str!("DOUBLE"), 15, 0), "DOUBLE");
277        assert_eq!(parse_type(cow_str!("DATE"), 15, 0), "DATE");
278        assert_eq!(parse_type(cow_str!("BIGINT"), 15, 0), "BIGINT");
279        assert_eq!(parse_type(cow_str!("TINYINT"), 15, 0), "TINYINT");
280        assert_eq!(parse_type(cow_str!("BIT"), 15, 0), "BIT");
281        assert_eq!(parse_type(cow_str!("OTHER"), 15, 0), "OTHER");
282    }
283
284    #[test]
285    fn test_unknown_integer_and_other_data_types_are_parsed() {
286        assert_eq!(parse_type(cow_str!("UNKNOWN"), 0, 0), "UNKNOWN");
287        assert_eq!(parse_type(cow_str!("INTEGER"), 0, 0), "INTEGER");
288        assert_eq!(parse_type(cow_str!("SMALLINT"), 0, 0), "SMALLINT");
289        assert_eq!(parse_type(cow_str!("REAL"), 0, 0), "REAL");
290        assert_eq!(parse_type(cow_str!("DOUBLE"), 0, 0), "DOUBLE");
291        assert_eq!(parse_type(cow_str!("DATE"), 0, 0), "DATE");
292        assert_eq!(parse_type(cow_str!("BIGINT"), 0, 0), "BIGINT");
293        assert_eq!(parse_type(cow_str!("TINYINT"), 0, 0), "TINYINT");
294        assert_eq!(parse_type(cow_str!("BIT"), 0, 0), "BIT");
295        assert_eq!(parse_type(cow_str!("OTHER"), 0, 0), "OTHER");
296    }
297
298    #[test]
299    fn test_nullability_types_are_parsed() {
300        assert_eq!(parse_null(cow_str!("0")), Nullability::Nullable); // SQLite
301        assert_eq!(parse_null(cow_str!("1")), Nullability::NoNulls); // SQLite
302        assert_eq!(parse_null(cow_str!("YES")), Nullability::Nullable); // MySQL
303        assert_eq!(parse_null(cow_str!("NO")), Nullability::NoNulls); // MySQL
304        assert_eq!(parse_null(cow_str!("")), Nullability::Unknown);
305    }
306}