1use super::{DatabaseDriver, DatabaseRow};
2use sqlx::{Database, Decode, Row, ValueRef};
3use zino_core::{Decimal, Uuid, error::Error, warn};
4
5#[inline]
7pub fn decode<'r, T>(row: &'r DatabaseRow, field: &str) -> Result<T, Error>
8where
9 T: Decode<'r, DatabaseDriver>,
10{
11 row.try_get_unchecked(field)
12 .map_err(|err| warn!("fail to decode the `{}` field: {}", field, err))
13}
14
15#[inline]
18pub fn decode_optional<'r, T>(row: &'r DatabaseRow, field: &str) -> Result<Option<T>, Error>
19where
20 T: Decode<'r, DatabaseDriver>,
21{
22 match row.try_get_raw(field) {
23 Ok(value) => {
24 if value.is_null() {
25 Ok(None)
26 } else {
27 let value = decode_raw(field, value)?;
28 Ok(Some(value))
29 }
30 }
31 Err(err) => {
32 if let sqlx::Error::ColumnNotFound(_) = err {
33 Ok(None)
34 } else {
35 Err(warn!("fail to get the `{}` field: {}", field, err))
36 }
37 }
38 }
39}
40
41#[cfg(any(
43 feature = "orm-mariadb",
44 feature = "orm-mysql",
45 feature = "orm-postgres",
46 feature = "orm-tidb"
47))]
48#[inline]
49pub fn decode_decimal(row: &DatabaseRow, field: &str) -> Result<Decimal, Error> {
50 match row.try_get_raw(field) {
51 Ok(value) => {
52 if value.is_null() {
53 warn!("decode the NULL as a zero value");
54 Ok(Decimal::ZERO)
55 } else {
56 let value = decode_raw(field, value)?;
57 Ok(value)
58 }
59 }
60 Err(err) => {
61 if let sqlx::Error::ColumnNotFound(_) = err {
62 Ok(Decimal::ZERO)
63 } else {
64 Err(warn!("fail to get the `{}` field: {}", field, err))
65 }
66 }
67 }
68}
69
70#[cfg(not(any(
72 feature = "orm-mariadb",
73 feature = "orm-mysql",
74 feature = "orm-postgres",
75 feature = "orm-tidb"
76)))]
77#[inline]
78pub fn decode_decimal(row: &DatabaseRow, field: &str) -> Result<Decimal, Error> {
79 let Some(value) = decode_optional::<String>(row, field)? else {
80 return Ok(Decimal::ZERO);
81 };
82 value
83 .parse()
84 .map_err(|err| warn!("fail to decode the `{}` field: {}", field, err))
85}
86
87#[cfg(feature = "orm-postgres")]
89#[inline]
90pub fn decode_uuid(row: &DatabaseRow, field: &str) -> Result<Uuid, Error> {
91 match row.try_get_raw(field) {
92 Ok(value) => {
93 if value.is_null() {
94 Ok(Uuid::nil())
95 } else {
96 let id = decode_raw(field, value)?;
97 Ok(id)
98 }
99 }
100 Err(err) => {
101 if let sqlx::Error::ColumnNotFound(_) = err {
102 Ok(Uuid::nil())
103 } else {
104 Err(warn!("fail to get the `{}` field: {}", field, err))
105 }
106 }
107 }
108}
109
110#[cfg(not(feature = "orm-postgres"))]
112#[inline]
113pub fn decode_uuid(row: &DatabaseRow, field: &str) -> Result<Uuid, Error> {
114 let Some(value) = decode_optional::<String>(row, field)? else {
115 return Ok(Uuid::nil());
116 };
117 value
118 .parse()
119 .map_err(|err| warn!("fail to decode the `{}` field: {}", field, err))
120}
121
122#[cfg(feature = "orm-postgres")]
124#[inline]
125pub fn decode_array<'r, T>(row: &'r DatabaseRow, field: &str) -> Result<Vec<T>, Error>
126where
127 T: for<'a> Decode<'a, DatabaseDriver> + sqlx::Type<DatabaseDriver>,
128{
129 match row.try_get_raw(field) {
130 Ok(value) => {
131 if value.is_null() {
132 warn!("decode the NULL as an empty array");
133 Ok(Vec::new())
134 } else {
135 let vec = decode_raw(field, value)?;
136 Ok(vec)
137 }
138 }
139 Err(err) => {
140 if let sqlx::Error::ColumnNotFound(_) = err {
141 Ok(Vec::new())
142 } else {
143 Err(warn!("fail to get the `{}` field: {}", field, err))
144 }
145 }
146 }
147}
148
149#[cfg(feature = "orm-mariadb")]
151#[inline]
152pub fn decode_array<'r, T>(row: &'r DatabaseRow, field: &str) -> Result<Vec<T>, Error>
153where
154 T: Decode<'r, DatabaseDriver> + serde::de::DeserializeOwned,
155{
156 let Some(value) = decode_optional::<String>(row, field)? else {
157 return Ok(Vec::new());
158 };
159 if value.starts_with('[') && value.ends_with(']') {
160 serde_json::from_str(&value)
161 .map_err(|err| warn!("fail to decode the `{}` field: {}", field, err))
162 } else {
163 zino_core::bail!("invalid array data for the `{}` field", field);
164 }
165}
166
167#[cfg(not(any(feature = "orm-mariadb", feature = "orm-postgres")))]
169#[inline]
170pub fn decode_array<'r, T>(row: &'r DatabaseRow, field: &str) -> Result<Vec<T>, Error>
171where
172 T: Decode<'r, DatabaseDriver> + std::str::FromStr,
173 <T as std::str::FromStr>::Err: std::error::Error + Send + 'static,
174{
175 use zino_core::{JsonValue, extension::JsonValueExt};
176
177 let Some(value) = decode_optional::<JsonValue>(row, field)? else {
178 return Ok(Vec::new());
179 };
180 if let Some(result) = value.parse_array() {
181 result.map_err(|err| warn!("fail to decode the `{}` field: {}", field, err))
182 } else {
183 Ok(Vec::new())
184 }
185}
186
187#[inline]
189pub(super) fn decode_raw<'r, T>(
190 field: &str,
191 value: <DatabaseDriver as Database>::ValueRef<'r>,
192) -> Result<T, sqlx::Error>
193where
194 T: Decode<'r, DatabaseDriver>,
195{
196 T::decode(value).map_err(|source| {
197 tracing::error!("fail to decode the `{}` field", field);
198 sqlx::Error::ColumnDecode {
199 index: field.to_owned(),
200 source,
201 }
202 })
203}