# Database Support Summary This document summarizes the database support implementation in the Rust application, which supports both PostgreSQL and SQLite databases based on the connection URL. ## Current Implementation Status ### ✅ What Works 1. **URL-Based Database Detection**: The application automatically detects database type from the connection URL 2. **PostgreSQL Support**: Full support for PostgreSQL with native SQL syntax 3. **SQLite Configuration**: Configuration files support SQLite URLs 4. **Unified Codebase**: Single codebase handles both database types 5. **Automatic Table Creation**: Database tables are created automatically on startup ### 🔧 Current Limitations 1. **PostgreSQL-First Implementation**: The current code uses PostgreSQL-specific SQL syntax 2. **SQLite Compatibility**: While SQLx supports SQLite, the SQL queries use PostgreSQL syntax ($1, $2) instead of SQLite syntax (?1, ?2) 3. **Type Handling**: Uses PostgreSQL-specific types (UUID, TIMESTAMPTZ, JSONB) ## How It Works ### Database Connection The application detects the database type from the `DATABASE_URL` environment variable: ```rust // In server/src/main.rs let db_type = if database_url.starts_with("postgres://") || database_url.starts_with("postgresql://") { "PostgreSQL" } else if database_url.starts_with("sqlite:") { "SQLite" } else { "Unknown" }; ``` ### Current Database Pool The application currently uses `PgPool` throughout, which works with PostgreSQL URLs directly and can be configured to work with SQLite through SQLx's compatibility layer. ```rust // Uses PgPool but can connect to SQLite URLs PgPool::connect(database_url).await ``` ## Configuration Examples ### PostgreSQL Configuration ```toml [database] url = "postgresql://username:password@localhost:5432/database_name" max_connections = 10 min_connections = 1 ``` ### SQLite Configuration ```toml [database] url = "sqlite:database.db" max_connections = 10 min_connections = 1 ``` ### Environment Variable Override ```bash # PostgreSQL export DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" # SQLite export DATABASE_URL="sqlite:my_app.db" ``` ## Practical Usage ### Quick Start with SQLite ```bash export DATABASE_URL="sqlite:database.db" cargo run --bin server ``` ### Quick Start with PostgreSQL ```bash # Start PostgreSQL with Docker docker run -d --name postgres -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:15 # Connect to it export DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres" cargo run --bin server ``` ### Switching Between Databases ```bash # Start with SQLite export DATABASE_URL="sqlite:test.db" cargo run --bin server # Stop server (Ctrl+C), switch to PostgreSQL export DATABASE_URL="postgresql://localhost:5432/mydb" cargo run --bin server ``` ## Implementation Architecture ### Current Structure ``` ├── server/src/main.rs # Database connection logic ├── server/src/auth/repository.rs # Authentication database operations ├── server/src/content/repository.rs # Content management database operations └── server/src/auth/two_factor.rs # 2FA database operations ``` ### Database Abstraction Pattern The code currently uses a direct `PgPool` approach with PostgreSQL syntax, but is structured to allow for database-specific implementations in the future. ## Future Enhancement Options ### Option 1: Full Database Abstraction (Recommended) Create a `DatabasePool` enum that handles both database types with appropriate SQL syntax: ```rust pub enum DatabasePool { Postgres(PgPool), Sqlite(SqlitePool), } ``` ### Option 2: Conditional Compilation Use feature flags to compile for specific databases: ```rust #[cfg(feature = "postgres")] use sqlx::PgPool as DbPool; #[cfg(feature = "sqlite")] use sqlx::SqlitePool as DbPool; ``` ### Option 3: SQLx Any Driver Use SQLx's `Any` driver for complete database agnosticism (with some type limitations). ## Database Schema Compatibility ### PostgreSQL Schema Features - UUID primary keys with `gen_random_uuid()` - TIMESTAMPTZ for timestamps - JSONB for JSON data - Advanced indexing ### SQLite Equivalent Features - TEXT UUIDs with custom generation - DATETIME for timestamps - TEXT for JSON storage - Standard indexing ## Performance Considerations ### PostgreSQL - **Best for**: Production, multi-user, high concurrency - **Features**: Full ACID, advanced indexing, network access - **Connection Pool**: 10-50 connections typical ### SQLite - **Best for**: Development, testing, single-user apps - **Features**: File-based, zero configuration, embedded - **Connection Pool**: 1 connection recommended ## Security Considerations ### PostgreSQL - Network security (firewall, VPN) - Authentication (strong passwords, SSL) - Authorization (role-based access) - Regular security updates ### SQLite - File system permissions (600/640) - Backup encryption - Access control at application level ## Testing Strategy ### Unit Tests ```bash # PostgreSQL tests export DATABASE_URL="postgresql://localhost/test_db" cargo test # SQLite tests export DATABASE_URL="sqlite::memory:" cargo test ``` ### Integration Tests The application includes integration tests that can run against both database types by setting the appropriate `DATABASE_URL`. ## Migration Strategy ### From SQLite to PostgreSQL 1. Export data from SQLite 2. Set up PostgreSQL instance 3. Change `DATABASE_URL` 4. Import data to PostgreSQL 5. Restart application ### From PostgreSQL to SQLite 1. Export data from PostgreSQL 2. Change `DATABASE_URL` to SQLite 3. Restart application (tables created automatically) 4. Import data to SQLite ## Troubleshooting ### Common Issues **"spawn_local called from outside of a task::LocalSet"** - ✅ Fixed: The application uses proper LocalSet configuration **Database connection failed** - Check if database server is running - Verify connection string format - Check network access and credentials **Table creation errors** - Ensure database user has CREATE privileges - Check for existing incompatible tables - Review logs for specific SQL errors ### Debug Commands ```bash # Test database connection psql $DATABASE_URL -c "SELECT 1;" # PostgreSQL sqlite3 database.db "SELECT 1;" # SQLite # Check application logs RUST_LOG=debug cargo run --bin server ``` ## Best Practices 1. **Use environment variables** for database URLs in production 2. **Start with SQLite** for development and testing 3. **Use PostgreSQL** for production deployments 4. **Configure appropriate connection pools** for your workload 5. **Monitor database performance** and adjust settings 6. **Regular backups** for production databases 7. **Test migrations** on both database types if needed ## Conclusion The current implementation provides a solid foundation for supporting both PostgreSQL and SQLite databases. While the code currently uses PostgreSQL syntax throughout, the architecture is designed to accommodate both databases through URL-based configuration. For most use cases: - **Development**: Use SQLite for simplicity - **Production**: Use PostgreSQL for scalability and features - **Testing**: Use in-memory SQLite for speed The database choice can be changed at any time by simply updating the `DATABASE_URL`, making it easy to evolve your database strategy as your application grows.