sqlx_postgres/types/
int.rs

1use byteorder::{BigEndian, ByteOrder};
2use std::num::{NonZeroI16, NonZeroI32, NonZeroI64};
3
4use crate::decode::Decode;
5use crate::encode::{Encode, IsNull};
6use crate::error::BoxDynError;
7use crate::types::Type;
8use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
9
10fn int_decode(value: PgValueRef<'_>) -> Result<i64, BoxDynError> {
11    Ok(match value.format() {
12        PgValueFormat::Text => value.as_str()?.parse()?,
13        PgValueFormat::Binary => {
14            let buf = value.as_bytes()?;
15
16            // Return error if buf is empty or is more than 8 bytes
17            match buf.len() {
18                0 => {
19                    return Err("Value Buffer found empty while decoding to integer type".into());
20                }
21                buf_len @ 9.. => {
22                    return Err(format!(
23                        "Value Buffer exceeds 8 bytes while decoding to integer type. Buffer size = {} bytes ", buf_len
24                    )
25                    .into());
26                }
27                _ => {}
28            }
29
30            BigEndian::read_int(buf, buf.len())
31        }
32    })
33}
34
35impl Type<Postgres> for i8 {
36    fn type_info() -> PgTypeInfo {
37        PgTypeInfo::CHAR
38    }
39}
40
41impl PgHasArrayType for i8 {
42    fn array_type_info() -> PgTypeInfo {
43        PgTypeInfo::CHAR_ARRAY
44    }
45}
46
47impl Encode<'_, Postgres> for i8 {
48    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
49        buf.extend(&self.to_be_bytes());
50
51        Ok(IsNull::No)
52    }
53}
54
55impl Decode<'_, Postgres> for i8 {
56    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
57        // note: decoding here is for the `"char"` type as Postgres does not have a native 1-byte integer type.
58        // https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/char.c#L58-L60
59        match value.format() {
60            PgValueFormat::Binary => int_decode(value)?.try_into().map_err(Into::into),
61            PgValueFormat::Text => {
62                let text = value.as_str()?;
63
64                // A value of 0 is represented with the empty string.
65                if text.is_empty() {
66                    return Ok(0);
67                }
68
69                if text.starts_with('\\') {
70                    // For values between 0x80 and 0xFF, it's encoded in octal.
71                    return Ok(i8::from_str_radix(text.trim_start_matches('\\'), 8)?);
72                }
73
74                // Wrapping is the whole idea.
75                #[allow(clippy::cast_possible_wrap)]
76                Ok(text.as_bytes()[0] as i8)
77            }
78        }
79    }
80}
81
82impl Type<Postgres> for i16 {
83    fn type_info() -> PgTypeInfo {
84        PgTypeInfo::INT2
85    }
86}
87
88impl PgHasArrayType for i16 {
89    fn array_type_info() -> PgTypeInfo {
90        PgTypeInfo::INT2_ARRAY
91    }
92}
93
94impl Encode<'_, Postgres> for i16 {
95    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
96        buf.extend(&self.to_be_bytes());
97
98        Ok(IsNull::No)
99    }
100}
101
102impl Decode<'_, Postgres> for i16 {
103    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
104        int_decode(value)?.try_into().map_err(Into::into)
105    }
106}
107
108impl Type<Postgres> for i32 {
109    fn type_info() -> PgTypeInfo {
110        PgTypeInfo::INT4
111    }
112}
113
114impl PgHasArrayType for i32 {
115    fn array_type_info() -> PgTypeInfo {
116        PgTypeInfo::INT4_ARRAY
117    }
118}
119
120impl Encode<'_, Postgres> for i32 {
121    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
122        buf.extend(&self.to_be_bytes());
123
124        Ok(IsNull::No)
125    }
126}
127
128impl Decode<'_, Postgres> for i32 {
129    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
130        int_decode(value)?.try_into().map_err(Into::into)
131    }
132}
133
134impl Type<Postgres> for i64 {
135    fn type_info() -> PgTypeInfo {
136        PgTypeInfo::INT8
137    }
138}
139
140impl PgHasArrayType for i64 {
141    fn array_type_info() -> PgTypeInfo {
142        PgTypeInfo::INT8_ARRAY
143    }
144}
145
146impl Encode<'_, Postgres> for i64 {
147    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
148        buf.extend(&self.to_be_bytes());
149
150        Ok(IsNull::No)
151    }
152}
153
154impl Decode<'_, Postgres> for i64 {
155    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
156        int_decode(value)
157    }
158}
159
160impl PgHasArrayType for NonZeroI16 {
161    fn array_type_info() -> PgTypeInfo {
162        PgTypeInfo::INT2_ARRAY
163    }
164}
165
166impl PgHasArrayType for NonZeroI32 {
167    fn array_type_info() -> PgTypeInfo {
168        PgTypeInfo::INT4_ARRAY
169    }
170}
171
172impl PgHasArrayType for NonZeroI64 {
173    fn array_type_info() -> PgTypeInfo {
174        PgTypeInfo::INT8_ARRAY
175    }
176}