use std::collections::HashMap;
use std::collections::VecDeque;
use super::CachedStatement;
use super::StatementOptions;
use crate::error::Error;
pub(crate) struct StatementCache {
max_size: usize,
cached_statements: Vec<Option<CachedStatement>>,
slots_by_name: HashMap<String, usize>,
empty_slots: VecDeque<usize>,
free_used_slots: VecDeque<usize>,
cursors_to_close: Vec<u16>,
}
impl StatementCache {
fn close_cursor(&mut self, info: &CachedStatement) {
if info.has_cursor() && !info.is_nested() {
self.cursors_to_close.push(info.cursor_id());
}
}
fn get_available_slot(&mut self) -> usize {
if let Some(slot_num) = self.empty_slots.pop_front() {
slot_num
} else {
let slot_num = self.free_used_slots.pop_front().unwrap();
let orig_info =
self.cached_statements[slot_num - 1].take().unwrap();
self.slots_by_name.remove(orig_info.sql());
self.close_cursor(&orig_info);
slot_num
}
}
fn get_statement_from_entry(
&mut self,
slot_num: usize,
cache_statement: bool,
options: &StatementOptions,
) -> CachedStatement {
let cached_info =
self.cached_statements[slot_num - 1].as_ref().unwrap();
let mut info = cached_info.clone_with_options(options);
if let Some(index) =
self.free_used_slots.iter().position(|x| *x == slot_num)
{
self.free_used_slots.remove(index);
if !cache_statement {
self.slots_by_name.remove(cached_info.sql());
self.cached_statements[slot_num - 1] = None;
self.empty_slots.push_back(slot_num);
info.set_cache_slot_num(0);
}
} else {
info.set_cache_slot_num(0);
info.clear_cursor();
}
info
}
fn get_statement_no_entry(
&mut self,
sql: &str,
cache_statement: bool,
options: &StatementOptions,
) -> Result<CachedStatement, Error> {
let mut statement = CachedStatement::new(sql, options)?;
if cache_statement && !statement.is_ddl() && self.has_space() {
let slot_num = self.get_available_slot();
statement.set_cache_slot_num(slot_num);
self.cached_statements[slot_num - 1] = Some(statement.clone());
self.slots_by_name.insert(sql.to_string(), slot_num);
}
Ok(statement)
}
fn has_space(&self) -> bool {
!self.empty_slots.is_empty() || !self.free_used_slots.is_empty()
}
pub(crate) fn has_cursors_to_close(&self) -> bool {
!self.cursors_to_close.is_empty()
}
pub(crate) fn get_statement(
&mut self,
sql: &str,
cache_statement: bool,
options: &StatementOptions,
) -> Result<CachedStatement, Error> {
match self.slots_by_name.get(sql) {
Some(slot_num) => Ok(self.get_statement_from_entry(
*slot_num,
cache_statement,
options,
)),
None => self.get_statement_no_entry(sql, cache_statement, options),
}
}
pub(crate) fn max_size(&self) -> usize {
self.max_size
}
pub(crate) fn new(max_size: usize) -> Self {
Self {
max_size,
cached_statements: vec![None; max_size],
slots_by_name: HashMap::new(),
empty_slots: (1..=max_size).collect(),
free_used_slots: VecDeque::with_capacity(max_size),
cursors_to_close: Vec::new(),
}
}
pub(crate) fn resize(&mut self, new_size: usize) {
assert!(self.slots_by_name.is_empty());
self.max_size = new_size;
self.cached_statements = vec![None; new_size];
self.empty_slots = (1..=new_size).collect();
self.free_used_slots = VecDeque::with_capacity(new_size);
}
pub(crate) fn return_statement(&mut self, info: &CachedStatement) {
if info.is_cached() {
let slot_num = info.cache_slot_num();
self.cached_statements[slot_num - 1] = Some(info.clone());
self.free_used_slots.push_back(slot_num);
} else {
self.close_cursor(info);
}
}
pub(crate) fn take_cursors_to_close(&mut self) -> Vec<u16> {
std::mem::take(&mut self.cursors_to_close)
}
}