use rustc_hash::FxHashMap;
use crate::common::CompactArc;
use crate::core::{Result, Row, Value};
pub trait QueryResult: Send {
fn columns(&self) -> &[String];
fn columns_arc(&self) -> Option<CompactArc<Vec<String>>> {
None
}
fn next(&mut self) -> bool;
fn scan(&self, dest: &mut [Value]) -> Result<()>;
fn row(&self) -> &Row;
fn take_row(&mut self) -> Row {
self.row().clone()
}
fn close(&mut self) -> Result<()> {
Ok(())
}
fn rows_affected(&self) -> i64 {
0
}
fn last_insert_id(&self) -> i64 {
0
}
fn try_into_arc_rows(&mut self) -> Option<CompactArc<Vec<Row>>> {
None
}
fn estimated_count(&self) -> Option<usize> {
None
}
fn last_error(&mut self) -> Option<crate::core::Error> {
None
}
fn with_aliases(self: Box<Self>, aliases: FxHashMap<String, String>) -> Box<dyn QueryResult>;
}
pub struct MemoryResult {
columns: Vec<String>,
rows: Vec<Row>,
current_index: Option<usize>,
rows_affected: i64,
last_insert_id: i64,
closed: bool,
}
impl MemoryResult {
pub fn new(columns: Vec<String>) -> Self {
Self {
columns,
rows: Vec::new(),
current_index: None,
rows_affected: 0,
last_insert_id: 0,
closed: false,
}
}
pub fn with_rows(columns: Vec<String>, rows: Vec<Row>) -> Self {
Self {
columns,
rows,
current_index: None,
rows_affected: 0,
last_insert_id: 0,
closed: false,
}
}
pub fn for_modification(rows_affected: i64, last_insert_id: i64) -> Self {
Self {
columns: Vec::new(),
rows: Vec::new(),
current_index: None,
rows_affected,
last_insert_id,
closed: false,
}
}
pub fn add_row(&mut self, row: Row) {
self.rows.push(row);
}
pub fn set_rows_affected(&mut self, count: i64) {
self.rows_affected = count;
}
pub fn set_last_insert_id(&mut self, id: i64) {
self.last_insert_id = id;
}
}
impl QueryResult for MemoryResult {
fn columns(&self) -> &[String] {
&self.columns
}
fn estimated_count(&self) -> Option<usize> {
Some(self.rows.len())
}
fn next(&mut self) -> bool {
if self.closed {
return false;
}
let next_index = match self.current_index {
None => 0,
Some(i) => i + 1,
};
if next_index < self.rows.len() {
self.current_index = Some(next_index);
true
} else {
false
}
}
fn scan(&self, dest: &mut [Value]) -> Result<()> {
let row = self.row();
if dest.len() != row.len() {
return Err(crate::core::Error::internal(format!(
"scan destination has {} values but row has {} columns",
dest.len(),
row.len()
)));
}
for (i, value) in row.iter().enumerate() {
dest[i] = value.clone();
}
Ok(())
}
fn row(&self) -> &Row {
match self.current_index {
Some(i) if i < self.rows.len() => &self.rows[i],
_ => panic!("row() called without successful next()"),
}
}
fn take_row(&mut self) -> Row {
match self.current_index {
Some(i) if i < self.rows.len() => std::mem::take(&mut self.rows[i]),
_ => panic!("take_row() called without successful next()"),
}
}
fn close(&mut self) -> Result<()> {
self.closed = true;
Ok(())
}
fn rows_affected(&self) -> i64 {
self.rows_affected
}
fn last_insert_id(&self) -> i64 {
self.last_insert_id
}
fn with_aliases(
mut self: Box<Self>,
aliases: FxHashMap<String, String>,
) -> Box<dyn QueryResult> {
for col in &mut self.columns {
for (alias, original) in &aliases {
if col == original {
*col = alias.clone();
break;
}
}
}
self
}
}
pub struct EmptyResult {
columns: Vec<String>,
rows_affected: i64,
last_insert_id: i64,
}
impl EmptyResult {
pub fn new() -> Self {
Self {
columns: Vec::new(),
rows_affected: 0,
last_insert_id: 0,
}
}
pub fn for_modification(rows_affected: i64, last_insert_id: i64) -> Self {
Self {
columns: Vec::new(),
rows_affected,
last_insert_id,
}
}
}
impl Default for EmptyResult {
fn default() -> Self {
Self::new()
}
}
impl QueryResult for EmptyResult {
fn columns(&self) -> &[String] {
&self.columns
}
fn next(&mut self) -> bool {
false
}
fn scan(&self, _dest: &mut [Value]) -> Result<()> {
Err(crate::core::Error::internal(
"scan() called on empty result",
))
}
fn row(&self) -> &Row {
panic!("row() called on empty result")
}
fn close(&mut self) -> Result<()> {
Ok(())
}
fn rows_affected(&self) -> i64 {
self.rows_affected
}
fn last_insert_id(&self) -> i64 {
self.last_insert_id
}
fn with_aliases(self: Box<Self>, _aliases: FxHashMap<String, String>) -> Box<dyn QueryResult> {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_result_empty() {
let mut result = MemoryResult::new(vec!["id".to_string(), "name".to_string()]);
assert_eq!(result.columns(), &["id", "name"]);
assert!(!result.next());
assert_eq!(result.rows_affected(), 0);
assert_eq!(result.last_insert_id(), 0);
}
#[test]
fn test_memory_result_with_rows() {
let rows = vec![
Row::from_values(vec![Value::Integer(1), Value::text("Alice")]),
Row::from_values(vec![Value::Integer(2), Value::text("Bob")]),
];
let mut result = MemoryResult::with_rows(vec!["id".to_string(), "name".to_string()], rows);
assert!(result.next());
assert_eq!(result.row().get(0), Some(&Value::Integer(1)));
assert!(result.next());
assert_eq!(result.row().get(0), Some(&Value::Integer(2)));
assert!(!result.next());
}
#[test]
fn test_memory_result_scan() {
let rows = vec![Row::from_values(vec![
Value::Integer(42),
Value::text("test"),
])];
let mut result = MemoryResult::with_rows(vec!["id".to_string(), "name".to_string()], rows);
assert!(result.next());
let mut dest = vec![Value::null_unknown(), Value::null_unknown()];
result.scan(&mut dest).unwrap();
assert_eq!(dest[0], Value::Integer(42));
assert_eq!(dest[1], Value::text("test"));
}
#[test]
fn test_memory_result_for_modification() {
let result = MemoryResult::for_modification(5, 100);
assert_eq!(result.rows_affected(), 5);
assert_eq!(result.last_insert_id(), 100);
}
#[test]
fn test_memory_result_close() {
let rows = vec![Row::from_values(vec![Value::Integer(1)])];
let mut result = MemoryResult::with_rows(vec!["id".to_string()], rows);
assert!(result.next());
assert!(result.close().is_ok());
assert!(!result.next()); }
#[test]
fn test_memory_result_with_aliases() {
let rows = vec![Row::from_values(vec![Value::Integer(1)])];
let result = Box::new(MemoryResult::with_rows(vec!["user_id".to_string()], rows));
let mut aliases = FxHashMap::default();
aliases.insert("id".to_string(), "user_id".to_string());
let aliased = result.with_aliases(aliases);
assert_eq!(aliased.columns(), &["id"]);
}
#[test]
fn test_empty_result() {
let mut result = EmptyResult::new();
assert!(result.columns().is_empty());
assert!(!result.next());
assert_eq!(result.rows_affected(), 0);
assert!(result.close().is_ok());
}
#[test]
fn test_empty_result_for_modification() {
let result = EmptyResult::for_modification(10, 0);
assert_eq!(result.rows_affected(), 10);
assert_eq!(result.last_insert_id(), 0);
}
}