sqlx_postgres/types/
bytes.rs

1use crate::decode::Decode;
2use crate::encode::{Encode, IsNull};
3use crate::error::BoxDynError;
4use crate::types::Type;
5use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
6
7impl PgHasArrayType for u8 {
8    fn array_type_info() -> PgTypeInfo {
9        PgTypeInfo::BYTEA
10    }
11}
12
13impl PgHasArrayType for &'_ [u8] {
14    fn array_type_info() -> PgTypeInfo {
15        PgTypeInfo::BYTEA_ARRAY
16    }
17}
18
19impl PgHasArrayType for Box<[u8]> {
20    fn array_type_info() -> PgTypeInfo {
21        <[&[u8]] as Type<Postgres>>::type_info()
22    }
23}
24
25impl PgHasArrayType for Vec<u8> {
26    fn array_type_info() -> PgTypeInfo {
27        <[&[u8]] as Type<Postgres>>::type_info()
28    }
29}
30
31impl<const N: usize> PgHasArrayType for [u8; N] {
32    fn array_type_info() -> PgTypeInfo {
33        <[&[u8]] as Type<Postgres>>::type_info()
34    }
35}
36
37impl Encode<'_, Postgres> for &'_ [u8] {
38    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
39        buf.extend_from_slice(self);
40
41        Ok(IsNull::No)
42    }
43}
44
45impl Encode<'_, Postgres> for Box<[u8]> {
46    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
47        <&[u8] as Encode<Postgres>>::encode(self.as_ref(), buf)
48    }
49}
50
51impl Encode<'_, Postgres> for Vec<u8> {
52    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
53        <&[u8] as Encode<Postgres>>::encode(self, buf)
54    }
55}
56
57impl<const N: usize> Encode<'_, Postgres> for [u8; N] {
58    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
59        <&[u8] as Encode<Postgres>>::encode(self.as_slice(), buf)
60    }
61}
62
63impl<'r> Decode<'r, Postgres> for &'r [u8] {
64    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
65        match value.format() {
66            PgValueFormat::Binary => value.as_bytes(),
67            PgValueFormat::Text => {
68                Err("unsupported decode to `&[u8]` of BYTEA in a simple query; use a prepared query or decode to `Vec<u8>`".into())
69            }
70        }
71    }
72}
73
74fn text_hex_decode_input(value: PgValueRef<'_>) -> Result<&[u8], BoxDynError> {
75    // BYTEA is formatted as \x followed by hex characters
76    value
77        .as_bytes()?
78        .strip_prefix(b"\\x")
79        .ok_or("text does not start with \\x")
80        .map_err(Into::into)
81}
82
83impl Decode<'_, Postgres> for Box<[u8]> {
84    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
85        Ok(match value.format() {
86            PgValueFormat::Binary => Box::from(value.as_bytes()?),
87            PgValueFormat::Text => Box::from(hex::decode(text_hex_decode_input(value)?)?),
88        })
89    }
90}
91
92impl Decode<'_, Postgres> for Vec<u8> {
93    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
94        Ok(match value.format() {
95            PgValueFormat::Binary => value.as_bytes()?.to_owned(),
96            PgValueFormat::Text => hex::decode(text_hex_decode_input(value)?)?,
97        })
98    }
99}
100
101impl<const N: usize> Decode<'_, Postgres> for [u8; N] {
102    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
103        let mut bytes = [0u8; N];
104        match value.format() {
105            PgValueFormat::Binary => {
106                bytes = value.as_bytes()?.try_into()?;
107            }
108            PgValueFormat::Text => hex::decode_to_slice(text_hex_decode_input(value)?, &mut bytes)?,
109        };
110        Ok(bytes)
111    }
112}