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
38pub trait SqlSafeStr {
39 /// Convert `self` to a [`SqlStr`].
40 fn into_sql_str(self) -> SqlStr;
41}
42
43impl SqlSafeStr for &'static str {
44 #[inline]
45 fn into_sql_str(self) -> SqlStr {
46 SqlStr(Repr::Static(self))
47 }
48}
49
50/// Assert that a query string is safe to execute on a database connection.
51///
52/// Using this API means that **you** have made sure that the string contents do not contain a
53/// [SQL injection vulnerability][injection]. It means that, if the string was constructed
54/// dynamically, and/or from user input, you have taken care to sanitize the input yourself.
55/// SQLx does not provide any sort of sanitization; the design of SQLx prefers the use
56/// of prepared statements for dynamic input.
57///
58/// The maintainers of SQLx take no responsibility for any data leaks or loss resulting from misuse
59/// of this API. **Use at your own risk.**
60///
61/// Note that `&'static str` implements [`SqlSafeStr`] directly and so does not need to be wrapped
62/// with this type.
63///
64/// [injection]: https://en.wikipedia.org/wiki/SQL_injection
65pub struct AssertSqlSafe<T>(pub T);
66
67/// Note: copies the string.
68///
69/// It is recommended to pass one of the supported owned string types instead.
70impl SqlSafeStr for AssertSqlSafe<&str> {
71 #[inline]
72 fn into_sql_str(self) -> SqlStr {
73 SqlStr(Repr::Arced(self.0.into()))
74 }
75}
76impl SqlSafeStr for AssertSqlSafe<String> {
77 #[inline]
78 fn into_sql_str(self) -> SqlStr {
79 SqlStr(Repr::Owned(self.0))
80 }
81}
82
83impl SqlSafeStr for AssertSqlSafe<Box<str>> {
84 #[inline]
85 fn into_sql_str(self) -> SqlStr {
86 SqlStr(Repr::Boxed(self.0))
87 }
88}
89
90// Note: this is not implemented for `Rc<str>` because it would make `QueryString: !Send`.
91impl SqlSafeStr for AssertSqlSafe<Arc<str>> {
92 #[inline]
93 fn into_sql_str(self) -> SqlStr {
94 SqlStr(Repr::Arced(self.0))
95 }
96}
97
98impl SqlSafeStr for AssertSqlSafe<Arc<String>> {
99 #[inline]
100 fn into_sql_str(self) -> SqlStr {
101 SqlStr(Repr::ArcString(self.0))
102 }
103}
104
105impl SqlSafeStr for AssertSqlSafe<Cow<'static, str>> {
106 fn into_sql_str(self) -> SqlStr {
107 match self.0 {
108 Cow::Borrowed(str) => str.into_sql_str(),
109 Cow::Owned(str) => AssertSqlSafe(str).into_sql_str(),
110 }
111 }
112}
113
114/// A SQL string that is ready to execute on a database connection.
115///
116/// This is essentially `Cow<'static, str>` but which can be constructed from additional types
117/// without copying.
118///
119/// See [`SqlSafeStr`] for details.
120#[derive(Debug)]
121pub struct SqlStr(Repr);
122
123#[derive(Debug)]
124enum Repr {
125 /// We need a variant to memoize when we already have a static string, so we don't copy it.
126 Static(&'static str),
127 /// Thanks to the new niche in `String`, this doesn't increase the size beyond 3 words.
128 /// We essentially get all these variants for free.
129 Owned(String),
130 Boxed(Box<str>),
131 Arced(Arc<str>),
132 /// Allows for dynamic shared ownership with `query_builder`.
133 ArcString(Arc<String>),
134}
135
136impl Clone for SqlStr {
137 fn clone(&self) -> Self {
138 Self(match &self.0 {
139 Repr::Static(s) => Repr::Static(s),
140 Repr::Arced(s) => Repr::Arced(s.clone()),
141 // If `.clone()` gets called once, assume it might get called again.
142 _ => Repr::Arced(self.as_str().into()),
143 })
144 }
145}
146
147impl SqlSafeStr for SqlStr {
148 #[inline]
149 fn into_sql_str(self) -> SqlStr {
150 self
151 }
152}
153
154impl SqlStr {
155 /// Borrow the inner query string.
156 #[inline]
157 pub fn as_str(&self) -> &str {
158 match &self.0 {
159 Repr::Static(s) => s,
160 Repr::Owned(s) => s,
161 Repr::Boxed(s) => s,
162 Repr::Arced(s) => s,
163 Repr::ArcString(s) => s,
164 }
165 }
166
167 pub const fn from_static(sql: &'static str) -> Self {
168 SqlStr(Repr::Static(sql))
169 }
170}
171
172impl AsRef<str> for SqlStr {
173 #[inline]
174 fn as_ref(&self) -> &str {
175 self.as_str()
176 }
177}
178
179impl Borrow<str> for SqlStr {
180 #[inline]
181 fn borrow(&self) -> &str {
182 self.as_str()
183 }
184}
185
186impl<T> PartialEq<T> for SqlStr
187where
188 T: AsRef<str>,
189{
190 fn eq(&self, other: &T) -> bool {
191 self.as_str() == other.as_ref()
192 }
193}
194
195impl Eq for SqlStr {}
196
197impl Hash for SqlStr {
198 fn hash<H: Hasher>(&self, state: &mut H) {
199 self.as_str().hash(state)
200 }
201}