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/// A query ready to be executed by an [`Executor`].
8///
9/// Represents either raw SQL (`Raw`) or a backend prepared statement
10/// (`Prepared`) carrying driver-specific caching / parsing state.
11#[derive(Debug)]
12pub enum Query<D: Driver> {
13    /// Unprepared SQL text.
14    Raw(String),
15    /// Driver prepared handle.
16    Prepared(D::Prepared),
17}
18
19impl<D: Driver> Query<D> {
20    pub fn is_prepared(&self) -> bool {
21        matches!(self, Query::Prepared(..))
22    }
23    /// Remove all the previously bound values
24    pub fn clear_bindings(&mut self) -> Result<&mut Self> {
25        let Self::Prepared(prepared) = self else {
26            return Err(Error::msg("Cannot clear bindings of a raw query"));
27        };
28        prepared.clear_bindings()?;
29        Ok(self)
30    }
31    /// Append a parameter value.
32    pub fn bind(&mut self, value: impl AsValue) -> Result<&mut Self> {
33        let Self::Prepared(prepared) = self else {
34            return Err(Error::msg("Cannot bind a raw query"));
35        };
36        prepared.bind(value)?;
37        Ok(self)
38    }
39    /// Bind a value at a specific index.
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    /// Total number of rows impacted.
129    pub rows_affected: u64,
130    /// Backend-specific last inserted / affected identifier when available.
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/// A result row with its corresponding column labels.
140#[derive(Debug, Clone)]
141pub struct RowLabeled {
142    /// Column names.
143    pub labels: RowNames,
144    /// Data values (aligned by index 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    pub fn names(&self) -> &[String] {
156        &self.labels
157    }
158    pub fn values(&self) -> &[Value] {
159        &self.values
160    }
161    pub fn get_column(&self, name: &str) -> Option<&Value> {
162        self.labels
163            .iter()
164            .position(|v| v == name)
165            .map(|i| &self.values()[i])
166    }
167}
168
169/// Heterogeneous items emitted by `Executor::run` combining rows and modify results.
170#[derive(Debug)]
171pub enum QueryResult {
172    /// A labeled row.
173    Row(RowLabeled),
174    /// A modify effect aggregation.
175    Affected(RowsAffected),
176}
177
178impl Extend<RowsAffected> for RowsAffected {
179    fn extend<T: IntoIterator<Item = RowsAffected>>(&mut self, iter: T) {
180        for elem in iter {
181            self.rows_affected += elem.rows_affected;
182            if elem.last_affected_id.is_some() {
183                self.last_affected_id = elem.last_affected_id;
184            }
185        }
186    }
187}
188
189impl From<RowLabeled> for Row {
190    fn from(value: RowLabeled) -> Self {
191        value.values
192    }
193}
194
195impl<'a> From<&'a RowLabeled> for &'a Row {
196    fn from(value: &'a RowLabeled) -> Self {
197        &value.values
198    }
199}
200
201impl From<RowLabeled> for QueryResult {
202    fn from(value: RowLabeled) -> Self {
203        QueryResult::Row(value)
204    }
205}
206
207impl From<RowsAffected> for QueryResult {
208    fn from(value: RowsAffected) -> Self {
209        QueryResult::Affected(value)
210    }
211}