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 let mut primary_keys = Vec::new(); // Track primary key columns to support composite keys
554
555 // Build column definitions
556 for col in &columns {
557 // Strip 'r#' prefix if present (for Rust keywords used as column names)
558 let col_name = col.name.strip_prefix("r#").unwrap_or(col.name).to_snake_case();
559 let mut def = format!("\"{}\" {}", col_name, col.sql_type);
560
561 // Collect primary key columns to be added as a table constraint later
562 if col.is_primary_key {
563 primary_keys.push(col_name.clone());
564 }
565
566 // Ensure non-nullable columns and primary keys are marked as NOT NULL
567 if !col.is_nullable || col.is_primary_key {
568 def.push_str(" NOT NULL");
569 }
570
571 // Add DEFAULT CURRENT_TIMESTAMP for create_time fields
572 if col.create_time {
573 def.push_str(" DEFAULT CURRENT_TIMESTAMP");
574 }
575
576 // Add UNIQUE constraint
577 if col.unique {
578 def.push_str(" UNIQUE");
579 }
580
581 column_defs.push(def);
582
583 // Generate index creation statement if needed
584 if col.index {
585 let index_type = if col.unique { "UNIQUE INDEX" } else { "INDEX" };
586 let index_name = format!("idx_{}_{}", table_name, col_name);
587
588 let index_query = format!(
589 "CREATE {} IF NOT EXISTS \"{}\" ON \"{}\" (\"{}\")",
590 index_type, index_name, table_name, col_name,
591 );
592
593 index_statements.push(index_query);
594 }
595 }
596
597 // Add PRIMARY KEY constraint if any columns were marked as primary keys
598 if !primary_keys.is_empty() {
599 let pk_columns = primary_keys.iter().map(|pk| format!("\"{}\"", pk)).collect::<Vec<_>>().join(", ");
600
601 let pk_constraint = format!("PRIMARY KEY ({})", pk_columns);
602 column_defs.push(pk_constraint);
603 }
604
605 // Add SQLite Foreign Keys inline (SQLite doesn't support ADD CONSTRAINT)
606 if let Drivers::SQLite = self.driver {
607 for col in &columns {
608 if let (Some(f_table), Some(f_key)) = (col.foreign_table, col.foreign_key) {
609 let col_name = col.name.strip_prefix("r#").unwrap_or(col.name).to_snake_case();
610 let f_table_clean = f_table.to_snake_case();
611 let f_key_clean = f_key.to_snake_case();
612
613 let fk_def = format!(
614 "FOREIGN KEY (\"{}\") REFERENCES \"{}\" (\"{}\")",
615 col_name, f_table_clean, f_key_clean
616 );
617 column_defs.push(fk_def);
618 }
619 }
620 }
621
622 // Build and execute CREATE TABLE statement
623 let create_table_query = format!("CREATE TABLE IF NOT EXISTS \"{}\" ({})", table_name, column_defs.join(", "));
624 log::info!("{}", create_table_query);
625
626 sqlx::query(&create_table_query).execute(&self.pool).await?;
627
628 // Create indexes
629 for idx_stmt in index_statements {
630 sqlx::query(&idx_stmt).execute(&self.pool).await?;
631 }
632
633 Ok(self)
634 }
635
636 /// Starts a new database transaction.
637 ///
638 /// Returns a `Transaction` wrapper that can be used to execute multiple
639 /// queries atomically. The transaction must be explicitly committed
640 /// using `commit()`, otherwise it will be rolled back when dropped.
641 ///
642 /// # Returns
643 ///
644 /// * `Ok(Transaction)` - A new transaction instance
645 /// * `Err(sqlx::Error)` - Database error starting transaction
646 ///
647 /// # Example
648 ///
649 /// ```rust,ignore
650 /// let mut tx = db.begin().await?;
651 /// // ... perform operations ...
652 /// tx.commit().await?;
653 /// ```
654 pub async fn begin<'a>(&self) -> Result<Transaction<'a>, sqlx::Error> {
655 let tx = self.pool.begin().await?;
656 Ok(Transaction { tx, driver: self.driver })
657 }
658
659 // ========================================================================
660 // Foreign Key Management
661 // ========================================================================
662
663 /// Checks for and assigns Foreign Keys for model `T`.
664 ///
665 /// This method examines all columns marked with `#[orm(foreign_key = "Table::Column")]`
666 /// and creates the appropriate foreign key constraints. It verifies that constraints
667 /// don't already exist before attempting to create them, preventing duplication errors.
668 ///
669 /// # Type Parameters
670 ///
671 /// * `T` - The Model type to process for foreign keys
672 ///
673 /// # Returns
674 ///
675 /// * `Ok(&Self)` - Reference to self for method chaining
676 /// * `Err(sqlx::Error)` - Database error during foreign key creation
677 ///
678 /// # Constraint Naming
679 ///
680 /// Foreign key constraints are named using the pattern:
681 /// `fk_{table_name}_{column_name}`
682 ///
683 /// # Example
684 ///
685 /// ```rust,ignore
686 /// use bottle_orm::Model;
687 /// use uuid::Uuid;
688 ///
689 /// #[derive(Model)]
690 /// struct User {
691 /// #[orm(primary_key)]
692 /// id: Uuid,
693 /// username: String,
694 /// }
695 ///
696 /// #[derive(Model)]
697 /// struct Post {
698 /// #[orm(primary_key)]
699 /// id: Uuid,
700 /// #[orm(foreign_key = "User::id")]
701 /// user_id: Uuid,
702 /// title: String,
703 /// }
704 ///
705 /// // Create tables first
706 /// db.create_table::<User>().await?;
707 /// db.create_table::<Post>().await?;
708 ///
709 /// // Then assign foreign keys
710 /// db.assign_foreign_keys::<Post>().await?;
711 /// ```
712 ///
713 /// # Generated SQL Example
714 ///
715 /// ```sql
716 /// ALTER TABLE "post"
717 /// ADD CONSTRAINT "fk_post_user_id"
718 /// FOREIGN KEY ("user_id")
719 /// REFERENCES "user" ("id");
720 /// ```
721 ///
722 /// # Important Notes
723 ///
724 /// - Foreign key assignment should be done **after** all tables are created
725 /// - The referenced table and column must exist before creating the foreign key
726 /// - Use the `Migrator` to handle the correct order automatically
727 /// - Currently optimized for PostgreSQL (uses `information_schema`)
728 ///
729 /// # See Also
730 ///
731 /// * [`Migrator`] - For automatic migration order management
732 pub async fn assign_foreign_keys<T: Model>(&self) -> Result<&Self, Error> {
733 // SQLite handles FKs in create_table, so we skip here
734 if let Drivers::SQLite = self.driver {
735 return Ok(self);
736 }
737
738 // Get table name in snake_case format
739 let table_name = T::table_name().to_snake_case();
740 let columns = T::columns();
741
742 // Process each column that has a foreign key definition
743 for col in columns {
744 if let (Some(f_table), Some(f_key)) = (col.foreign_table, col.foreign_key) {
745 // Clean up column and reference names
746 let col_name = col.name.strip_prefix("r#").unwrap_or(col.name).to_snake_case();
747 let f_table_clean = f_table.to_snake_case();
748 let f_key_clean = f_key.to_snake_case();
749
750 // Generate constraint name
751 let constraint_name = format!("fk_{}_{}", table_name, col_name);
752
753 // Check if constraint already exists
754 let count: i64 = match self.driver {
755 Drivers::Postgres => {
756 let check_query =
757 "SELECT count(*) FROM information_schema.table_constraints WHERE constraint_name = $1";
758 let row = sqlx::query(check_query).bind(&constraint_name).fetch_one(&self.pool).await?;
759 row.try_get(0).unwrap_or(0)
760 }
761 Drivers::MySQL => {
762 let check_query = "SELECT count(*) FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = ? AND TABLE_SCHEMA = DATABASE()";
763 let row = sqlx::query(check_query).bind(&constraint_name).fetch_one(&self.pool).await?;
764 row.try_get(0).unwrap_or(0)
765 }
766 Drivers::SQLite => 0, // Unreachable
767 };
768
769 // Skip if constraint already exists
770 if count > 0 {
771 continue;
772 }
773
774 // Create foreign key constraint
775 let alter_query = format!(
776 "ALTER TABLE \"{}\" ADD CONSTRAINT \"{}\" FOREIGN KEY (\"{}\") REFERENCES \"{}\" (\"{}\")",
777 table_name, constraint_name, col_name, f_table_clean, f_key_clean
778 );
779
780 sqlx::query(&alter_query).execute(&self.pool).await?;
781 }
782 }
783
784 Ok(self)
785 }
786}
787
788/// A trait representing a database connection or transaction.
789///
790/// This trait abstracts over `Database` (pool) and `Transaction` types, allowing
791/// the `QueryBuilder` to work seamlessly with both. It uses Generic Associated Types (GATs)
792/// to handle the lifetimes of the executor references correctly.
793pub trait Connection {
794 /// The type of the executor returned by this connection.
795 ///
796 /// This uses GATs to bind the lifetime of the executor (`'c`) to the lifetime
797 /// of the borrow of the connection (`&'c mut self`).
798 type Exec<'c>: sqlx::Executor<'c, Database = sqlx::Any>
799 where
800 Self: 'c;
801
802 /// Returns a mutable reference to the SQLx executor.
803 ///
804 /// # Returns
805 ///
806 /// An executor capable of running SQL queries (either a Pool or a Transaction).
807 fn executor<'c>(&'c mut self) -> Self::Exec<'c>;
808}
809
810/// Implementation of Connection for the main Database struct.
811///
812/// Uses the internal connection pool to execute queries.
813impl Connection for Database {
814 type Exec<'c> = &'c sqlx::Pool<sqlx::Any>;
815
816 fn executor<'c>(&'c mut self) -> Self::Exec<'c> {
817 &self.pool
818 }
819}
820
821/// Implementation of Connection for a mutable reference to Database.
822impl<'a> Connection for &'a mut Database {
823 type Exec<'c>
824 = &'c sqlx::Pool<sqlx::Any>
825 where
826 Self: 'c;
827
828 fn executor<'c>(&'c mut self) -> Self::Exec<'c> {
829 &self.pool
830 }
831}
832
833/// Implementation of Connection for a mutable reference to sqlx::Transaction.
834impl<'a> Connection for &mut sqlx::Transaction<'a, sqlx::Any> {
835 type Exec<'c>
836 = &'c mut sqlx::AnyConnection
837 where
838 Self: 'c;
839
840 fn executor<'c>(&'c mut self) -> Self::Exec<'c> {
841 &mut **self
842 }
843}
844
845// ============================================================================
846// Raw SQL Query Builder
847// ============================================================================
848/// A builder for executing raw SQL queries with parameter binding.
849///
850/// Returned by `Database::raw()` or `Transaction::raw()`. Allows constructing safe, parameterized
851/// SQL queries that can bypass the standard model-based QueryBuilder when
852/// complex SQL features (CTEs, Window Functions, etc.) are needed.
853pub struct RawQuery<'a, C> {
854 conn: C,
855 sql: &'a str,
856 args: AnyArguments<'a>,
857}
858
859impl<'a, C> RawQuery<'a, C>
860where
861 C: Connection + Send,
862{
863 /// Creates a new RawQuery instance.
864 pub(crate) fn new(conn: C, sql: &'a str) -> Self {
865 Self { conn, sql, args: AnyArguments::default() }
866 }
867
868 /// Binds a parameter to the query.
869 ///
870 /// # Arguments
871 ///
872 /// * `value` - The value to bind. Must implement `sqlx::Encode` and `sqlx::Type`.
873 pub fn bind<T>(mut self, value: T) -> Self
874 where
875 T: 'a + sqlx::Encode<'a, sqlx::Any> + sqlx::Type<sqlx::Any> + Send + Sync,
876 {
877 let _ = self.args.add(value);
878 self
879 }
880
881 /// Executes the query and returns all matching rows mapped to type `T`.
882 pub async fn fetch_all<T>(mut self) -> Result<Vec<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_all(self.conn.executor()).await
887 }
888
889 /// Executes the query and returns the first matching row mapped to type `T`.
890 pub async fn fetch_one<T>(mut self) -> Result<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_one(self.conn.executor()).await
895 }
896
897 /// Executes the query and returns the first matching row, or None if not found.
898 pub async fn fetch_optional<T>(mut self) -> Result<Option<T>, Error>
899 where
900 T: for<'r> sqlx::FromRow<'r, sqlx::any::AnyRow> + Send + Unpin,
901 {
902 sqlx::query_as_with::<_, T, _>(self.sql, self.args).fetch_optional(self.conn.executor()).await
903 }
904
905 /// Executes the query and returns a single scalar value.
906 ///
907 /// Useful for queries like `SELECT count(*) ...` or `SELECT id ...`.
908 pub async fn fetch_scalar<O>(mut self) -> Result<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_one(self.conn.executor()).await
913 }
914
915 /// Executes the query and returns a single scalar value, or None if not found.
916 pub async fn fetch_scalar_optional<O>(mut self) -> Result<Option<O>, Error>
917 where
918 O: for<'r> sqlx::Decode<'r, sqlx::Any> + sqlx::Type<sqlx::Any> + Send + Unpin,
919 {
920 sqlx::query_scalar_with::<_, O, _>(self.sql, self.args).fetch_optional(self.conn.executor()).await
921 }
922
923 /// Executes the query (INSERT, UPDATE, DELETE) and returns the number of affected rows.
924 pub async fn execute(mut self) -> Result<u64, Error> {
925 let result = sqlx::query_with(self.sql, self.args).execute(self.conn.executor()).await?;
926
927 Ok(result.rows_affected())
928 }
929}