1pub mod base64;
9pub mod builder;
10pub mod config;
11pub mod connections;
12pub mod credentials;
13pub mod dialect;
14pub mod driver;
15pub mod migration;
16pub mod model;
17pub mod mysql;
18pub mod pagination;
19pub mod pool;
20pub mod postgres;
21pub mod random;
22pub mod row;
23pub mod schema;
24#[cfg(feature = "sqlite")]
27pub mod sqlite;
28pub mod sqlserver;
29pub mod tls;
30pub mod value;
31
32pub use builder::{Direction, QueryBuilder};
33pub use config::DatabaseConfig;
34pub use dialect::{ColumnType, Dialect, ReturningStyle};
35pub use driver::{Driver, DriverConnection, QueryResult};
36pub use connections::{Connections, DEFAULT_BUDGET};
37pub use migration::{Faker, Migration, Migrator, Seeder};
38pub use model::{Model, ModelExt, belongs_to, has_many};
39pub use mysql::{MySqlConnection, MySqlDriver};
40pub use pagination::{CursorPage, Page};
41pub use postgres::connection::{log_bindings, set_log_bindings};
42pub use pool::{Pool, PooledConnection};
43pub use schema::{Schema, Table};
44pub use sqlserver::{SqlServerConnection, SqlServerDriver};
45#[cfg(feature = "sqlite")]
46pub use sqlite::{SqliteConnection, SqliteDriver};
47pub use row::{Row, rows_to_json};
48pub use value::{FromValue, Value};
49
50pub use rustlavel_core::{Error, Result};
51use std::sync::Arc;
52
53fn driver_for(config: DatabaseConfig) -> Result<Arc<dyn Driver>> {
58 match config.driver.as_str() {
59 "postgres" => Ok(Arc::new(postgres::PostgresDriver::new(config))),
60 "mysql" => Ok(Arc::new(mysql::MySqlDriver::new(config))),
61 "sqlserver" => Ok(Arc::new(sqlserver::SqlServerDriver::new(config))),
62 #[cfg(feature = "sqlite")]
63 "sqlite" => Ok(Arc::new(sqlite::SqliteDriver::new(config))),
64 #[cfg(not(feature = "sqlite"))]
68 "sqlite" => Err(Error::msg(
69 "this build has no SQLite support. Enable the `sqlite` feature on rustlavel-db, \
70 or `sqlite` on the rustlavel meta-crate. It is off by default because it is the \
71 one driver that links a C library.",
72 )),
73 other => Err(Error::msg(format!(
74 "the `{other}` driver is not available in this build. \
75 Point DATABASE_URL at a database this build supports."
76 ))),
77 }
78}
79
80pub use rustlavel_macros::Model;
82
83pub mod prelude {
85 pub use crate::connections::Connections;
86 pub use crate::migration::{Faker, Migrator, Seeder};
87 pub use crate::model::{ModelExt, belongs_to, has_many};
88 pub use crate::schema::{Schema, Table};
89 pub use crate::{CursorPage, Database, Model, Page, QueryBuilder, Row, Value};
90 pub use rustlavel_core::{Error, Json, Result};
91}
92
93#[derive(Clone)]
98pub struct Database {
99 pool: Pool,
100 dialect: Arc<dyn Dialect>,
101}
102
103impl Database {
104 pub async fn connect(url: &str) -> Result<Database> {
106 Database::with_config(DatabaseConfig::from_url(url)?).await
107 }
108
109 pub async fn with_config(config: DatabaseConfig) -> Result<Database> {
111 let database = Database::lazy(config)?;
112 database.pool.verify().await?;
113 Ok(database)
114 }
115
116 pub fn lazy(config: DatabaseConfig) -> Result<Database> {
119 Ok(Database::with_driver(driver_for(config)?))
120 }
121
122 pub fn with_driver(driver: Arc<dyn Driver>) -> Database {
125 let dialect = driver.dialect();
126 Database { pool: Pool::new(driver), dialect }
127 }
128
129 pub fn dialect(&self) -> &dyn Dialect {
134 self.dialect.as_ref()
135 }
136
137 pub fn pool(&self) -> &Pool {
138 &self.pool
139 }
140
141 pub fn table(&self, name: &str) -> QueryBuilder {
143 QueryBuilder::new(name)
144 }
145
146 pub async fn select(&self, sql: &str, params: &[Value]) -> Result<Vec<Row>> {
148 let mut connection = self.pool.acquire().await?;
149 Ok(connection.query(sql, params).await?.rows)
150 }
151
152 pub async fn select_one(&self, sql: &str, params: &[Value]) -> Result<Option<Row>> {
154 Ok(self.select(sql, params).await?.into_iter().next())
155 }
156
157 pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<u64> {
159 let mut connection = self.pool.acquire().await?;
160 Ok(connection.query(sql, params).await?.affected)
161 }
162
163 pub async fn run(&self, sql: &str) -> Result<u64> {
165 let mut connection = self.pool.acquire().await?;
166 Ok(connection.simple_query(sql).await?.affected)
167 }
168
169 pub async fn insert_returning_key(
175 &self,
176 sql: &str,
177 params: &[Value],
178 column: &str,
179 ) -> Result<Option<Value>> {
180 let mut connection = self.pool.acquire().await?;
181 let result = connection.query(sql, params).await?;
182
183 if let Some(row) = result.rows.first() {
184 return Ok(Some(row.value(column).or_else(|_| row.value_at(0))?.clone()));
187 }
188 Ok(result.last_insert_id.map(Value::Int))
189 }
190
191 pub async fn scalar<T: FromValue>(&self, sql: &str, params: &[Value]) -> Result<Option<T>> {
193 match self.select_one(sql, params).await? {
194 Some(row) => row.get_at::<T>(0).map(Some),
195 None => Ok(None),
196 }
197 }
198
199 pub async fn begin(&self) -> Result<Transaction> {
214 let mut connection = self.pool.acquire().await?;
215 connection.simple_query(self.dialect.begin_sql()).await?;
216 Ok(Transaction {
217 connection: Some(connection),
218 dialect: Arc::clone(&self.dialect),
219 finished: false,
220 })
221 }
222
223 pub async fn close(&self) {
225 self.pool.close().await;
226 }
227}
228
229pub struct Transaction {
231 connection: Option<PooledConnection>,
232 dialect: Arc<dyn Dialect>,
234 finished: bool,
235}
236
237impl Transaction {
238 fn connection(&mut self) -> Result<&mut PooledConnection> {
239 self.connection
240 .as_mut()
241 .ok_or_else(|| Error::msg("this transaction has already finished"))
242 }
243
244 pub fn dialect(&self) -> &dyn Dialect {
247 self.dialect.as_ref()
248 }
249
250 pub async fn select(&mut self, sql: &str, params: &[Value]) -> Result<Vec<Row>> {
251 Ok(self.connection()?.query(sql, params).await?.rows)
252 }
253
254 pub async fn select_one(&mut self, sql: &str, params: &[Value]) -> Result<Option<Row>> {
255 Ok(self.select(sql, params).await?.into_iter().next())
256 }
257
258 pub async fn execute(&mut self, sql: &str, params: &[Value]) -> Result<u64> {
259 Ok(self.connection()?.query(sql, params).await?.affected)
260 }
261
262 pub async fn run(&mut self, sql: &str) -> Result<u64> {
263 Ok(self.connection()?.simple_query(sql).await?.affected)
264 }
265
266 pub async fn scalar<T: FromValue>(&mut self, sql: &str, params: &[Value]) -> Result<Option<T>> {
267 match self.select_one(sql, params).await? {
268 Some(row) => row.get_at::<T>(0).map(Some),
269 None => Ok(None),
270 }
271 }
272
273 pub async fn savepoint(&mut self, name: &str) -> Result<()> {
275 validate_identifier(name)?;
276 let sql = self.dialect.savepoint_sql(name);
277 self.connection()?.simple_query(&sql).await?;
278 Ok(())
279 }
280
281 pub async fn rollback_to(&mut self, name: &str) -> Result<()> {
282 validate_identifier(name)?;
283 let sql = self.dialect.rollback_to_savepoint_sql(name);
284 self.connection()?.simple_query(&sql).await?;
285 Ok(())
286 }
287
288 pub async fn commit(mut self) -> Result<()> {
290 let sql = self.dialect.commit_sql();
291 self.connection()?.simple_query(sql).await?;
292 self.finished = true;
293 Ok(())
294 }
295
296 pub async fn rollback(mut self) -> Result<()> {
298 let sql = self.dialect.rollback_sql();
299 self.connection()?.simple_query(sql).await?;
300 self.finished = true;
301 Ok(())
302 }
303}
304
305impl Drop for Transaction {
306 fn drop(&mut self) {
307 if self.finished {
308 return;
309 }
310 if let Some(mut connection) = self.connection.take() {
314 let sql = self.dialect.rollback_sql();
315 tokio::spawn(async move {
316 let _ = connection.simple_query(sql).await;
317 });
318 }
319 }
320}
321
322pub fn validate_identifier(name: &str) -> Result<()> {
327 let valid = !name.is_empty()
328 && name.len() <= 63
329 && name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
330 && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
331
332 if valid {
333 Ok(())
334 } else {
335 Err(Error::msg(format!(
336 "`{name}` is not a valid SQL identifier. Identifiers may contain letters, digits and \
337 underscores, and must not start with a digit."
338 )))
339 }
340}
341
342pub fn quote_identifier(name: &str) -> Result<String> {
344 let quoted: Result<Vec<String>> = name
346 .split('.')
347 .map(|part| validate_identifier(part).map(|_| format!("\"{part}\"")))
348 .collect();
349 Ok(quoted?.join("."))
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn accepts_ordinary_identifiers() {
358 for name in ["users", "user_profiles", "_private", "t1"] {
359 assert!(validate_identifier(name).is_ok(), "{name} should be valid");
360 }
361 }
362
363 #[test]
364 fn rejects_anything_that_could_alter_a_statement() {
365 for name in ["users; drop table users", "user\"s", "1abc", "", "a b", "users--"] {
366 assert!(validate_identifier(name).is_err(), "{name:?} should be rejected");
367 }
368 }
369
370 #[test]
371 fn quotes_qualified_names_part_by_part() {
372 assert_eq!(quote_identifier("public.users").unwrap(), "\"public\".\"users\"");
373 assert!(quote_identifier("public.users; drop table x").is_err());
374 }
375}