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