1pub mod base64;
9pub mod builder;
10pub mod config;
11pub mod credentials;
12pub mod dialect;
13pub mod driver;
14pub mod migration;
15pub mod model;
16pub mod mysql;
17pub mod pagination;
18pub mod pool;
19pub mod postgres;
20pub mod random;
21pub mod row;
22pub mod schema;
23pub mod sqlserver;
24pub mod tls;
25pub mod value;
26
27pub use builder::{Direction, QueryBuilder};
28pub use config::DatabaseConfig;
29pub use dialect::{ColumnType, Dialect, ReturningStyle};
30pub use driver::{Driver, DriverConnection, QueryResult};
31pub use migration::{Faker, Migration, Migrator, Seeder};
32pub use model::{Model, ModelExt, belongs_to, has_many};
33pub use mysql::{MySqlConnection, MySqlDriver};
34pub use pagination::{CursorPage, Page};
35pub use postgres::connection::{log_bindings, set_log_bindings};
36pub use pool::{Pool, PooledConnection};
37pub use schema::{Schema, Table};
38pub use sqlserver::{SqlServerConnection, SqlServerDriver};
39pub use row::{Row, rows_to_json};
40pub use value::{FromValue, Value};
41
42pub use rustlavel_core::{Error, Result};
43use std::sync::Arc;
44
45fn driver_for(config: DatabaseConfig) -> Result<Arc<dyn Driver>> {
50 match config.driver.as_str() {
51 "postgres" => Ok(Arc::new(postgres::PostgresDriver::new(config))),
52 "mysql" => Ok(Arc::new(mysql::MySqlDriver::new(config))),
53 "sqlserver" => Ok(Arc::new(sqlserver::SqlServerDriver::new(config))),
54 other => Err(Error::msg(format!(
55 "the `{other}` driver is not available in this build. \
56 Point DATABASE_URL at a database this build supports."
57 ))),
58 }
59}
60
61pub use rustlavel_macros::Model;
63
64pub mod prelude {
66 pub use crate::migration::{Faker, Migrator, Seeder};
67 pub use crate::model::{ModelExt, belongs_to, has_many};
68 pub use crate::schema::{Schema, Table};
69 pub use crate::{CursorPage, Database, Model, Page, QueryBuilder, Row, Value};
70 pub use rustlavel_core::{Error, Json, Result};
71}
72
73#[derive(Clone)]
78pub struct Database {
79 pool: Pool,
80 dialect: Arc<dyn Dialect>,
81}
82
83impl Database {
84 pub async fn connect(url: &str) -> Result<Database> {
86 Database::with_config(DatabaseConfig::from_url(url)?).await
87 }
88
89 pub async fn with_config(config: DatabaseConfig) -> Result<Database> {
91 let database = Database::lazy(config)?;
92 database.pool.verify().await?;
93 Ok(database)
94 }
95
96 pub fn lazy(config: DatabaseConfig) -> Result<Database> {
99 Ok(Database::with_driver(driver_for(config)?))
100 }
101
102 pub fn with_driver(driver: Arc<dyn Driver>) -> Database {
105 let dialect = driver.dialect();
106 Database { pool: Pool::new(driver), dialect }
107 }
108
109 pub fn dialect(&self) -> &dyn Dialect {
114 self.dialect.as_ref()
115 }
116
117 pub fn pool(&self) -> &Pool {
118 &self.pool
119 }
120
121 pub fn table(&self, name: &str) -> QueryBuilder {
123 QueryBuilder::new(name)
124 }
125
126 pub async fn select(&self, sql: &str, params: &[Value]) -> Result<Vec<Row>> {
128 let mut connection = self.pool.acquire().await?;
129 Ok(connection.query(sql, params).await?.rows)
130 }
131
132 pub async fn select_one(&self, sql: &str, params: &[Value]) -> Result<Option<Row>> {
134 Ok(self.select(sql, params).await?.into_iter().next())
135 }
136
137 pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<u64> {
139 let mut connection = self.pool.acquire().await?;
140 Ok(connection.query(sql, params).await?.affected)
141 }
142
143 pub async fn run(&self, sql: &str) -> Result<u64> {
145 let mut connection = self.pool.acquire().await?;
146 Ok(connection.simple_query(sql).await?.affected)
147 }
148
149 pub async fn insert_returning_key(
155 &self,
156 sql: &str,
157 params: &[Value],
158 column: &str,
159 ) -> Result<Option<Value>> {
160 let mut connection = self.pool.acquire().await?;
161 let result = connection.query(sql, params).await?;
162
163 if let Some(row) = result.rows.first() {
164 return Ok(Some(row.value(column).or_else(|_| row.value_at(0))?.clone()));
167 }
168 Ok(result.last_insert_id.map(Value::Int))
169 }
170
171 pub async fn scalar<T: FromValue>(&self, sql: &str, params: &[Value]) -> Result<Option<T>> {
173 match self.select_one(sql, params).await? {
174 Some(row) => row.get_at::<T>(0).map(Some),
175 None => Ok(None),
176 }
177 }
178
179 pub async fn begin(&self) -> Result<Transaction> {
194 let mut connection = self.pool.acquire().await?;
195 connection.simple_query(self.dialect.begin_sql()).await?;
196 Ok(Transaction {
197 connection: Some(connection),
198 dialect: Arc::clone(&self.dialect),
199 finished: false,
200 })
201 }
202
203 pub async fn close(&self) {
205 self.pool.close().await;
206 }
207}
208
209pub struct Transaction {
211 connection: Option<PooledConnection>,
212 dialect: Arc<dyn Dialect>,
214 finished: bool,
215}
216
217impl Transaction {
218 fn connection(&mut self) -> Result<&mut PooledConnection> {
219 self.connection
220 .as_mut()
221 .ok_or_else(|| Error::msg("this transaction has already finished"))
222 }
223
224 pub async fn select(&mut self, sql: &str, params: &[Value]) -> Result<Vec<Row>> {
225 Ok(self.connection()?.query(sql, params).await?.rows)
226 }
227
228 pub async fn select_one(&mut self, sql: &str, params: &[Value]) -> Result<Option<Row>> {
229 Ok(self.select(sql, params).await?.into_iter().next())
230 }
231
232 pub async fn execute(&mut self, sql: &str, params: &[Value]) -> Result<u64> {
233 Ok(self.connection()?.query(sql, params).await?.affected)
234 }
235
236 pub async fn run(&mut self, sql: &str) -> Result<u64> {
237 Ok(self.connection()?.simple_query(sql).await?.affected)
238 }
239
240 pub async fn scalar<T: FromValue>(&mut self, sql: &str, params: &[Value]) -> Result<Option<T>> {
241 match self.select_one(sql, params).await? {
242 Some(row) => row.get_at::<T>(0).map(Some),
243 None => Ok(None),
244 }
245 }
246
247 pub async fn savepoint(&mut self, name: &str) -> Result<()> {
249 validate_identifier(name)?;
250 let sql = self.dialect.savepoint_sql(name);
251 self.connection()?.simple_query(&sql).await?;
252 Ok(())
253 }
254
255 pub async fn rollback_to(&mut self, name: &str) -> Result<()> {
256 validate_identifier(name)?;
257 let sql = self.dialect.rollback_to_savepoint_sql(name);
258 self.connection()?.simple_query(&sql).await?;
259 Ok(())
260 }
261
262 pub async fn commit(mut self) -> Result<()> {
264 let sql = self.dialect.commit_sql();
265 self.connection()?.simple_query(sql).await?;
266 self.finished = true;
267 Ok(())
268 }
269
270 pub async fn rollback(mut self) -> Result<()> {
272 let sql = self.dialect.rollback_sql();
273 self.connection()?.simple_query(sql).await?;
274 self.finished = true;
275 Ok(())
276 }
277}
278
279impl Drop for Transaction {
280 fn drop(&mut self) {
281 if self.finished {
282 return;
283 }
284 if let Some(mut connection) = self.connection.take() {
288 let sql = self.dialect.rollback_sql();
289 tokio::spawn(async move {
290 let _ = connection.simple_query(sql).await;
291 });
292 }
293 }
294}
295
296pub fn validate_identifier(name: &str) -> Result<()> {
301 let valid = !name.is_empty()
302 && name.len() <= 63
303 && name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
304 && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
305
306 if valid {
307 Ok(())
308 } else {
309 Err(Error::msg(format!(
310 "`{name}` is not a valid SQL identifier. Identifiers may contain letters, digits and \
311 underscores, and must not start with a digit."
312 )))
313 }
314}
315
316pub fn quote_identifier(name: &str) -> Result<String> {
318 let quoted: Result<Vec<String>> = name
320 .split('.')
321 .map(|part| validate_identifier(part).map(|_| format!("\"{part}\"")))
322 .collect();
323 Ok(quoted?.join("."))
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn accepts_ordinary_identifiers() {
332 for name in ["users", "user_profiles", "_private", "t1"] {
333 assert!(validate_identifier(name).is_ok(), "{name} should be valid");
334 }
335 }
336
337 #[test]
338 fn rejects_anything_that_could_alter_a_statement() {
339 for name in ["users; drop table users", "user\"s", "1abc", "", "a b", "users--"] {
340 assert!(validate_identifier(name).is_err(), "{name:?} should be rejected");
341 }
342 }
343
344 #[test]
345 fn quotes_qualified_names_part_by_part() {
346 assert_eq!(quote_identifier("public.users").unwrap(), "\"public\".\"users\"");
347 assert!(quote_identifier("public.users; drop table x").is_err());
348 }
349}