Skip to main content

sqlx_core/
sql_str.rs

1use std::borrow::{Borrow, Cow};
2use std::hash::{Hash, Hasher};
3use std::sync::Arc;
4
5/// A SQL string that is safe to execute on a database connection.
6///
7/// A "safe" SQL string is one that is unlikely to contain a [SQL injection vulnerability][injection].
8///
9/// In practice, this means a string type that is unlikely to contain dynamic data or user input.
10///
11/// `&'static str` is the only string type that satisfies the requirements of this trait
12/// (ignoring [`String::leak()`] which has niche use-cases) and so is the only string type that
13/// natively implements this trait by default.
14///
15/// For other string types, use [`AssertSqlSafe`] to assert this property.
16/// This is the only intended way to pass an owned `String` to [`query()`] and its related functions
17/// as well as [`raw_sql()`].
18///
19/// The maintainers of SQLx take no responsibility for any data leaks or loss resulting from misuse
20/// of this API.
21///
22/// ### Motivation
23/// This is designed to act as a speed bump against naively using `format!()` to add dynamic data
24/// or user input to a query, which is a classic vector for SQL injection as SQLx does not
25/// provide any sort of escaping or sanitization (which would have to be specially implemented
26/// for each database flavor/locale).
27///
28/// The recommended way to incorporate dynamic data or user input in a query is to use
29/// bind parameters, which requires the query to execute as a prepared statement.
30/// See [`query()`] for details.
31///
32/// This trait and [`AssertSqlSafe`] are intentionally analogous to
33/// [`std::panic::UnwindSafe`] and [`std::panic::AssertUnwindSafe`], respectively.
34///
35/// [injection]: https://en.wikipedia.org/wiki/SQL_injection
36/// [`query()`]: crate::query::query
37/// [`raw_sql()`]: crate::raw_sql::raw_sql
38#[diagnostic::on_unimplemented(
39    label = "dynamic SQL string",
40    message = "dynamic SQL strings should be audited for possible injections",
41    note = "prefer literal SQL strings with bind parameters or `QueryBuilder` to add dynamic data to a query.
42
43To bypass this error, manually audit for potential injection vulnerabilities and wrap with `AssertSqlSafe()`.
44For details, see the docs for `SqlSafeStr`.\n",
45    note = "this trait is only implemented for `&'static str`, not all `&str` like the compiler error may suggest"
46)]
47pub trait SqlSafeStr {
48    /// Convert `self` to a [`SqlStr`].
49    fn into_sql_str(self) -> SqlStr;
50}
51
52impl SqlSafeStr for &'static str {
53    #[inline]
54    fn into_sql_str(self) -> SqlStr {
55        SqlStr(Repr::Static(self))
56    }
57}
58
59/// Assert that a query string is safe to execute on a database connection.
60///
61/// Using this API means that **you** have made sure that the string contents do not contain a
62/// [SQL injection vulnerability][injection]. It means that, if the string was constructed
63/// dynamically, and/or from user input, you have taken care to sanitize the input yourself.
64/// SQLx does not provide any sort of sanitization; the design of SQLx prefers the use
65/// of prepared statements for dynamic input.
66///
67/// The maintainers of SQLx take no responsibility for any data leaks or loss resulting from misuse
68/// of this API. **Use at your own risk.**
69///
70/// Note that `&'static str` implements [`SqlSafeStr`] directly and so does not need to be wrapped
71/// with this type.
72///
73/// [injection]: https://en.wikipedia.org/wiki/SQL_injection
74pub struct AssertSqlSafe<T>(pub T);
75
76/// Note: copies the string.
77///
78/// It is recommended to pass one of the supported owned string types instead.
79impl SqlSafeStr for AssertSqlSafe<&str> {
80    #[inline]
81    fn into_sql_str(self) -> SqlStr {
82        SqlStr(Repr::Arced(self.0.into()))
83    }
84}
85impl SqlSafeStr for AssertSqlSafe<String> {
86    #[inline]
87    fn into_sql_str(self) -> SqlStr {
88        SqlStr(Repr::Owned(self.0))
89    }
90}
91
92impl SqlSafeStr for AssertSqlSafe<Box<str>> {
93    #[inline]
94    fn into_sql_str(self) -> SqlStr {
95        SqlStr(Repr::Boxed(self.0))
96    }
97}
98
99// Note: this is not implemented for `Rc<str>` because it would make `QueryString: !Send`.
100impl SqlSafeStr for AssertSqlSafe<Arc<str>> {
101    #[inline]
102    fn into_sql_str(self) -> SqlStr {
103        SqlStr(Repr::Arced(self.0))
104    }
105}
106
107impl SqlSafeStr for AssertSqlSafe<Arc<String>> {
108    #[inline]
109    fn into_sql_str(self) -> SqlStr {
110        SqlStr(Repr::ArcString(self.0))
111    }
112}
113
114impl SqlSafeStr for AssertSqlSafe<Cow<'static, str>> {
115    fn into_sql_str(self) -> SqlStr {
116        match self.0 {
117            Cow::Borrowed(str) => str.into_sql_str(),
118            Cow::Owned(str) => AssertSqlSafe(str).into_sql_str(),
119        }
120    }
121}
122
123/// A SQL string that is ready to execute on a database connection.
124///
125/// This is essentially `Cow<'static, str>` but which can be constructed from additional types
126/// without copying.
127///
128/// See [`SqlSafeStr`] for details.
129#[derive(Debug)]
130pub struct SqlStr(Repr);
131
132#[derive(Debug)]
133enum Repr {
134    /// We need a variant to memoize when we already have a static string, so we don't copy it.
135    Static(&'static str),
136    /// Thanks to the new niche in `String`, this doesn't increase the size beyond 3 words.
137    /// We essentially get all these variants for free.
138    Owned(String),
139    Boxed(Box<str>),
140    Arced(Arc<str>),
141    /// Allows for dynamic shared ownership with `query_builder`.
142    ArcString(Arc<String>),
143}
144
145impl Clone for SqlStr {
146    fn clone(&self) -> Self {
147        Self(match &self.0 {
148            Repr::Static(s) => Repr::Static(s),
149            Repr::Arced(s) => Repr::Arced(s.clone()),
150            // If `.clone()` gets called once, assume it might get called again.
151            _ => Repr::Arced(self.as_str().into()),
152        })
153    }
154}
155
156impl SqlSafeStr for SqlStr {
157    #[inline]
158    fn into_sql_str(self) -> SqlStr {
159        self
160    }
161}
162
163impl SqlStr {
164    /// Borrow the inner query string.
165    #[inline]
166    pub fn as_str(&self) -> &str {
167        match &self.0 {
168            Repr::Static(s) => s,
169            Repr::Owned(s) => s,
170            Repr::Boxed(s) => s,
171            Repr::Arced(s) => s,
172            Repr::ArcString(s) => s,
173        }
174    }
175
176    pub const fn from_static(sql: &'static str) -> Self {
177        SqlStr(Repr::Static(sql))
178    }
179}
180
181impl AsRef<str> for SqlStr {
182    #[inline]
183    fn as_ref(&self) -> &str {
184        self.as_str()
185    }
186}
187
188impl Borrow<str> for SqlStr {
189    #[inline]
190    fn borrow(&self) -> &str {
191        self.as_str()
192    }
193}
194
195impl<T> PartialEq<T> for SqlStr
196where
197    T: AsRef<str>,
198{
199    fn eq(&self, other: &T) -> bool {
200        self.as_str() == other.as_ref()
201    }
202}
203
204impl Eq for SqlStr {}
205
206impl Hash for SqlStr {
207    fn hash<H: Hasher>(&self, state: &mut H) {
208        self.as_str().hash(state)
209    }
210}