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 {
87 fn query_raw(
89 &self,
90 sql: &str,
91 sql_hash: u64,
92 params: &[&(dyn Encode + Sync)],
93 ) -> BsqlResult<OwnedResult>;
94
95 fn query_raw_readonly(
97 &self,
98 sql: &str,
99 sql_hash: u64,
100 params: &[&(dyn Encode + Sync)],
101 ) -> BsqlResult<OwnedResult>;
102
103 fn execute_raw(
105 &self,
106 sql: &str,
107 sql_hash: u64,
108 params: &[&(dyn Encode + Sync)],
109 ) -> BsqlResult<u64>;
110}
111
112impl Executor for Pool {
113 #[inline]
114 fn query_raw(
115 &self,
116 sql: &str,
117 sql_hash: u64,
118 params: &[&(dyn Encode + Sync)],
119 ) -> BsqlResult<OwnedResult> {
120 let mut guard = self.inner.acquire().map_err(BsqlError::from)?;
121 let result = guard
122 .query(sql, sql_hash, params)
123 .map_err(BsqlError::from_driver_query)?;
124 Ok(OwnedResult::without_arena(result))
125 }
126
127 #[inline]
128 fn query_raw_readonly(
129 &self,
130 sql: &str,
131 sql_hash: u64,
132 params: &[&(dyn Encode + Sync)],
133 ) -> BsqlResult<OwnedResult> {
134 let pool = self.read_pool.as_ref().unwrap_or(&self.inner);
135 let mut guard = pool.acquire().map_err(BsqlError::from)?;
136 let result = guard
137 .query(sql, sql_hash, params)
138 .map_err(BsqlError::from_driver_query)?;
139 Ok(OwnedResult::without_arena(result))
140 }
141
142 #[inline]
143 fn execute_raw(
144 &self,
145 sql: &str,
146 sql_hash: u64,
147 params: &[&(dyn Encode + Sync)],
148 ) -> BsqlResult<u64> {
149 let mut guard = self.inner.acquire().map_err(BsqlError::from)?;
150 guard
151 .execute(sql, sql_hash, params)
152 .map_err(BsqlError::from_driver_query)
153 }
154}
155
156impl Executor for PoolConnection {
157 #[inline]
158 fn query_raw(
159 &self,
160 sql: &str,
161 sql_hash: u64,
162 params: &[&(dyn Encode + Sync)],
163 ) -> BsqlResult<OwnedResult> {
164 let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
165 let result = guard
166 .query(sql, sql_hash, params)
167 .map_err(BsqlError::from_driver_query)?;
168 Ok(OwnedResult::without_arena(result))
169 }
170
171 #[inline]
172 fn query_raw_readonly(
173 &self,
174 sql: &str,
175 sql_hash: u64,
176 params: &[&(dyn Encode + Sync)],
177 ) -> BsqlResult<OwnedResult> {
178 self.query_raw(sql, sql_hash, params)
179 }
180
181 #[inline]
182 fn execute_raw(
183 &self,
184 sql: &str,
185 sql_hash: u64,
186 params: &[&(dyn Encode + Sync)],
187 ) -> BsqlResult<u64> {
188 let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
189 guard
190 .execute(sql, sql_hash, params)
191 .map_err(BsqlError::from_driver_query)
192 }
193}
194
195impl Executor for Transaction {
196 fn query_raw(
197 &self,
198 sql: &str,
199 sql_hash: u64,
200 params: &[&(dyn Encode + Sync)],
201 ) -> BsqlResult<OwnedResult> {
202 self.query_inner(sql, sql_hash, params)
203 }
204
205 #[inline]
206 fn query_raw_readonly(
207 &self,
208 sql: &str,
209 sql_hash: u64,
210 params: &[&(dyn Encode + Sync)],
211 ) -> BsqlResult<OwnedResult> {
212 self.query_raw(sql, sql_hash, params)
213 }
214
215 #[inline]
216 fn execute_raw(
217 &self,
218 sql: &str,
219 sql_hash: u64,
220 params: &[&(dyn Encode + Sync)],
221 ) -> BsqlResult<u64> {
222 self.execute_inner(sql, sql_hash, params)
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use bsql_driver_postgres::arena::{acquire_arena, release_arena};
230 use bsql_driver_postgres::{ColumnDesc, QueryResult};
231 use std::sync::Arc;
232
233 fn make_owned_result(num_rows: usize, num_cols: usize) -> OwnedResult {
237 let arena = acquire_arena();
238 let cols: Arc<[ColumnDesc]> = (0..num_cols)
239 .map(|i| ColumnDesc {
240 name: format!("c{i}").into(),
241 type_oid: 23, type_size: 4,
243 table_oid: 0,
244 column_id: 0,
245 })
246 .collect::<Vec<_>>()
247 .into();
248
249 let col_offsets: Vec<(usize, i32)> = vec![(0, -1); num_rows * num_cols]; let result = QueryResult::from_parts(col_offsets, num_cols, cols, 0);
251 OwnedResult { result, arena }
252 }
253
254 #[test]
257 fn owned_result_new_zero_rows() {
258 let owned = make_owned_result(0, 2);
259 assert_eq!(owned.len(), 0);
260 assert!(owned.is_empty());
261 }
262
263 #[test]
264 fn owned_result_new_single_row() {
265 let owned = make_owned_result(1, 3);
266 assert_eq!(owned.len(), 1);
267 assert!(!owned.is_empty());
268 }
269
270 #[test]
271 fn owned_result_new_multiple_rows() {
272 let owned = make_owned_result(5, 2);
273 assert_eq!(owned.len(), 5);
274 assert!(!owned.is_empty());
275 }
276
277 #[test]
280 fn owned_result_row_access() {
281 let owned = make_owned_result(3, 2);
282 let _r0 = owned.row(0);
284 let _r1 = owned.row(1);
285 let _r2 = owned.row(2);
286 }
287
288 #[test]
289 #[should_panic]
290 fn owned_result_row_out_of_bounds_panics() {
291 let owned = make_owned_result(2, 1);
292 let _r = owned.row(2); }
294
295 #[test]
298 fn owned_result_iter_count() {
299 let owned = make_owned_result(4, 2);
300 let count = owned.iter().count();
301 assert_eq!(count, 4);
302 }
303
304 #[test]
305 fn owned_result_iter_empty() {
306 let owned = make_owned_result(0, 2);
307 let count = owned.iter().count();
308 assert_eq!(count, 0);
309 }
310
311 #[test]
314 fn owned_result_drop_releases_arena() {
315 let owned = make_owned_result(1, 1);
318 drop(owned);
319 let arena = acquire_arena();
321 release_arena(arena);
322 }
323
324 #[test]
327 fn owned_result_zero_columns() {
328 let arena = acquire_arena();
330 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
331 let result = QueryResult::from_parts(vec![], 0, cols, 42);
332 let owned = OwnedResult { result, arena };
333 assert_eq!(owned.len(), 0);
334 assert!(owned.is_empty());
335 assert_eq!(owned.result.affected_rows(), 42);
336 }
337
338 #[test]
341 fn owned_result_without_arena_len_zero() {
342 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
343 let result = QueryResult::from_parts(vec![], 0, cols, 0);
344 let owned = OwnedResult::without_arena(result);
345 assert_eq!(owned.len(), 0);
346 }
347
348 #[test]
349 fn owned_result_without_arena_is_empty() {
350 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
351 let result = QueryResult::from_parts(vec![], 0, cols, 0);
352 let owned = OwnedResult::without_arena(result);
353 assert!(owned.is_empty());
354 }
355
356 #[test]
357 fn owned_result_without_arena_with_rows() {
358 let cols: Arc<[ColumnDesc]> = vec![ColumnDesc {
359 name: "c0".into(),
360 type_oid: 23,
361 type_size: 4,
362 table_oid: 0,
363 column_id: 0,
364 }]
365 .into();
366 let col_offsets = vec![(0, -1); 3]; let result = QueryResult::from_parts(col_offsets, 1, cols, 0);
368 let owned = OwnedResult::without_arena(result);
369 assert_eq!(owned.len(), 3);
370 assert!(!owned.is_empty());
371 }
372
373 #[test]
376 fn owned_result_debug_format() {
377 let owned = make_owned_result(5, 2);
378 let dbg = format!("{owned:?}");
379 assert!(
380 dbg.contains("OwnedResult"),
381 "Debug should contain struct name: {dbg}"
382 );
383 assert!(dbg.contains("5"), "Debug should contain row count: {dbg}");
384 }
385
386 #[test]
389 fn owned_result_without_arena_drop_does_not_panic() {
390 let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
391 let result = QueryResult::from_parts(vec![], 0, cols, 0);
392 let owned = OwnedResult::without_arena(result);
393 drop(owned); }
395}