use rudb_bind::Parameters;
use rudb_common::{Error, Result, Value};
use rudb_parse::ast::Ast;
use rudb_parse::parse_ast;
use crate::connection::single;
use crate::database::Shared;
use crate::result::QueryResult;
#[derive(Debug, Clone)]
pub struct Prepared {
shared: Shared,
sql: String,
ast: Ast,
names: Vec<String>,
}
impl Prepared {
pub(crate) fn new(shared: Shared, sql: &str) -> Result<Self> {
let ast = parse_ast(sql)?;
let names = ast.parameters().into_iter().map(str::to_string).collect();
Ok(Self { shared, sql: sql.to_string(), ast, names })
}
#[must_use]
pub fn sql(&self) -> &str {
&self.sql
}
#[must_use]
pub fn parameters(&self) -> &[String] {
&self.names
}
pub fn execute(&self, values: &[Value]) -> Result<QueryResult> {
self.run(Parameters::positional(values.to_vec()))
}
pub fn execute_named(&self, values: &[(&str, Value)]) -> Result<QueryResult> {
let mut parameters = Parameters::new();
for (name, value) in values {
parameters.set(*name, value.clone());
}
self.run(parameters)
}
pub fn value(&self, values: &[Value]) -> Result<Value> {
single(&self.execute(values)?)
}
fn run(&self, parameters: Parameters) -> Result<QueryResult> {
self.check(¶meters)?;
self.shared.execute_ast(&self.ast, ¶meters, &self.shared.token())
}
fn check(&self, parameters: &Parameters) -> Result<()> {
let missing: Vec<&str> = self
.names
.iter()
.filter(|name| parameters.get(name).is_none())
.map(String::as_str)
.collect();
if !missing.is_empty() {
return Err(Error::invalid_input(format!(
"Values were not provided for the following prepared statement parameters: {}",
missing.join(", ")
)));
}
let excess: Vec<&str> = parameters
.names()
.filter(|name| !self.names.iter().any(|held| held.eq_ignore_ascii_case(name)))
.collect();
if !excess.is_empty() {
return Err(Error::invalid_input(format!(
"Parameter argument/count mismatch, identifiers of the excess parameters: {}",
excess.join(", ")
)));
}
Ok(())
}
}