1use pylon_value::DecodedValue;
46
47use crate::numeric;
48
49pub use crate::error::Error;
50pub type Result<T> = crate::Result<T>;
51
52const OID_BOOL: u32 = 16;
58const OID_BYTEA: u32 = 17;
59const OID_INT8: u32 = 20;
60const OID_INT2: u32 = 21;
61const OID_INT4: u32 = 23;
62const OID_TEXT: u32 = 25;
63const OID_JSONB: u32 = 3802;
64const OID_FLOAT4: u32 = 700;
65const OID_FLOAT8: u32 = 701;
66const OID_BPCHAR: u32 = 1042;
67const OID_VARCHAR: u32 = 1043;
68const OID_NUMERIC: u32 = 1700;
69const OID_DATE: u32 = 1082;
70const OID_TIME: u32 = 1083;
71const OID_TIMESTAMP: u32 = 1114;
72const OID_TIMESTAMPTZ: u32 = 1184;
73const OID_INTERVAL: u32 = 1186;
74const OID_UUID: u32 = 2950;
75const OID_RECORD: u32 = 2249;
76const OID_RECORD_ARRAY: u32 = 2287;
77const OID_UNKNOWN: u32 = 705;
82const OID_NAME: u32 = 19;
85
86const OID_BOOL_ARRAY: u32 = 1000;
87const OID_BYTEA_ARRAY: u32 = 1001;
88const OID_INT2_ARRAY: u32 = 1005;
89const OID_INT4_ARRAY: u32 = 1007;
90const OID_TEXT_ARRAY: u32 = 1009;
91const OID_BPCHAR_ARRAY: u32 = 1014;
92const OID_VARCHAR_ARRAY: u32 = 1015;
93const OID_INT8_ARRAY: u32 = 1016;
94const OID_FLOAT4_ARRAY: u32 = 1021;
95const OID_FLOAT8_ARRAY: u32 = 1022;
96const OID_NUMERIC_ARRAY: u32 = 1231;
97const OID_UUID_ARRAY: u32 = 2951;
98const OID_JSONB_ARRAY: u32 = 3807;
99
100const OID_INT4RANGE: u32 = 3904;
106const OID_INT8RANGE: u32 = 3926;
107const OID_NUMRANGE: u32 = 3906;
108const OID_TSRANGE: u32 = 3908;
109const OID_TSTZRANGE: u32 = 3910;
110const OID_DATERANGE: u32 = 3912;
111const OID_INT4MULTIRANGE: u32 = 4451;
112const OID_INT8MULTIRANGE: u32 = 4536;
113const OID_NUMMULTIRANGE: u32 = 4532;
114const OID_TSMULTIRANGE: u32 = 4533;
115const OID_TSTZMULTIRANGE: u32 = 4534;
116const OID_DATEMULTIRANGE: u32 = 4535;
117
118fn range_element_oid(oid: u32) -> Option<u32> {
122 match oid {
123 OID_INT4RANGE | OID_INT4MULTIRANGE => Some(OID_INT4),
124 OID_INT8RANGE | OID_INT8MULTIRANGE => Some(OID_INT8),
125 OID_NUMRANGE | OID_NUMMULTIRANGE => Some(OID_NUMERIC),
126 OID_TSRANGE | OID_TSMULTIRANGE => Some(OID_TIMESTAMP),
127 OID_TSTZRANGE | OID_TSTZMULTIRANGE => Some(OID_TIMESTAMPTZ),
128 OID_DATERANGE | OID_DATEMULTIRANGE => Some(OID_DATE),
129 _ => None,
130 }
131}
132
133#[derive(Debug, Clone, Default, PartialEq, Eq)]
147pub struct ExtensionOids {
148 pub vector: Option<u32>,
150 pub enums: std::collections::HashSet<u32>,
153 pub domains: std::collections::HashMap<u32, u32>,
156 pub arrays: std::collections::HashSet<u32>,
162}
163
164pub(crate) const TYPE_DISCOVERY_SQL: &str = "\
173SELECT t.oid::int8, t.typtype::text, COALESCE(b.oid, 0)::int8, t.typname::text \
174FROM pg_type t \
175LEFT JOIN pg_type b ON b.oid = t.typbasetype \
176WHERE t.typtype IN ('e', 'd') OR t.typname = 'vector' \
177UNION ALL \
178SELECT a.oid::int8, 'A', e.oid::int8, a.typname::text \
179FROM pg_type a \
180JOIN pg_type e ON e.oid = a.typelem \
181WHERE a.typcategory = 'A' AND e.typtype IN ('e', 'd')";
182
183impl ExtensionOids {
184 pub(crate) fn from_discovery_rows(rows: impl IntoIterator<Item = (u32, String, u32, String)>) -> Self {
187 let mut out = Self::default();
188 for (oid, typtype, base_oid, typname) in rows {
189 match typtype.as_str() {
190 "e" => {
191 out.enums.insert(oid);
192 }
193 "d" if base_oid != 0 => {
194 out.domains.insert(oid, base_oid);
195 }
196 "A" => {
197 out.arrays.insert(oid);
198 }
199 _ => {}
200 }
201 if typname == "vector" {
204 out.vector = Some(oid);
205 }
206 }
207 out
208 }
209}
210
211pub fn decode_value(oid: u32, data: &[u8], ext: &ExtensionOids) -> Result<DecodedValue> {
217 if let Some(vector_oid) = ext.vector
218 && oid == vector_oid
219 {
220 return Ok(DecodedValue::Array(decode_vector(data)?));
221 }
222 match oid {
223 OID_BOOL => Ok(DecodedValue::Bool(data.first().copied().unwrap_or(0) != 0)),
224 OID_INT2 => Ok(DecodedValue::I64(i16::from_be_bytes(data.try_into()?) as i64)),
225 OID_INT4 => Ok(DecodedValue::I64(i32::from_be_bytes(data.try_into()?) as i64)),
226 OID_INT8 => Ok(DecodedValue::I64(i64::from_be_bytes(data.try_into()?))),
227 OID_FLOAT4 => Ok(DecodedValue::F64(f32::from_be_bytes(data.try_into()?) as f64)),
228 OID_FLOAT8 => Ok(DecodedValue::F64(f64::from_be_bytes(data.try_into()?))),
229 OID_TEXT | OID_VARCHAR | OID_BPCHAR | OID_UNKNOWN | OID_NAME => {
230 Ok(DecodedValue::Str(std::str::from_utf8(data)?.to_string()))
231 }
232 OID_UUID => {
233 let mut bytes = [0u8; 16];
234 bytes.copy_from_slice(data);
235 Ok(DecodedValue::Uuid(bytes))
236 }
237 OID_BYTEA => Ok(DecodedValue::Bytes(data.to_vec())),
238 OID_NUMERIC => decode_numeric(data),
239 OID_INTERVAL => decode_interval(data),
240 OID_DATE => Ok(DecodedValue::Date(i32::from_be_bytes(data.try_into()?))),
241 OID_TIME => Ok(DecodedValue::Time(i64::from_be_bytes(data.try_into()?))),
242 OID_TIMESTAMP => Ok(DecodedValue::Timestamp(i64::from_be_bytes(data.try_into()?))),
243 OID_TIMESTAMPTZ => Ok(DecodedValue::Timestamptz(i64::from_be_bytes(data.try_into()?))),
244 OID_JSONB => decode_jsonb(data),
245 OID_RECORD => decode_record(data, ext),
246 OID_RECORD_ARRAY => decode_array(data, ext),
247 OID_BOOL_ARRAY | OID_BYTEA_ARRAY | OID_INT2_ARRAY | OID_INT4_ARRAY | OID_INT8_ARRAY | OID_TEXT_ARRAY
248 | OID_BPCHAR_ARRAY | OID_VARCHAR_ARRAY | OID_FLOAT4_ARRAY | OID_FLOAT8_ARRAY | OID_NUMERIC_ARRAY
249 | OID_UUID_ARRAY | OID_JSONB_ARRAY => decode_array(data, ext),
250 OID_INT4RANGE | OID_INT8RANGE | OID_NUMRANGE | OID_TSRANGE | OID_TSTZRANGE | OID_DATERANGE => {
251 decode_range(data, range_element_oid(oid).expect("range OID"), ext)
252 }
253 OID_INT4MULTIRANGE | OID_INT8MULTIRANGE | OID_NUMMULTIRANGE | OID_TSMULTIRANGE | OID_TSTZMULTIRANGE
254 | OID_DATEMULTIRANGE => decode_multirange(data, range_element_oid(oid).expect("multirange OID"), ext),
255 _ if ext.enums.contains(&oid) => {
258 Ok(DecodedValue::Str(std::str::from_utf8(data)?.to_string()))
260 }
261 _ if ext.arrays.contains(&oid) => decode_array(data, ext),
264 _ => match ext.domains.get(&oid) {
265 Some(&base_oid) => decode_value(base_oid, data, ext),
267 None => Err(Error::UnknownTypeOid { oid }),
268 },
269 }
270}
271
272fn decode_numeric(data: &[u8]) -> Result<DecodedValue> {
273 Ok(DecodedValue::Decimal(numeric::decode(data)?))
274}
275
276fn decode_interval(data: &[u8]) -> Result<DecodedValue> {
282 if data.len() != 16 {
283 return Err(Error::message(format!(
284 "malformed interval: expected 16 bytes, got {}",
285 data.len()
286 )));
287 }
288 let microseconds = i64::from_be_bytes(data[0..8].try_into()?);
289 let days = i32::from_be_bytes(data[8..12].try_into()?);
290 let months = i32::from_be_bytes(data[12..16].try_into()?);
291 Ok(DecodedValue::Interval {
292 months,
293 days,
294 microseconds,
295 })
296}
297
298const RANGE_EMPTY: u8 = 0x01;
300const RANGE_LB_INC: u8 = 0x02;
301const RANGE_UB_INC: u8 = 0x04;
302const RANGE_LB_INF: u8 = 0x08;
303const RANGE_UB_INF: u8 = 0x10;
304
305fn decode_range(data: &[u8], element_oid: u32, ext: &ExtensionOids) -> Result<DecodedValue> {
311 let flags = data[0];
312 let mut offset = 1usize;
313 if flags & RANGE_EMPTY != 0 {
314 return Ok(DecodedValue::Range {
315 lower: None,
316 upper: None,
317 inc_lower: false,
318 inc_upper: false,
319 empty: true,
320 });
321 }
322 let lower = if flags & RANGE_LB_INF != 0 {
323 None
324 } else {
325 let len = i32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
326 offset += 4;
327 let value = decode_value(element_oid, &data[offset..offset + len], ext)?;
328 offset += len;
329 Some(Box::new(value))
330 };
331 let upper = if flags & RANGE_UB_INF != 0 {
332 None
333 } else {
334 let len = i32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
335 offset += 4;
336 Some(Box::new(decode_value(element_oid, &data[offset..offset + len], ext)?))
337 };
338 Ok(DecodedValue::Range {
339 lower,
340 upper,
341 inc_lower: flags & RANGE_LB_INC != 0,
342 inc_upper: flags & RANGE_UB_INC != 0,
343 empty: false,
344 })
345}
346
347fn decode_multirange(data: &[u8], element_oid: u32, ext: &ExtensionOids) -> Result<DecodedValue> {
353 let mut offset = 0usize;
354 let count = i32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
355 offset += 4;
356 let mut ranges = Vec::with_capacity(count);
357 for _ in 0..count {
358 let len = i32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
359 offset += 4;
360 ranges.push(decode_range(&data[offset..offset + len], element_oid, ext)?);
361 offset += len;
362 }
363 Ok(DecodedValue::Array(ranges))
364}
365
366fn decode_jsonb(data: &[u8]) -> Result<DecodedValue> {
371 let text = std::str::from_utf8(&data[1..])?;
372 let raw: &serde_json::value::RawValue = serde_json::from_str(text)?;
373 json_to_cached(raw)
374}
375
376fn json_to_cached(raw: &serde_json::value::RawValue) -> Result<DecodedValue> {
384 let text = raw.get().trim();
385 Ok(match text.as_bytes().first() {
386 None | Some(b'n') => DecodedValue::Null,
387 Some(b't') => DecodedValue::Bool(true),
388 Some(b'f') => DecodedValue::Bool(false),
389 Some(b'"') => DecodedValue::Str(serde_json::from_str(text)?),
390 Some(b'[') => {
391 let items: Vec<&serde_json::value::RawValue> = serde_json::from_str(text)?;
392 DecodedValue::Array(items.into_iter().map(json_to_cached).collect::<Result<Vec<_>>>()?)
393 }
394 Some(b'{') => {
395 let fields: std::collections::BTreeMap<String, &serde_json::value::RawValue> = serde_json::from_str(text)?;
399 DecodedValue::Object(
400 fields
401 .into_iter()
402 .map(|(key, value)| Ok((key, json_to_cached(value)?)))
403 .collect::<Result<Vec<_>>>()?,
404 )
405 }
406 _ => match text.parse::<i64>() {
409 Ok(integer) => DecodedValue::I64(integer),
410 Err(_) => DecodedValue::JsonNumber(text.to_string()),
411 },
412 })
413}
414
415fn decode_vector(data: &[u8]) -> Result<Vec<DecodedValue>> {
418 let ndim = u16::from_be_bytes(data[0..2].try_into()?) as usize;
419 let mut values = Vec::with_capacity(ndim);
420 for i in 0..ndim {
421 let start = 4 + i * 4;
422 let f = f32::from_be_bytes(data[start..start + 4].try_into()?);
423 values.push(DecodedValue::F64(f as f64));
424 }
425 Ok(values)
426}
427
428fn encode_vector(items: &[DecodedValue], out: &mut bytes::BytesMut) -> Result<()> {
431 let ndim: u16 = items
432 .len()
433 .try_into()
434 .map_err(|_| Error::message("vector has too many dimensions to encode"))?;
435 out.put_u16(ndim);
436 out.put_u16(0); for item in items {
438 let f = match item {
439 DecodedValue::F64(f) => *f as f32,
440 DecodedValue::I64(i) => *i as f32,
441 other => return Err(Error::message(format!("cannot encode {other:?} as a vector element"))),
442 };
443 out.put_f32(f);
444 }
445 Ok(())
446}
447
448fn decode_record(data: &[u8], ext: &ExtensionOids) -> Result<DecodedValue> {
451 let mut offset = 0usize;
452 let nfields = i32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
453 offset += 4;
454 let mut fields = Vec::with_capacity(nfields);
455 for _ in 0..nfields {
456 let type_oid = u32::from_be_bytes(data[offset..offset + 4].try_into()?);
457 offset += 4;
458 let field_len = i32::from_be_bytes(data[offset..offset + 4].try_into()?);
459 offset += 4;
460 if field_len == -1 {
461 fields.push(DecodedValue::Null);
462 } else {
463 let len = field_len as usize;
464 fields.push(decode_value(type_oid, &data[offset..offset + len], ext)?);
465 offset += len;
466 }
467 }
468 Ok(DecodedValue::Composite(fields))
469}
470
471fn decode_array(data: &[u8], ext: &ExtensionOids) -> Result<DecodedValue> {
476 let mut offset = 0usize;
477 let ndim = i32::from_be_bytes(data[offset..offset + 4].try_into()?);
478 offset += 4;
479 offset += 4; let element_oid = u32::from_be_bytes(data[offset..offset + 4].try_into()?);
481 offset += 4;
482 if ndim == 0 {
483 return Ok(DecodedValue::Array(vec![]));
484 }
485 let dim_size = i32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
486 offset += 4;
487 offset += 4; let mut items = Vec::with_capacity(dim_size);
490 for _ in 0..dim_size {
491 let elem_len = i32::from_be_bytes(data[offset..offset + 4].try_into()?);
492 offset += 4;
493 if elem_len == -1 {
494 items.push(DecodedValue::Null);
495 } else {
496 let len = elem_len as usize;
497 items.push(decode_value(element_oid, &data[offset..offset + len], ext)?);
498 offset += len;
499 }
500 }
501 Ok(DecodedValue::Array(items))
502}
503
504use bytes::BufMut;
518use postgres_types::{IsNull, Kind, Type};
519
520fn accepts_text_bytes(ty: &Type) -> bool {
529 match ty.kind() {
530 Kind::Enum(_) => true,
531 Kind::Domain(base) => accepts_text_bytes(base),
533 _ => matches!(
534 *ty,
535 Type::TEXT | Type::VARCHAR | Type::BPCHAR | Type::NAME | Type::JSON | Type::BYTEA | Type::UNKNOWN
536 ),
537 }
538}
539
540pub fn encode_value(value: &DecodedValue, ty: &Type, out: &mut bytes::BytesMut) -> Result<IsNull> {
546 let DecodedValue::Null = value else {
547 return encode_non_null(value, ty, out);
548 };
549 Ok(IsNull::Yes)
550}
551
552fn encode_non_null(value: &DecodedValue, ty: &Type, out: &mut bytes::BytesMut) -> Result<IsNull> {
553 if *ty == Type::NUMERIC {
565 let text = match value {
566 DecodedValue::Decimal(s) | DecodedValue::Str(s) => s.clone(),
567 DecodedValue::I64(i) => i.to_string(),
568 DecodedValue::F64(f) => format!("{f}"),
571 _ => return Err(Error::message("cannot bind this value as a numeric parameter")),
572 };
573 numeric::encode(&text, out)?;
574 return Ok(IsNull::No);
575 }
576 match value {
577 DecodedValue::Null => unreachable!("caller already handled NULL"),
578 DecodedValue::Bool(b) => out.put_u8(*b as u8),
579 DecodedValue::I64(i) => {
580 if *ty == Type::INT2 {
581 out.put_i16(*i as i16);
582 } else if *ty == Type::INT4 {
583 out.put_i32(*i as i32);
584 } else {
585 out.put_i64(*i);
586 }
587 }
588 DecodedValue::F64(f) => {
589 if *ty == Type::FLOAT4 {
590 out.put_f32(*f as f32);
591 } else {
592 out.put_f64(*f);
593 }
594 }
595 DecodedValue::Str(s) => {
596 if *ty == Type::UUID {
597 out.put_slice(&parse_uuid_str(s)?);
605 } else if *ty == Type::JSONB {
606 out.put_u8(1);
615 out.put_slice(s.as_bytes());
616 } else if accepts_text_bytes(ty) {
617 out.put_slice(s.as_bytes());
618 } else {
619 return Err(Error::message(format!(
627 "cannot bind a string as a parameter of type {:?}",
628 ty.name()
629 )));
630 }
631 }
632 DecodedValue::Bytes(b) => out.put_slice(b),
633 DecodedValue::Uuid(bytes) => out.put_slice(bytes),
634 DecodedValue::Decimal(s) => numeric::encode(s, out)?,
635 DecodedValue::Array(items) if ty.name() == "vector" => {
636 encode_vector(items, out)?;
643 }
644 DecodedValue::Array(items) => {
645 let element_ty = match ty.kind() {
646 Kind::Array(inner) => inner.clone(),
647 _ => Type::TEXT,
652 };
653 encode_array(items, &element_ty, out)?;
654 }
655 DecodedValue::Composite(items) if *ty == Type::JSONB => {
656 out.put_u8(1); let mut json = String::new();
662 write_cached_json(value, &mut json);
663 out.put_slice(json.as_bytes());
664 }
665 DecodedValue::Composite(_) => {
666 return Err(Error::message("cannot bind a composite value as a query parameter"));
673 }
674 DecodedValue::Object(fields) => {
675 out.put_u8(1); let mut json = String::new();
677 write_cached_object_json(fields, &mut json);
678 out.put_slice(json.as_bytes());
679 }
680 DecodedValue::JsonNumber(digits) if *ty == Type::JSONB => {
684 out.put_u8(1);
685 out.put_slice(digits.as_bytes());
686 }
687 DecodedValue::JsonNumber(digits) if *ty == Type::NUMERIC => numeric::encode(digits, out)?,
688 DecodedValue::JsonNumber(digits) if accepts_text_bytes(ty) => out.put_slice(digits.as_bytes()),
689 DecodedValue::JsonNumber(digits) => match digits.parse::<f64>() {
690 Ok(number) => encode_value(&DecodedValue::F64(number), ty, out).map(|_| ())?,
691 Err(_) => {
692 return Err(Error::message(format!(
693 "cannot bind the JSON number {digits} as a parameter of type {:?}",
694 ty.name()
695 )));
696 }
697 },
698 DecodedValue::Interval {
699 months,
700 days,
701 microseconds,
702 } => {
703 out.put_i64(*microseconds);
705 out.put_i32(*days);
706 out.put_i32(*months);
707 }
708 DecodedValue::Date(days) => out.put_i32(*days),
709 DecodedValue::Time(us) => out.put_i64(*us),
710 DecodedValue::Timestamp(us) => out.put_i64(*us),
711 DecodedValue::Timestamptz(us) => out.put_i64(*us),
712 DecodedValue::Range {
713 lower,
714 upper,
715 inc_lower,
716 inc_upper,
717 empty,
718 } => {
719 if *empty {
720 out.put_u8(RANGE_EMPTY);
721 return Ok(IsNull::No);
722 }
723 let element_ty = match ty.kind() {
724 Kind::Range(inner) => inner.clone(),
725 _ => Type::TEXT,
730 };
731 let mut flags = 0u8;
732 if *inc_lower {
733 flags |= RANGE_LB_INC;
734 }
735 if *inc_upper {
736 flags |= RANGE_UB_INC;
737 }
738 if lower.is_none() {
739 flags |= RANGE_LB_INF;
740 }
741 if upper.is_none() {
742 flags |= RANGE_UB_INF;
743 }
744 out.put_u8(flags);
745 for bound in [lower, upper].into_iter().flatten() {
746 let mut buf = bytes::BytesMut::new();
747 encode_value(bound, &element_ty, &mut buf)?;
748 out.put_i32(buf.len() as i32);
749 out.put_slice(&buf);
750 }
751 }
752 }
753 Ok(IsNull::No)
754}
755
756fn write_cached_object_json(fields: &[(String, DecodedValue)], out: &mut String) {
757 out.push('{');
758 for (index, (key, value)) in fields.iter().enumerate() {
759 if index > 0 {
760 out.push(',');
761 }
762 out.push_str(&serde_json::Value::String(key.clone()).to_string());
763 out.push(':');
764 write_cached_json(value, out);
765 }
766 out.push('}');
767}
768
769fn write_cached_json(value: &DecodedValue, out: &mut String) {
774 match value {
775 DecodedValue::JsonNumber(digits) => out.push_str(digits),
776 DecodedValue::Object(fields) => write_cached_object_json(fields, out),
777 DecodedValue::Array(items) | DecodedValue::Composite(items) => {
778 out.push('[');
779 for (index, item) in items.iter().enumerate() {
780 if index > 0 {
781 out.push(',');
782 }
783 write_cached_json(item, out);
784 }
785 out.push(']');
786 }
787 other => out.push_str(&cached_to_json(other).to_string()),
788 }
789}
790
791fn cached_to_json(value: &DecodedValue) -> serde_json::Value {
792 match value {
793 DecodedValue::Null => serde_json::Value::Null,
794 DecodedValue::Bool(b) => serde_json::Value::Bool(*b),
795 DecodedValue::I64(i) => serde_json::Value::Number((*i).into()),
796 DecodedValue::F64(f) => serde_json::Number::from_f64(*f)
797 .map(serde_json::Value::Number)
798 .unwrap_or(serde_json::Value::Null),
799 DecodedValue::Str(s) => serde_json::Value::String(s.clone()),
800 DecodedValue::Bytes(b) => serde_json::Value::String(hex::encode(b)),
801 DecodedValue::Uuid(bytes) => serde_json::Value::String(format_uuid(bytes)),
802 DecodedValue::Decimal(s) => serde_json::Value::String(s.clone()),
803 DecodedValue::Array(items) | DecodedValue::Composite(items) => {
804 serde_json::Value::Array(items.iter().map(cached_to_json).collect())
805 }
806 DecodedValue::Object(fields) => {
807 serde_json::Value::Object(fields.iter().map(|(k, v)| (k.clone(), cached_to_json(v))).collect())
808 }
809 DecodedValue::JsonNumber(digits) => digits
813 .parse::<serde_json::Number>()
814 .map(serde_json::Value::Number)
815 .unwrap_or(serde_json::Value::Null),
816 DecodedValue::Interval {
821 months,
822 days,
823 microseconds,
824 } => serde_json::json!({
825 "months": months, "days": days, "microseconds": microseconds,
826 }),
827 DecodedValue::Date(days) => serde_json::json!({ "days_since_2000_01_01": days }),
832 DecodedValue::Time(us) => serde_json::json!({ "microseconds_since_midnight": us }),
833 DecodedValue::Timestamp(us) => serde_json::json!({ "microseconds_since_2000_01_01": us }),
834 DecodedValue::Timestamptz(us) => serde_json::json!({ "microseconds_since_2000_01_01_utc": us }),
835 DecodedValue::Range {
836 lower,
837 upper,
838 inc_lower,
839 inc_upper,
840 empty,
841 } => serde_json::json!({
842 "lower": lower.as_deref().map(cached_to_json),
843 "upper": upper.as_deref().map(cached_to_json),
844 "inc_lower": inc_lower,
845 "inc_upper": inc_upper,
846 "empty": empty,
847 }),
848 }
849}
850
851fn format_uuid(bytes: &[u8; 16]) -> String {
852 let hex = hex::encode(bytes);
853 format!(
854 "{}-{}-{}-{}-{}",
855 &hex[0..8],
856 &hex[8..12],
857 &hex[12..16],
858 &hex[16..20],
859 &hex[20..32]
860 )
861}
862
863fn parse_uuid_str(s: &str) -> Result<[u8; 16]> {
869 let hex_only: String = s.chars().filter(|c| *c != '-').collect();
870 let bytes = hex::decode(&hex_only).map_err(|_| Error::message(format!("invalid UUID string: {s:?}")))?;
871 bytes
872 .try_into()
873 .map_err(|_: Vec<u8>| Error::message(format!("invalid UUID string: {s:?}")))
874}
875
876fn encode_array(items: &[DecodedValue], element_ty: &Type, out: &mut bytes::BytesMut) -> Result<()> {
879 if items.is_empty() {
880 out.put_i32(0); out.put_i32(0); out.put_u32(element_ty.oid());
883 return Ok(());
884 }
885 let has_null = items.iter().any(|v| matches!(v, DecodedValue::Null));
886 out.put_i32(1); out.put_i32(has_null as i32);
888 out.put_u32(element_ty.oid());
889 out.put_i32(items.len() as i32); out.put_i32(1); for item in items {
893 if matches!(item, DecodedValue::Null) {
894 out.put_i32(-1);
895 continue;
896 }
897 let start = out.len();
898 out.put_i32(0); let is_null = encode_value(item, element_ty, out)?;
900 let len = (out.len() - start - 4) as i32;
901 let len = if matches!(is_null, IsNull::Yes) { -1 } else { len };
902 out[start..start + 4].copy_from_slice(&len.to_be_bytes());
903 }
904 Ok(())
905}
906
907#[cfg(test)]
908mod tests {
909 use super::*;
910
911 fn no_ext() -> ExtensionOids {
912 ExtensionOids::default()
913 }
914
915 #[test]
916 fn decodes_bool() {
917 assert_eq!(
918 decode_value(OID_BOOL, &[1], &no_ext()).unwrap(),
919 DecodedValue::Bool(true)
920 );
921 assert_eq!(
922 decode_value(OID_BOOL, &[0], &no_ext()).unwrap(),
923 DecodedValue::Bool(false)
924 );
925 }
926
927 #[test]
928 fn decodes_integers() {
929 assert_eq!(
930 decode_value(OID_INT2, &7i16.to_be_bytes(), &no_ext()).unwrap(),
931 DecodedValue::I64(7)
932 );
933 assert_eq!(
934 decode_value(OID_INT4, &(-42i32).to_be_bytes(), &no_ext()).unwrap(),
935 DecodedValue::I64(-42)
936 );
937 assert_eq!(
938 decode_value(OID_INT8, &9_223_372_036_854_775_807i64.to_be_bytes(), &no_ext()).unwrap(),
939 DecodedValue::I64(9_223_372_036_854_775_807)
940 );
941 }
942
943 #[test]
944 fn decodes_floats() {
945 assert_eq!(
946 decode_value(OID_FLOAT4, &1.5f32.to_be_bytes(), &no_ext()).unwrap(),
947 DecodedValue::F64(1.5)
948 );
949 assert_eq!(
950 decode_value(OID_FLOAT8, &2.25f64.to_be_bytes(), &no_ext()).unwrap(),
951 DecodedValue::F64(2.25)
952 );
953 }
954
955 #[test]
956 fn decodes_text_varchar_bpchar() {
957 for oid in [OID_TEXT, OID_VARCHAR, OID_BPCHAR] {
958 assert_eq!(
959 decode_value(oid, "hello".as_bytes(), &no_ext()).unwrap(),
960 DecodedValue::Str("hello".to_string())
961 );
962 }
963 }
964
965 #[test]
966 fn decodes_unicode_text() {
967 assert_eq!(
968 decode_value(OID_TEXT, "héllo wörld 🎉".as_bytes(), &no_ext()).unwrap(),
969 DecodedValue::Str("héllo wörld 🎉".to_string())
970 );
971 }
972
973 #[test]
974 fn decodes_uuid() {
975 let bytes: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
976 assert_eq!(
977 decode_value(OID_UUID, &bytes, &no_ext()).unwrap(),
978 DecodedValue::Uuid(bytes)
979 );
980 }
981
982 #[test]
983 fn decodes_bytea() {
984 assert_eq!(
985 decode_value(OID_BYTEA, &[1, 2, 3, 255], &no_ext()).unwrap(),
986 DecodedValue::Bytes(vec![1, 2, 3, 255])
987 );
988 }
989
990 #[test]
991 fn decodes_interval() {
992 let mut data = Vec::new();
996 data.extend_from_slice(&3_600_000_000i64.to_be_bytes()); data.extend_from_slice(&2i32.to_be_bytes()); data.extend_from_slice(&1i32.to_be_bytes()); assert_eq!(
1000 decode_value(OID_INTERVAL, &data, &no_ext()).unwrap(),
1001 DecodedValue::Interval {
1002 months: 1,
1003 days: 2,
1004 microseconds: 3_600_000_000
1005 }
1006 );
1007 }
1008
1009 #[test]
1010 fn encodes_interval() {
1011 let value = DecodedValue::Interval {
1012 months: 1,
1013 days: 2,
1014 microseconds: 3_600_000_000,
1015 };
1016 let mut out = bytes::BytesMut::new();
1017 encode_value(&value, &postgres_types::Type::INTERVAL, &mut out).unwrap();
1018 assert_eq!(decode_value(OID_INTERVAL, &out, &no_ext()).unwrap(), value);
1019 }
1020
1021 #[test]
1022 fn decodes_date_time_timestamp_timestamptz() {
1023 assert_eq!(
1027 decode_value(OID_DATE, &9525i32.to_be_bytes(), &no_ext()).unwrap(),
1028 DecodedValue::Date(9525)
1029 );
1030 assert_eq!(
1031 decode_value(OID_TIME, &3_600_000_000i64.to_be_bytes(), &no_ext()).unwrap(),
1032 DecodedValue::Time(3_600_000_000)
1033 );
1034 assert_eq!(
1035 decode_value(OID_TIMESTAMP, &1_000_000_000i64.to_be_bytes(), &no_ext()).unwrap(),
1036 DecodedValue::Timestamp(1_000_000_000)
1037 );
1038 assert_eq!(
1039 decode_value(OID_TIMESTAMPTZ, &1_000_000_000i64.to_be_bytes(), &no_ext()).unwrap(),
1040 DecodedValue::Timestamptz(1_000_000_000)
1041 );
1042 }
1043
1044 #[test]
1045 fn encodes_date_time_timestamp_timestamptz() {
1046 for (value, ty) in [
1047 (DecodedValue::Date(9525), postgres_types::Type::DATE),
1048 (DecodedValue::Time(3_600_000_000), postgres_types::Type::TIME),
1049 (DecodedValue::Timestamp(1_000_000_000), postgres_types::Type::TIMESTAMP),
1050 (
1051 DecodedValue::Timestamptz(1_000_000_000),
1052 postgres_types::Type::TIMESTAMPTZ,
1053 ),
1054 ] {
1055 let mut out = bytes::BytesMut::new();
1056 encode_value(&value, &ty, &mut out).unwrap();
1057 let oid = match &value {
1058 DecodedValue::Date(_) => OID_DATE,
1059 DecodedValue::Time(_) => OID_TIME,
1060 DecodedValue::Timestamp(_) => OID_TIMESTAMP,
1061 DecodedValue::Timestamptz(_) => OID_TIMESTAMPTZ,
1062 _ => unreachable!(),
1063 };
1064 assert_eq!(decode_value(oid, &out, &no_ext()).unwrap(), value);
1065 }
1066 }
1067
1068 #[test]
1069 fn decodes_a_bounded_int8range() {
1070 let mut data = vec![RANGE_LB_INC];
1072 data.extend_from_slice(&8i32.to_be_bytes());
1073 data.extend_from_slice(&1i64.to_be_bytes());
1074 data.extend_from_slice(&8i32.to_be_bytes());
1075 data.extend_from_slice(&10i64.to_be_bytes());
1076 assert_eq!(
1077 decode_value(OID_INT8RANGE, &data, &no_ext()).unwrap(),
1078 DecodedValue::Range {
1079 lower: Some(Box::new(DecodedValue::I64(1))),
1080 upper: Some(Box::new(DecodedValue::I64(10))),
1081 inc_lower: true,
1082 inc_upper: false,
1083 empty: false,
1084 }
1085 );
1086 }
1087
1088 #[test]
1089 fn decodes_an_empty_range() {
1090 assert_eq!(
1091 decode_value(OID_INT8RANGE, &[RANGE_EMPTY], &no_ext()).unwrap(),
1092 DecodedValue::Range {
1093 lower: None,
1094 upper: None,
1095 inc_lower: false,
1096 inc_upper: false,
1097 empty: true
1098 }
1099 );
1100 }
1101
1102 #[test]
1103 fn decodes_an_unbounded_range() {
1104 let data = [RANGE_LB_INF | RANGE_UB_INF];
1106 assert_eq!(
1107 decode_value(OID_INT8RANGE, &data, &no_ext()).unwrap(),
1108 DecodedValue::Range {
1109 lower: None,
1110 upper: None,
1111 inc_lower: false,
1112 inc_upper: false,
1113 empty: false
1114 }
1115 );
1116 }
1117
1118 #[test]
1119 fn encodes_and_round_trips_an_int8range() {
1120 let value = DecodedValue::Range {
1121 lower: Some(Box::new(DecodedValue::I64(1))),
1122 upper: Some(Box::new(DecodedValue::I64(10))),
1123 inc_lower: true,
1124 inc_upper: false,
1125 empty: false,
1126 };
1127 let mut out = bytes::BytesMut::new();
1128 encode_value(&value, &postgres_types::Type::INT8_RANGE, &mut out).unwrap();
1129 assert_eq!(decode_value(OID_INT8RANGE, &out, &no_ext()).unwrap(), value);
1130 }
1131
1132 #[test]
1133 fn decodes_a_multirange_of_int8ranges() {
1134 let mut range1 = vec![RANGE_LB_INC];
1135 range1.extend_from_slice(&8i32.to_be_bytes());
1136 range1.extend_from_slice(&1i64.to_be_bytes());
1137 range1.extend_from_slice(&8i32.to_be_bytes());
1138 range1.extend_from_slice(&3i64.to_be_bytes());
1139
1140 let mut range2 = vec![RANGE_LB_INC];
1141 range2.extend_from_slice(&8i32.to_be_bytes());
1142 range2.extend_from_slice(&5i64.to_be_bytes());
1143 range2.extend_from_slice(&8i32.to_be_bytes());
1144 range2.extend_from_slice(&7i64.to_be_bytes());
1145
1146 let mut data = 2i32.to_be_bytes().to_vec();
1147 data.extend_from_slice(&(range1.len() as i32).to_be_bytes());
1148 data.extend_from_slice(&range1);
1149 data.extend_from_slice(&(range2.len() as i32).to_be_bytes());
1150 data.extend_from_slice(&range2);
1151
1152 let decoded = decode_value(OID_INT8MULTIRANGE, &data, &no_ext()).unwrap();
1153 assert_eq!(
1154 decoded,
1155 DecodedValue::Array(vec![
1156 DecodedValue::Range {
1157 lower: Some(Box::new(DecodedValue::I64(1))),
1158 upper: Some(Box::new(DecodedValue::I64(3))),
1159 inc_lower: true,
1160 inc_upper: false,
1161 empty: false,
1162 },
1163 DecodedValue::Range {
1164 lower: Some(Box::new(DecodedValue::I64(5))),
1165 upper: Some(Box::new(DecodedValue::I64(7))),
1166 inc_lower: true,
1167 inc_upper: false,
1168 empty: false,
1169 },
1170 ])
1171 );
1172 }
1173
1174 #[test]
1175 fn errors_on_an_oid_no_rule_or_discovery_covers() {
1176 let err = decode_value(999_999, &[0xff, 0xfe], &no_ext()).unwrap_err();
1181 assert!(matches!(err, Error::UnknownTypeOid { oid: 999_999 }), "got {err:?}");
1182 }
1183
1184 #[test]
1185 fn decodes_a_discovered_enum_oid_as_its_label_text() {
1186 let ext = ExtensionOids {
1187 enums: std::collections::HashSet::from([50_001]),
1188 ..Default::default()
1189 };
1190 assert_eq!(
1191 decode_value(50_001, "Active".as_bytes(), &ext).unwrap(),
1192 DecodedValue::Str("Active".to_string())
1193 );
1194 }
1195
1196 #[test]
1197 fn decodes_a_discovered_domain_through_its_base_type() {
1198 let ext = ExtensionOids {
1201 domains: std::collections::HashMap::from([(50_002, OID_INT8)]),
1202 ..Default::default()
1203 };
1204 assert_eq!(
1205 decode_value(50_002, &7i64.to_be_bytes(), &ext).unwrap(),
1206 DecodedValue::I64(7)
1207 );
1208 }
1209
1210 #[test]
1211 fn discovery_rows_populate_vector_enums_and_domains() {
1212 let ext = ExtensionOids::from_discovery_rows([
1213 (50_000, "b".to_string(), 0, "vector".to_string()),
1214 (50_001, "e".to_string(), 0, "status".to_string()),
1215 (50_002, "d".to_string(), OID_INT8, "positive_int".to_string()),
1216 (50_003, "d".to_string(), 0, "broken".to_string()),
1219 ]);
1220 assert_eq!(ext.vector, Some(50_000));
1221 assert!(ext.enums.contains(&50_001));
1222 assert_eq!(ext.domains.get(&50_002), Some(&OID_INT8));
1223 assert!(!ext.domains.contains_key(&50_003));
1224 }
1225
1226 #[test]
1227 fn discovery_rows_record_array_types() {
1228 let ext = ExtensionOids::from_discovery_rows([
1229 (50_001, "e".to_string(), 0, "status".to_string()),
1230 (50_010, "A".to_string(), 50_001, "_status".to_string()),
1231 ]);
1232 assert!(ext.enums.contains(&50_001));
1233 assert!(ext.arrays.contains(&50_010));
1234 }
1235
1236 #[test]
1237 fn decodes_an_array_of_a_discovered_enum() {
1238 let ext = ExtensionOids {
1243 enums: std::collections::HashSet::from([50_001]),
1244 arrays: std::collections::HashSet::from([50_010]),
1245 ..Default::default()
1246 };
1247 let encoded = encode_array(50_001, &[Some(b"Password"), Some(b"Passkey")]);
1248 assert_eq!(
1249 decode_value(50_010, &encoded, &ext).unwrap(),
1250 DecodedValue::Array(vec![
1251 DecodedValue::Str("Password".to_string()),
1252 DecodedValue::Str("Passkey".to_string()),
1253 ])
1254 );
1255 }
1256
1257 #[test]
1258 fn an_array_of_an_undiscovered_type_is_still_an_error() {
1259 let ext = ExtensionOids::default();
1260 let encoded = encode_array(50_001, &[Some(b"Password")]);
1261 let err = decode_value(50_010, &encoded, &ext).unwrap_err();
1262 assert!(matches!(err, Error::UnknownTypeOid { oid: 50_010 }), "got {err:?}");
1263 }
1264
1265 #[test]
1266 fn a_vector_inside_a_record_decodes_when_discovery_ran() {
1267 let mut vec_bytes = 2u16.to_be_bytes().to_vec();
1271 vec_bytes.extend_from_slice(&0u16.to_be_bytes());
1272 vec_bytes.extend_from_slice(&1.5f32.to_be_bytes());
1273 vec_bytes.extend_from_slice(&2.5f32.to_be_bytes());
1274 let rec = encode_record(&[(OID_TEXT, Some(b"doc")), (50_000, Some(&vec_bytes))]);
1275
1276 let ext = ExtensionOids {
1277 vector: Some(50_000),
1278 ..Default::default()
1279 };
1280 let decoded = decode_value(OID_RECORD, &rec, &ext).unwrap();
1281 let DecodedValue::Composite(fields) = decoded else {
1282 panic!("expected Composite, got {decoded:?}")
1283 };
1284 assert_eq!(fields[0], DecodedValue::Str("doc".to_string()));
1285 assert_eq!(
1286 fields[1],
1287 DecodedValue::Array(vec![DecodedValue::F64(1.5), DecodedValue::F64(2.5)])
1288 );
1289
1290 let err = decode_value(OID_RECORD, &rec, &no_ext()).unwrap_err();
1292 assert!(matches!(err, Error::UnknownTypeOid { oid: 50_000 }), "got {err:?}");
1293 }
1294
1295 fn encode_numeric(sign: u16, weight: i16, dscale: i16, digits: &[u16]) -> Vec<u8> {
1299 let mut buf = Vec::new();
1300 buf.extend_from_slice(&(digits.len() as u16).to_be_bytes());
1301 buf.extend_from_slice(&weight.to_be_bytes());
1302 buf.extend_from_slice(&sign.to_be_bytes());
1303 buf.extend_from_slice(&dscale.to_be_bytes());
1304 for d in digits {
1305 buf.extend_from_slice(&d.to_be_bytes());
1306 }
1307 buf
1308 }
1309
1310 #[test]
1311 fn decodes_numeric_integer() {
1312 let data = encode_numeric(0x0000, 1, 0, &[1, 2345]);
1314 assert_eq!(
1315 decode_value(OID_NUMERIC, &data, &no_ext()).unwrap(),
1316 DecodedValue::Decimal("12345".to_string())
1317 );
1318 }
1319
1320 #[test]
1321 fn decodes_numeric_with_fraction() {
1322 let data = encode_numeric(0x0000, 0, 2, &[12, 5000]);
1324 assert_eq!(
1325 decode_value(OID_NUMERIC, &data, &no_ext()).unwrap(),
1326 DecodedValue::Decimal("12.50".to_string())
1327 );
1328 }
1329
1330 #[test]
1331 fn decodes_negative_numeric() {
1332 let data = encode_numeric(0x4000, 0, 2, &[12, 5000]);
1333 assert_eq!(
1334 decode_value(OID_NUMERIC, &data, &no_ext()).unwrap(),
1335 DecodedValue::Decimal("-12.50".to_string())
1336 );
1337 }
1338
1339 #[test]
1340 fn refuses_a_string_for_a_parameter_whose_binary_form_is_not_text() {
1341 for ty in [
1345 Type::INT8,
1346 Type::INT4,
1347 Type::BOOL,
1348 Type::INTERVAL,
1349 Type::TIMESTAMPTZ,
1350 Type::DATE,
1351 ] {
1352 let mut buffer = bytes::BytesMut::new();
1353 let Err(err) = encode_value(&DecodedValue::Str("25 days".into()), &ty, &mut buffer) else {
1354 panic!("{} must refuse a string", ty.name());
1355 };
1356 assert!(
1357 err.to_string().contains(ty.name()),
1358 "the message must name the type that was wanted: {err}"
1359 );
1360 assert!(buffer.is_empty(), "nothing may be written for a refused parameter");
1361 }
1362 }
1363
1364 #[test]
1365 fn a_string_still_reaches_the_types_it_is_the_wire_form_of() {
1366 for ty in [
1367 Type::TEXT,
1368 Type::VARCHAR,
1369 Type::BPCHAR,
1370 Type::NAME,
1371 Type::JSON,
1372 Type::BYTEA,
1373 Type::UNKNOWN,
1374 ] {
1375 let mut buffer = bytes::BytesMut::new();
1376 encode_value(&DecodedValue::Str("hello".into()), &ty, &mut buffer)
1377 .unwrap_or_else(|e| panic!("{} must take a string: {e}", ty.name()));
1378 assert_eq!(&buffer[..], b"hello", "{}", ty.name());
1379 }
1380 }
1381
1382 #[test]
1383 fn a_string_still_converts_for_uuid_jsonb_and_numeric() {
1384 let mut buffer = bytes::BytesMut::new();
1385 encode_value(
1386 &DecodedValue::Str("00000000-0000-0000-0000-000000000001".into()),
1387 &Type::UUID,
1388 &mut buffer,
1389 )
1390 .expect("uuid takes a string");
1391 assert_eq!(buffer.len(), 16, "a uuid is converted, not copied");
1392
1393 let mut buffer = bytes::BytesMut::new();
1394 encode_value(&DecodedValue::Str(r#"{"a":1}"#.into()), &Type::JSONB, &mut buffer).expect("jsonb takes a string");
1395 assert_eq!(buffer[0], 1, "jsonb needs its version byte");
1396
1397 let mut buffer = bytes::BytesMut::new();
1398 encode_value(&DecodedValue::Str("12.50".into()), &Type::NUMERIC, &mut buffer).expect("numeric takes a string");
1399 assert_eq!(numeric::decode(&buffer).unwrap(), "12.50");
1400 }
1401
1402 #[test]
1403 fn decodes_jsonb_object() {
1404 let mut data = vec![1u8]; data.extend_from_slice(br#"{"a":1,"b":"two","c":[1,2,3],"d":null}"#);
1406 let decoded = decode_value(OID_JSONB, &data, &no_ext()).unwrap();
1407 assert_eq!(
1408 decoded,
1409 DecodedValue::Object(vec![
1410 ("a".into(), DecodedValue::I64(1)),
1411 ("b".into(), DecodedValue::Str("two".into())),
1412 (
1413 "c".into(),
1414 DecodedValue::Array(vec![DecodedValue::I64(1), DecodedValue::I64(2), DecodedValue::I64(3)])
1415 ),
1416 ("d".into(), DecodedValue::Null),
1417 ])
1418 );
1419 }
1420
1421 #[test]
1422 fn decodes_jsonb_scalar_and_array() {
1423 let mut data = vec![1u8];
1424 data.extend_from_slice(b"42");
1425 assert_eq!(
1426 decode_value(OID_JSONB, &data, &no_ext()).unwrap(),
1427 DecodedValue::I64(42)
1428 );
1429
1430 let mut data2 = vec![1u8];
1434 data2.extend_from_slice(b"[1.5, 2.5]");
1435 assert_eq!(
1436 decode_value(OID_JSONB, &data2, &no_ext()).unwrap(),
1437 DecodedValue::Array(vec![
1438 DecodedValue::JsonNumber("1.5".to_string()),
1439 DecodedValue::JsonNumber("2.5".to_string()),
1440 ])
1441 );
1442 }
1443
1444 #[test]
1445 fn decodes_a_jsonb_number_as_the_digits_it_was_written_with() {
1446 for digits in [
1449 "12.3400",
1450 "0.00000039999999999999998189924473035450347424557548947632312774658203125",
1451 "1e3",
1452 ] {
1453 let mut data = vec![1u8];
1454 data.extend_from_slice(format!(r#"{{"n": {digits}}}"#).as_bytes());
1455 assert_eq!(
1456 decode_value(OID_JSONB, &data, &no_ext()).unwrap(),
1457 DecodedValue::Object(vec![("n".to_string(), DecodedValue::JsonNumber(digits.to_string()))])
1458 );
1459 }
1460 }
1461
1462 #[test]
1463 fn a_jsonb_number_that_kept_its_digits_goes_back_out_as_a_number() {
1464 let value = DecodedValue::Object(vec![("n".to_string(), DecodedValue::JsonNumber("12.3400".to_string()))]);
1466 let mut out = bytes::BytesMut::new();
1467 encode_value(&value, &postgres_types::Type::JSONB, &mut out).unwrap();
1468 assert_eq!(out.as_ref(), [&[1u8][..], br#"{"n":12.3400}"#].concat());
1469 }
1470
1471 #[test]
1472 fn decodes_jsonb_object_keys_in_the_order_it_always_has() {
1473 let mut data = vec![1u8];
1477 data.extend_from_slice(br#"{"b": 1, "aa": 2}"#);
1478 let DecodedValue::Object(fields) = decode_value(OID_JSONB, &data, &no_ext()).unwrap() else {
1479 panic!("expected an object")
1480 };
1481 assert_eq!(
1482 fields.iter().map(|(k, _)| k.as_str()).collect::<Vec<_>>(),
1483 vec!["aa", "b"]
1484 );
1485 }
1486
1487 fn encode_record(fields: &[(u32, Option<&[u8]>)]) -> Vec<u8> {
1491 let mut buf = Vec::new();
1492 buf.extend_from_slice(&(fields.len() as i32).to_be_bytes());
1493 for (oid, data) in fields {
1494 buf.extend_from_slice(&oid.to_be_bytes());
1495 match data {
1496 None => buf.extend_from_slice(&(-1i32).to_be_bytes()),
1497 Some(bytes) => {
1498 buf.extend_from_slice(&(bytes.len() as i32).to_be_bytes());
1499 buf.extend_from_slice(bytes);
1500 }
1501 }
1502 }
1503 buf
1504 }
1505
1506 #[test]
1507 fn decodes_flat_record() {
1508 let data = encode_record(&[
1509 (OID_INT8, Some(&42i64.to_be_bytes())),
1510 (OID_TEXT, Some(b"alice")),
1511 (OID_BOOL, None),
1512 ]);
1513 let decoded = decode_value(OID_RECORD, &data, &no_ext()).unwrap();
1514 assert_eq!(
1515 decoded,
1516 DecodedValue::Composite(vec![
1517 DecodedValue::I64(42),
1518 DecodedValue::Str("alice".into()),
1519 DecodedValue::Null
1520 ])
1521 );
1522 }
1523
1524 #[test]
1525 fn decodes_nested_record() {
1526 let inner = encode_record(&[(OID_INT8, Some(&1i64.to_be_bytes()))]);
1527 let outer = encode_record(&[(OID_RECORD, Some(&inner)), (OID_TEXT, Some(b"outer"))]);
1528 let decoded = decode_value(OID_RECORD, &outer, &no_ext()).unwrap();
1529 assert_eq!(
1530 decoded,
1531 DecodedValue::Composite(vec![
1532 DecodedValue::Composite(vec![DecodedValue::I64(1)]),
1533 DecodedValue::Str("outer".into()),
1534 ])
1535 );
1536 }
1537
1538 fn encode_array(element_oid: u32, elements: &[Option<&[u8]>]) -> Vec<u8> {
1542 if elements.is_empty() {
1543 let mut buf = Vec::new();
1544 buf.extend_from_slice(&0i32.to_be_bytes());
1545 buf.extend_from_slice(&0i32.to_be_bytes());
1546 buf.extend_from_slice(&element_oid.to_be_bytes());
1547 return buf;
1548 }
1549 let mut buf = Vec::new();
1550 buf.extend_from_slice(&1i32.to_be_bytes());
1551 buf.extend_from_slice(&0i32.to_be_bytes());
1552 buf.extend_from_slice(&element_oid.to_be_bytes());
1553 buf.extend_from_slice(&(elements.len() as i32).to_be_bytes());
1554 buf.extend_from_slice(&1i32.to_be_bytes());
1555 for data in elements {
1556 match data {
1557 None => buf.extend_from_slice(&(-1i32).to_be_bytes()),
1558 Some(bytes) => {
1559 buf.extend_from_slice(&(bytes.len() as i32).to_be_bytes());
1560 buf.extend_from_slice(bytes);
1561 }
1562 }
1563 }
1564 buf
1565 }
1566
1567 #[test]
1568 fn decodes_array_of_scalars() {
1569 let data = encode_array(OID_TEXT, &[Some(b"a"), Some(b"b"), None]);
1570 let decoded = decode_value(OID_TEXT_ARRAY, &data, &no_ext()).unwrap();
1571 assert_eq!(
1572 decoded,
1573 DecodedValue::Array(vec![
1574 DecodedValue::Str("a".into()),
1575 DecodedValue::Str("b".into()),
1576 DecodedValue::Null
1577 ])
1578 );
1579 }
1580
1581 #[test]
1582 fn decodes_empty_array() {
1583 let data = encode_array(OID_TEXT, &[]);
1584 assert_eq!(
1585 decode_value(OID_TEXT_ARRAY, &data, &no_ext()).unwrap(),
1586 DecodedValue::Array(vec![])
1587 );
1588 }
1589
1590 #[test]
1591 fn decodes_array_of_records() {
1592 let rec1 = encode_record(&[(OID_INT8, Some(&1i64.to_be_bytes()))]);
1593 let rec2 = encode_record(&[(OID_INT8, Some(&2i64.to_be_bytes()))]);
1594 let data = encode_array(OID_RECORD, &[Some(&rec1), Some(&rec2)]);
1595 let decoded = decode_value(OID_RECORD_ARRAY, &data, &no_ext()).unwrap();
1596 assert_eq!(
1597 decoded,
1598 DecodedValue::Array(vec![
1599 DecodedValue::Composite(vec![DecodedValue::I64(1)]),
1600 DecodedValue::Composite(vec![DecodedValue::I64(2)]),
1601 ])
1602 );
1603 }
1604
1605 #[test]
1606 fn decodes_vector_when_extension_oid_known() {
1607 let mut data = 2u16.to_be_bytes().to_vec(); data.extend_from_slice(&0u16.to_be_bytes()); data.extend_from_slice(&1.5f32.to_be_bytes());
1610 data.extend_from_slice(&2.5f32.to_be_bytes());
1611
1612 let ext = ExtensionOids {
1613 vector: Some(50_000),
1614 ..Default::default()
1615 };
1616 let decoded = decode_value(50_000, &data, &ext).unwrap();
1617 assert_eq!(
1618 decoded,
1619 DecodedValue::Array(vec![DecodedValue::F64(1.5), DecodedValue::F64(2.5)])
1620 );
1621 }
1622
1623 #[test]
1624 fn unknown_oid_without_vector_extension_is_an_error_not_a_guess() {
1625 let err = decode_value(50_000, "some-domain-value".as_bytes(), &no_ext()).unwrap_err();
1630 assert!(matches!(err, Error::UnknownTypeOid { oid: 50_000 }), "got {err:?}");
1631 }
1632
1633 #[test]
1634 fn encodes_a_str_value_as_uuid_binary_when_the_target_type_is_uuid() {
1635 let value = DecodedValue::Str("11111111-2222-3333-4444-555555555555".to_string());
1642 let mut out = bytes::BytesMut::new();
1643 encode_value(&value, &postgres_types::Type::UUID, &mut out).unwrap();
1644 assert_eq!(
1645 out.as_ref(),
1646 &[
1647 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55
1648 ]
1649 );
1650 }
1651
1652 #[test]
1653 fn a_str_value_still_encodes_as_plain_text_for_a_text_target() {
1654 let value = DecodedValue::Str("11111111-2222-3333-4444-555555555555".to_string());
1655 let mut out = bytes::BytesMut::new();
1656 encode_value(&value, &postgres_types::Type::TEXT, &mut out).unwrap();
1657 assert_eq!(out.as_ref(), "11111111-2222-3333-4444-555555555555".as_bytes());
1658 }
1659
1660 #[test]
1661 fn rejects_a_malformed_uuid_string_instead_of_sending_garbage_bytes() {
1662 let value = DecodedValue::Str("not-a-uuid".to_string());
1663 let mut out = bytes::BytesMut::new();
1664 assert!(encode_value(&value, &postgres_types::Type::UUID, &mut out).is_err());
1665 }
1666
1667 fn vector_type() -> Type {
1668 Type::new(
1673 "vector".to_string(),
1674 50_000,
1675 postgres_types::Kind::Simple,
1676 "public".to_string(),
1677 )
1678 }
1679
1680 #[test]
1681 fn encodes_an_array_value_as_pgvector_binary_when_the_target_type_is_vector() {
1682 let value = DecodedValue::Array(vec![
1689 DecodedValue::F64(1.5),
1690 DecodedValue::F64(-2.25),
1691 DecodedValue::F64(0.0),
1692 ]);
1693 let mut out = bytes::BytesMut::new();
1694 encode_value(&value, &vector_type(), &mut out).unwrap();
1695 let mut expected = vec![0u8, 3, 0, 0];
1696 expected.extend_from_slice(&1.5f32.to_be_bytes());
1697 expected.extend_from_slice(&(-2.25f32).to_be_bytes());
1698 expected.extend_from_slice(&0.0f32.to_be_bytes());
1699 assert_eq!(out.as_ref(), expected.as_slice());
1700 }
1701
1702 #[test]
1703 fn a_vector_encoded_value_round_trips_through_decode_vector() {
1704 let value = DecodedValue::Array(vec![
1705 DecodedValue::F64(1.0),
1706 DecodedValue::F64(2.0),
1707 DecodedValue::F64(3.0),
1708 ]);
1709 let mut out = bytes::BytesMut::new();
1710 encode_value(&value, &vector_type(), &mut out).unwrap();
1711 let decoded = decode_vector(out.as_ref()).unwrap();
1712 assert_eq!(
1713 decoded,
1714 vec![DecodedValue::F64(1.0), DecodedValue::F64(2.0), DecodedValue::F64(3.0)]
1715 );
1716 }
1717
1718 #[test]
1719 fn an_array_value_still_encodes_as_a_plain_postgres_array_for_a_non_vector_target() {
1720 let value = DecodedValue::Array(vec![DecodedValue::F64(1.0), DecodedValue::F64(2.0)]);
1721 let mut out = bytes::BytesMut::new();
1722 encode_value(&value, &postgres_types::Type::FLOAT8_ARRAY, &mut out).unwrap();
1723 assert_eq!(&out.as_ref()[0..4], &1i32.to_be_bytes());
1726 }
1727
1728 #[test]
1729 fn encodes_a_str_value_as_jsonb_binary_when_the_target_type_is_jsonb() {
1730 let value = DecodedValue::Str(r#"{"a":1}"#.to_string());
1736 let mut out = bytes::BytesMut::new();
1737 encode_value(&value, &postgres_types::Type::JSONB, &mut out).unwrap();
1738 assert_eq!(out.as_ref(), [&[1u8][..], br#"{"a":1}"#].concat());
1739 assert_eq!(
1741 decode_value(OID_JSONB, &out, &no_ext()).unwrap(),
1742 DecodedValue::Object(vec![("a".into(), DecodedValue::I64(1))])
1743 );
1744 }
1745
1746 #[test]
1747 fn encodes_a_composite_as_a_jsonb_array_but_refuses_it_elsewhere() {
1748 let value = DecodedValue::Composite(vec![DecodedValue::Str("left".into()), DecodedValue::Bool(true)]);
1752 let mut out = bytes::BytesMut::new();
1753 encode_value(&value, &postgres_types::Type::JSONB, &mut out).unwrap();
1754 assert_eq!(out.as_ref(), [&[1u8][..], br#"["left",true]"#].concat());
1755
1756 let mut out = bytes::BytesMut::new();
1757 let refused = encode_value(&value, &postgres_types::Type::RECORD, &mut out);
1758 assert!(
1759 refused.is_err_and(|e| e.to_string().contains("cannot bind a composite value")),
1760 "a record parameter has no per-field type to encode against"
1761 );
1762 }
1763}