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