tank_core/
query.rs

1use crate::{AsValue, Driver, Error, Prepared, Result, Value, truncate_long};
2use std::{
3    fmt::{self, Display},
4    sync::Arc,
5};
6
7/// Executable query: raw SQL or prepared statement.
8#[derive(Debug)]
9pub enum Query<D: Driver> {
10    /// Unprepared SQL text.
11    Raw(String),
12    /// Driver prepared statement.
13    Prepared(D::Prepared),
14}
15
16impl<D: Driver> Query<D> {
17    /// Returns `true` when this `Query` contains a backend-prepared statement.
18    pub fn is_prepared(&self) -> bool {
19        matches!(self, Query::Prepared(..))
20    }
21    /// Clear all bound values.
22    pub fn clear_bindings(&mut self) -> Result<&mut Self> {
23        if let Self::Prepared(prepared) = self {
24            prepared.clear_bindings()?;
25        };
26        Ok(self)
27    }
28    /// Append a bound value.
29    /// It results in an error if the query is not prepared.
30    pub fn bind(&mut self, value: impl AsValue) -> Result<&mut Self> {
31        let Self::Prepared(prepared) = self else {
32            return Err(Error::msg("Cannot bind a raw query"));
33        };
34        prepared.bind(value)?;
35        Ok(self)
36    }
37    /// Bind a value at a specific index.
38    /// It results in an error if the query is not prepared.
39    pub fn bind_index(&mut self, value: impl AsValue, index: u64) -> Result<&mut Self> {
40        let Self::Prepared(prepared) = self else {
41            return Err(Error::msg("Cannot bind index of a raw query"));
42        };
43        prepared.bind_index(value, index)?;
44        Ok(self)
45    }
46}
47
48pub trait AsQuery<D: Driver> {
49    type Output: AsMut<Query<D>> + Send;
50    fn as_query(self) -> Self::Output;
51}
52
53impl<D: Driver> AsQuery<D> for Query<D> {
54    type Output = Query<D>;
55    fn as_query(self) -> Self::Output {
56        self
57    }
58}
59
60impl<'q, D: Driver + 'q> AsQuery<D> for &'q mut Query<D> {
61    type Output = &'q mut Query<D>;
62    fn as_query(self) -> Self::Output {
63        self
64    }
65}
66
67impl<D: Driver> AsQuery<D> for String {
68    type Output = Query<D>;
69    fn as_query(self) -> Self::Output {
70        Query::Raw(self)
71    }
72}
73
74impl<D: Driver> AsQuery<D> for &str {
75    type Output = Query<D>;
76    fn as_query(self) -> Self::Output {
77        Query::Raw(self.to_owned())
78    }
79}
80
81impl<D: Driver> Default for Query<D> {
82    fn default() -> Self {
83        Self::Raw(Default::default())
84    }
85}
86
87impl<D: Driver> From<&str> for Query<D> {
88    fn from(value: &str) -> Self {
89        Query::Raw(value.into())
90    }
91}
92
93impl<D: Driver> From<String> for Query<D> {
94    fn from(value: String) -> Self {
95        Query::Raw(value.into())
96    }
97}
98
99impl<D, P> From<P> for Query<D>
100where
101    D: Driver<Prepared = P>,
102    P: Prepared,
103{
104    fn from(value: P) -> Self {
105        Query::Prepared(value)
106    }
107}
108
109impl<D: Driver> Display for Query<D> {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            Query::Raw(query) => write!(f, "{}", truncate_long!(query)),
113            Query::Prepared(query) => query.fmt(f),
114        }
115    }
116}
117
118impl<D: Driver> AsMut<Query<D>> for Query<D> {
119    fn as_mut(&mut self) -> &mut Query<D> {
120        self
121    }
122}
123
124/// Metadata about modifying operations (INSERT/UPDATE/DELETE).
125#[derive(Default, Debug, Clone, Copy)]
126pub struct RowsAffected {
127    /// Optional count of affected rows reported by the backend.
128    /// `None` means the backend did not provide a count.
129    pub rows_affected: Option<u64>,
130    /// Optional last affected identifier (driver-dependent meaning).
131    /// For many drivers this is the last inserted id.
132    pub last_affected_id: Option<i64>,
133}
134
135/// Shared reference-counted column name list.
136pub type RowNames = Arc<[String]>;
137/// Owned row value slice matching `RowNames` length.
138pub type Row = Box<[Value]>;
139
140/// Row with column labels.
141#[derive(Debug, Clone)]
142pub struct RowLabeled {
143    /// Column names
144    pub labels: RowNames,
145    /// Values aligned with labels
146    pub values: Row,
147}
148
149impl RowLabeled {
150    pub fn new(names: RowNames, values: Row) -> Self {
151        Self {
152            labels: names,
153            values,
154        }
155    }
156    /// Returns the column labels for this row.
157    pub fn names(&self) -> &[String] {
158        &self.labels
159    }
160    /// Returns the values associated with `names()`.
161    pub fn values(&self) -> &[Value] {
162        &self.values
163    }
164    /// Look up a column value by its label name.
165    pub fn get_column(&self, name: &str) -> Option<&Value> {
166        self.labels
167            .iter()
168            .position(|v| v == name)
169            .map(|i| &self.values()[i])
170    }
171}
172
173/// Items from `Executor::run`: rows or effects.
174#[derive(Debug)]
175pub enum QueryResult {
176    /// A labeled row
177    Row(RowLabeled),
178    /// A modify effect aggregation
179    Affected(RowsAffected),
180}
181
182impl Extend<RowsAffected> for RowsAffected {
183    fn extend<T: IntoIterator<Item = RowsAffected>>(&mut self, iter: T) {
184        for elem in iter {
185            if self.rows_affected.is_some() || elem.rows_affected.is_some() {
186                self.rows_affected = Some(
187                    self.rows_affected.unwrap_or_default() + elem.rows_affected.unwrap_or_default(),
188                );
189            }
190            if elem.last_affected_id.is_some() {
191                self.last_affected_id = elem.last_affected_id;
192            }
193        }
194    }
195}
196
197impl From<RowLabeled> for Row {
198    fn from(value: RowLabeled) -> Self {
199        value.values
200    }
201}
202
203impl<'a> From<&'a RowLabeled> for &'a Row {
204    fn from(value: &'a RowLabeled) -> Self {
205        &value.values
206    }
207}
208
209impl From<RowLabeled> for QueryResult {
210    fn from(value: RowLabeled) -> Self {
211        QueryResult::Row(value)
212    }
213}
214
215impl From<RowsAffected> for QueryResult {
216    fn from(value: RowsAffected) -> Self {
217        QueryResult::Affected(value)
218    }
219}