1use std::fmt;
2
3use sqlx::{MySql, Row, Type, ValueRef, mysql::MySqlRow};
4
5use crate::{
6 Result,
7 error::{invalid_column, result_limit_exceeded, unsupported_column_type},
8};
9
10pub const MAX_FIELD_BYTES: usize = 1_048_576;
13pub const MAX_RESULT_BYTES: usize = 8_388_608;
15
16pub struct DbRow(pub(crate) MySqlRow);
18
19impl fmt::Debug for DbRow {
20 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
21 formatter
22 .debug_struct("DbRow")
23 .field("columns", &self.0.len())
24 .field("values", &"<redacted>")
25 .finish()
26 }
27}
28
29impl DbRow {
30 pub fn bool(&self, column: &str) -> Result<bool> {
31 self.0.try_get(column).map_err(|_| invalid_column())
32 }
33 pub fn i64(&self, column: &str) -> Result<i64> {
34 self.0.try_get(column).map_err(|_| invalid_column())
35 }
36 pub fn u64(&self, column: &str) -> Result<u64> {
37 self.0.try_get(column).map_err(|_| invalid_column())
38 }
39 pub fn f64(&self, column: &str) -> Result<f64> {
40 self.0.try_get(column).map_err(|_| invalid_column())
41 }
42 pub fn string(&self, column: &str) -> Result<String> {
43 self.0.try_get(column).map_err(|_| invalid_column())
44 }
45 pub fn bytes(&self, column: &str) -> Result<Vec<u8>> {
46 self.0.try_get(column).map_err(|_| invalid_column())
47 }
48 pub fn optional_i64(&self, column: &str) -> Result<Option<i64>> {
49 self.0.try_get(column).map_err(|_| invalid_column())
50 }
51 pub fn optional_bool(&self, column: &str) -> Result<Option<bool>> {
52 self.0.try_get(column).map_err(|_| invalid_column())
53 }
54 pub fn optional_u64(&self, column: &str) -> Result<Option<u64>> {
55 self.0.try_get(column).map_err(|_| invalid_column())
56 }
57 pub fn optional_f64(&self, column: &str) -> Result<Option<f64>> {
58 self.0.try_get(column).map_err(|_| invalid_column())
59 }
60 pub fn optional_string(&self, column: &str) -> Result<Option<String>> {
61 self.0.try_get(column).map_err(|_| invalid_column())
62 }
63 pub fn optional_bytes(&self, column: &str) -> Result<Option<Vec<u8>>> {
64 self.0.try_get(column).map_err(|_| invalid_column())
65 }
66}
67
68pub(crate) fn row_payload_bytes(row: &MySqlRow) -> Result<usize> {
69 let mut total = 0_usize;
70 for index in 0..row.len() {
71 let raw = row.try_get_raw(index).map_err(|_| invalid_column())?;
72 if raw.is_null() {
73 continue;
74 }
75 let type_info = raw.type_info();
76 let bytes = if <&[u8] as Type<MySql>>::compatible(type_info.as_ref()) {
77 row.try_get::<&[u8], _>(index)
78 .map_err(|_| invalid_column())?
79 .len()
80 } else if <i64 as Type<MySql>>::compatible(type_info.as_ref())
81 || <u64 as Type<MySql>>::compatible(type_info.as_ref())
82 || <f64 as Type<MySql>>::compatible(type_info.as_ref())
83 || <bool as Type<MySql>>::compatible(type_info.as_ref())
84 {
85 8
86 } else {
87 return Err(unsupported_column_type());
88 };
89 if bytes > MAX_FIELD_BYTES {
90 return Err(result_limit_exceeded());
91 }
92 total = total.saturating_add(bytes);
93 if total > MAX_RESULT_BYTES {
94 return Err(result_limit_exceeded());
95 }
96 }
97 Ok(total)
98}
99
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub struct WriteResult {
103 rows_affected: u64,
104 last_insert_id: u64,
105}
106
107impl WriteResult {
108 pub(crate) const fn new(rows_affected: u64, last_insert_id: u64) -> Self {
109 Self {
110 rows_affected,
111 last_insert_id,
112 }
113 }
114 pub const fn rows_affected(self) -> u64 {
115 self.rows_affected
116 }
117 pub const fn last_insert_id(self) -> u64 {
118 self.last_insert_id
119 }
120}