1use core::num::ParseIntError;
2use std::net::{AddrParseError, Ipv4Addr};
3
4#[cfg(feature = "chrono")]
5use clickhouse_data_value::datetime::{
6 NaiveDateTime as DatetimeNaiveDateTime, ParseError as DatetimeParseError,
7};
8#[cfg(feature = "num-bigint")]
9use num_bigint::{BigInt, BigUint, ParseBigIntError};
10use sqlx_clickhouse_ext::sqlx_core::error::Error;
11#[cfg(feature = "chrono")]
12use sqlx_clickhouse_ext::sqlx_core::types::chrono::{NaiveDate, NaiveDateTime};
13#[cfg(feature = "bigdecimal")]
14use sqlx_clickhouse_ext::sqlx_core::types::BigDecimal;
15#[cfg(feature = "uuid")]
16use sqlx_clickhouse_ext::sqlx_core::types::Uuid;
17
18pub(crate) enum ClickhousePgType {
20 Char,
21 Int2,
22 Int4,
23 Int8,
24 Float4,
25 Float8,
26 Varchar,
27 #[cfg(feature = "chrono")]
28 Date,
29 #[cfg(feature = "bigdecimal")]
30 Numeric,
31 #[cfg(feature = "uuid")]
32 Uuid,
33}
34
35impl TryFrom<(&str, usize)> for ClickhousePgType {
36 type Error = Error;
37
38 fn try_from(t: (&str, usize)) -> Result<Self, Self::Error> {
39 let (s, index) = t;
40
41 match s {
43 "\"CHAR\"" => Ok(Self::Char),
44 "INT2" => Ok(Self::Int2),
45 "INT4" => Ok(Self::Int4),
46 "INT8" => Ok(Self::Int8),
47 "FLOAT4" => Ok(Self::Float4),
48 "FLOAT8" => Ok(Self::Float8),
49 "VARCHAR" => Ok(Self::Varchar),
50 #[cfg(feature = "chrono")]
51 "DATE" => Ok(Self::Date),
52 #[cfg(not(feature = "chrono"))]
53 "DATE" => Err(Error::ColumnDecode {
54 index: format!("{index:?}"),
55 source: format!("unknown SQL type `{}`, should enable chrono feature", s).into(),
56 }),
57 #[cfg(feature = "bigdecimal")]
58 "NUMERIC" => Ok(Self::Numeric),
59 #[cfg(not(feature = "bigdecimal"))]
60 "NUMERIC" => Err(Error::ColumnDecode {
61 index: format!("{index:?}"),
62 source: format!("unknown SQL type `{}`, should enable bigdecimal feature", s)
63 .into(),
64 }),
65 #[cfg(feature = "uuid")]
66 "UUID" => Ok(Self::Uuid),
67 #[cfg(not(feature = "uuid"))]
68 "UUID" => Err(Error::ColumnDecode {
69 index: format!("{index:?}"),
70 source: format!("unknown SQL type `{}`, should enable uuid feature", s).into(),
71 }),
72 _ => Err(Error::ColumnDecode {
73 index: format!("{index:?}"),
74 source: format!("unknown SQL type `{s}`").into(),
75 }),
76 }
77 }
78}
79
80#[derive(PartialEq, Debug, Clone)]
81pub enum ClickhousePgValue {
82 Bool(bool),
83 Char(i8),
84 I16(i16),
85 I32(i32),
86 I64(i64),
87 F32(f32),
88 F64(f64),
89 String(String),
90 #[cfg(feature = "chrono")]
91 NaiveDate(NaiveDate),
92 #[cfg(feature = "bigdecimal")]
93 BigDecimal(BigDecimal),
94 #[cfg(feature = "uuid")]
95 Uuid(Uuid),
96}
97impl From<bool> for ClickhousePgValue {
98 fn from(val: bool) -> Self {
99 Self::Bool(val)
100 }
101}
102impl From<i8> for ClickhousePgValue {
103 fn from(val: i8) -> Self {
104 Self::Char(val)
105 }
106}
107impl From<i16> for ClickhousePgValue {
108 fn from(val: i16) -> Self {
109 Self::I16(val)
110 }
111}
112impl From<u8> for ClickhousePgValue {
113 fn from(val: u8) -> Self {
114 Self::I16(val.into())
115 }
116}
117impl From<i32> for ClickhousePgValue {
118 fn from(val: i32) -> Self {
119 Self::I32(val)
120 }
121}
122impl From<u16> for ClickhousePgValue {
123 fn from(val: u16) -> Self {
124 Self::I32(val.into())
125 }
126}
127impl From<i64> for ClickhousePgValue {
128 fn from(val: i64) -> Self {
129 Self::I64(val)
130 }
131}
132impl From<u32> for ClickhousePgValue {
133 fn from(val: u32) -> Self {
134 Self::I64(val.into())
135 }
136}
137impl From<f32> for ClickhousePgValue {
138 fn from(val: f32) -> Self {
139 Self::F32(val)
140 }
141}
142impl From<f64> for ClickhousePgValue {
143 fn from(val: f64) -> Self {
144 Self::F64(val)
145 }
146}
147impl From<String> for ClickhousePgValue {
148 fn from(val: String) -> Self {
149 Self::String(val)
150 }
151}
152impl From<&str> for ClickhousePgValue {
153 fn from(val: &str) -> Self {
154 Self::String(val.into())
155 }
156}
157#[cfg(feature = "chrono")]
158impl From<NaiveDate> for ClickhousePgValue {
159 fn from(val: NaiveDate) -> Self {
160 Self::NaiveDate(val)
161 }
162}
163#[cfg(feature = "bigdecimal")]
164impl From<BigDecimal> for ClickhousePgValue {
165 fn from(val: BigDecimal) -> Self {
166 Self::BigDecimal(val)
167 }
168}
169#[cfg(feature = "uuid")]
170impl From<Uuid> for ClickhousePgValue {
171 fn from(val: Uuid) -> Self {
172 Self::Uuid(val)
173 }
174}
175
176impl ClickhousePgValue {
177 pub fn as_bool(&self) -> Option<bool> {
178 match *self {
179 Self::Bool(v) => Some(v),
180 Self::Char(v) if v == '1' as i8 => Some(true),
181 Self::Char(v) if v == '0' as i8 => Some(false),
182 _ => self.as_u8().and_then(|v| match v {
183 1 => Some(true),
184 0 => Some(false),
185 _ => None,
186 }),
187 }
188 }
189 pub fn as_char(&self) -> Option<i8> {
190 match *self {
191 Self::Char(v) => Some(v),
192 _ => None,
193 }
194 }
195 pub fn as_u8(&self) -> Option<u8> {
196 match *self {
197 Self::I16(v) if (u8::MIN as i16..=u8::MAX as i16).contains(&v) => Some(v as u8),
198 _ => None,
199 }
200 }
201 pub fn as_i16(&self) -> Option<i16> {
202 match *self {
203 Self::I16(v) => Some(v),
204 _ => None,
205 }
206 }
207 pub fn as_u16(&self) -> Option<u16> {
208 match *self {
209 Self::I32(v) if (u16::MIN as i32..=u16::MAX as i32).contains(&v) => Some(v as u16),
210 _ => None,
211 }
212 }
213 pub fn as_i32(&self) -> Option<i32> {
214 match *self {
215 Self::I32(v) => Some(v),
216 _ => None,
217 }
218 }
219 pub fn as_u32(&self) -> Option<u32> {
220 match *self {
221 Self::I64(v) if (u32::MIN as i64..=u32::MAX as i64).contains(&v) => Some(v as u32),
222 _ => None,
223 }
224 }
225 pub fn as_i64(&self) -> Option<i64> {
226 match *self {
227 Self::I64(v) => Some(v),
228 _ => None,
229 }
230 }
231 pub fn as_u64(&self) -> Option<Result<u64, ParseIntError>> {
232 match *self {
233 Self::String(ref v) => Some(v.parse()),
234 _ => None,
235 }
236 }
237 pub fn as_i128(&self) -> Option<Result<i128, ParseIntError>> {
238 match *self {
239 Self::String(ref v) => Some(v.parse()),
240 _ => None,
241 }
242 }
243 pub fn as_u128(&self) -> Option<Result<u128, ParseIntError>> {
244 match *self {
245 Self::String(ref v) => Some(v.parse()),
246 _ => None,
247 }
248 }
249 #[cfg(feature = "num-bigint")]
250 pub fn as_big_int(&self) -> Option<Result<BigInt, ParseBigIntError>> {
251 match *self {
252 Self::String(ref v) => Some(v.parse()),
253 _ => None,
254 }
255 }
256 #[cfg(feature = "num-bigint")]
257 pub fn as_big_uint(&self) -> Option<Result<BigUint, ParseBigIntError>> {
258 match *self {
259 Self::String(ref v) => Some(v.parse()),
260 _ => None,
261 }
262 }
263 pub fn as_f32(&self) -> Option<f32> {
264 match *self {
265 Self::F32(v) => Some(v),
266 _ => None,
267 }
268 }
269 pub fn as_f64(&self) -> Option<f64> {
270 match *self {
271 Self::F64(v) => Some(v),
272 _ => None,
273 }
274 }
275 pub fn as_str(&self) -> Option<&str> {
276 match *self {
277 Self::String(ref v) => Some(v),
278 _ => None,
279 }
280 }
281 #[cfg(feature = "chrono")]
282 pub fn as_naive_date(&self) -> Option<&NaiveDate> {
283 match *self {
284 Self::NaiveDate(ref v) => Some(v),
285 _ => None,
286 }
287 }
288 #[cfg(feature = "bigdecimal")]
289 pub fn as_big_decimal(&self) -> Option<&BigDecimal> {
290 match *self {
291 Self::BigDecimal(ref v) => Some(v),
292 _ => None,
293 }
294 }
295 #[cfg(feature = "uuid")]
296 pub fn as_uuid(&self) -> Option<&Uuid> {
297 match *self {
298 Self::Uuid(ref v) => Some(v),
299 _ => None,
300 }
301 }
302
303 #[cfg(feature = "chrono")]
304 pub fn as_naive_date_time(&self) -> Option<Result<NaiveDateTime, DatetimeParseError>> {
305 match *self {
306 Self::String(ref v) => Some(v.parse::<DatetimeNaiveDateTime>().map(|x| x.0)),
307 _ => None,
308 }
309 }
310
311 pub fn as_ipv4_addr(&self) -> Option<Result<Ipv4Addr, AddrParseError>> {
312 match *self {
313 Self::String(ref v) => Some(v.parse()),
314 _ => self.as_u32().map(|v| Ok(v.into())),
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_as_bool() {
325 assert_eq!(ClickhousePgValue::from(false).as_bool(), Some(false));
326 assert_eq!(ClickhousePgValue::from(true).as_bool(), Some(true));
327
328 assert_eq!(ClickhousePgValue::from('0' as i8).as_bool(), Some(false));
329 assert_eq!(ClickhousePgValue::from('1' as i8).as_bool(), Some(true));
330 assert_eq!(ClickhousePgValue::from('2' as i8).as_bool(), None);
331
332 assert_eq!(ClickhousePgValue::from(0_u8).as_bool(), Some(false));
333 assert_eq!(ClickhousePgValue::from(1_u8).as_bool(), Some(true));
334 assert_eq!(ClickhousePgValue::from(2_u8).as_bool(), None);
335 }
336 #[test]
337 fn test_as_char() {
338 assert_eq!(
339 ClickhousePgValue::from('3' as i8).as_char(),
340 Some('3' as i8)
341 );
342 }
343 #[test]
344 fn test_as_u8() {
345 assert_eq!(ClickhousePgValue::from(u8::MIN).as_u8(), Some(u8::MIN));
346 assert_eq!(ClickhousePgValue::from(u8::MAX).as_u8(), Some(u8::MAX));
347 }
348 #[test]
349 fn test_as_i16() {
350 assert_eq!(ClickhousePgValue::from(i16::MIN).as_i16(), Some(i16::MIN));
351 assert_eq!(ClickhousePgValue::from(i16::MAX).as_i16(), Some(i16::MAX));
352 }
353 #[test]
354 fn test_as_u16() {
355 assert_eq!(ClickhousePgValue::from(u16::MIN).as_u16(), Some(u16::MIN));
356 assert_eq!(ClickhousePgValue::from(u16::MAX).as_u16(), Some(u16::MAX));
357 }
358 #[test]
359 fn test_as_i32() {
360 assert_eq!(ClickhousePgValue::from(i32::MIN).as_i32(), Some(i32::MIN));
361 assert_eq!(ClickhousePgValue::from(i32::MAX).as_i32(), Some(i32::MAX));
362 }
363 #[test]
364 fn test_as_u32() {
365 assert_eq!(ClickhousePgValue::from(u32::MIN).as_u32(), Some(u32::MIN));
366 assert_eq!(ClickhousePgValue::from(u32::MAX).as_u32(), Some(u32::MAX));
367 }
368 #[test]
369 fn test_as_i64() {
370 assert_eq!(ClickhousePgValue::from(i64::MIN).as_i64(), Some(i64::MIN));
371 assert_eq!(ClickhousePgValue::from(i64::MAX).as_i64(), Some(i64::MAX));
372 }
373 #[test]
374 fn test_as_u64() {
375 assert_eq!(
376 ClickhousePgValue::from(format!("{}", u64::MIN)).as_u64(),
377 Some(Ok(u64::MIN))
378 );
379 assert_eq!(
380 ClickhousePgValue::from(format!("{}", u64::MAX)).as_u64(),
381 Some(Ok(u64::MAX))
382 );
383 }
384 #[test]
385 fn test_as_i128() {
386 assert_eq!(
387 ClickhousePgValue::from(format!("{}", i128::MIN)).as_i128(),
388 Some(Ok(i128::MIN))
389 );
390 assert_eq!(
391 ClickhousePgValue::from(format!("{}", i128::MAX)).as_i128(),
392 Some(Ok(i128::MAX))
393 );
394 }
395 #[test]
396 fn test_as_u128() {
397 assert_eq!(
398 ClickhousePgValue::from(format!("{}", u128::MIN)).as_u128(),
399 Some(Ok(u128::MIN))
400 );
401 assert_eq!(
402 ClickhousePgValue::from(format!("{}", u128::MAX)).as_u128(),
403 Some(Ok(u128::MAX))
404 );
405 }
406 #[cfg(feature = "num-bigint")]
407 #[test]
408 fn test_as_big_int() {
409 assert_eq!(
410 ClickhousePgValue::from(
411 "-57896044618658097711785492504343953926634992332820282019728792003956564819968"
412 )
413 .as_big_int(),
414 Some(Ok(BigInt::parse_bytes(
415 b"-57896044618658097711785492504343953926634992332820282019728792003956564819968",
416 10
417 )
418 .unwrap()))
419 );
420 assert_eq!(
421 ClickhousePgValue::from(
422 "57896044618658097711785492504343953926634992332820282019728792003956564819967"
423 )
424 .as_big_int(),
425 Some(Ok(BigInt::parse_bytes(
426 b"57896044618658097711785492504343953926634992332820282019728792003956564819967",
427 10
428 )
429 .unwrap()))
430 );
431 }
432 #[cfg(feature = "num-bigint")]
433 #[test]
434 fn test_as_big_uint() {
435 assert_eq!(
436 ClickhousePgValue::from("0").as_big_uint(),
437 Some(Ok(BigUint::parse_bytes(b"0", 10).unwrap()))
438 );
439 assert_eq!(
440 ClickhousePgValue::from(
441 "115792089237316195423570985008687907853269984665640564039457584007913129639935"
442 )
443 .as_big_uint(),
444 Some(Ok(BigUint::parse_bytes(
445 b"115792089237316195423570985008687907853269984665640564039457584007913129639935",
446 10
447 )
448 .unwrap()))
449 );
450 }
451 #[test]
452 fn test_as_f32() {
453 assert_eq!(ClickhousePgValue::from(f32::MIN).as_f32(), Some(f32::MIN));
454 assert_eq!(ClickhousePgValue::from(f32::MAX).as_f32(), Some(f32::MAX));
455 }
456 #[test]
457 fn test_as_f64() {
458 assert_eq!(ClickhousePgValue::from(f64::MIN).as_f64(), Some(f64::MIN));
459 assert_eq!(ClickhousePgValue::from(f64::MAX).as_f64(), Some(f64::MAX));
460 }
461 #[test]
462 fn test_as_str() {
463 assert_eq!(ClickhousePgValue::from("foo").as_str(), Some("foo"));
464 }
465
466 #[cfg(feature = "chrono")]
467 #[test]
468 fn test_as_naive_date() {
469 let naive_date = NaiveDate::from_ymd_opt(2021, 1, 1).expect("");
470 assert_eq!(
471 ClickhousePgValue::from(naive_date).as_naive_date(),
472 Some(&NaiveDate::from_ymd_opt(2021, 1, 1).expect(""))
473 );
474 }
475 #[cfg(feature = "bigdecimal")]
476 #[test]
477 fn test_as_big_decimal() {
478 let big_decimal = BigDecimal::parse_bytes(b"1.1", 10).unwrap();
479 assert_eq!(
480 ClickhousePgValue::from(big_decimal.clone()).as_big_decimal(),
481 Some(&big_decimal)
482 );
483 }
484 #[cfg(feature = "uuid")]
485 #[test]
486 fn test_as_uuid() {
487 let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap();
488 assert_eq!(ClickhousePgValue::from(uuid).as_uuid(), Some(&uuid));
489 }
490
491 #[cfg(feature = "chrono")]
492 #[test]
493 fn test_as_naive_date_time() {
494 let dt = NaiveDate::from_ymd_opt(2021, 1, 1)
495 .expect("")
496 .and_hms_nano_opt(0, 0, 0, 123456789)
497 .expect("");
498 match ClickhousePgValue::from(dt.format("%Y-%m-%d %H:%M:%S").to_string())
499 .as_naive_date_time()
500 {
501 Some(Ok(dt)) => assert_eq!(
502 dt,
503 NaiveDate::from_ymd_opt(2021, 1, 1)
504 .expect("")
505 .and_hms_opt(0, 0, 0)
506 .expect("")
507 ),
508 Some(Err(err)) => panic!("{err:?}"),
509 None => panic!(),
510 }
511 }
512
513 #[test]
514 fn test_as_ipv4_addr() {
515 assert_eq!(
516 ClickhousePgValue::from("127.0.0.1").as_ipv4_addr(),
517 Some(Ok(Ipv4Addr::new(127, 0, 0, 1)))
518 );
519
520 assert_eq!(
521 ClickhousePgValue::from(u32::from(Ipv4Addr::new(127, 0, 0, 1))).as_ipv4_addr(),
522 Some(Ok(Ipv4Addr::new(127, 0, 0, 1)))
523 );
524 }
525}