1use crate::error::{PgError, PgResult};
3
4pub mod oid {
6 pub const BOOL: u32 = 16;
7 pub const BYTEA: u32 = 17;
8 pub const CHAR: u32 = 18;
9 pub const INT8: u32 = 20;
10 pub const INT2: u32 = 21;
11 pub const INT4: u32 = 23;
12 pub const TEXT: u32 = 25;
13 pub const OID: u32 = 26;
14 pub const FLOAT4: u32 = 700;
15 pub const FLOAT8: u32 = 701;
16 pub const VARCHAR: u32 = 1043;
17 pub const DATE: u32 = 1082;
18 pub const TIME: u32 = 1083;
19 pub const TIMESTAMP: u32 = 1114;
20 pub const TIMESTAMPTZ: u32 = 1184;
21 pub const INTERVAL: u32 = 1186;
22 pub const NUMERIC: u32 = 1700;
23 pub const UUID: u32 = 2950;
24 pub const JSONB: u32 = 3802;
25 pub const JSON: u32 = 114;
26 pub const INET: u32 = 869;
27 pub const CIDR: u32 = 650;
28 pub const MACADDR: u32 = 829;
29 pub const MACADDR8: u32 = 774;
30 pub const POINT: u32 = 600;
31 pub const LINE: u32 = 628;
32 pub const LSEG: u32 = 601;
33 pub const BOX: u32 = 603;
34 pub const PATH: u32 = 602;
35 pub const POLYGON: u32 = 604;
36 pub const CIRCLE: u32 = 718;
37
38 pub const BOOL_ARRAY: u32 = 1000;
40 pub const INT2_ARRAY: u32 = 1005;
41 pub const INT4_ARRAY: u32 = 1007;
42 pub const INT8_ARRAY: u32 = 1016;
43 pub const TEXT_ARRAY: u32 = 1009;
44 pub const FLOAT4_ARRAY: u32 = 1021;
45 pub const FLOAT8_ARRAY: u32 = 1022;
46 pub const VARCHAR_ARRAY: u32 = 1015;
47 pub const UUID_ARRAY: u32 = 2951;
48 pub const JSONB_ARRAY: u32 = 3807;
49 pub const JSON_ARRAY: u32 = 199;
50
51 pub const BIT: u32 = 1560;
53 pub const VARBIT: u32 = 1562;
54
55 pub const INT4RANGE: u32 = 3904;
57 pub const INT8RANGE: u32 = 3926;
58 pub const NUMRANGE: u32 = 3906;
59 pub const TSRANGE: u32 = 3908;
60 pub const TSTZRANGE: u32 = 3910;
61 pub const DATERANGE: u32 = 3912;
62}
63
64#[derive(Debug, Clone, PartialEq)]
66pub enum PgValue {
67 Null,
68 Bool(bool),
69 Int2(i16),
70 Int4(i32),
71 Int8(i64),
72 Float4(f32),
73 Float8(f64),
74 Text(String),
75 Bytes(Vec<u8>),
76 Json(String),
77 Jsonb(Vec<u8>),
78 Uuid([u8; 16]),
80 Date(i32),
82 Time(i64),
84 Timestamp(i64),
86 Timestamptz(i64),
88 Interval {
90 months: i32,
91 days: i32,
92 microseconds: i64,
93 },
94 Inet(String),
96 Numeric(String),
98 MacAddr([u8; 6]),
100 Point {
102 x: f64,
103 y: f64,
104 },
105 MacAddr8([u8; 8]),
107 Bit {
109 len: u32,
110 data: Vec<u8>,
111 },
112 Range(String),
115 Array(Vec<PgValue>),
117}
118
119impl PgValue {
120 pub fn to_text_bytes(&self) -> Option<Vec<u8>> {
122 match self {
123 PgValue::Null => None,
124 PgValue::Bool(b) => Some(if *b { b"t".to_vec() } else { b"f".to_vec() }),
125 PgValue::Int2(v) => Some(v.to_string().into_bytes()),
126 PgValue::Int4(v) => Some(v.to_string().into_bytes()),
127 PgValue::Int8(v) => Some(v.to_string().into_bytes()),
128 PgValue::Float4(v) => Some(v.to_string().into_bytes()),
129 PgValue::Float8(v) => Some(v.to_string().into_bytes()),
130 PgValue::Text(s) => Some(s.as_bytes().to_vec()),
131 PgValue::Bytes(b) => Some(b.clone()),
132 PgValue::Json(s) => Some(s.as_bytes().to_vec()),
133 PgValue::Jsonb(b) => Some(b.clone()),
134 PgValue::Uuid(bytes) => Some(format_uuid(bytes).into_bytes()),
135 PgValue::Date(days) => Some(format_date(*days).into_bytes()),
136 PgValue::Time(us) => Some(format_time(*us).into_bytes()),
137 PgValue::Timestamp(us) => Some(format_timestamp(*us).into_bytes()),
138 PgValue::Timestamptz(us) => Some(format_timestamp_tz(*us).into_bytes()),
139 PgValue::Interval {
140 months,
141 days,
142 microseconds,
143 } => Some(format_interval(*months, *days, *microseconds).into_bytes()),
144 PgValue::Inet(s) => Some(s.as_bytes().to_vec()),
145 PgValue::Numeric(s) => Some(s.as_bytes().to_vec()),
146 PgValue::MacAddr(bytes) => Some(
147 format!(
148 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
149 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
150 )
151 .into_bytes(),
152 ),
153 PgValue::Point { x, y } => Some(format!("({},{})", x, y).into_bytes()),
154 PgValue::MacAddr8(bytes) => Some(
155 format!(
156 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
157 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]
158 )
159 .into_bytes(),
160 ),
161 PgValue::Bit { len, data } => {
162 let mut s = String::with_capacity(*len as usize);
163 for i in 0..*len as usize {
164 let byte_idx = i / 8;
165 let bit_idx = 7 - (i % 8);
166 if byte_idx < data.len() && (data[byte_idx] >> bit_idx) & 1 == 1 {
167 s.push('1');
168 } else {
169 s.push('0');
170 }
171 }
172 Some(s.into_bytes())
173 }
174 PgValue::Range(s) => Some(s.as_bytes().to_vec()),
175 PgValue::Array(values) => {
176 let inner: Vec<String> = values
177 .iter()
178 .map(|v| match v {
179 PgValue::Null => "NULL".to_string(),
180 _ => match v.to_text_bytes() {
181 Some(b) => {
182 let s = String::from_utf8_lossy(&b).to_string();
183 escape_array_element(&s)
184 }
185 None => "NULL".to_string(),
186 },
187 })
188 .collect();
189 Some(format!("{{{}}}", inner.join(",")).into_bytes())
190 }
191 }
192 }
193
194 pub fn to_binary_bytes(&self) -> Option<Vec<u8>> {
200 match self {
201 PgValue::Null => None,
202 PgValue::Bool(b) => Some(vec![if *b { 1 } else { 0 }]),
203 PgValue::Int2(v) => Some(v.to_be_bytes().to_vec()),
204 PgValue::Int4(v) => Some(v.to_be_bytes().to_vec()),
205 PgValue::Int8(v) => Some(v.to_be_bytes().to_vec()),
206 PgValue::Float4(v) => Some(v.to_be_bytes().to_vec()),
207 PgValue::Float8(v) => Some(v.to_be_bytes().to_vec()),
208 PgValue::Text(s) => Some(s.as_bytes().to_vec()),
209 PgValue::Bytes(b) => Some(b.clone()),
210 PgValue::Json(s) => Some(s.as_bytes().to_vec()),
211 PgValue::Jsonb(b) => {
212 let mut buf = Vec::with_capacity(1 + b.len());
214 buf.push(1);
215 buf.extend_from_slice(b);
216 Some(buf)
217 }
218 PgValue::Uuid(bytes) => Some(bytes.to_vec()),
219 PgValue::Date(days) => Some(days.to_be_bytes().to_vec()),
220 PgValue::Time(us) => Some(us.to_be_bytes().to_vec()),
221 PgValue::Timestamp(us) | PgValue::Timestamptz(us) => Some(us.to_be_bytes().to_vec()),
222 PgValue::Interval {
223 months,
224 days,
225 microseconds,
226 } => {
227 let mut buf = Vec::with_capacity(16);
228 buf.extend_from_slice(µseconds.to_be_bytes());
229 buf.extend_from_slice(&days.to_be_bytes());
230 buf.extend_from_slice(&months.to_be_bytes());
231 Some(buf)
232 }
233 PgValue::Inet(s) => encode_inet_binary(s).ok(),
234 PgValue::Numeric(s) => {
235 Some(s.as_bytes().to_vec())
237 }
238 PgValue::MacAddr(bytes) => Some(bytes.to_vec()),
239 PgValue::MacAddr8(bytes) => Some(bytes.to_vec()),
240 PgValue::Bit { len, data } => {
241 let mut buf = Vec::with_capacity(4 + data.len());
243 buf.extend_from_slice(&(*len as i32).to_be_bytes());
244 buf.extend_from_slice(data);
245 Some(buf)
246 }
247 PgValue::Point { x, y } => {
248 let mut buf = Vec::with_capacity(16);
249 buf.extend_from_slice(&x.to_be_bytes());
250 buf.extend_from_slice(&y.to_be_bytes());
251 Some(buf)
252 }
253 PgValue::Range(s) => {
254 Some(s.as_bytes().to_vec())
256 }
257 PgValue::Array(values) => {
258 let inner: Vec<String> = values
261 .iter()
262 .map(|v| match v {
263 PgValue::Null => "NULL".to_string(),
264 _ => match v.to_text_bytes() {
265 Some(b) => {
266 let s = String::from_utf8_lossy(&b).to_string();
267 escape_array_element(&s)
268 }
269 None => "NULL".to_string(),
270 },
271 })
272 .collect();
273 Some(format!("{{{}}}", inner.join(",")).into_bytes())
274 }
275 }
276 }
277
278 pub fn prefers_binary(&self) -> bool {
284 matches!(
285 self,
286 PgValue::Bool(_)
287 | PgValue::Int2(_)
288 | PgValue::Int4(_)
289 | PgValue::Int8(_)
290 | PgValue::Float4(_)
291 | PgValue::Float8(_)
292 | PgValue::Bytes(_)
293 | PgValue::Uuid(_)
294 | PgValue::Date(_)
295 | PgValue::Time(_)
296 | PgValue::Timestamp(_)
297 | PgValue::Timestamptz(_)
298 | PgValue::Interval { .. }
299 | PgValue::Jsonb(_)
300 | PgValue::MacAddr(_)
301 | PgValue::Point { .. }
302 )
303 }
304
305 pub fn from_text(type_oid: u32, data: &[u8]) -> PgResult<Self> {
307 let s = std::str::from_utf8(data)
308 .map_err(|_| PgError::TypeConversion("Invalid UTF-8".to_string()))?;
309 match type_oid {
310 oid::BOOL => Ok(PgValue::Bool(s == "t" || s == "true" || s == "1")),
311 oid::INT2 => {
312 Ok(PgValue::Int2(s.parse().map_err(|_| {
313 PgError::TypeConversion("Invalid INT2".to_string())
314 })?))
315 }
316 oid::INT4 | oid::OID => {
317 Ok(PgValue::Int4(s.parse().map_err(|_| {
318 PgError::TypeConversion("Invalid INT4/OID".to_string())
319 })?))
320 }
321 oid::INT8 => {
322 Ok(PgValue::Int8(s.parse().map_err(|_| {
323 PgError::TypeConversion("Invalid INT8".to_string())
324 })?))
325 }
326 oid::FLOAT4 => {
327 Ok(PgValue::Float4(s.parse().map_err(|_| {
328 PgError::TypeConversion("Invalid FLOAT4".to_string())
329 })?))
330 }
331 oid::FLOAT8 => {
332 Ok(PgValue::Float8(s.parse().map_err(|_| {
333 PgError::TypeConversion("Invalid FLOAT8".to_string())
334 })?))
335 }
336 oid::NUMERIC => Ok(PgValue::Numeric(s.to_string())),
337 oid::JSONB => Ok(PgValue::Jsonb(data.to_vec())),
338 oid::JSON => Ok(PgValue::Json(s.to_string())),
339 oid::BYTEA => Ok(PgValue::Bytes(decode_bytea_hex(s))),
340 oid::UUID => Ok(PgValue::Uuid(parse_uuid_text(s)?)),
341 oid::DATE => Ok(PgValue::Date(parse_date_text(s)?)),
342 oid::TIME => Ok(PgValue::Time(parse_time_text(s)?)),
343 oid::TIMESTAMP => Ok(PgValue::Timestamp(parse_timestamp_text(s)?)),
344 oid::TIMESTAMPTZ => Ok(PgValue::Timestamptz(parse_timestamp_text(s)?)),
345 oid::INTERVAL => {
346 let (months, days, us) = parse_interval_text(s)?;
347 Ok(PgValue::Interval {
348 months,
349 days,
350 microseconds: us,
351 })
352 }
353 oid::INET | oid::CIDR => Ok(PgValue::Inet(s.to_string())),
354 oid::MACADDR => {
355 let bytes = parse_macaddr_text(s)?;
357 Ok(PgValue::MacAddr(bytes))
358 }
359 oid::MACADDR8 => {
360 let bytes = parse_macaddr8_text(s)?;
362 Ok(PgValue::MacAddr8(bytes))
363 }
364 oid::BIT | oid::VARBIT => {
365 let len = s.len() as u32;
367 let mut data = vec![0u8; (len as usize).div_ceil(8)];
368 for (i, ch) in s.chars().enumerate() {
369 if ch == '1' {
370 let byte_idx = i / 8;
371 let bit_idx = 7 - (i % 8);
372 data[byte_idx] |= 1 << bit_idx;
373 }
374 }
375 Ok(PgValue::Bit { len, data })
376 }
377 oid::POINT => {
378 let (x, y) = parse_point_text(s)?;
380 Ok(PgValue::Point { x, y })
381 }
382 oid::INT4RANGE
383 | oid::INT8RANGE
384 | oid::NUMRANGE
385 | oid::TSRANGE
386 | oid::TSTZRANGE
387 | oid::DATERANGE => Ok(PgValue::Range(s.to_string())),
388 _ => Ok(PgValue::Text(s.to_string())),
389 }
390 }
391
392 pub fn from_binary(type_oid: u32, data: &[u8]) -> PgResult<Self> {
394 match type_oid {
395 oid::BOOL => Ok(PgValue::Bool(data.first().is_some_and(|&b| b != 0))),
396 oid::INT2 if data.len() >= 2 => {
397 Ok(PgValue::Int2(i16::from_be_bytes([data[0], data[1]])))
398 }
399 oid::INT4 | oid::OID if data.len() >= 4 => Ok(PgValue::Int4(i32::from_be_bytes([
400 data[0], data[1], data[2], data[3],
401 ]))),
402 oid::INT8 if data.len() >= 8 => Ok(PgValue::Int8(i64::from_be_bytes([
403 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
404 ]))),
405 oid::FLOAT4 if data.len() >= 4 => Ok(PgValue::Float4(f32::from_be_bytes([
406 data[0], data[1], data[2], data[3],
407 ]))),
408 oid::FLOAT8 if data.len() >= 8 => Ok(PgValue::Float8(f64::from_be_bytes([
409 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
410 ]))),
411 oid::UUID if data.len() >= 16 => {
412 let mut bytes = [0u8; 16];
413 bytes.copy_from_slice(&data[..16]);
414 Ok(PgValue::Uuid(bytes))
415 }
416 oid::DATE if data.len() >= 4 => Ok(PgValue::Date(i32::from_be_bytes([
417 data[0], data[1], data[2], data[3],
418 ]))),
419 oid::TIME if data.len() >= 8 => Ok(PgValue::Time(i64::from_be_bytes([
420 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
421 ]))),
422 oid::TIMESTAMP | oid::TIMESTAMPTZ if data.len() >= 8 => {
423 let us = i64::from_be_bytes([
424 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
425 ]);
426 if type_oid == oid::TIMESTAMPTZ {
427 Ok(PgValue::Timestamptz(us))
428 } else {
429 Ok(PgValue::Timestamp(us))
430 }
431 }
432 oid::INTERVAL if data.len() >= 16 => {
433 let microseconds = i64::from_be_bytes([
434 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
435 ]);
436 let days = i32::from_be_bytes([data[8], data[9], data[10], data[11]]);
437 let months = i32::from_be_bytes([data[12], data[13], data[14], data[15]]);
438 Ok(PgValue::Interval {
439 months,
440 days,
441 microseconds,
442 })
443 }
444 oid::JSONB => {
445 if data.len() > 1 {
447 Ok(PgValue::Jsonb(data[1..].to_vec()))
448 } else {
449 Ok(PgValue::Jsonb(Vec::new()))
450 }
451 }
452 oid::BYTEA => Ok(PgValue::Bytes(data.to_vec())),
453 oid::INET | oid::CIDR => {
454 if data.len() < 4 {
456 return Err(PgError::TypeConversion("INET/CIDR binary too short".into()));
457 }
458 let family = data[0];
459 let mask = data[1];
460 let addr_len = data[3] as usize;
462 if data.len() < 4 + addr_len {
463 return Err(PgError::TypeConversion(
464 "INET/CIDR address truncated".into(),
465 ));
466 }
467 let addr_bytes = &data[4..4 + addr_len];
468 let addr_str = match family {
469 2 if addr_len == 4 => {
471 format!(
472 "{}.{}.{}.{}",
473 addr_bytes[0], addr_bytes[1], addr_bytes[2], addr_bytes[3]
474 )
475 }
476 3 if addr_len == 16 => format_ipv6(addr_bytes),
478 _ => {
479 return Err(PgError::TypeConversion(format!(
480 "Unknown INET family: {}",
481 family
482 )));
483 }
484 };
485 let default_mask = if family == 2 { 32 } else { 128 };
487 if mask != default_mask || type_oid == oid::CIDR {
488 Ok(PgValue::Inet(format!("{}/{}", addr_str, mask)))
489 } else {
490 Ok(PgValue::Inet(addr_str))
491 }
492 }
493 oid::NUMERIC => {
494 if data.len() < 8 {
498 return Err(PgError::TypeConversion("NUMERIC binary too short".into()));
499 }
500 let ndigits = u16::from_be_bytes([data[0], data[1]]) as usize;
501 let weight = i16::from_be_bytes([data[2], data[3]]);
502 let sign = u16::from_be_bytes([data[4], data[5]]);
503 let dscale = u16::from_be_bytes([data[6], data[7]]) as usize;
504
505 if data.len() < 8 + ndigits * 2 {
506 return Err(PgError::TypeConversion("NUMERIC binary truncated".into()));
507 }
508
509 let mut digits = Vec::with_capacity(ndigits);
510 for i in 0..ndigits {
511 let off = 8 + i * 2;
512 digits.push(u16::from_be_bytes([data[off], data[off + 1]]));
513 }
514
515 Ok(PgValue::Numeric(format_numeric_binary(
516 weight, sign, dscale, &digits,
517 )))
518 }
519 oid::MACADDR if data.len() >= 6 => {
520 let mut bytes = [0u8; 6];
521 bytes.copy_from_slice(&data[..6]);
522 Ok(PgValue::MacAddr(bytes))
523 }
524 oid::MACADDR8 if data.len() >= 8 => {
525 let mut bytes = [0u8; 8];
526 bytes.copy_from_slice(&data[..8]);
527 Ok(PgValue::MacAddr8(bytes))
528 }
529 oid::BIT | oid::VARBIT if data.len() >= 4 => {
530 let bit_len = i32::from_be_bytes([data[0], data[1], data[2], data[3]]) as u32;
532 let packed = data[4..].to_vec();
533 Ok(PgValue::Bit {
534 len: bit_len,
535 data: packed,
536 })
537 }
538 oid::POINT if data.len() >= 16 => {
539 let x = f64::from_be_bytes([
540 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
541 ]);
542 let y = f64::from_be_bytes([
543 data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
544 ]);
545 Ok(PgValue::Point { x, y })
546 }
547 oid::BOOL_ARRAY
548 | oid::INT2_ARRAY
549 | oid::INT4_ARRAY
550 | oid::INT8_ARRAY
551 | oid::FLOAT4_ARRAY
552 | oid::FLOAT8_ARRAY
553 | oid::TEXT_ARRAY
554 | oid::VARCHAR_ARRAY
555 | oid::UUID_ARRAY
556 | oid::JSONB_ARRAY
557 | oid::JSON_ARRAY => parse_binary_array(data),
558 _ => {
559 Ok(PgValue::Text(String::from_utf8_lossy(data).to_string()))
561 }
562 }
563 }
564
565 pub fn as_i32(&self) -> Option<i32> {
567 match self {
568 PgValue::Int4(v) => Some(*v),
569 PgValue::Int2(v) => Some(*v as i32),
570 _ => None,
571 }
572 }
573
574 pub fn as_i64(&self) -> Option<i64> {
576 match self {
577 PgValue::Int8(v) => Some(*v),
578 PgValue::Int4(v) => Some(*v as i64),
579 PgValue::Int2(v) => Some(*v as i64),
580 _ => None,
581 }
582 }
583
584 pub fn as_str(&self) -> Option<&str> {
586 match self {
587 PgValue::Text(s) => Some(s),
588 PgValue::Json(s) => Some(s),
589 PgValue::Inet(s) => Some(s),
590 PgValue::Numeric(s) => Some(s),
591 _ => None,
592 }
593 }
594
595 pub fn as_bool(&self) -> Option<bool> {
597 match self {
598 PgValue::Bool(b) => Some(*b),
599 _ => None,
600 }
601 }
602
603 pub fn as_f64(&self) -> Option<f64> {
605 match self {
606 PgValue::Float8(v) => Some(*v),
607 PgValue::Float4(v) => Some(*v as f64),
608 PgValue::Int4(v) => Some(*v as f64),
609 PgValue::Int8(v) => Some(*v as f64),
610 _ => None,
611 }
612 }
613
614 pub fn is_null(&self) -> bool {
616 matches!(self, PgValue::Null)
617 }
618}
619
620pub trait ToSql {
626 fn to_sql(&self) -> PgValue;
628
629 fn type_oid(&self) -> u32 {
631 0
632 }
633}
634
635pub trait FromSql: Sized {
637 fn from_sql(value: &PgValue) -> PgResult<Self>;
639}
640
641impl ToSql for i16 {
644 fn to_sql(&self) -> PgValue {
645 PgValue::Int2(*self)
646 }
647 fn type_oid(&self) -> u32 {
648 oid::INT2
649 }
650}
651
652impl ToSql for i32 {
653 fn to_sql(&self) -> PgValue {
654 PgValue::Int4(*self)
655 }
656 fn type_oid(&self) -> u32 {
657 oid::INT4
658 }
659}
660
661impl ToSql for i64 {
662 fn to_sql(&self) -> PgValue {
663 PgValue::Int8(*self)
664 }
665 fn type_oid(&self) -> u32 {
666 oid::INT8
667 }
668}
669
670impl ToSql for f32 {
671 fn to_sql(&self) -> PgValue {
672 PgValue::Float4(*self)
673 }
674 fn type_oid(&self) -> u32 {
675 oid::FLOAT4
676 }
677}
678
679impl ToSql for f64 {
680 fn to_sql(&self) -> PgValue {
681 PgValue::Float8(*self)
682 }
683 fn type_oid(&self) -> u32 {
684 oid::FLOAT8
685 }
686}
687
688impl ToSql for bool {
689 fn to_sql(&self) -> PgValue {
690 PgValue::Bool(*self)
691 }
692 fn type_oid(&self) -> u32 {
693 oid::BOOL
694 }
695}
696
697impl ToSql for &str {
698 fn to_sql(&self) -> PgValue {
699 PgValue::Text(self.to_string())
700 }
701 fn type_oid(&self) -> u32 {
702 oid::TEXT
703 }
704}
705
706impl ToSql for String {
707 fn to_sql(&self) -> PgValue {
708 PgValue::Text(self.clone())
709 }
710 fn type_oid(&self) -> u32 {
711 oid::TEXT
712 }
713}
714
715impl ToSql for &[u8] {
716 fn to_sql(&self) -> PgValue {
717 PgValue::Bytes(self.to_vec())
718 }
719 fn type_oid(&self) -> u32 {
720 oid::BYTEA
721 }
722}
723
724impl ToSql for Vec<u8> {
725 fn to_sql(&self) -> PgValue {
726 PgValue::Bytes(self.clone())
727 }
728 fn type_oid(&self) -> u32 {
729 oid::BYTEA
730 }
731}
732
733impl<T: ToSql> ToSql for Option<T> {
734 fn to_sql(&self) -> PgValue {
735 match self {
736 Some(v) => v.to_sql(),
737 None => PgValue::Null,
738 }
739 }
740}
741
742impl ToSql for PgValue {
743 fn to_sql(&self) -> PgValue {
744 self.clone()
745 }
746}
747
748impl ToSql for Vec<i16> {
751 fn to_sql(&self) -> PgValue {
752 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
753 }
754 fn type_oid(&self) -> u32 {
755 oid::INT2_ARRAY
756 }
757}
758
759impl ToSql for Vec<i32> {
760 fn to_sql(&self) -> PgValue {
761 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
762 }
763 fn type_oid(&self) -> u32 {
764 oid::INT4_ARRAY
765 }
766}
767
768impl ToSql for Vec<i64> {
769 fn to_sql(&self) -> PgValue {
770 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
771 }
772 fn type_oid(&self) -> u32 {
773 oid::INT8_ARRAY
774 }
775}
776
777impl ToSql for Vec<f32> {
778 fn to_sql(&self) -> PgValue {
779 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
780 }
781 fn type_oid(&self) -> u32 {
782 oid::FLOAT4_ARRAY
783 }
784}
785
786impl ToSql for Vec<f64> {
787 fn to_sql(&self) -> PgValue {
788 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
789 }
790 fn type_oid(&self) -> u32 {
791 oid::FLOAT8_ARRAY
792 }
793}
794
795impl ToSql for Vec<bool> {
796 fn to_sql(&self) -> PgValue {
797 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
798 }
799 fn type_oid(&self) -> u32 {
800 oid::BOOL_ARRAY
801 }
802}
803
804impl ToSql for Vec<String> {
805 fn to_sql(&self) -> PgValue {
806 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
807 }
808 fn type_oid(&self) -> u32 {
809 oid::TEXT_ARRAY
810 }
811}
812
813impl ToSql for &[i16] {
814 fn to_sql(&self) -> PgValue {
815 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
816 }
817 fn type_oid(&self) -> u32 {
818 oid::INT2_ARRAY
819 }
820}
821
822impl ToSql for &[i32] {
823 fn to_sql(&self) -> PgValue {
824 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
825 }
826 fn type_oid(&self) -> u32 {
827 oid::INT4_ARRAY
828 }
829}
830
831impl ToSql for &[i64] {
832 fn to_sql(&self) -> PgValue {
833 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
834 }
835 fn type_oid(&self) -> u32 {
836 oid::INT8_ARRAY
837 }
838}
839
840impl ToSql for &[f32] {
841 fn to_sql(&self) -> PgValue {
842 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
843 }
844 fn type_oid(&self) -> u32 {
845 oid::FLOAT4_ARRAY
846 }
847}
848
849impl ToSql for &[f64] {
850 fn to_sql(&self) -> PgValue {
851 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
852 }
853 fn type_oid(&self) -> u32 {
854 oid::FLOAT8_ARRAY
855 }
856}
857
858impl ToSql for &[bool] {
859 fn to_sql(&self) -> PgValue {
860 PgValue::Array(self.iter().map(|v| v.to_sql()).collect())
861 }
862 fn type_oid(&self) -> u32 {
863 oid::BOOL_ARRAY
864 }
865}
866
867impl ToSql for std::net::IpAddr {
870 fn to_sql(&self) -> PgValue {
871 PgValue::Inet(self.to_string())
872 }
873 fn type_oid(&self) -> u32 {
874 oid::INET
875 }
876}
877
878impl ToSql for std::net::Ipv4Addr {
879 fn to_sql(&self) -> PgValue {
880 PgValue::Inet(self.to_string())
881 }
882 fn type_oid(&self) -> u32 {
883 oid::INET
884 }
885}
886
887impl ToSql for std::net::Ipv6Addr {
888 fn to_sql(&self) -> PgValue {
889 PgValue::Inet(self.to_string())
890 }
891 fn type_oid(&self) -> u32 {
892 oid::INET
893 }
894}
895
896impl ToSql for [u8; 6] {
899 fn to_sql(&self) -> PgValue {
900 PgValue::MacAddr(*self)
901 }
902 fn type_oid(&self) -> u32 {
903 oid::MACADDR
904 }
905}
906
907impl ToSql for (f64, f64) {
908 fn to_sql(&self) -> PgValue {
909 PgValue::Point {
910 x: self.0,
911 y: self.1,
912 }
913 }
914 fn type_oid(&self) -> u32 {
915 oid::POINT
916 }
917}
918
919#[cfg(feature = "chrono")]
923const PG_EPOCH_OFFSET_SECS: i64 = 946_684_800;
924
925#[cfg(feature = "chrono")]
926impl ToSql for chrono::NaiveDateTime {
927 fn to_sql(&self) -> PgValue {
928 let unix_secs = self.and_utc().timestamp();
929 let sub_micros = self.and_utc().timestamp_subsec_micros() as i64;
930 let pg_micros = (unix_secs - PG_EPOCH_OFFSET_SECS) * 1_000_000 + sub_micros;
931 PgValue::Timestamp(pg_micros)
932 }
933 fn type_oid(&self) -> u32 {
934 oid::TIMESTAMP
935 }
936}
937
938#[cfg(feature = "decimal")]
941impl ToSql for rust_decimal::Decimal {
942 fn to_sql(&self) -> PgValue {
943 PgValue::Numeric(self.to_string())
944 }
945 fn type_oid(&self) -> u32 {
946 oid::NUMERIC
947 }
948}
949
950impl FromSql for i16 {
953 fn from_sql(value: &PgValue) -> PgResult<Self> {
954 match value {
955 PgValue::Int2(v) => Ok(*v),
956 PgValue::Text(s) => s
957 .parse()
958 .map_err(|_| PgError::TypeConversion("Not an i16".into())),
959 _ => Err(PgError::TypeConversion("Cannot convert to i16".into())),
960 }
961 }
962}
963
964impl FromSql for i32 {
965 fn from_sql(value: &PgValue) -> PgResult<Self> {
966 match value {
967 PgValue::Int4(v) => Ok(*v),
968 PgValue::Int2(v) => Ok(*v as i32),
969 PgValue::Text(s) => s
970 .parse()
971 .map_err(|_| PgError::TypeConversion("Not an i32".into())),
972 _ => Err(PgError::TypeConversion("Cannot convert to i32".into())),
973 }
974 }
975}
976
977impl FromSql for i64 {
978 fn from_sql(value: &PgValue) -> PgResult<Self> {
979 match value {
980 PgValue::Int8(v) => Ok(*v),
981 PgValue::Int4(v) => Ok(*v as i64),
982 PgValue::Int2(v) => Ok(*v as i64),
983 PgValue::Text(s) => s
984 .parse()
985 .map_err(|_| PgError::TypeConversion("Not an i64".into())),
986 _ => Err(PgError::TypeConversion("Cannot convert to i64".into())),
987 }
988 }
989}
990
991impl FromSql for f32 {
992 fn from_sql(value: &PgValue) -> PgResult<Self> {
993 match value {
994 PgValue::Float4(v) => Ok(*v),
995 PgValue::Text(s) => s
996 .parse()
997 .map_err(|_| PgError::TypeConversion("Not an f32".into())),
998 _ => Err(PgError::TypeConversion("Cannot convert to f32".into())),
999 }
1000 }
1001}
1002
1003impl FromSql for f64 {
1004 fn from_sql(value: &PgValue) -> PgResult<Self> {
1005 match value {
1006 PgValue::Float8(v) => Ok(*v),
1007 PgValue::Float4(v) => Ok(*v as f64),
1008 PgValue::Int4(v) => Ok(*v as f64),
1009 PgValue::Int8(v) => Ok(*v as f64),
1010 PgValue::Text(s) => s
1011 .parse()
1012 .map_err(|_| PgError::TypeConversion("Not an f64".into())),
1013 _ => Err(PgError::TypeConversion("Cannot convert to f64".into())),
1014 }
1015 }
1016}
1017
1018impl FromSql for bool {
1019 fn from_sql(value: &PgValue) -> PgResult<Self> {
1020 match value {
1021 PgValue::Bool(v) => Ok(*v),
1022 PgValue::Text(s) => Ok(s == "t" || s == "true" || s == "1"),
1023 _ => Err(PgError::TypeConversion("Cannot convert to bool".into())),
1024 }
1025 }
1026}
1027
1028impl FromSql for String {
1029 fn from_sql(value: &PgValue) -> PgResult<Self> {
1030 match value {
1031 PgValue::Text(s) => Ok(s.clone()),
1032 PgValue::Json(s) => Ok(s.clone()),
1033 PgValue::Inet(s) => Ok(s.clone()),
1034 PgValue::Numeric(s) => Ok(s.clone()),
1035 PgValue::Int2(v) => Ok(v.to_string()),
1036 PgValue::Int4(v) => Ok(v.to_string()),
1037 PgValue::Int8(v) => Ok(v.to_string()),
1038 PgValue::Float4(v) => Ok(v.to_string()),
1039 PgValue::Float8(v) => Ok(v.to_string()),
1040 PgValue::Bool(v) => Ok(v.to_string()),
1041 PgValue::Uuid(b) => Ok(format_uuid(b)),
1042 PgValue::Null => Err(PgError::TypeConversion(
1043 "Cannot convert NULL to String".into(),
1044 )),
1045 _ => Err(PgError::TypeConversion("Cannot convert to String".into())),
1046 }
1047 }
1048}
1049
1050impl<T: FromSql> FromSql for Option<T> {
1051 fn from_sql(value: &PgValue) -> PgResult<Self> {
1052 if value.is_null() {
1053 Ok(None)
1054 } else {
1055 T::from_sql(value).map(Some)
1056 }
1057 }
1058}
1059
1060impl FromSql for Vec<u8> {
1061 fn from_sql(value: &PgValue) -> PgResult<Self> {
1062 match value {
1063 PgValue::Bytes(b) => Ok(b.clone()),
1064 PgValue::Null => Err(PgError::TypeConversion(
1065 "Cannot convert NULL to Vec<u8>".into(),
1066 )),
1067 _ => Err(PgError::TypeConversion("Cannot convert to Vec<u8>".into())),
1068 }
1069 }
1070}
1071
1072impl FromSql for [u8; 16] {
1073 fn from_sql(value: &PgValue) -> PgResult<Self> {
1074 match value {
1075 PgValue::Uuid(b) => Ok(*b),
1076 PgValue::Null => Err(PgError::TypeConversion(
1077 "Cannot convert NULL to [u8; 16]".into(),
1078 )),
1079 _ => Err(PgError::TypeConversion("Cannot convert to [u8; 16]".into())),
1080 }
1081 }
1082}
1083
1084impl FromSql for Vec<i16> {
1087 fn from_sql(value: &PgValue) -> PgResult<Self> {
1088 match value {
1089 PgValue::Array(arr) => arr.iter().map(i16::from_sql).collect(),
1090 PgValue::Null => Err(PgError::TypeConversion(
1091 "Cannot convert NULL to Vec<i16>".into(),
1092 )),
1093 _ => Err(PgError::TypeConversion("Cannot convert to Vec<i16>".into())),
1094 }
1095 }
1096}
1097
1098impl FromSql for Vec<i32> {
1099 fn from_sql(value: &PgValue) -> PgResult<Self> {
1100 match value {
1101 PgValue::Array(arr) => arr.iter().map(i32::from_sql).collect(),
1102 PgValue::Null => Err(PgError::TypeConversion(
1103 "Cannot convert NULL to Vec<i32>".into(),
1104 )),
1105 _ => Err(PgError::TypeConversion("Cannot convert to Vec<i32>".into())),
1106 }
1107 }
1108}
1109
1110impl FromSql for Vec<i64> {
1111 fn from_sql(value: &PgValue) -> PgResult<Self> {
1112 match value {
1113 PgValue::Array(arr) => arr.iter().map(i64::from_sql).collect(),
1114 PgValue::Null => Err(PgError::TypeConversion(
1115 "Cannot convert NULL to Vec<i64>".into(),
1116 )),
1117 _ => Err(PgError::TypeConversion("Cannot convert to Vec<i64>".into())),
1118 }
1119 }
1120}
1121
1122impl FromSql for Vec<f32> {
1123 fn from_sql(value: &PgValue) -> PgResult<Self> {
1124 match value {
1125 PgValue::Array(arr) => arr.iter().map(f32::from_sql).collect(),
1126 PgValue::Null => Err(PgError::TypeConversion(
1127 "Cannot convert NULL to Vec<f32>".into(),
1128 )),
1129 _ => Err(PgError::TypeConversion("Cannot convert to Vec<f32>".into())),
1130 }
1131 }
1132}
1133
1134impl FromSql for Vec<f64> {
1135 fn from_sql(value: &PgValue) -> PgResult<Self> {
1136 match value {
1137 PgValue::Array(arr) => arr.iter().map(f64::from_sql).collect(),
1138 PgValue::Null => Err(PgError::TypeConversion(
1139 "Cannot convert NULL to Vec<f64>".into(),
1140 )),
1141 _ => Err(PgError::TypeConversion("Cannot convert to Vec<f64>".into())),
1142 }
1143 }
1144}
1145
1146impl FromSql for Vec<bool> {
1147 fn from_sql(value: &PgValue) -> PgResult<Self> {
1148 match value {
1149 PgValue::Array(arr) => arr.iter().map(bool::from_sql).collect(),
1150 PgValue::Null => Err(PgError::TypeConversion(
1151 "Cannot convert NULL to Vec<bool>".into(),
1152 )),
1153 _ => Err(PgError::TypeConversion(
1154 "Cannot convert to Vec<bool>".into(),
1155 )),
1156 }
1157 }
1158}
1159
1160impl FromSql for Vec<String> {
1161 fn from_sql(value: &PgValue) -> PgResult<Self> {
1162 match value {
1163 PgValue::Array(arr) => arr.iter().map(String::from_sql).collect(),
1164 PgValue::Null => Err(PgError::TypeConversion(
1165 "Cannot convert NULL to Vec<String>".into(),
1166 )),
1167 _ => Err(PgError::TypeConversion(
1168 "Cannot convert to Vec<String>".into(),
1169 )),
1170 }
1171 }
1172}
1173
1174impl FromSql for std::net::IpAddr {
1177 fn from_sql(value: &PgValue) -> PgResult<Self> {
1178 match value {
1179 PgValue::Inet(s) => {
1180 let addr_str = s.split('/').next().unwrap_or(s);
1181 addr_str
1182 .parse()
1183 .map_err(|_| PgError::TypeConversion(format!("Invalid IP address: {}", s)))
1184 }
1185 PgValue::Null => Err(PgError::TypeConversion(
1186 "Cannot convert NULL to IpAddr".into(),
1187 )),
1188 _ => Err(PgError::TypeConversion("Cannot convert to IpAddr".into())),
1189 }
1190 }
1191}
1192
1193impl FromSql for std::net::Ipv4Addr {
1194 fn from_sql(value: &PgValue) -> PgResult<Self> {
1195 match value {
1196 PgValue::Inet(s) => {
1197 let addr_str = s.split('/').next().unwrap_or(s);
1198 addr_str
1199 .parse()
1200 .map_err(|_| PgError::TypeConversion(format!("Invalid IPv4 address: {}", s)))
1201 }
1202 PgValue::Null => Err(PgError::TypeConversion(
1203 "Cannot convert NULL to Ipv4Addr".into(),
1204 )),
1205 _ => Err(PgError::TypeConversion("Cannot convert to Ipv4Addr".into())),
1206 }
1207 }
1208}
1209
1210impl FromSql for std::net::Ipv6Addr {
1211 fn from_sql(value: &PgValue) -> PgResult<Self> {
1212 match value {
1213 PgValue::Inet(s) => {
1214 let addr_str = s.split('/').next().unwrap_or(s);
1215 addr_str
1216 .parse()
1217 .map_err(|_| PgError::TypeConversion(format!("Invalid IPv6 address: {}", s)))
1218 }
1219 PgValue::Null => Err(PgError::TypeConversion(
1220 "Cannot convert NULL to Ipv6Addr".into(),
1221 )),
1222 _ => Err(PgError::TypeConversion("Cannot convert to Ipv6Addr".into())),
1223 }
1224 }
1225}
1226
1227impl FromSql for [u8; 6] {
1230 fn from_sql(value: &PgValue) -> PgResult<Self> {
1231 match value {
1232 PgValue::MacAddr(bytes) => Ok(*bytes),
1233 PgValue::Null => Err(PgError::TypeConversion(
1234 "Cannot convert NULL to [u8; 6]".into(),
1235 )),
1236 _ => Err(PgError::TypeConversion(
1237 "Cannot convert to [u8; 6] (MacAddr)".into(),
1238 )),
1239 }
1240 }
1241}
1242
1243impl FromSql for (f64, f64) {
1244 fn from_sql(value: &PgValue) -> PgResult<Self> {
1245 match value {
1246 PgValue::Point { x, y } => Ok((*x, *y)),
1247 PgValue::Null => Err(PgError::TypeConversion(
1248 "Cannot convert NULL to (f64, f64)".into(),
1249 )),
1250 _ => Err(PgError::TypeConversion(
1251 "Cannot convert to (f64, f64) (Point)".into(),
1252 )),
1253 }
1254 }
1255}
1256
1257pub trait ToParam {
1262 fn to_param(&self) -> PgValue;
1263}
1264
1265impl<T: ToSql> ToParam for T {
1266 fn to_param(&self) -> PgValue {
1267 self.to_sql()
1268 }
1269}
1270
1271fn parse_macaddr_text(s: &str) -> PgResult<[u8; 6]> {
1275 let parts: Vec<&str> = if s.contains(':') {
1276 s.split(':').collect()
1277 } else if s.contains('-') {
1278 s.split('-').collect()
1279 } else {
1280 return Err(PgError::TypeConversion(format!(
1281 "Invalid MAC address format: {}",
1282 s
1283 )));
1284 };
1285 if parts.len() != 6 {
1286 return Err(PgError::TypeConversion(format!(
1287 "Invalid MAC address: {}",
1288 s
1289 )));
1290 }
1291 let mut bytes = [0u8; 6];
1292 for (i, part) in parts.iter().enumerate() {
1293 bytes[i] = u8::from_str_radix(part, 16)
1294 .map_err(|_| PgError::TypeConversion(format!("Invalid MAC address hex: {}", part)))?;
1295 }
1296 Ok(bytes)
1297}
1298
1299fn parse_macaddr8_text(s: &str) -> PgResult<[u8; 8]> {
1301 let parts: Vec<&str> = if s.contains(':') {
1302 s.split(':').collect()
1303 } else if s.contains('-') {
1304 s.split('-').collect()
1305 } else {
1306 return Err(PgError::TypeConversion(format!(
1307 "Invalid MACADDR8 format: {}",
1308 s
1309 )));
1310 };
1311 if parts.len() != 8 {
1312 return Err(PgError::TypeConversion(format!("Invalid MACADDR8: {}", s)));
1313 }
1314 let mut bytes = [0u8; 8];
1315 for (i, part) in parts.iter().enumerate() {
1316 bytes[i] = u8::from_str_radix(part, 16)
1317 .map_err(|_| PgError::TypeConversion(format!("Invalid MACADDR8 hex: {}", part)))?;
1318 }
1319 Ok(bytes)
1320}
1321
1322fn parse_point_text(s: &str) -> PgResult<(f64, f64)> {
1324 let trimmed = s.trim();
1325 let inner = if trimmed.starts_with('(') && trimmed.ends_with(')') {
1326 &trimmed[1..trimmed.len() - 1]
1327 } else {
1328 trimmed
1329 };
1330 let comma = inner
1331 .find(',')
1332 .ok_or_else(|| PgError::TypeConversion(format!("Invalid point format: {}", s)))?;
1333 let x: f64 = inner[..comma]
1334 .trim()
1335 .parse()
1336 .map_err(|_| PgError::TypeConversion(format!("Invalid point x: {}", &inner[..comma])))?;
1337 let y: f64 = inner[comma + 1..].trim().parse().map_err(|_| {
1338 PgError::TypeConversion(format!("Invalid point y: {}", &inner[comma + 1..]))
1339 })?;
1340 Ok((x, y))
1341}
1342
1343fn format_numeric_binary(weight: i16, sign: u16, dscale: usize, digits: &[u16]) -> String {
1350 const _NUMERIC_POS: u16 = 0x0000;
1352 const NUMERIC_NEG: u16 = 0x4000;
1353 const NUMERIC_NAN: u16 = 0xC000;
1354 const NUMERIC_PINF: u16 = 0xD000;
1355 const NUMERIC_NINF: u16 = 0xF000;
1356
1357 match sign {
1358 NUMERIC_NAN => return "NaN".to_string(),
1359 NUMERIC_PINF => return "Infinity".to_string(),
1360 NUMERIC_NINF => return "-Infinity".to_string(),
1361 _ => {}
1362 }
1363
1364 if digits.is_empty() {
1365 return if dscale > 0 {
1367 format!("0.{}", "0".repeat(dscale))
1368 } else {
1369 "0".to_string()
1370 };
1371 }
1372
1373 let mut result = String::with_capacity(digits.len() * 4 + 4);
1378
1379 if sign == NUMERIC_NEG {
1380 result.push('-');
1381 }
1382
1383 let int_groups = (weight + 1).max(0) as usize;
1385
1386 if int_groups == 0 {
1387 result.push('0');
1388 } else {
1389 for i in 0..int_groups {
1390 let d = if i < digits.len() { digits[i] } else { 0 };
1391 if i == 0 {
1392 result.push_str(&d.to_string());
1394 } else {
1395 result.push_str(&format!("{:04}", d));
1397 }
1398 }
1399 }
1400
1401 if dscale > 0 {
1403 result.push('.');
1404 let mut frac_chars = 0;
1405 let frac_start = int_groups;
1406 let mut i = frac_start;
1407 while frac_chars < dscale {
1408 let d = if i < digits.len() { digits[i] } else { 0 };
1409 let s = format!("{:04}", d);
1410 for ch in s.chars() {
1411 if frac_chars >= dscale {
1412 break;
1413 }
1414 result.push(ch);
1415 frac_chars += 1;
1416 }
1417 i += 1;
1418 }
1419 }
1420
1421 result
1422}
1423
1424fn parse_binary_array(data: &[u8]) -> PgResult<PgValue> {
1433 if data.len() < 12 {
1434 return Err(PgError::TypeConversion("Binary array too short".into()));
1435 }
1436
1437 let ndim = i32::from_be_bytes([data[0], data[1], data[2], data[3]]);
1438 let element_oid = u32::from_be_bytes([data[8], data[9], data[10], data[11]]);
1440
1441 if ndim == 0 {
1442 return Ok(PgValue::Array(Vec::new()));
1443 }
1444 if ndim != 1 {
1445 return Err(PgError::TypeConversion(format!(
1447 "Unsupported array dimensions: {}",
1448 ndim
1449 )));
1450 }
1451
1452 let mut pos = 12;
1453 if data.len() < pos + 8 {
1455 return Err(PgError::TypeConversion(
1456 "Binary array dimension truncated".into(),
1457 ));
1458 }
1459 let num_elements =
1460 i32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
1461 pos += 4;
1462 let _lower_bound = i32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
1463 pos += 4;
1464
1465 let mut values = Vec::with_capacity(num_elements);
1466 for _ in 0..num_elements {
1467 if data.len() < pos + 4 {
1468 return Err(PgError::TypeConversion(
1469 "Binary array element truncated".into(),
1470 ));
1471 }
1472 let elem_len = i32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
1473 pos += 4;
1474
1475 if elem_len < 0 {
1476 values.push(PgValue::Null);
1477 } else {
1478 let elem_len = elem_len as usize;
1479 if data.len() < pos + elem_len {
1480 return Err(PgError::TypeConversion(
1481 "Binary array element data truncated".into(),
1482 ));
1483 }
1484 let elem_data = &data[pos..pos + elem_len];
1485 values.push(PgValue::from_binary(element_oid, elem_data)?);
1486 pos += elem_len;
1487 }
1488 }
1489
1490 Ok(PgValue::Array(values))
1491}
1492
1493fn format_uuid(bytes: &[u8; 16]) -> String {
1497 fn hex(b: u8) -> (char, char) {
1498 const HEX: &[u8; 16] = b"0123456789abcdef";
1499 (
1500 HEX[(b >> 4) as usize] as char,
1501 HEX[(b & 0xf) as usize] as char,
1502 )
1503 }
1504 let mut s = String::with_capacity(36);
1505 for (i, &b) in bytes.iter().enumerate() {
1506 if i == 4 || i == 6 || i == 8 || i == 10 {
1507 s.push('-');
1508 }
1509 let (hi, lo) = hex(b);
1510 s.push(hi);
1511 s.push(lo);
1512 }
1513 s
1514}
1515
1516fn parse_uuid_text(s: &str) -> PgResult<[u8; 16]> {
1518 let hex: String = s.chars().filter(|c| *c != '-').collect();
1519 if hex.len() != 32 {
1520 return Err(PgError::TypeConversion(format!("Invalid UUID: {}", s)));
1521 }
1522 let mut bytes = [0u8; 16];
1523 for i in 0..16 {
1524 bytes[i] = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16)
1525 .map_err(|_| PgError::TypeConversion(format!("Invalid UUID hex: {}", s)))?;
1526 }
1527 Ok(bytes)
1528}
1529
1530const PG_EPOCH_DAYS: i32 = 10957;
1534
1535fn format_date(days: i32) -> String {
1537 let (y, m, d) = days_to_ymd(days + PG_EPOCH_DAYS);
1538 format!("{:04}-{:02}-{:02}", y, m, d)
1539}
1540
1541fn format_time(us: i64) -> String {
1543 let total_secs = us / 1_000_000;
1544 let frac = us % 1_000_000;
1545 let h = total_secs / 3600;
1546 let m = (total_secs % 3600) / 60;
1547 let s = total_secs % 60;
1548 if frac > 0 {
1549 format!("{:02}:{:02}:{:02}.{:06}", h, m, s, frac)
1550 } else {
1551 format!("{:02}:{:02}:{:02}", h, m, s)
1552 }
1553}
1554
1555fn format_timestamp(us: i64) -> String {
1557 let total_days = (us / 86_400_000_000) as i32;
1558 let time_us = us % 86_400_000_000;
1559 let (time_us, total_days) = if time_us < 0 {
1560 (time_us + 86_400_000_000, total_days - 1)
1561 } else {
1562 (time_us, total_days)
1563 };
1564 let date = format_date(total_days);
1565 let time = format_time(time_us);
1566 format!("{} {}", date, time)
1567}
1568
1569fn format_timestamp_tz(us: i64) -> String {
1571 format!("{}+00", format_timestamp(us))
1572}
1573
1574fn format_interval(months: i32, days: i32, us: i64) -> String {
1576 let mut parts = Vec::new();
1577 if months != 0 {
1578 let years = months / 12;
1579 let mons = months % 12;
1580 if years != 0 {
1581 parts.push(format!(
1582 "{} year{}",
1583 years,
1584 if years.abs() != 1 { "s" } else { "" }
1585 ));
1586 }
1587 if mons != 0 {
1588 parts.push(format!(
1589 "{} mon{}",
1590 mons,
1591 if mons.abs() != 1 { "s" } else { "" }
1592 ));
1593 }
1594 }
1595 if days != 0 {
1596 parts.push(format!(
1597 "{} day{}",
1598 days,
1599 if days.abs() != 1 { "s" } else { "" }
1600 ));
1601 }
1602 if us != 0 || parts.is_empty() {
1603 parts.push(format_time(us));
1604 }
1605 parts.join(" ")
1606}
1607
1608fn parse_date_text(s: &str) -> PgResult<i32> {
1610 let parts: Vec<&str> = s.split('-').collect();
1611 if parts.len() != 3 {
1612 return Err(PgError::TypeConversion(format!("Invalid date: {}", s)));
1613 }
1614 let y: i32 = parts[0]
1615 .parse()
1616 .map_err(|_| PgError::TypeConversion("Bad year".into()))?;
1617 let m: u32 = parts[1]
1618 .parse()
1619 .map_err(|_| PgError::TypeConversion("Bad month".into()))?;
1620 let d: u32 = parts[2]
1621 .parse()
1622 .map_err(|_| PgError::TypeConversion("Bad day".into()))?;
1623 Ok(ymd_to_days(y, m, d) - PG_EPOCH_DAYS)
1624}
1625
1626fn parse_time_text(s: &str) -> PgResult<i64> {
1628 let parts: Vec<&str> = s.split(':').collect();
1629 if parts.len() < 2 {
1630 return Err(PgError::TypeConversion(format!("Invalid time: {}", s)));
1631 }
1632 let h: i64 = parts[0]
1633 .parse()
1634 .map_err(|_| PgError::TypeConversion("Bad hour".into()))?;
1635 let m: i64 = parts[1]
1636 .parse()
1637 .map_err(|_| PgError::TypeConversion("Bad minute".into()))?;
1638 let (s_int, frac) = if parts.len() > 2 {
1639 parse_secs_frac(parts[2])?
1640 } else {
1641 (0, 0)
1642 };
1643 Ok(h * 3_600_000_000 + m * 60_000_000 + s_int * 1_000_000 + frac)
1644}
1645
1646fn parse_timestamp_text(s: &str) -> PgResult<i64> {
1648 let s = s.trim_end_matches("+00").trim_end_matches("+00:00");
1650 let parts: Vec<&str> = s.splitn(2, ' ').collect();
1651 if parts.len() != 2 {
1652 return Err(PgError::TypeConversion(format!("Invalid timestamp: {}", s)));
1653 }
1654 let date_days = parse_date_text(parts[0])?;
1655 let time_us = parse_time_text(parts[1])?;
1656 Ok(date_days as i64 * 86_400_000_000 + time_us)
1657}
1658
1659fn parse_interval_text(s: &str) -> PgResult<(i32, i32, i64)> {
1661 let mut months = 0i32;
1663 let mut days = 0i32;
1664 let mut microseconds = 0i64;
1665
1666 let parts: Vec<&str> = s.split_whitespace().collect();
1667 let mut i = 0;
1668 while i < parts.len() {
1669 if parts[i].contains(':') {
1670 microseconds = parse_time_text(parts[i])?;
1672 i += 1;
1673 } else if i + 1 < parts.len() {
1674 let val: i32 = parts[i]
1675 .parse()
1676 .map_err(|_| PgError::TypeConversion(format!("Invalid interval: {}", s)))?;
1677 let unit = parts[i + 1].to_lowercase();
1678 if unit.starts_with("year") {
1679 months += val * 12;
1680 } else if unit.starts_with("mon") {
1681 months += val;
1682 } else if unit.starts_with("day") {
1683 days += val;
1684 } else if unit.starts_with("hour") {
1685 microseconds += val as i64 * 3_600_000_000;
1686 } else if unit.starts_with("min") {
1687 microseconds += val as i64 * 60_000_000;
1688 } else if unit.starts_with("sec") {
1689 microseconds += val as i64 * 1_000_000;
1690 }
1691 i += 2;
1692 } else {
1693 i += 1;
1694 }
1695 }
1696
1697 Ok((months, days, microseconds))
1698}
1699
1700fn parse_secs_frac(s: &str) -> PgResult<(i64, i64)> {
1702 if let Some((int_s, frac_s)) = s.split_once('.') {
1703 let int_val: i64 = int_s
1704 .parse()
1705 .map_err(|_| PgError::TypeConversion("Bad seconds".into()))?;
1706 let frac_str = if frac_s.len() >= 6 {
1708 &frac_s[..6]
1709 } else {
1710 frac_s
1711 };
1712 let frac_val: i64 = frac_str
1713 .parse()
1714 .map_err(|_| PgError::TypeConversion("Bad fractional seconds".into()))?;
1715 let padding = 10i64.pow(6 - frac_str.len() as u32);
1716 Ok((int_val, frac_val * padding))
1717 } else {
1718 let int_val: i64 = s
1719 .parse()
1720 .map_err(|_| PgError::TypeConversion("Bad seconds".into()))?;
1721 Ok((int_val, 0))
1722 }
1723}
1724
1725fn format_ipv6(bytes: &[u8]) -> String {
1729 let mut groups = [0u16; 8];
1731 for i in 0..8 {
1732 groups[i] = u16::from_be_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
1733 }
1734 groups
1736 .iter()
1737 .map(|g| format!("{:x}", g))
1738 .collect::<Vec<_>>()
1739 .join(":")
1740}
1741
1742pub fn encode_inet_binary(s: &str) -> PgResult<Vec<u8>> {
1747 let (addr_part, mask) = if let Some((a, m)) = s.split_once('/') {
1748 let mask: u8 = m
1749 .parse()
1750 .map_err(|_| PgError::TypeConversion(format!("Invalid mask: {}", m)))?;
1751 (a, Some(mask))
1752 } else {
1753 (s, None)
1754 };
1755
1756 if let Some(bytes) = parse_ipv4(addr_part) {
1758 let mask = mask.unwrap_or(32);
1759 Ok(vec![2, mask, 0, 4, bytes[0], bytes[1], bytes[2], bytes[3]])
1760 } else if let Some(bytes) = parse_ipv6(addr_part) {
1761 let mask = mask.unwrap_or(128);
1762 let mut buf = vec![3, mask, 0, 16];
1763 buf.extend_from_slice(&bytes);
1764 Ok(buf)
1765 } else {
1766 Err(PgError::TypeConversion(format!(
1767 "Invalid IP address: {}",
1768 s
1769 )))
1770 }
1771}
1772
1773fn parse_ipv4(s: &str) -> Option<[u8; 4]> {
1775 let parts: Vec<&str> = s.split('.').collect();
1776 if parts.len() != 4 {
1777 return None;
1778 }
1779 let mut bytes = [0u8; 4];
1780 for (i, part) in parts.iter().enumerate() {
1781 bytes[i] = part.parse().ok()?;
1782 }
1783 Some(bytes)
1784}
1785
1786fn parse_ipv6(s: &str) -> Option<[u8; 16]> {
1789 let mut bytes = [0u8; 16];
1790
1791 if s == "::" {
1792 return Some(bytes); }
1794
1795 let (left, right) = if let Some((l, r)) = s.split_once("::") {
1797 (l, r)
1798 } else {
1799 (s, "")
1800 };
1801
1802 let left_groups: Vec<&str> = if left.is_empty() {
1803 Vec::new()
1804 } else {
1805 left.split(':').collect()
1806 };
1807 let right_groups: Vec<&str> = if right.is_empty() {
1808 Vec::new()
1809 } else {
1810 right.split(':').collect()
1811 };
1812
1813 let left_count = left_groups.len();
1814 let right_count = right_groups.len();
1815
1816 if s.contains("::") {
1817 if left_count + right_count > 8 {
1818 return None;
1819 }
1820 } else if left_count != 8 {
1821 return None;
1822 }
1823
1824 for (i, group) in left_groups.iter().enumerate() {
1826 let val = u16::from_str_radix(group, 16).ok()?;
1827 bytes[i * 2] = (val >> 8) as u8;
1828 bytes[i * 2 + 1] = val as u8;
1829 }
1830
1831 let right_start = 8 - right_count;
1833 for (i, group) in right_groups.iter().enumerate() {
1834 let val = u16::from_str_radix(group, 16).ok()?;
1835 let idx = (right_start + i) * 2;
1836 bytes[idx] = (val >> 8) as u8;
1837 bytes[idx + 1] = val as u8;
1838 }
1839
1840 Some(bytes)
1841}
1842
1843fn escape_array_element(s: &str) -> String {
1851 if s.is_empty() || s.eq_ignore_ascii_case("null") || needs_array_quoting(s) {
1852 let mut out = String::with_capacity(s.len() + 2);
1853 out.push('"');
1854 for ch in s.chars() {
1855 if ch == '"' || ch == '\\' {
1856 out.push('\\');
1857 }
1858 out.push(ch);
1859 }
1860 out.push('"');
1861 out
1862 } else {
1863 s.to_string()
1864 }
1865}
1866
1867fn needs_array_quoting(s: &str) -> bool {
1869 s.chars()
1870 .any(|c| matches!(c, '{' | '}' | ',' | '"' | '\\') || c.is_whitespace())
1871}
1872
1873fn ymd_to_days(y: i32, m: u32, d: u32) -> i32 {
1877 let y = if m <= 2 { y - 1 } else { y };
1879 let era = if y >= 0 { y } else { y - 399 } / 400;
1880 let yoe = (y - era * 400) as u32;
1881 let doy = (153 * (if m > 2 { m - 3 } else { m + 9 }) + 2) / 5 + d - 1;
1882 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
1883 era * 146097 + doe as i32 - 719468
1884}
1885
1886fn days_to_ymd(days: i32) -> (i32, u32, u32) {
1888 let z = days + 719468;
1889 let era = if z >= 0 { z } else { z - 146096 } / 146097;
1890 let doe = (z - era * 146097) as u32;
1891 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
1892 let y = yoe as i32 + era * 400;
1893 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
1894 let mp = (5 * doy + 2) / 153;
1895 let d = doy - (153 * mp + 2) / 5 + 1;
1896 let m = if mp < 10 { mp + 3 } else { mp - 9 };
1897 let y = if m <= 2 { y + 1 } else { y };
1898 (y, m, d)
1899}
1900
1901fn decode_bytea_hex(s: &str) -> Vec<u8> {
1903 if let Some(hex) = s.strip_prefix("\\x") {
1904 let mut result = Vec::with_capacity(hex.len() / 2);
1905 let bytes = hex.as_bytes();
1906 let mut i = 0;
1907 while i + 1 < bytes.len() {
1908 let hi = hex_digit(bytes[i]);
1909 let lo = hex_digit(bytes[i + 1]);
1910 result.push((hi << 4) | lo);
1911 i += 2;
1912 }
1913 result
1914 } else {
1915 s.as_bytes().to_vec()
1916 }
1917}
1918
1919fn hex_digit(b: u8) -> u8 {
1920 match b {
1921 b'0'..=b'9' => b - b'0',
1922 b'a'..=b'f' => b - b'a' + 10,
1923 b'A'..=b'F' => b - b'A' + 10,
1924 _ => 0,
1925 }
1926}
1927
1928pub type CustomEncoder = Box<dyn Fn(&PgValue) -> Option<Vec<u8>> + Send + Sync>;
1932pub type CustomDecoder = Box<dyn Fn(&[u8]) -> PgResult<PgValue> + Send + Sync>;
1934
1935pub struct TypeRegistry {
1950 decoders: std::collections::HashMap<u32, CustomDecoder>,
1951 encoders: std::collections::HashMap<u32, CustomEncoder>,
1952}
1953
1954impl TypeRegistry {
1955 pub fn new() -> Self {
1957 Self {
1958 decoders: std::collections::HashMap::new(),
1959 encoders: std::collections::HashMap::new(),
1960 }
1961 }
1962
1963 pub fn register(&mut self, type_oid: u32, encoder: CustomEncoder, decoder: CustomDecoder) {
1965 self.encoders.insert(type_oid, encoder);
1966 self.decoders.insert(type_oid, decoder);
1967 }
1968
1969 pub fn decode(&self, type_oid: u32, data: &[u8]) -> Option<PgResult<PgValue>> {
1972 self.decoders.get(&type_oid).map(|f| f(data))
1973 }
1974
1975 pub fn encode(&self, type_oid: u32, value: &PgValue) -> Option<Option<Vec<u8>>> {
1978 self.encoders.get(&type_oid).map(|f| f(value))
1979 }
1980
1981 pub fn has_decoder(&self, type_oid: u32) -> bool {
1983 self.decoders.contains_key(&type_oid)
1984 }
1985
1986 pub fn register_enum(&mut self, type_oid: u32, variants: &[&str]) {
1997 let variants_owned: Vec<String> = variants.iter().map(|s| s.to_string()).collect();
1998
1999 let decode_variants = variants_owned.clone();
2000 let decoder: CustomDecoder = Box::new(move |data: &[u8]| {
2001 let text = std::str::from_utf8(data)
2002 .map_err(|_| PgError::Protocol("Invalid UTF-8 in enum value".to_string()))?;
2003 if !decode_variants.iter().any(|v| v == text) {
2004 return Err(PgError::Protocol(format!(
2005 "Unknown enum variant: '{}' (expected one of: {:?})",
2006 text, decode_variants
2007 )));
2008 }
2009 Ok(PgValue::Text(text.to_string()))
2010 });
2011
2012 let encoder: CustomEncoder = Box::new(|value: &PgValue| match value {
2013 PgValue::Text(s) => Some(s.as_bytes().to_vec()),
2014 _ => None,
2015 });
2016
2017 self.register(type_oid, encoder, decoder);
2018 }
2019
2020 pub fn register_composite(&mut self, type_oid: u32, field_oids: &[u32]) {
2033 let oids = field_oids.to_vec();
2034
2035 let decoder: CustomDecoder = Box::new(move |data: &[u8]| {
2036 let text = std::str::from_utf8(data)
2037 .map_err(|_| PgError::Protocol("Invalid UTF-8 in composite value".to_string()))?;
2038 let inner = text.trim();
2040 let inner = if inner.starts_with('(') && inner.ends_with(')') {
2041 &inner[1..inner.len() - 1]
2042 } else {
2043 inner
2044 };
2045
2046 let fields = parse_composite_fields(inner);
2047 if fields.len() != oids.len() {
2048 return Err(PgError::Protocol(format!(
2049 "Composite field count mismatch: expected {}, got {}",
2050 oids.len(),
2051 fields.len()
2052 )));
2053 }
2054
2055 let mut values = Vec::with_capacity(oids.len());
2056 for (field_val, &field_oid) in fields.iter().zip(oids.iter()) {
2057 if field_val.is_empty() {
2058 values.push(PgValue::Null);
2059 } else {
2060 values.push(PgValue::from_text(field_oid, field_val.as_bytes())?);
2061 }
2062 }
2063 Ok(PgValue::Array(values))
2064 });
2065
2066 let encoder: CustomEncoder = Box::new(|value: &PgValue| {
2067 match value {
2068 PgValue::Array(fields) => {
2069 let mut out = String::from("(");
2070 for (i, field) in fields.iter().enumerate() {
2071 if i > 0 {
2072 out.push(',');
2073 }
2074 match field {
2075 PgValue::Null => {} PgValue::Text(s) => out.push_str(s),
2077 other => {
2078 if let Some(bytes) = other.to_text_bytes()
2079 && let Ok(s) = std::str::from_utf8(&bytes)
2080 {
2081 out.push_str(s);
2082 }
2083 }
2084 }
2085 }
2086 out.push(')');
2087 Some(out.into_bytes())
2088 }
2089 _ => None,
2090 }
2091 });
2092
2093 self.register(type_oid, encoder, decoder);
2094 }
2095}
2096
2097impl Default for TypeRegistry {
2098 fn default() -> Self {
2099 Self::new()
2100 }
2101}
2102
2103fn parse_composite_fields(input: &str) -> Vec<String> {
2106 let mut fields = Vec::new();
2107 let mut current = String::new();
2108 let mut in_quotes = false;
2109 let mut chars = input.chars().peekable();
2110
2111 while let Some(ch) = chars.next() {
2112 if in_quotes {
2113 if ch == '"' {
2114 if chars.peek() == Some(&'"') {
2115 chars.next();
2117 current.push('"');
2118 } else {
2119 in_quotes = false;
2120 }
2121 } else {
2122 current.push(ch);
2123 }
2124 } else if ch == '"' {
2125 in_quotes = true;
2126 } else if ch == ',' {
2127 fields.push(std::mem::take(&mut current));
2128 } else {
2129 current.push(ch);
2130 }
2131 }
2132 fields.push(current);
2133 fields
2134}
2135
2136#[cfg(test)]
2137mod tests {
2138 use super::*;
2139
2140 #[test]
2141 fn test_uuid_roundtrip() {
2142 let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
2143 let bytes = parse_uuid_text(uuid_str).unwrap();
2144 let formatted = format_uuid(&bytes);
2145 assert_eq!(formatted, uuid_str);
2146 }
2147
2148 #[test]
2149 fn test_date_roundtrip() {
2150 let s = "2024-03-15";
2151 let days = parse_date_text(s).unwrap();
2152 let formatted = format_date(days);
2153 assert_eq!(formatted, s);
2154 }
2155
2156 #[test]
2157 fn test_time_roundtrip() {
2158 let s = "14:30:45";
2159 let us = parse_time_text(s).unwrap();
2160 let formatted = format_time(us);
2161 assert_eq!(formatted, s);
2162 }
2163
2164 #[test]
2165 fn test_time_with_frac() {
2166 let s = "14:30:45.123456";
2167 let us = parse_time_text(s).unwrap();
2168 let formatted = format_time(us);
2169 assert_eq!(formatted, s);
2170 }
2171
2172 #[test]
2173 fn test_to_sql_i32() {
2174 assert_eq!(42i32.to_sql(), PgValue::Int4(42));
2175 }
2176
2177 #[test]
2178 fn test_from_sql_i32() {
2179 let val = PgValue::Int4(42);
2180 assert_eq!(i32::from_sql(&val).unwrap(), 42);
2181 }
2182
2183 #[test]
2184 fn test_from_sql_option() {
2185 let null = PgValue::Null;
2186 assert_eq!(Option::<i32>::from_sql(&null).unwrap(), None);
2187 let val = PgValue::Int4(42);
2188 assert_eq!(Option::<i32>::from_sql(&val).unwrap(), Some(42));
2189 }
2190
2191 #[test]
2192 fn test_pg_epoch() {
2193 assert_eq!(parse_date_text("2000-01-01").unwrap(), 0);
2195 assert_eq!(parse_date_text("2000-01-02").unwrap(), 1);
2197 }
2198
2199 #[test]
2202 fn test_inet_binary_ipv4() {
2203 let data = vec![2, 32, 0, 4, 192, 168, 1, 1];
2205 let val = PgValue::from_binary(oid::INET, &data).unwrap();
2206 assert_eq!(val, PgValue::Inet("192.168.1.1".to_string()));
2207 }
2208
2209 #[test]
2210 fn test_inet_binary_ipv4_cidr() {
2211 let data = vec![2, 8, 1, 4, 10, 0, 0, 0];
2213 let val = PgValue::from_binary(oid::CIDR, &data).unwrap();
2214 assert_eq!(val, PgValue::Inet("10.0.0.0/8".to_string()));
2215 }
2216
2217 #[test]
2218 fn test_inet_binary_ipv6() {
2219 let mut data = vec![3, 128, 0, 16];
2221 data.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
2222 let val = PgValue::from_binary(oid::INET, &data).unwrap();
2223 assert_eq!(val, PgValue::Inet("0:0:0:0:0:0:0:1".to_string()));
2224 }
2225
2226 #[test]
2227 fn test_encode_inet_binary_ipv4() {
2228 let encoded = encode_inet_binary("192.168.1.1").unwrap();
2229 assert_eq!(encoded, vec![2, 32, 0, 4, 192, 168, 1, 1]);
2230 }
2231
2232 #[test]
2233 fn test_encode_inet_binary_ipv4_cidr() {
2234 let encoded = encode_inet_binary("10.0.0.0/8").unwrap();
2235 assert_eq!(encoded, vec![2, 8, 0, 4, 10, 0, 0, 0]);
2236 }
2237
2238 #[test]
2239 fn test_encode_inet_binary_ipv6_loopback() {
2240 let encoded = encode_inet_binary("::1").unwrap();
2241 assert_eq!(encoded.len(), 20); assert_eq!(encoded[0], 3); assert_eq!(encoded[1], 128); assert_eq!(encoded[19], 1); }
2246
2247 #[test]
2250 fn test_array_simple() {
2251 let arr = PgValue::Array(vec![PgValue::Int4(1), PgValue::Int4(2), PgValue::Int4(3)]);
2252 let bytes = arr.to_text_bytes().unwrap();
2253 assert_eq!(String::from_utf8(bytes).unwrap(), "{1,2,3}");
2254 }
2255
2256 #[test]
2257 fn test_array_with_null() {
2258 let arr = PgValue::Array(vec![
2259 PgValue::Text("hello".to_string()),
2260 PgValue::Null,
2261 PgValue::Text("world".to_string()),
2262 ]);
2263 let bytes = arr.to_text_bytes().unwrap();
2264 assert_eq!(String::from_utf8(bytes).unwrap(), "{hello,NULL,world}");
2265 }
2266
2267 #[test]
2268 fn test_array_escaping_special_chars() {
2269 let arr = PgValue::Array(vec![
2270 PgValue::Text("hello world".to_string()), PgValue::Text("a,b".to_string()), PgValue::Text("say \"hi\"".to_string()), ]);
2274 let bytes = arr.to_text_bytes().unwrap();
2275 let s = String::from_utf8(bytes).unwrap();
2276 assert_eq!(s, r#"{"hello world","a,b","say \"hi\""}"#);
2277 }
2278
2279 #[test]
2280 fn test_array_escaping_empty_string() {
2281 let arr = PgValue::Array(vec![PgValue::Text("".to_string())]);
2282 let bytes = arr.to_text_bytes().unwrap();
2283 assert_eq!(String::from_utf8(bytes).unwrap(), r#"{""}"#);
2284 }
2285
2286 #[test]
2287 fn test_array_escaping_null_string() {
2288 let arr = PgValue::Array(vec![PgValue::Text("NULL".to_string())]);
2290 let bytes = arr.to_text_bytes().unwrap();
2291 assert_eq!(String::from_utf8(bytes).unwrap(), r#"{"NULL"}"#);
2292 }
2293
2294 #[test]
2297 fn test_parse_ipv6_loopback() {
2298 let bytes = parse_ipv6("::1").unwrap();
2299 let mut expected = [0u8; 16];
2300 expected[15] = 1;
2301 assert_eq!(bytes, expected);
2302 }
2303
2304 #[test]
2305 fn test_parse_ipv6_full() {
2306 let bytes = parse_ipv6("2001:db8:0:0:0:0:0:1").unwrap();
2307 assert_eq!(bytes[0], 0x20);
2308 assert_eq!(bytes[1], 0x01);
2309 assert_eq!(bytes[2], 0x0d);
2310 assert_eq!(bytes[3], 0xb8);
2311 assert_eq!(bytes[15], 1);
2312 }
2313
2314 #[test]
2315 fn test_parse_ipv6_abbreviated() {
2316 let bytes = parse_ipv6("2001:db8::1").unwrap();
2317 assert_eq!(bytes[0], 0x20);
2318 assert_eq!(bytes[1], 0x01);
2319 assert_eq!(bytes[15], 1);
2320 }
2321
2322 #[test]
2325 fn test_from_sql_vec_u8() {
2326 let val = PgValue::Bytes(vec![1, 2, 3]);
2327 assert_eq!(Vec::<u8>::from_sql(&val).unwrap(), vec![1, 2, 3]);
2328 }
2329
2330 #[test]
2331 fn test_from_sql_vec_u8_null() {
2332 let val = PgValue::Null;
2333 assert!(Vec::<u8>::from_sql(&val).is_err());
2334 }
2335
2336 #[test]
2337 fn test_from_sql_uuid_bytes() {
2338 let bytes = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
2339 let val = PgValue::Uuid(bytes);
2340 assert_eq!(<[u8; 16]>::from_sql(&val).unwrap(), bytes);
2341 }
2342
2343 #[test]
2344 fn test_from_sql_uuid_bytes_null() {
2345 let val = PgValue::Null;
2346 assert!(<[u8; 16]>::from_sql(&val).is_err());
2347 }
2348
2349 #[test]
2352 fn test_to_sql_vec_i32() {
2353 let arr = vec![1i32, 2, 3];
2354 assert_eq!(
2355 arr.to_sql(),
2356 PgValue::Array(vec![PgValue::Int4(1), PgValue::Int4(2), PgValue::Int4(3)])
2357 );
2358 }
2359
2360 #[test]
2361 fn test_to_sql_vec_i32_type_oid() {
2362 let arr = vec![1i32, 2, 3];
2363 assert_eq!(arr.type_oid(), oid::INT4_ARRAY);
2364 }
2365
2366 #[test]
2367 fn test_from_sql_vec_i32() {
2368 let val = PgValue::Array(vec![PgValue::Int4(1), PgValue::Int4(2), PgValue::Int4(3)]);
2369 assert_eq!(Vec::<i32>::from_sql(&val).unwrap(), vec![1, 2, 3]);
2370 }
2371
2372 #[test]
2373 fn test_to_sql_vec_string() {
2374 let arr = vec!["hello".to_string(), "world".to_string()];
2375 assert_eq!(
2376 arr.to_sql(),
2377 PgValue::Array(vec![
2378 PgValue::Text("hello".to_string()),
2379 PgValue::Text("world".to_string())
2380 ])
2381 );
2382 }
2383
2384 #[test]
2385 fn test_from_sql_vec_string() {
2386 let val = PgValue::Array(vec![
2387 PgValue::Text("hello".to_string()),
2388 PgValue::Text("world".to_string()),
2389 ]);
2390 assert_eq!(
2391 Vec::<String>::from_sql(&val).unwrap(),
2392 vec!["hello".to_string(), "world".to_string()]
2393 );
2394 }
2395
2396 #[test]
2397 fn test_to_sql_vec_bool() {
2398 let arr = vec![true, false, true];
2399 assert_eq!(
2400 arr.to_sql(),
2401 PgValue::Array(vec![
2402 PgValue::Bool(true),
2403 PgValue::Bool(false),
2404 PgValue::Bool(true)
2405 ])
2406 );
2407 }
2408
2409 #[test]
2410 fn test_from_sql_vec_bool() {
2411 let val = PgValue::Array(vec![PgValue::Bool(true), PgValue::Bool(false)]);
2412 assert_eq!(Vec::<bool>::from_sql(&val).unwrap(), vec![true, false]);
2413 }
2414
2415 #[test]
2416 fn test_to_sql_vec_f64() {
2417 let arr = vec![1.5f64, 2.5];
2418 assert_eq!(
2419 arr.to_sql(),
2420 PgValue::Array(vec![PgValue::Float8(1.5), PgValue::Float8(2.5)])
2421 );
2422 }
2423
2424 #[test]
2425 fn test_from_sql_vec_f64() {
2426 let val = PgValue::Array(vec![PgValue::Float8(1.5), PgValue::Float8(2.5)]);
2427 assert_eq!(Vec::<f64>::from_sql(&val).unwrap(), vec![1.5, 2.5]);
2428 }
2429
2430 #[test]
2431 fn test_to_sql_slice_i32() {
2432 let arr: &[i32] = &[10, 20, 30];
2433 assert_eq!(
2434 arr.to_sql(),
2435 PgValue::Array(vec![
2436 PgValue::Int4(10),
2437 PgValue::Int4(20),
2438 PgValue::Int4(30)
2439 ])
2440 );
2441 }
2442
2443 #[test]
2444 fn test_from_sql_vec_i64() {
2445 let val = PgValue::Array(vec![PgValue::Int8(100), PgValue::Int8(200)]);
2446 assert_eq!(Vec::<i64>::from_sql(&val).unwrap(), vec![100i64, 200]);
2447 }
2448
2449 #[test]
2450 fn test_from_sql_empty_array() {
2451 let val = PgValue::Array(vec![]);
2452 assert_eq!(Vec::<i32>::from_sql(&val).unwrap(), Vec::<i32>::new());
2453 }
2454
2455 #[test]
2458 fn test_to_sql_ipaddr_v4() {
2459 use std::net::{IpAddr, Ipv4Addr};
2460 let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
2461 assert_eq!(ip.to_sql(), PgValue::Inet("192.168.1.1".to_string()));
2462 assert_eq!(ip.type_oid(), oid::INET);
2463 }
2464
2465 #[test]
2466 fn test_to_sql_ipaddr_v6() {
2467 use std::net::{IpAddr, Ipv6Addr};
2468 let ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
2469 assert_eq!(ip.to_sql(), PgValue::Inet("::1".to_string()));
2470 }
2471
2472 #[test]
2473 fn test_to_sql_ipv4addr() {
2474 use std::net::Ipv4Addr;
2475 let ip = Ipv4Addr::new(10, 0, 0, 1);
2476 assert_eq!(ip.to_sql(), PgValue::Inet("10.0.0.1".to_string()));
2477 }
2478
2479 #[test]
2480 fn test_to_sql_ipv6addr() {
2481 use std::net::Ipv6Addr;
2482 let ip = Ipv6Addr::LOCALHOST;
2483 assert_eq!(ip.to_sql(), PgValue::Inet("::1".to_string()));
2484 }
2485
2486 #[test]
2487 fn test_from_sql_ipaddr() {
2488 use std::net::{IpAddr, Ipv4Addr};
2489 let val = PgValue::Inet("192.168.1.1".to_string());
2490 let ip: IpAddr = IpAddr::from_sql(&val).unwrap();
2491 assert_eq!(ip, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
2492 }
2493
2494 #[test]
2495 fn test_from_sql_ipaddr_with_cidr() {
2496 use std::net::{IpAddr, Ipv4Addr};
2497 let val = PgValue::Inet("10.0.0.0/8".to_string());
2499 let ip: IpAddr = IpAddr::from_sql(&val).unwrap();
2500 assert_eq!(ip, IpAddr::V4(Ipv4Addr::new(10, 0, 0, 0)));
2501 }
2502
2503 #[test]
2504 fn test_from_sql_ipv4addr() {
2505 use std::net::Ipv4Addr;
2506 let val = PgValue::Inet("10.0.0.1".to_string());
2507 let ip = Ipv4Addr::from_sql(&val).unwrap();
2508 assert_eq!(ip, Ipv4Addr::new(10, 0, 0, 1));
2509 }
2510
2511 #[test]
2512 fn test_from_sql_ipv6addr() {
2513 use std::net::Ipv6Addr;
2514 let val = PgValue::Inet("::1".to_string());
2515 let ip = Ipv6Addr::from_sql(&val).unwrap();
2516 assert_eq!(ip, Ipv6Addr::LOCALHOST);
2517 }
2518
2519 #[test]
2520 fn test_from_sql_ipaddr_null() {
2521 use std::net::IpAddr;
2522 let val = PgValue::Null;
2523 assert!(IpAddr::from_sql(&val).is_err());
2524 }
2525
2526 #[test]
2529 fn test_to_binary_bytes_bool() {
2530 assert_eq!(PgValue::Bool(true).to_binary_bytes(), Some(vec![1]));
2531 assert_eq!(PgValue::Bool(false).to_binary_bytes(), Some(vec![0]));
2532 }
2533
2534 #[test]
2535 fn test_to_binary_bytes_int2() {
2536 let val = PgValue::Int2(256);
2537 assert_eq!(val.to_binary_bytes(), Some(vec![1, 0]));
2538 }
2539
2540 #[test]
2541 fn test_to_binary_bytes_int4() {
2542 let val = PgValue::Int4(0x01020304);
2543 assert_eq!(val.to_binary_bytes(), Some(vec![1, 2, 3, 4]));
2544 }
2545
2546 #[test]
2547 fn test_to_binary_bytes_int8() {
2548 let val = PgValue::Int8(1);
2549 assert_eq!(val.to_binary_bytes(), Some(vec![0, 0, 0, 0, 0, 0, 0, 1]));
2550 }
2551
2552 #[test]
2553 fn test_to_binary_bytes_float4() {
2554 let val = PgValue::Float4(1.0);
2555 assert_eq!(val.to_binary_bytes(), Some(1.0_f32.to_be_bytes().to_vec()));
2556 }
2557
2558 #[test]
2559 fn test_to_binary_bytes_float8() {
2560 let val = PgValue::Float8(std::f64::consts::PI);
2561 assert_eq!(
2562 val.to_binary_bytes(),
2563 Some(std::f64::consts::PI.to_be_bytes().to_vec())
2564 );
2565 }
2566
2567 #[test]
2568 fn test_to_binary_bytes_uuid() {
2569 let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
2570 let val = PgValue::Uuid(bytes);
2571 assert_eq!(val.to_binary_bytes(), Some(bytes.to_vec()));
2572 }
2573
2574 #[test]
2575 fn test_to_binary_bytes_date() {
2576 let val = PgValue::Date(0);
2578 assert_eq!(val.to_binary_bytes(), Some(vec![0, 0, 0, 0]));
2579 }
2580
2581 #[test]
2582 fn test_to_binary_bytes_interval() {
2583 let val = PgValue::Interval {
2584 months: 1,
2585 days: 2,
2586 microseconds: 3_000_000,
2587 };
2588 let mut expected = Vec::new();
2589 expected.extend_from_slice(&3_000_000_i64.to_be_bytes());
2590 expected.extend_from_slice(&2_i32.to_be_bytes());
2591 expected.extend_from_slice(&1_i32.to_be_bytes());
2592 assert_eq!(val.to_binary_bytes(), Some(expected));
2593 }
2594
2595 #[test]
2596 fn test_to_binary_bytes_jsonb() {
2597 let val = PgValue::Jsonb(b"{}".to_vec());
2598 assert_eq!(val.to_binary_bytes(), Some(vec![1, b'{', b'}']));
2599 }
2600
2601 #[test]
2602 fn test_to_binary_bytes_null() {
2603 assert_eq!(PgValue::Null.to_binary_bytes(), None);
2604 }
2605
2606 #[test]
2607 fn test_to_binary_bytes_text() {
2608 let val = PgValue::Text("hello".to_string());
2609 assert_eq!(val.to_binary_bytes(), Some(b"hello".to_vec()));
2610 }
2611
2612 #[test]
2613 fn test_prefers_binary() {
2614 assert!(PgValue::Int4(1).prefers_binary());
2615 assert!(PgValue::Bool(true).prefers_binary());
2616 assert!(PgValue::Float8(1.0).prefers_binary());
2617 assert!(PgValue::Uuid([0; 16]).prefers_binary());
2618 assert!(!PgValue::Text("hi".into()).prefers_binary());
2619 assert!(!PgValue::Numeric("1.23".into()).prefers_binary());
2620 assert!(!PgValue::Array(vec![]).prefers_binary());
2621 assert!(!PgValue::Inet("127.0.0.1".into()).prefers_binary());
2622 }
2623
2624 #[test]
2625 fn test_from_binary_numeric_zero() {
2626 let data = [0u8, 0, 0, 0, 0, 0, 0, 0];
2628 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2629 assert_eq!(val, PgValue::Numeric("0".to_string()));
2630 }
2631
2632 #[test]
2633 fn test_from_binary_numeric_simple_integer() {
2634 let data = [
2637 0, 1, 0, 0, 0, 0, 0, 0, 0, 42, ];
2643 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2644 assert_eq!(val, PgValue::Numeric("42".to_string()));
2645 }
2646
2647 #[test]
2648 fn test_from_binary_numeric_negative() {
2649 let data = [
2652 0, 1, 0, 0, 0x40, 0x00, 0, 0, 0, 42, ];
2658 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2659 assert_eq!(val, PgValue::Numeric("-42".to_string()));
2660 }
2661
2662 #[test]
2663 fn test_from_binary_numeric_with_decimal() {
2664 let data = [
2668 0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0x08, 0xFC, ];
2675 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2676 assert_eq!(val, PgValue::Numeric("1.23".to_string()));
2677 }
2678
2679 #[test]
2680 fn test_from_binary_numeric_nan() {
2681 let data = [0, 0, 0, 0, 0xC0, 0x00, 0, 0];
2683 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2684 assert_eq!(val, PgValue::Numeric("NaN".to_string()));
2685 }
2686
2687 #[test]
2688 fn test_from_binary_numeric_zero_with_scale() {
2689 let data = [0, 0, 0, 0, 0, 0, 0, 2];
2691 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2692 assert_eq!(val, PgValue::Numeric("0.00".to_string()));
2693 }
2694
2695 #[test]
2696 fn test_from_binary_numeric_large() {
2697 let data = [
2701 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, ];
2707 let val = PgValue::from_binary(oid::NUMERIC, &data).unwrap();
2708 assert_eq!(val, PgValue::Numeric("10000".to_string()));
2709 }
2710
2711 #[test]
2712 fn test_from_binary_array_i32() {
2713 let mut data = Vec::new();
2717 data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&0_i32.to_be_bytes()); data.extend_from_slice(&23_u32.to_be_bytes()); data.extend_from_slice(&3_i32.to_be_bytes()); data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&4_i32.to_be_bytes()); data.extend_from_slice(&10_i32.to_be_bytes()); data.extend_from_slice(&4_i32.to_be_bytes());
2727 data.extend_from_slice(&20_i32.to_be_bytes());
2728 data.extend_from_slice(&4_i32.to_be_bytes());
2730 data.extend_from_slice(&30_i32.to_be_bytes());
2731
2732 let val = PgValue::from_binary(oid::INT4_ARRAY, &data).unwrap();
2733 assert_eq!(
2734 val,
2735 PgValue::Array(vec![
2736 PgValue::Int4(10),
2737 PgValue::Int4(20),
2738 PgValue::Int4(30),
2739 ])
2740 );
2741 }
2742
2743 #[test]
2744 fn test_from_binary_array_with_null() {
2745 let mut data = Vec::new();
2747 data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&23_u32.to_be_bytes()); data.extend_from_slice(&2_i32.to_be_bytes()); data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&4_i32.to_be_bytes());
2754 data.extend_from_slice(&42_i32.to_be_bytes());
2755 data.extend_from_slice(&(-1_i32).to_be_bytes());
2757
2758 let val = PgValue::from_binary(oid::INT4_ARRAY, &data).unwrap();
2759 assert_eq!(val, PgValue::Array(vec![PgValue::Int4(42), PgValue::Null,]));
2760 }
2761
2762 #[test]
2763 fn test_from_binary_array_empty() {
2764 let mut data = Vec::new();
2766 data.extend_from_slice(&0_i32.to_be_bytes()); data.extend_from_slice(&0_i32.to_be_bytes()); data.extend_from_slice(&23_u32.to_be_bytes()); let val = PgValue::from_binary(oid::INT4_ARRAY, &data).unwrap();
2771 assert_eq!(val, PgValue::Array(Vec::new()));
2772 }
2773
2774 #[test]
2775 fn test_from_binary_array_bool() {
2776 let mut data = Vec::new();
2777 data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&0_i32.to_be_bytes()); data.extend_from_slice(&16_u32.to_be_bytes()); data.extend_from_slice(&2_i32.to_be_bytes()); data.extend_from_slice(&1_i32.to_be_bytes()); data.extend_from_slice(&1_i32.to_be_bytes()); data.push(1);
2785 data.extend_from_slice(&1_i32.to_be_bytes());
2787 data.push(0);
2788
2789 let val = PgValue::from_binary(oid::BOOL_ARRAY, &data).unwrap();
2790 assert_eq!(
2791 val,
2792 PgValue::Array(vec![PgValue::Bool(true), PgValue::Bool(false)])
2793 );
2794 }
2795
2796 #[test]
2797 fn test_from_binary_array_float8() {
2798 let mut data = Vec::new();
2799 data.extend_from_slice(&1_i32.to_be_bytes());
2800 data.extend_from_slice(&0_i32.to_be_bytes());
2801 data.extend_from_slice(&701_u32.to_be_bytes()); data.extend_from_slice(&2_i32.to_be_bytes());
2803 data.extend_from_slice(&1_i32.to_be_bytes());
2804 data.extend_from_slice(&8_i32.to_be_bytes());
2806 data.extend_from_slice(&1.5_f64.to_be_bytes());
2807 data.extend_from_slice(&8_i32.to_be_bytes());
2809 data.extend_from_slice(&2.5_f64.to_be_bytes());
2810
2811 let val = PgValue::from_binary(oid::FLOAT8_ARRAY, &data).unwrap();
2812 assert_eq!(
2813 val,
2814 PgValue::Array(vec![PgValue::Float8(1.5), PgValue::Float8(2.5)])
2815 );
2816 }
2817
2818 #[test]
2819 fn test_binary_roundtrip_int4() {
2820 let original = PgValue::Int4(12345);
2821 let bytes = original.to_binary_bytes().unwrap();
2822 let decoded = PgValue::from_binary(oid::INT4, &bytes).unwrap();
2823 assert_eq!(original, decoded);
2824 }
2825
2826 #[test]
2827 fn test_binary_roundtrip_float8() {
2828 let original = PgValue::Float8(std::f64::consts::PI);
2829 let bytes = original.to_binary_bytes().unwrap();
2830 let decoded = PgValue::from_binary(oid::FLOAT8, &bytes).unwrap();
2831 assert_eq!(original, decoded);
2832 }
2833
2834 #[test]
2835 fn test_binary_roundtrip_uuid() {
2836 let original = PgValue::Uuid([
2837 0xDE, 0xAD, 0xBE, 0xEF, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2838 ]);
2839 let bytes = original.to_binary_bytes().unwrap();
2840 let decoded = PgValue::from_binary(oid::UUID, &bytes).unwrap();
2841 assert_eq!(original, decoded);
2842 }
2843
2844 #[test]
2845 fn test_binary_roundtrip_interval() {
2846 let original = PgValue::Interval {
2847 months: 13,
2848 days: 5,
2849 microseconds: 7_200_000_000,
2850 };
2851 let bytes = original.to_binary_bytes().unwrap();
2852 let decoded = PgValue::from_binary(oid::INTERVAL, &bytes).unwrap();
2853 assert_eq!(original, decoded);
2854 }
2855
2856 #[test]
2857 fn test_binary_roundtrip_bool() {
2858 for b in [true, false] {
2859 let original = PgValue::Bool(b);
2860 let bytes = original.to_binary_bytes().unwrap();
2861 let decoded = PgValue::from_binary(oid::BOOL, &bytes).unwrap();
2862 assert_eq!(original, decoded);
2863 }
2864 }
2865
2866 #[test]
2869 fn test_macaddr_text_roundtrip() {
2870 let mac = [0x08, 0x00, 0x2b, 0x01, 0x02, 0x03];
2871 let val = PgValue::MacAddr(mac);
2872 let text = val.to_text_bytes().unwrap();
2873 assert_eq!(std::str::from_utf8(&text).unwrap(), "08:00:2b:01:02:03");
2874 let decoded = PgValue::from_text(oid::MACADDR, b"08:00:2b:01:02:03").unwrap();
2875 assert_eq!(decoded, PgValue::MacAddr(mac));
2876 }
2877
2878 #[test]
2879 fn test_macaddr_binary_roundtrip() {
2880 let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
2881 let val = PgValue::MacAddr(mac);
2882 let bytes = val.to_binary_bytes().unwrap();
2883 assert_eq!(bytes, vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
2884 let decoded = PgValue::from_binary(oid::MACADDR, &bytes).unwrap();
2885 assert_eq!(decoded, PgValue::MacAddr(mac));
2886 }
2887
2888 #[test]
2889 fn test_macaddr_dash_format() {
2890 let decoded = parse_macaddr_text("08-00-2b-01-02-03").unwrap();
2891 assert_eq!(decoded, [0x08, 0x00, 0x2b, 0x01, 0x02, 0x03]);
2892 }
2893
2894 #[test]
2895 fn test_macaddr_invalid() {
2896 assert!(parse_macaddr_text("not_a_mac").is_err());
2897 assert!(parse_macaddr_text("08:00:2b").is_err()); assert!(parse_macaddr_text("08:00:2b:01:02:03:04").is_err()); assert!(parse_macaddr_text("ZZ:00:2b:01:02:03").is_err()); }
2901
2902 #[test]
2903 fn test_macaddr_tosql_fromsql() {
2904 let mac: [u8; 6] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB];
2905 let val = mac.to_sql();
2906 assert!(matches!(val, PgValue::MacAddr(_)));
2907 assert_eq!(mac.type_oid(), oid::MACADDR);
2908 let decoded: [u8; 6] = FromSql::from_sql(&val).unwrap();
2909 assert_eq!(decoded, mac);
2910 }
2911
2912 #[test]
2915 fn test_point_text_roundtrip() {
2916 let val = PgValue::Point { x: 1.5, y: -2.5 };
2917 let text = val.to_text_bytes().unwrap();
2918 assert_eq!(std::str::from_utf8(&text).unwrap(), "(1.5,-2.5)");
2919 let decoded = PgValue::from_text(oid::POINT, b"(1.5,-2.5)").unwrap();
2920 match decoded {
2921 PgValue::Point { x, y } => {
2922 assert!((x - 1.5).abs() < f64::EPSILON);
2923 assert!((y - (-2.5)).abs() < f64::EPSILON);
2924 }
2925 _ => panic!("Expected Point"),
2926 }
2927 }
2928
2929 #[test]
2930 fn test_point_binary_roundtrip() {
2931 let val = PgValue::Point {
2932 x: std::f64::consts::PI,
2933 y: std::f64::consts::E,
2934 };
2935 let bytes = val.to_binary_bytes().unwrap();
2936 assert_eq!(bytes.len(), 16);
2937 let decoded = PgValue::from_binary(oid::POINT, &bytes).unwrap();
2938 match decoded {
2939 PgValue::Point { x, y } => {
2940 assert!((x - std::f64::consts::PI).abs() < f64::EPSILON);
2941 assert!((y - std::f64::consts::E).abs() < f64::EPSILON);
2942 }
2943 _ => panic!("Expected Point"),
2944 }
2945 }
2946
2947 #[test]
2948 fn test_point_tosql_fromsql() {
2949 let pt: (f64, f64) = (10.0, 20.0);
2950 let val = pt.to_sql();
2951 assert!(matches!(val, PgValue::Point { .. }));
2952 assert_eq!(pt.type_oid(), oid::POINT);
2953 let decoded: (f64, f64) = FromSql::from_sql(&val).unwrap();
2954 assert!((decoded.0 - 10.0).abs() < f64::EPSILON);
2955 assert!((decoded.1 - 20.0).abs() < f64::EPSILON);
2956 }
2957
2958 #[test]
2959 fn test_point_parse_without_parens() {
2960 let (x, y) = parse_point_text("3.5,7.2").unwrap();
2961 assert!((x - 3.5).abs() < f64::EPSILON);
2962 assert!((y - 7.2).abs() < f64::EPSILON);
2963 }
2964
2965 #[test]
2966 fn test_point_parse_with_spaces() {
2967 let (x, y) = parse_point_text("( 3.5 , 7.2 )").unwrap();
2968 assert!((x - 3.5).abs() < f64::EPSILON);
2969 assert!((y - 7.2).abs() < f64::EPSILON);
2970 }
2971
2972 #[test]
2973 fn test_point_parse_invalid() {
2974 assert!(parse_point_text("nope").is_err());
2975 assert!(parse_point_text("(1.0)").is_err());
2976 }
2977
2978 #[test]
2981 fn test_range_text_roundtrip() {
2982 let val = PgValue::Range("[1,10)".to_string());
2983 let text = val.to_text_bytes().unwrap();
2984 assert_eq!(std::str::from_utf8(&text).unwrap(), "[1,10)");
2985 }
2986
2987 #[test]
2988 fn test_range_from_text_int4range() {
2989 let decoded = PgValue::from_text(oid::INT4RANGE, b"[1,10)").unwrap();
2990 assert_eq!(decoded, PgValue::Range("[1,10)".to_string()));
2991 }
2992
2993 #[test]
2994 fn test_range_from_text_tsrange() {
2995 let decoded = PgValue::from_text(oid::TSRANGE, b"[2024-01-01,2024-12-31]").unwrap();
2996 assert_eq!(
2997 decoded,
2998 PgValue::Range("[2024-01-01,2024-12-31]".to_string())
2999 );
3000 }
3001
3002 #[test]
3003 fn test_range_empty() {
3004 let decoded = PgValue::from_text(oid::INT4RANGE, b"empty").unwrap();
3005 assert_eq!(decoded, PgValue::Range("empty".to_string()));
3006 }
3007
3008 #[test]
3009 fn test_range_all_oid_variants() {
3010 for oid_val in [
3011 oid::INT4RANGE,
3012 oid::INT8RANGE,
3013 oid::NUMRANGE,
3014 oid::TSRANGE,
3015 oid::TSTZRANGE,
3016 oid::DATERANGE,
3017 ] {
3018 let decoded = PgValue::from_text(oid_val, b"[1,10)").unwrap();
3019 assert!(matches!(decoded, PgValue::Range(_)));
3020 }
3021 }
3022
3023 #[test]
3026 fn test_pgconfig_with_socket_dir() {
3027 use crate::connection::PgConfig;
3028 let config = PgConfig::new("localhost", 5432, "user", "pass", "mydb")
3029 .with_socket_dir("/var/run/postgresql");
3030 assert_eq!(config.socket_dir, Some("/var/run/postgresql".to_string()));
3031 }
3032
3033 #[test]
3034 fn test_pgconfig_from_url_unix_query_param() {
3035 use crate::connection::PgConfig;
3036 let config =
3037 PgConfig::from_url("postgres://user:pass@/mydb?host=/var/run/postgresql").unwrap();
3038 assert_eq!(config.socket_dir, Some("/var/run/postgresql".to_string()));
3039 assert_eq!(config.database, "mydb");
3040 assert_eq!(config.user, "user");
3041 }
3042
3043 #[test]
3044 fn test_pgconfig_from_url_percent_encoded() {
3045 use crate::connection::PgConfig;
3046 let config =
3047 PgConfig::from_url("postgres://user:pass@%2Fvar%2Frun%2Fpostgresql/mydb").unwrap();
3048 assert_eq!(config.socket_dir, Some("/var/run/postgresql".to_string()));
3049 assert_eq!(config.database, "mydb");
3050 }
3051
3052 #[test]
3053 fn test_pgconfig_from_url_tcp_unchanged() {
3054 use crate::connection::PgConfig;
3055 let config = PgConfig::from_url("postgres://user:pass@localhost:5433/mydb").unwrap();
3056 assert!(config.socket_dir.is_none());
3057 assert_eq!(config.host, "localhost");
3058 assert_eq!(config.port, 5433);
3059 assert_eq!(config.database, "mydb");
3060 }
3061
3062 #[test]
3063 fn test_macaddr_prefers_binary() {
3064 let val = PgValue::MacAddr([0; 6]);
3065 assert!(val.prefers_binary());
3066 }
3067
3068 #[test]
3069 fn test_point_prefers_binary() {
3070 let val = PgValue::Point { x: 0.0, y: 0.0 };
3071 assert!(val.prefers_binary());
3072 }
3073
3074 #[test]
3077 fn test_type_registry_custom_codec() {
3078 let mut registry = TypeRegistry::new();
3079 let oid = 99999;
3080 registry.register(
3081 oid,
3082 Box::new(|v| match v {
3083 PgValue::Text(s) => Some(s.as_bytes().to_vec()),
3084 _ => None,
3085 }),
3086 Box::new(|data| {
3087 let s =
3088 std::str::from_utf8(data).map_err(|_| PgError::Protocol("bad utf8".into()))?;
3089 Ok(PgValue::Text(s.to_uppercase()))
3090 }),
3091 );
3092 assert!(registry.has_decoder(oid));
3093 let decoded = registry.decode(oid, b"hello").unwrap().unwrap();
3094 assert_eq!(decoded, PgValue::Text("HELLO".to_string()));
3095
3096 let encoded = registry
3097 .encode(oid, &PgValue::Text("world".to_string()))
3098 .unwrap();
3099 assert_eq!(encoded, Some(b"world".to_vec()));
3100 }
3101
3102 #[test]
3103 fn test_type_registry_unknown_oid() {
3104 let registry = TypeRegistry::new();
3105 assert!(registry.decode(12345, b"data").is_none());
3106 assert!(registry.encode(12345, &PgValue::Null).is_none());
3107 assert!(!registry.has_decoder(12345));
3108 }
3109
3110 #[test]
3113 fn test_register_enum_decode_valid() {
3114 let mut registry = TypeRegistry::new();
3115 registry.register_enum(50001, &["active", "inactive", "pending"]);
3116 let val = registry.decode(50001, b"active").unwrap().unwrap();
3117 assert_eq!(val, PgValue::Text("active".to_string()));
3118 }
3119
3120 #[test]
3121 fn test_register_enum_decode_invalid_variant() {
3122 let mut registry = TypeRegistry::new();
3123 registry.register_enum(50001, &["active", "inactive"]);
3124 let result = registry.decode(50001, b"deleted");
3125 assert!(result.unwrap().is_err());
3126 }
3127
3128 #[test]
3129 fn test_register_enum_encode() {
3130 let mut registry = TypeRegistry::new();
3131 registry.register_enum(50001, &["active", "inactive"]);
3132 let encoded = registry
3133 .encode(50001, &PgValue::Text("active".to_string()))
3134 .unwrap();
3135 assert_eq!(encoded, Some(b"active".to_vec()));
3136 }
3137
3138 #[test]
3141 fn test_register_composite_decode() {
3142 let mut registry = TypeRegistry::new();
3143 registry.register_composite(60001, &[oid::INT4, oid::TEXT, oid::BOOL]);
3145 let val = registry.decode(60001, b"(42,hello,t)").unwrap().unwrap();
3146 match val {
3147 PgValue::Array(fields) => {
3148 assert_eq!(fields.len(), 3);
3149 assert_eq!(fields[0], PgValue::Int4(42));
3150 assert_eq!(fields[1], PgValue::Text("hello".to_string()));
3151 assert_eq!(fields[2], PgValue::Bool(true));
3152 }
3153 other => panic!("Expected Array, got {:?}", other),
3154 }
3155 }
3156
3157 #[test]
3158 fn test_register_composite_with_nulls() {
3159 let mut registry = TypeRegistry::new();
3160 registry.register_composite(60002, &[oid::INT4, oid::TEXT]);
3161 let val = registry.decode(60002, b"(42,)").unwrap().unwrap();
3162 match val {
3163 PgValue::Array(fields) => {
3164 assert_eq!(fields.len(), 2);
3165 assert_eq!(fields[0], PgValue::Int4(42));
3166 assert_eq!(fields[1], PgValue::Null);
3167 }
3168 other => panic!("Expected Array, got {:?}", other),
3169 }
3170 }
3171
3172 #[test]
3173 fn test_register_composite_encode() {
3174 let mut registry = TypeRegistry::new();
3175 registry.register_composite(60001, &[oid::INT4, oid::TEXT]);
3176 let val = PgValue::Array(vec![PgValue::Int4(42), PgValue::Text("hello".to_string())]);
3177 let encoded = registry.encode(60001, &val).unwrap();
3178 assert_eq!(encoded, Some(b"(42,hello)".to_vec()));
3179 }
3180
3181 #[test]
3182 fn test_parse_composite_fields_quoted() {
3183 let fields = parse_composite_fields(r#"42,"hello, world",t"#);
3184 assert_eq!(fields, vec!["42", "hello, world", "t"]);
3185 }
3186
3187 #[test]
3188 fn test_parse_composite_fields_escaped_quote() {
3189 let fields = parse_composite_fields(r#""say ""hi""",42"#);
3190 assert_eq!(fields, vec![r#"say "hi""#, "42"]);
3191 }
3192}