bottle_orm/database.rs
1//! # Database Module
2//!
3//! This module provides the core database connection and management functionality for Bottle ORM.
4//! It handles connection pooling, driver detection, table creation, and foreign key management
5//! across multiple database backends.
6//!
7//! ## Supported Database Drivers
8//!
9//! - **PostgreSQL**: Full support with advanced features like UUID, JSONB, arrays
10//! - **MySQL**: Complete support for standard MySQL/MariaDB features
11//! - **SQLite**: In-memory and file-based SQLite databases
12//!
13//! ## Features
14//!
15//! - **Connection Pooling**: Automatic connection pool management via sqlx
16//! - **Driver Detection**: Automatic database driver detection from connection URL
17//! - **Schema Management**: Table creation with indexes, constraints, and foreign keys
18//! - **Type Safety**: Type-safe operations across different database backends
19//!
20//! ## Example Usage
21//!
22//! ```rust,ignore
23//! use bottle_orm::Database;
24//!
25//! // Connect to PostgreSQL
26//! let db = Database::connect("postgres://user:pass@localhost/mydb").await?;
27//!
28//! // Connect to SQLite
29//! let db = Database::connect("sqlite::memory:").await?;
30//!
31//! // Connect to MySQL
32//! let db = Database::connect("mysql://user:pass@localhost/mydb").await?;
33//!
34//! // Create table for a model
35//! db.create_table::<User>().await?;
36//!
37//! // Assign foreign keys
38//! db.assign_foreign_keys::<Post>().await?;
39//!
40//! // Start building queries
41//! let users = db.model::<User>().scan().await?;
42//! ```
43
44// ============================================================================
45// External Crate Imports
46// ============================================================================
47
48use heck::ToSnakeCase;
49use sqlx::{
50 any::{AnyArguments, AnyPoolOptions},
51 AnyPool, Arguments, Error, Row,
52};
53use std::time::Duration;
54
55// ============================================================================
56// Internal Crate Imports
57// ============================================================================
58
59use crate::{migration::Migrator, model::Model, query_builder::QueryBuilder, Transaction};
60
61// ============================================================================
62// Database Driver Enumeration
63// ============================================================================
64
65/// Supported database driver types.
66///
67/// This enum represents the different database backends that Bottle ORM can work with.
68/// The driver type is automatically detected from the connection URL and used to
69/// generate appropriate SQL syntax for each database system.
70///
71/// # Variants
72///
73/// * `Postgres` - PostgreSQL database (9.5+)
74/// * `SQLite` - SQLite database (3.x)
75/// * `MySQL` - MySQL or MariaDB database (5.7+/10.2+)
76///
77/// # SQL Dialect Differences
78///
79/// Different drivers use different SQL syntax:
80///
81/// - **Placeholders**:
82/// - PostgreSQL: `$1, $2, $3` (numbered)
83/// - SQLite/MySQL: `?, ?, ?` (positional)
84///
85/// - **Type Casting**:
86/// - PostgreSQL: `$1::UUID`, `$2::TIMESTAMPTZ`
87/// - SQLite/MySQL: Automatic type inference
88///
89/// - **Schema Queries**:
90/// - PostgreSQL: `information_schema` tables
91/// - SQLite: `sqlite_master` system table
92/// - MySQL: `information_schema` tables
93///
94/// # Example
95///
96/// ```rust,ignore
97/// match db.driver {
98/// Drivers::Postgres => println!("Using PostgreSQL"),
99/// Drivers::SQLite => println!("Using SQLite"),
100/// Drivers::MySQL => println!("Using MySQL"),
101/// }
102/// ```
103#[derive(Clone, Debug, Copy)]
104pub enum Drivers {
105 /// PostgreSQL driver.
106 ///
107 /// Used for PostgreSQL databases. Supports advanced features like:
108 /// - UUID native type
109 /// - JSONB for JSON data
110 /// - Array types
111 /// - Full-text search
112 /// - Advanced indexing (GiST, GIN, etc.)
113 Postgres,
114
115 /// SQLite driver.
116 ///
117 /// Used for SQLite databases (both in-memory and file-based). Characteristics:
118 /// - Lightweight and embedded
119 /// - Single-file database
120 /// - Limited concurrent write support
121 /// - Good for development and small applications
122 SQLite,
123
124 /// MySQL driver.
125 ///
126 /// Used for MySQL and MariaDB databases. Features:
127 /// - Wide compatibility
128 /// - Good performance for read-heavy workloads
129 /// - Mature ecosystem
130 /// - ACID compliance (with InnoDB)
131 MySQL,
132}
133
134// ============================================================================
135// Database Builder
136// ============================================================================
137
138/// A builder for creating a `Database` connection with custom options.
139///
140/// Allows configuration of connection pool settings such as maximum connections,
141/// timeouts, and lifetimes.
142///
143/// # Example
144///
145/// ```rust,ignore
146/// let db = Database::builder()
147/// .max_connections(10)
148/// .min_connections(2)
149/// .acquire_timeout(std::time::Duration::from_secs(5))
150/// .connect("postgres://...")
151/// .await?;
152/// ```
153#[derive(Debug)]
154pub struct DatabaseBuilder {
155 options: AnyPoolOptions,
156}
157
158impl DatabaseBuilder {
159 /// Sets the maximum number of connections that this pool should maintain.
160 pub fn max_connections(mut self, max: u32) -> Self {
161 self.options = self.options.max_connections(max);
162 self
163 }
164
165 /// Sets the minimum number of connections that this pool should maintain.
166 pub fn min_connections(mut self, min: u32) -> Self {
167 self.options = self.options.min_connections(min);
168 self
169 }
170
171 /// Sets the maximum amount of time to spend waiting for a connection.
172 pub fn acquire_timeout(mut self, timeout: Duration) -> Self {
173 self.options = self.options.acquire_timeout(timeout);
174 self
175 }
176
177 /// Sets the maximum amount of time a connection may be idle.
178 pub fn idle_timeout(mut self, timeout: Duration) -> Self {
179 self.options = self.options.idle_timeout(Some(timeout));
180 self
181 }
182
183 /// Sets the maximum lifetime of a connection.
184 pub fn max_lifetime(mut self, lifetime: Duration) -> Self {
185 self.options = self.options.max_lifetime(Some(lifetime));
186 self
187 }
188
189 /// Connects to the database using the configured options.
190 pub async fn connect(self, url: &str) -> Result<Database, Error> {
191 // Install default drivers for sqlx::Any
192 sqlx::any::install_default_drivers();
193
194 let pool = self.options.connect(url).await?;
195
196 // Detect driver type from URL scheme
197 let (driver_str, _) = url.split_once(':').unwrap_or(("sqlite", ""));
198 let driver = match driver_str {
199 "postgresql" | "postgres" => Drivers::Postgres,
200 "mysql" => Drivers::MySQL,
201 _ => Drivers::SQLite,
202 };
203
204 Ok(Database { pool, driver })
205 }
206}
207
208// ============================================================================
209// Database Connection and Management
210// ============================================================================
211
212/// The main entry point for database connection and management.
213///
214/// `Database` handles connection pooling, driver detection, and provides methods
215/// for schema operations and query building. It uses sqlx's `AnyPool` to support
216/// multiple database backends with a unified interface.
217///
218/// # Fields
219///
220/// * `pool` - The sqlx connection pool for executing queries
221/// * `driver` - The detected database driver type
222///
223/// # Thread Safety
224///
225/// `Database` implements `Clone` and can be safely shared across threads.
226/// The underlying connection pool is thread-safe and handles connection
227/// distribution automatically.
228///
229/// # Example
230///
231/// ```rust,ignore
232/// use bottle_orm::Database;
233///
234/// #[tokio::main]
235/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
236/// // Connect to database
237/// let db = Database::connect("postgres://localhost/mydb").await?;
238///
239/// // Create migrator
240/// let migrator = db.migrator();
241///
242/// // Build queries
243/// let query = db.model::<User>();
244///
245/// // Database can be cloned and shared
246/// let db_clone = db.clone();
247/// tokio::spawn(async move {
248/// let users = db_clone.model::<User>().scan().await;
249/// });
250///
251/// Ok(())
252/// }
253/// ```
254#[derive(Debug, Clone)]
255pub struct Database {
256 /// The sqlx connection pool for executing database queries.
257 ///
258 /// This pool manages a set of database connections that can be reused
259 /// across multiple queries, improving performance by avoiding the overhead
260 /// of creating new connections for each operation.
261 pub(crate) pool: AnyPool,
262
263 /// The detected database driver type.
264 ///
265 /// Used to generate driver-specific SQL syntax (e.g., placeholders,
266 /// type casting, schema queries).
267 pub(crate) driver: Drivers,
268}
269
270// ============================================================================
271// Database Implementation
272// ============================================================================
273
274impl Database {
275 // ========================================================================
276 // Connection Management
277 // ========================================================================
278
279 /// Creates a builder to configure the database connection options.
280 ///
281 /// Returns a `DatabaseBuilder` which allows setting pool options like
282 /// `max_connections`, timeouts, etc.
283 ///
284 /// # Example
285 ///
286 /// ```rust,ignore
287 /// let db = Database::builder()
288 /// .max_connections(20)
289 /// .connect("postgres://...")
290 /// .await?;
291 /// ```
292 pub fn builder() -> DatabaseBuilder {
293 DatabaseBuilder { options: AnyPoolOptions::new() }
294 }
295
296 /// Connects to the database using a connection string (Database URL).
297 ///
298 /// This method establishes a connection pool to the specified database and
299 /// automatically detects the driver type based on the URL scheme. The connection
300 /// pool is configured with a default maximum of 5 connections.
301 ///
302 /// # Arguments
303 ///
304 /// * `url` - The database connection string with the format:
305 /// `<scheme>://<user>:<password>@<host>:<port>/<database>`
306 ///
307 /// # Supported URL Schemes
308 ///
309 /// - **PostgreSQL**: `postgres://` or `postgresql://`
310 /// - **MySQL**: `mysql://`
311 /// - **SQLite**: `sqlite://` or `sqlite::memory:` (for in-memory databases)
312 ///
313 /// # Connection Pool Configuration
314 ///
315 /// - Maximum connections: 5
316 /// - Connection timeout: Default (30 seconds)
317 /// - Idle timeout: Default (10 minutes)
318 ///
319 /// # Returns
320 ///
321 /// * `Ok(Database)` - Successfully connected database instance
322 /// * `Err(sqlx::Error)` - Connection error (invalid URL, authentication failure, etc.)
323 ///
324 /// # Examples
325 ///
326 /// ```rust,ignore
327 /// // PostgreSQL connection
328 /// let db = Database::connect("postgres://user:password@localhost:5432/mydb").await?;
329 ///
330 /// // PostgreSQL with SSL
331 /// let db = Database::connect("postgres://user:password@localhost/mydb?sslmode=require").await?;
332 ///
333 /// // SQLite in-memory database (great for testing)
334 /// let db = Database::connect("sqlite::memory:").await?;
335 ///
336 /// // SQLite file-based database
337 /// let db = Database::connect("sqlite://./database.db").await?;
338 ///
339 /// // MySQL connection
340 /// let db = Database::connect("mysql://user:password@localhost:3306/mydb").await?;
341 /// ```
342 ///
343 /// # Error Handling
344 ///
345 /// ```rust,ignore
346 /// match Database::connect("postgres://localhost/mydb").await {
347 /// Ok(db) => println!("Connected successfully"),
348 /// Err(e) => eprintln!("Connection failed: {}", e),
349 /// }
350 /// ```
351 pub async fn connect(url: &str) -> Result<Self, Error> {
352 Self::builder().max_connections(5).connect(url).await
353 }
354
355 // ========================================================================
356 // Schema Management
357 // ========================================================================
358
359 /// Creates a `Migrator` instance to manage schema migrations.
360 ///
361 /// The migrator allows you to register multiple models and execute
362 /// all necessary schema changes (table creation, foreign keys) in the
363 /// correct order.
364 ///
365 /// # Returns
366 ///
367 /// A new `Migrator` instance associated with this database connection
368 ///
369 /// # Example
370 ///
371 /// ```rust,ignore
372 /// let db = Database::connect("sqlite::memory:").await?;
373 ///
374 /// db.migrator()
375 /// .register::<User>()
376 /// .register::<Post>()
377 /// .register::<Comment>()
378 /// .run()
379 /// .await?;
380 /// ```
381 ///
382 /// # See Also
383 ///
384 /// * [`Migrator`] - For detailed migration documentation
385 /// * [`Migrator::register()`] - For registering models
386 /// * [`Migrator::run()`] - For executing migrations
387 pub fn migrator(&self) -> Migrator<'_> {
388 Migrator::new(self)
389 }
390
391 // ========================================================================
392 // Query Building
393 // ========================================================================
394
395 /// Starts building a query for a specific Model.
396 ///
397 /// This method creates a new `QueryBuilder` instance configured for the
398 /// specified model type. The query builder provides a fluent interface
399 /// for constructing SELECT and INSERT queries.
400 ///
401 /// # Type Parameters
402 ///
403 /// * `T` - The Model type to query. Must implement `Model + Send + Sync + Unpin`
404 ///
405 /// # Returns
406 ///
407 /// A new `QueryBuilder` instance ready for query construction
408 ///
409 /// # Example
410 ///
411 /// ```rust,ignore
412 /// // Simple query
413 /// let users: Vec<User> = db.model::<User>().scan().await?;
414 ///
415 /// // Filtered query
416 /// let adults: Vec<User> = db.model::<User>()
417 /// .filter("age", ">=", 18)
418 /// .scan()
419 /// .await?;
420 ///
421 /// // Insert operation
422 /// let new_user = User { /* ... */ };
423 /// db.model::<User>().insert(&new_user).await?;
424 /// ```
425 ///
426 /// # See Also
427 ///
428 /// * [`QueryBuilder`] - For detailed query building documentation
429 /// * [`QueryBuilder::filter()`] - For adding WHERE clauses
430 /// * [`QueryBuilder::scan()`] - For executing SELECT queries
431 /// * [`QueryBuilder::insert()`] - For INSERT operations
432 pub fn model<T: Model + Send + Sync + Unpin>(&self) -> QueryBuilder<'_, T, Self> {
433 // Get active column names from the model
434 let active_columns = T::active_columns();
435 let mut columns: Vec<String> = Vec::with_capacity(active_columns.capacity());
436
437 // Convert column names to snake_case and strip 'r#' prefix if present
438 for col in active_columns {
439 columns.push(col.strip_prefix("r#").unwrap_or(col).to_snake_case());
440 }
441
442 // Create and return the query builder
443 QueryBuilder::new(self.clone(), self.driver, T::table_name(), T::columns(), columns)
444 }
445
446 /// Creates a raw SQL query builder.
447 ///
448 /// This provides a "safety hatch" to execute raw SQL queries when the fluent
449 /// QueryBuilder is not sufficient (e.g., complex joins, CTEs, specific DB features).
450 ///
451 /// # Arguments
452 ///
453 /// * `sql` - The raw SQL query string (use `$1`, `?`, etc. for placeholders based on driver)
454 ///
455 /// # Returns
456 ///
457 /// A `RawQuery` builder that allows binding parameters and executing the query.
458 ///
459 /// # Example
460 ///
461 /// ```rust,ignore
462 /// // Fetching complex data
463 /// let users: Vec<User> = db.raw("SELECT * FROM users WHERE age > $1")
464 /// .bind(18)
465 /// .fetch_all()
466 /// .await?;
467 ///
468 /// // Executing an update
469 /// let affected = db.raw("UPDATE users SET active = $1 WHERE last_login < $2")
470 /// .bind(false)
471 /// .bind(one_year_ago)
472 /// .execute()
473 /// .await?;
474 /// ```
475 pub fn raw<'a>(&'a self, sql: &'a str) -> RawQuery<'a, Self> {
476 RawQuery::new(self.clone(), sql)
477 }
478
479 // ========================================================================
480 // Table Creation
481 // ========================================================================
482
483 /// Creates the table for model `T` if it does not exist.
484 ///
485 /// This method generates and executes SQL to create a table based on the
486 /// model's structure. It handles column definitions, primary keys, unique
487 /// constraints, default values, and indexes.
488 ///
489 /// # Type Parameters
490 ///
491 /// * `T` - The Model type representing the table
492 ///
493 /// # Returns
494 ///
495 /// * `Ok(&Self)` - Reference to self for method chaining
496 /// * `Err(sqlx::Error)` - Database error during table creation
497 ///
498 /// # Generated SQL Features
499 ///
500 /// - **Primary Keys**: Automatically marked with `PRIMARY KEY`
501 /// - **NOT NULL**: Non-nullable fields get `NOT NULL` constraint
502 /// - **UNIQUE**: Fields marked with `#[orm(unique)]` get `UNIQUE` constraint
503 /// - **DEFAULT**: Fields marked with `#[orm(create_time)]` get `DEFAULT CURRENT_TIMESTAMP`
504 /// - **Indexes**: Fields marked with `#[orm(index)]` get database indexes
505 ///
506 /// # Example
507 ///
508 /// ```rust,ignore
509 /// use bottle_orm::Model;
510 /// use uuid::Uuid;
511 /// use chrono::{DateTime, Utc};
512 ///
513 /// #[derive(Model)]
514 /// struct User {
515 /// #[orm(primary_key)]
516 /// id: Uuid,
517 /// #[orm(size = 50, unique)]
518 /// username: String,
519 /// #[orm(index)]
520 /// email: String,
521 /// age: i32,
522 /// #[orm(create_time)]
523 /// created_at: DateTime<Utc>,
524 /// }
525 ///
526 /// // Creates table with:
527 /// // - UUID primary key
528 /// // - Unique username constraint
529 /// // - Index on email
530 /// // - created_at with DEFAULT CURRENT_TIMESTAMP
531 /// db.create_table::<User>().await?;
532 /// ```
533 ///
534 /// # Generated SQL Example (PostgreSQL)
535 ///
536 /// ```sql
537 /// CREATE TABLE IF NOT EXISTS "user" (
538 /// "id" UUID PRIMARY KEY,
539 /// "username" VARCHAR(50) NOT NULL UNIQUE,
540 /// "email" TEXT NOT NULL,
541 /// "age" INTEGER NOT NULL,
542 /// "created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
543 /// );
544 /// CREATE INDEX IF NOT EXISTS "idx_user_email" ON "user" ("email");
545 /// ```
546 pub async fn create_table<T: Model>(&self) -> Result<&Self, Error> {
547 // Get table name in snake_case format
548 let table_name = T::table_name().to_snake_case();
549 let columns = T::columns();
550
551 let mut column_defs = Vec::new();
552 let mut index_statements = Vec::new();
553
554 // Build column definitions
555 for col in &columns {
556 // Strip 'r#' prefix if present (for Rust keywords used as column names)
557 let col_name = col.name.strip_prefix("r#").unwrap_or(col.name).to_snake_case();
558 let mut def = format!("\"{}\" {}", col_name, col.sql_type);
559
560 // Add PRIMARY KEY constraint
561 if col.is_primary_key {
562 def.push_str(" PRIMARY KEY");
563 }
564
565 // Add NOT NULL constraint (except for primary keys, which are implicitly NOT NULL)
566 if !col.is_nullable && !col.is_primary_key {
567 def.push_str(" NOT NULL");
568 }
569
570 // Add DEFAULT CURRENT_TIMESTAMP for create_time fields
571 if col.create_time {
572 def.push_str(" DEFAULT CURRENT_TIMESTAMP");
573 }
574
575 // Add UNIQUE constraint
576 if col.unique {
577 def.push_str(" UNIQUE");
578 }
579
580 column_defs.push(def);
581
582 // Generate index creation statement if needed
583 if col.index {
584 let index_type = if col.unique { "UNIQUE INDEX" } else { "INDEX" };
585 let index_name = format!("idx_{}_{}", table_name, col_name);
586
587 let index_query = format!(
588 "CREATE {} IF NOT EXISTS \"{}\" ON \"{}\" (\"{}\")",
589 index_type, index_name, table_name, col_name,
590 );
591
592 index_statements.push(index_query);
593 }
594 }
595
596 // Add SQLite Foreign Keys inline (SQLite doesn't support ADD CONSTRAINT)
597 if let Drivers::SQLite = self.driver {
598 for col in &columns {
599 if let (Some(f_table), Some(f_key)) = (col.foreign_table, col.foreign_key) {
600 let col_name = col.name.strip_prefix("r#").unwrap_or(col.name).to_snake_case();
601 let f_table_clean = f_table.to_snake_case();
602 let f_key_clean = f_key.to_snake_case();
603
604 let fk_def = format!(
605 "FOREIGN KEY (\"{}\") REFERENCES \"{}\" (\"{}\")",
606 col_name, f_table_clean, f_key_clean
607 );
608 column_defs.push(fk_def);
609 }
610 }
611 }
612
613 // Build and execute CREATE TABLE statement
614 let create_table_query =
615 format!("CREATE TABLE IF NOT EXISTS \"{}\" ({})", table_name.to_snake_case(), column_defs.join(", "));
616 log::info!("{}", create_table_query);
617
618 sqlx::query(&create_table_query).execute(&self.pool).await?;
619
620 // Create indexes
621 for idx_stmt in index_statements {
622 sqlx::query(&idx_stmt).execute(&self.pool).await?;
623 }
624
625 Ok(self)
626 }
627
628 /// Starts a new database transaction.
629 ///
630 /// Returns a `Transaction` wrapper that can be used to execute multiple
631 /// queries atomically. The transaction must be explicitly committed
632 /// using `commit()`, otherwise it will be rolled back when dropped.
633 ///
634 /// # Returns
635 ///
636 /// * `Ok(Transaction)` - A new transaction instance
637 /// * `Err(sqlx::Error)` - Database error starting transaction
638 ///
639 /// # Example
640 ///
641 /// ```rust,ignore
642 /// let mut tx = db.begin().await?;
643 /// // ... perform operations ...
644 /// tx.commit().await?;
645 /// ```
646 pub async fn begin<'a>(&self) -> Result<Transaction<'a>, sqlx::Error> {
647 let tx = self.pool.begin().await?;
648 Ok(Transaction { tx, driver: self.driver })
649 }
650
651 // ========================================================================
652 // Foreign Key Management
653 // ========================================================================
654
655 /// Checks for and assigns Foreign Keys for model `T`.
656 ///
657 /// This method examines all columns marked with `#[orm(foreign_key = "Table::Column")]`
658 /// and creates the appropriate foreign key constraints. It verifies that constraints
659 /// don't already exist before attempting to create them, preventing duplication errors.
660 ///
661 /// # Type Parameters
662 ///
663 /// * `T` - The Model type to process for foreign keys
664 ///
665 /// # Returns
666 ///
667 /// * `Ok(&Self)` - Reference to self for method chaining
668 /// * `Err(sqlx::Error)` - Database error during foreign key creation
669 ///
670 /// # Constraint Naming
671 ///
672 /// Foreign key constraints are named using the pattern:
673 /// `fk_{table_name}_{column_name}`
674 ///
675 /// # Example
676 ///
677 /// ```rust,ignore
678 /// use bottle_orm::Model;
679 /// use uuid::Uuid;
680 ///
681 /// #[derive(Model)]
682 /// struct User {
683 /// #[orm(primary_key)]
684 /// id: Uuid,
685 /// username: String,
686 /// }
687 ///
688 /// #[derive(Model)]
689 /// struct Post {
690 /// #[orm(primary_key)]
691 /// id: Uuid,
692 /// #[orm(foreign_key = "User::id")]
693 /// user_id: Uuid,
694 /// title: String,
695 /// }
696 ///
697 /// // Create tables first
698 /// db.create_table::<User>().await?;
699 /// db.create_table::<Post>().await?;
700 ///
701 /// // Then assign foreign keys
702 /// db.assign_foreign_keys::<Post>().await?;
703 /// ```
704 ///
705 /// # Generated SQL Example
706 ///
707 /// ```sql
708 /// ALTER TABLE "post"
709 /// ADD CONSTRAINT "fk_post_user_id"
710 /// FOREIGN KEY ("user_id")
711 /// REFERENCES "user" ("id");
712 /// ```
713 ///
714 /// # Important Notes
715 ///
716 /// - Foreign key assignment should be done **after** all tables are created
717 /// - The referenced table and column must exist before creating the foreign key
718 /// - Use the `Migrator` to handle the correct order automatically
719 /// - Currently optimized for PostgreSQL (uses `information_schema`)
720 ///
721 /// # See Also
722 ///
723 /// * [`Migrator`] - For automatic migration order management
724 pub async fn assign_foreign_keys<T: Model>(&self) -> Result<&Self, Error> {
725 // SQLite handles FKs in create_table, so we skip here
726 if let Drivers::SQLite = self.driver {
727 return Ok(self);
728 }
729
730 // Get table name in snake_case format
731 let table_name = T::table_name().to_snake_case();
732 let columns = T::columns();
733
734 // Process each column that has a foreign key definition
735 for col in columns {
736 if let (Some(f_table), Some(f_key)) = (col.foreign_table, col.foreign_key) {
737 // Clean up column and reference names
738 let col_name = col.name.strip_prefix("r#").unwrap_or(col.name).to_snake_case();
739 let f_table_clean = f_table.to_snake_case();
740 let f_key_clean = f_key.to_snake_case();
741
742 // Generate constraint name
743 let constraint_name = format!("fk_{}_{}", table_name, col_name);
744
745 // Check if constraint already exists
746 let count: i64 = match self.driver {
747 Drivers::Postgres => {
748 let check_query =
749 "SELECT count(*) FROM information_schema.table_constraints WHERE constraint_name = $1";
750 let row = sqlx::query(check_query).bind(&constraint_name).fetch_one(&self.pool).await?;
751 row.try_get(0).unwrap_or(0)
752 }
753 Drivers::MySQL => {
754 let check_query = "SELECT count(*) FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = ? AND TABLE_SCHEMA = DATABASE()";
755 let row = sqlx::query(check_query).bind(&constraint_name).fetch_one(&self.pool).await?;
756 row.try_get(0).unwrap_or(0)
757 }
758 Drivers::SQLite => 0, // Unreachable
759 };
760
761 // Skip if constraint already exists
762 if count > 0 {
763 continue;
764 }
765
766 // Create foreign key constraint
767 let alter_query = format!(
768 "ALTER TABLE \"{}\" ADD CONSTRAINT \"{}\" FOREIGN KEY (\"{}\") REFERENCES \"{}\" (\"{}\")",
769 table_name, constraint_name, col_name, f_table_clean, f_key_clean
770 );
771
772 sqlx::query(&alter_query).execute(&self.pool).await?;
773 }
774 }
775
776 Ok(self)
777 }
778}
779
780/// A trait representing a database connection or transaction.
781///
782/// This trait abstracts over `Database` (pool) and `Transaction` types, allowing
783/// the `QueryBuilder` to work seamlessly with both. It uses Generic Associated Types (GATs)
784/// to handle the lifetimes of the executor references correctly.
785pub trait Connection {
786 /// The type of the executor returned by this connection.
787 ///
788 /// This uses GATs to bind the lifetime of the executor (`'c`) to the lifetime
789 /// of the borrow of the connection (`&'c mut self`).
790 type Exec<'c>: sqlx::Executor<'c, Database = sqlx::Any>
791 where
792 Self: 'c;
793
794 /// Returns a mutable reference to the SQLx executor.
795 ///
796 /// # Returns
797 ///
798 /// An executor capable of running SQL queries (either a Pool or a Transaction).
799 fn executor<'c>(&'c mut self) -> Self::Exec<'c>;
800}
801
802/// Implementation of Connection for the main Database struct.
803///
804/// Uses the internal connection pool to execute queries.
805impl Connection for Database {
806 type Exec<'c> = &'c sqlx::Pool<sqlx::Any>;
807
808 fn executor<'c>(&'c mut self) -> Self::Exec<'c> {
809 &self.pool
810 }
811}
812
813/// Implementation of Connection for a mutable reference to Database.
814impl<'a> Connection for &'a mut Database {
815 type Exec<'c>
816 = &'c sqlx::Pool<sqlx::Any>
817 where
818 Self: 'c;
819
820 fn executor<'c>(&'c mut self) -> Self::Exec<'c> {
821 &self.pool
822 }
823}
824
825/// Implementation of Connection for a mutable reference to sqlx::Transaction.
826impl<'a> Connection for &mut sqlx::Transaction<'a, sqlx::Any> {
827 type Exec<'c>
828 = &'c mut sqlx::AnyConnection
829 where
830 Self: 'c;
831
832 fn executor<'c>(&'c mut self) -> Self::Exec<'c> {
833 &mut **self
834 }
835}
836
837// ============================================================================
838// Raw SQL Query Builder
839// ============================================================================
840/// A builder for executing raw SQL queries with parameter binding.
841///
842/// Returned by `Database::raw()` or `Transaction::raw()`. Allows constructing safe, parameterized
843/// SQL queries that can bypass the standard model-based QueryBuilder when
844/// complex SQL features (CTEs, Window Functions, etc.) are needed.
845pub struct RawQuery<'a, C> {
846 conn: C,
847 sql: &'a str,
848 args: AnyArguments<'a>,
849}
850
851impl<'a, C> RawQuery<'a, C>
852where
853 C: Connection + Send,
854{
855 /// Creates a new RawQuery instance.
856 pub(crate) fn new(conn: C, sql: &'a str) -> Self {
857 Self { conn, sql, args: AnyArguments::default() }
858 }
859
860 /// Binds a parameter to the query.
861 ///
862 /// # Arguments
863 ///
864 /// * `value` - The value to bind. Must implement `sqlx::Encode` and `sqlx::Type`.
865 pub fn bind<T>(mut self, value: T) -> Self
866 where
867 T: 'a + sqlx::Encode<'a, sqlx::Any> + sqlx::Type<sqlx::Any> + Send + Sync,
868 {
869 let _ = self.args.add(value);
870 self
871 }
872
873 /// Executes the query and returns all matching rows mapped to type `T`.
874 pub async fn fetch_all<T>(mut self) -> Result<Vec<T>, Error>
875 where
876 T: for<'r> sqlx::FromRow<'r, sqlx::any::AnyRow> + Send + Unpin,
877 {
878 sqlx::query_as_with::<_, T, _>(self.sql, self.args).fetch_all(self.conn.executor()).await
879 }
880
881 /// Executes the query and returns the first matching row mapped to type `T`.
882 pub async fn fetch_one<T>(mut self) -> Result<T, Error>
883 where
884 T: for<'r> sqlx::FromRow<'r, sqlx::any::AnyRow> + Send + Unpin,
885 {
886 sqlx::query_as_with::<_, T, _>(self.sql, self.args).fetch_one(self.conn.executor()).await
887 }
888
889 /// Executes the query and returns the first matching row, or None if not found.
890 pub async fn fetch_optional<T>(mut self) -> Result<Option<T>, Error>
891 where
892 T: for<'r> sqlx::FromRow<'r, sqlx::any::AnyRow> + Send + Unpin,
893 {
894 sqlx::query_as_with::<_, T, _>(self.sql, self.args).fetch_optional(self.conn.executor()).await
895 }
896
897 /// Executes the query and returns a single scalar value.
898 ///
899 /// Useful for queries like `SELECT count(*) ...` or `SELECT id ...`.
900 pub async fn fetch_scalar<O>(mut self) -> Result<O, Error>
901 where
902 O: for<'r> sqlx::Decode<'r, sqlx::Any> + sqlx::Type<sqlx::Any> + Send + Unpin,
903 {
904 sqlx::query_scalar_with::<_, O, _>(self.sql, self.args).fetch_one(self.conn.executor()).await
905 }
906
907 /// Executes the query and returns a single scalar value, or None if not found.
908 pub async fn fetch_scalar_optional<O>(mut self) -> Result<Option<O>, Error>
909 where
910 O: for<'r> sqlx::Decode<'r, sqlx::Any> + sqlx::Type<sqlx::Any> + Send + Unpin,
911 {
912 sqlx::query_scalar_with::<_, O, _>(self.sql, self.args).fetch_optional(self.conn.executor()).await
913 }
914
915 /// Executes the query (INSERT, UPDATE, DELETE) and returns the number of affected rows.
916 pub async fn execute(mut self) -> Result<u64, Error> {
917 let result = sqlx::query_with(self.sql, self.args).execute(self.conn.executor()).await?;
918
919 Ok(result.rows_affected())
920 }
921}