1use bsql_driver_postgres::arena::release_arena;
10use bsql_driver_postgres::codec::Encode;
11use bsql_driver_postgres::{Arena, QueryResult};
12
13use crate::error::{BsqlError, BsqlResult};
14use crate::pool::{Pool, PoolConnection};
15use crate::transaction::Transaction;
16
17pub struct OwnedResult {
22 pub result: QueryResult,
23 arena: Arena,
24}
25
26impl std::fmt::Debug for OwnedResult {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("OwnedResult")
29 .field("rows", &self.result.len())
30 .finish()
31 }
32}
33
34impl OwnedResult {
35 pub(crate) fn without_arena(result: QueryResult) -> Self {
38 Self {
39 result,
40 arena: Arena::empty(),
41 }
42 }
43
44 pub fn len(&self) -> usize {
46 self.result.len()
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.result.is_empty()
52 }
53
54 pub fn row(&self, idx: usize) -> bsql_driver_postgres::Row<'_> {
56 self.result.row(idx, &self.arena)
57 }
58
59 pub fn iter(&self) -> impl Iterator<Item = bsql_driver_postgres::Row<'_>> {
61 self.result.rows(&self.arena)
62 }
63}
64
65impl Drop for OwnedResult {
66 fn drop(&mut self) {
67 let arena = std::mem::take(&mut self.arena);
69 release_arena(arena);
70 if let Some(buf) = self.result.take_data_buf() {
72 bsql_driver_postgres::release_resp_buf(buf);
73 }
74 }
75}
76
77pub trait Executor {
82 fn query_raw(
84 &self,
85 sql: &str,
86 sql_hash: u64,
87 params: &[&(dyn Encode + Sync)],
88 ) -> BsqlResult<OwnedResult>;
89
90 fn query_raw_readonly(
92 &self,
93 sql: &str,
94 sql_hash: u64,
95 params: &[&(dyn Encode + Sync)],
96 ) -> BsqlResult<OwnedResult>;
97
98 fn execute_raw(
100 &self,
101 sql: &str,
102 sql_hash: u64,
103 params: &[&(dyn Encode + Sync)],
104 ) -> BsqlResult<u64>;
105}
106
107impl Executor for Pool {
108 #[inline]
109 fn query_raw(
110 &self,
111 sql: &str,
112 sql_hash: u64,
113 params: &[&(dyn Encode + Sync)],
114 ) -> BsqlResult<OwnedResult> {
115 let mut guard = self.inner.acquire().map_err(BsqlError::from)?;
116 let result = guard
117 .query(sql, sql_hash, params)
118 .map_err(BsqlError::from_driver_query)?;
119 Ok(OwnedResult::without_arena(result))
120 }
121
122 #[inline]
123 fn query_raw_readonly(
124 &self,
125 sql: &str,
126 sql_hash: u64,
127 params: &[&(dyn Encode + Sync)],
128 ) -> BsqlResult<OwnedResult> {
129 let pool = self.read_pool.as_ref().unwrap_or(&self.inner);
130 let mut guard = pool.acquire().map_err(BsqlError::from)?;
131 let result = guard
132 .query(sql, sql_hash, params)
133 .map_err(BsqlError::from_driver_query)?;
134 Ok(OwnedResult::without_arena(result))
135 }
136
137 #[inline]
138 fn execute_raw(
139 &self,
140 sql: &str,
141 sql_hash: u64,
142 params: &[&(dyn Encode + Sync)],
143 ) -> BsqlResult<u64> {
144 let mut guard = self.inner.acquire().map_err(BsqlError::from)?;
145 guard
146 .execute(sql, sql_hash, params)
147 .map_err(BsqlError::from_driver_query)
148 }
149}
150
151impl Executor for PoolConnection {
152 #[inline]
153 fn query_raw(
154 &self,
155 sql: &str,
156 sql_hash: u64,
157 params: &[&(dyn Encode + Sync)],
158 ) -> BsqlResult<OwnedResult> {
159 let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
160 let result = guard
161 .query(sql, sql_hash, params)
162 .map_err(BsqlError::from_driver_query)?;
163 Ok(OwnedResult::without_arena(result))
164 }
165
166 #[inline]
167 fn query_raw_readonly(
168 &self,
169 sql: &str,
170 sql_hash: u64,
171 params: &[&(dyn Encode + Sync)],
172 ) -> BsqlResult<OwnedResult> {
173 self.query_raw(sql, sql_hash, params)
174 }
175
176 #[inline]
177 fn execute_raw(
178 &self,
179 sql: &str,
180 sql_hash: u64,
181 params: &[&(dyn Encode + Sync)],
182 ) -> BsqlResult<u64> {
183 let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
184 guard
185 .execute(sql, sql_hash, params)
186 .map_err(BsqlError::from_driver_query)
187 }
188}
189
190impl Executor for Transaction {
191 fn query_raw(
192 &self,
193 sql: &str,
194 sql_hash: u64,
195 params: &[&(dyn Encode + Sync)],
196 ) -> BsqlResult<OwnedResult> {
197 self.query_inner(sql, sql_hash, params)
198 }
199
200 #[inline]
201 fn query_raw_readonly(
202 &self,
203 sql: &str,
204 sql_hash: u64,
205 params: &[&(dyn Encode + Sync)],
206 ) -> BsqlResult<OwnedResult> {
207 self.query_raw(sql, sql_hash, params)
208 }
209
210 #[inline]
211 fn execute_raw(
212 &self,
213 sql: &str,
214 sql_hash: u64,
215 params: &[&(dyn Encode + Sync)],
216 ) -> BsqlResult<u64> {
217 self.execute_inner(sql, sql_hash, params)
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use bsql_driver_postgres::arena::{acquire_arena, release_arena};
225 use bsql_driver_postgres::{ColumnDesc, QueryResult};
226 use std::sync::Arc;
227
228 fn make_owned_result(num_rows: usize, num_cols: usize) -> OwnedResult {
232 let arena = acquire_arena();
233 let cols: Arc<[ColumnDesc]> = (0..num_cols)
234 .map(|i| ColumnDesc {
235 name: format!("c{i}").into(),
236 type_oid: 23, type_size: 4,
238 table_oid: 0,
239 column_id: 0,
240 })
241 .collect::<Vec<_>>()
242 .into();
243
244 let col_offsets: Vec<(usize, i32)> = vec![(0, -1); num_rows * num_cols]; let result = QueryResult::from_parts(col_offsets, num_cols, cols, 0);
246 OwnedResult { result, arena }
247 }
248
249 #[test]
252 fn owned_result_new_zero_rows() {
253 let owned = make_owned_result(0, 2);
254 assert_eq!(owned.len(), 0);
255 assert!(owned.is_empty());
256 }
257
258 #[test]
259 fn owned_result_new_single_row() {
260 let owned = make_owned_result(1, 3);
261 assert_eq!(owned.len(), 1);
262 assert!(!owned.is_empty());
263 }
264
265 #[test]
266 fn owned_result_new_multiple_rows() {
267 let owned = make_owned_result(5, 2);
268 assert_eq!(owned.len(), 5);
269 assert!(!owned.is_empty());
270 }
271
272 #[test]
275 fn owned_result_row_access() {
276 let owned = make_owned_result(3, 2);
277 let _r0 = owned.row(0);
279 let _r1 = owned.row(1);
280 let _r2 = owned.row(2);
281 }
282
283 #[test]
284 #[should_panic]
285 fn owned_result_row_out_of_bounds_panics() {
286 let owned = make_owned_result(2, 1);
287 let _r = owned.row(2); }
289
290 #[test]
293 fn owned_result_iter_count() {
294 let owned = make_owned_result(4, 2);
295 let count = owned.iter().count();
296 assert_eq!(count, 4);
297 }
298
299 #[test]
300 fn owned_result_iter_empty() {
301 let owned = make_owned_result(0, 2);
302 let count = owned.iter().count();
303 assert_eq!(count, 0);
304 }
305
306 #[test]
309 fn owned_result_drop_releases_arena() {
310 let owned = make_owned_result(1, 1);
313 drop(owned);
314 let arena = acquire_arena();
316 release_arena(arena);
317 }
318
319 #[test]
322 fn owned_result_zero_columns() {
323 let arena = acquire_arena();
325 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
326 let result = QueryResult::from_parts(vec![], 0, cols, 42);
327 let owned = OwnedResult { result, arena };
328 assert_eq!(owned.len(), 0);
329 assert!(owned.is_empty());
330 assert_eq!(owned.result.affected_rows(), 42);
331 }
332
333 #[test]
336 fn owned_result_without_arena_len_zero() {
337 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
338 let result = QueryResult::from_parts(vec![], 0, cols, 0);
339 let owned = OwnedResult::without_arena(result);
340 assert_eq!(owned.len(), 0);
341 }
342
343 #[test]
344 fn owned_result_without_arena_is_empty() {
345 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
346 let result = QueryResult::from_parts(vec![], 0, cols, 0);
347 let owned = OwnedResult::without_arena(result);
348 assert!(owned.is_empty());
349 }
350
351 #[test]
352 fn owned_result_without_arena_with_rows() {
353 let cols: Arc<[ColumnDesc]> = vec![ColumnDesc {
354 name: "c0".into(),
355 type_oid: 23,
356 type_size: 4,
357 table_oid: 0,
358 column_id: 0,
359 }]
360 .into();
361 let col_offsets = vec![(0, -1); 3]; let result = QueryResult::from_parts(col_offsets, 1, cols, 0);
363 let owned = OwnedResult::without_arena(result);
364 assert_eq!(owned.len(), 3);
365 assert!(!owned.is_empty());
366 }
367
368 #[test]
371 fn owned_result_debug_format() {
372 let owned = make_owned_result(5, 2);
373 let dbg = format!("{owned:?}");
374 assert!(
375 dbg.contains("OwnedResult"),
376 "Debug should contain struct name: {dbg}"
377 );
378 assert!(dbg.contains("5"), "Debug should contain row count: {dbg}");
379 }
380
381 #[test]
384 fn owned_result_without_arena_drop_does_not_panic() {
385 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
386 let result = QueryResult::from_parts(vec![], 0, cols, 0);
387 let owned = OwnedResult::without_arena(result);
388 drop(owned); }
390}