use bsql_driver_postgres::arena::release_arena;
use bsql_driver_postgres::codec::Encode;
use bsql_driver_postgres::{Arena, QueryResult};
use crate::error::{BsqlError, BsqlResult};
use crate::pool::{Pool, PoolConnection};
use crate::transaction::Transaction;
pub struct OwnedResult {
pub result: QueryResult,
arena: Arena,
}
impl std::fmt::Debug for OwnedResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OwnedResult")
.field("rows", &self.result.len())
.finish()
}
}
impl OwnedResult {
pub(crate) fn without_arena(result: QueryResult) -> Self {
Self {
result,
arena: Arena::empty(),
}
}
pub fn len(&self) -> usize {
self.result.len()
}
pub fn is_empty(&self) -> bool {
self.result.is_empty()
}
pub fn row(&self, idx: usize) -> bsql_driver_postgres::Row<'_> {
self.result.row(idx, &self.arena)
}
pub fn iter(&self) -> impl Iterator<Item = bsql_driver_postgres::Row<'_>> {
self.result.rows(&self.arena)
}
}
impl Drop for OwnedResult {
fn drop(&mut self) {
let arena = std::mem::take(&mut self.arena);
release_arena(arena);
if let Some(buf) = self.result.take_data_buf() {
bsql_driver_postgres::release_resp_buf(buf);
}
}
}
pub trait Executor {
fn query_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult>;
fn query_raw_readonly(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult>;
fn execute_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<u64>;
}
impl Executor for Pool {
#[inline]
fn query_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult> {
let mut guard = self.inner.acquire().map_err(BsqlError::from)?;
let result = guard
.query(sql, sql_hash, params)
.map_err(BsqlError::from_driver_query)?;
Ok(OwnedResult::without_arena(result))
}
#[inline]
fn query_raw_readonly(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult> {
let pool = self.read_pool.as_ref().unwrap_or(&self.inner);
let mut guard = pool.acquire().map_err(BsqlError::from)?;
let result = guard
.query(sql, sql_hash, params)
.map_err(BsqlError::from_driver_query)?;
Ok(OwnedResult::without_arena(result))
}
#[inline]
fn execute_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<u64> {
let mut guard = self.inner.acquire().map_err(BsqlError::from)?;
guard
.execute(sql, sql_hash, params)
.map_err(BsqlError::from_driver_query)
}
}
impl Executor for PoolConnection {
#[inline]
fn query_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult> {
let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
let result = guard
.query(sql, sql_hash, params)
.map_err(BsqlError::from_driver_query)?;
Ok(OwnedResult::without_arena(result))
}
#[inline]
fn query_raw_readonly(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult> {
self.query_raw(sql, sql_hash, params)
}
#[inline]
fn execute_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<u64> {
let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
guard
.execute(sql, sql_hash, params)
.map_err(BsqlError::from_driver_query)
}
}
impl Executor for Transaction {
fn query_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult> {
self.query_inner(sql, sql_hash, params)
}
#[inline]
fn query_raw_readonly(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<OwnedResult> {
self.query_raw(sql, sql_hash, params)
}
#[inline]
fn execute_raw(
&self,
sql: &str,
sql_hash: u64,
params: &[&(dyn Encode + Sync)],
) -> BsqlResult<u64> {
self.execute_inner(sql, sql_hash, params)
}
}
#[cfg(test)]
mod tests {
use super::*;
use bsql_driver_postgres::arena::{acquire_arena, release_arena};
use bsql_driver_postgres::{ColumnDesc, QueryResult};
use std::sync::Arc;
fn make_owned_result(num_rows: usize, num_cols: usize) -> OwnedResult {
let arena = acquire_arena();
let cols: Arc<[ColumnDesc]> = (0..num_cols)
.map(|i| ColumnDesc {
name: format!("c{i}").into(),
type_oid: 23, type_size: 4,
table_oid: 0,
column_id: 0,
})
.collect::<Vec<_>>()
.into();
let col_offsets: Vec<(usize, i32)> = vec![(0, -1); num_rows * num_cols]; let result = QueryResult::from_parts(col_offsets, num_cols, cols, 0);
OwnedResult { result, arena }
}
#[test]
fn owned_result_new_zero_rows() {
let owned = make_owned_result(0, 2);
assert_eq!(owned.len(), 0);
assert!(owned.is_empty());
}
#[test]
fn owned_result_new_single_row() {
let owned = make_owned_result(1, 3);
assert_eq!(owned.len(), 1);
assert!(!owned.is_empty());
}
#[test]
fn owned_result_new_multiple_rows() {
let owned = make_owned_result(5, 2);
assert_eq!(owned.len(), 5);
assert!(!owned.is_empty());
}
#[test]
fn owned_result_row_access() {
let owned = make_owned_result(3, 2);
let _r0 = owned.row(0);
let _r1 = owned.row(1);
let _r2 = owned.row(2);
}
#[test]
#[should_panic]
fn owned_result_row_out_of_bounds_panics() {
let owned = make_owned_result(2, 1);
let _r = owned.row(2); }
#[test]
fn owned_result_iter_count() {
let owned = make_owned_result(4, 2);
let count = owned.iter().count();
assert_eq!(count, 4);
}
#[test]
fn owned_result_iter_empty() {
let owned = make_owned_result(0, 2);
let count = owned.iter().count();
assert_eq!(count, 0);
}
#[test]
fn owned_result_drop_releases_arena() {
let owned = make_owned_result(1, 1);
drop(owned);
let arena = acquire_arena();
release_arena(arena);
}
#[test]
fn owned_result_zero_columns() {
let arena = acquire_arena();
let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
let result = QueryResult::from_parts(vec![], 0, cols, 42);
let owned = OwnedResult { result, arena };
assert_eq!(owned.len(), 0);
assert!(owned.is_empty());
assert_eq!(owned.result.affected_rows(), 42);
}
#[test]
fn owned_result_without_arena_len_zero() {
let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
let result = QueryResult::from_parts(vec![], 0, cols, 0);
let owned = OwnedResult::without_arena(result);
assert_eq!(owned.len(), 0);
}
#[test]
fn owned_result_without_arena_is_empty() {
let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
let result = QueryResult::from_parts(vec![], 0, cols, 0);
let owned = OwnedResult::without_arena(result);
assert!(owned.is_empty());
}
#[test]
fn owned_result_without_arena_with_rows() {
let cols: Arc<[ColumnDesc]> = vec![ColumnDesc {
name: "c0".into(),
type_oid: 23,
type_size: 4,
table_oid: 0,
column_id: 0,
}]
.into();
let col_offsets = vec![(0, -1); 3]; let result = QueryResult::from_parts(col_offsets, 1, cols, 0);
let owned = OwnedResult::without_arena(result);
assert_eq!(owned.len(), 3);
assert!(!owned.is_empty());
}
#[test]
fn owned_result_debug_format() {
let owned = make_owned_result(5, 2);
let dbg = format!("{owned:?}");
assert!(
dbg.contains("OwnedResult"),
"Debug should contain struct name: {dbg}"
);
assert!(dbg.contains("5"), "Debug should contain row count: {dbg}");
}
#[test]
fn owned_result_without_arena_drop_does_not_panic() {
let cols: Arc<[ColumnDesc]> = Arc::from(Vec::new());
let result = QueryResult::from_parts(vec![], 0, cols, 0);
let owned = OwnedResult::without_arena(result);
drop(owned); }
}