diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0fdfe8890..e99011fae7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,6 +37,214 @@ To start with, check out: Additionally, it's always good to work on improving/adding examples and documentation. +## Adding Support for New Databases + +Adding support for a new database to SQLx is a significant undertaking that requires implementing multiple traits and components. This guide provides a step-by-step approach to building a database driver progressively, with testing at each stage. + +### Overview of SQLx Architecture + +SQLx uses a trait-based architecture where each database implements a set of core traits: + +- **[`Database`](sqlx-core/src/database.rs)**: The main trait that defines all associated types for a database. This is the central trait that ties everything together and must be implemented for your database struct. + +- **[`Connection`](sqlx-core/src/connection.rs)**: Handles database connections and basic operations like connecting, closing, pinging, and transaction management. See examples: [PostgreSQL](sqlx-core/src/postgres/connection/mod.rs), [MySQL](sqlx-core/src/mysql/connection/mod.rs), [SQLite](sqlx-core/src/sqlite/connection/mod.rs). + +- **[`Row`](sqlx-core/src/row.rs)**: Represents a single row from a query result, providing access to column data by index or name. Examples: [PgRow](sqlx-core/src/postgres/row.rs), [MySqlRow](sqlx-core/src/mysql/row.rs). + +- **[`Column`](sqlx-core/src/column.rs)**: Provides metadata about columns (name, type, etc.). Examples: [PgColumn](sqlx-core/src/postgres/column.rs), [MySqlColumn](sqlx-core/src/mysql/column.rs). + +- **[`Value`](sqlx-core/src/value.rs) and [`ValueRef`](sqlx-core/src/value.rs)**: Handle owned and borrowed values from the database. Examples: [PgValue](sqlx-core/src/postgres/value.rs), [MySqlValue](sqlx-core/src/mysql/value.rs). + +- **[`TypeInfo`](sqlx-core/src/type_info.rs)**: Provides information about database types for the type system. Examples: [PgTypeInfo](sqlx-core/src/postgres/type_info.rs), [MySqlTypeInfo](sqlx-core/src/mysql/type_info.rs). + +- **[`Arguments`](sqlx-core/src/arguments.rs)**: Handles query parameter binding and encoding. Examples: [PgArguments](sqlx-core/src/postgres/arguments.rs), [MySqlArguments](sqlx-core/src/mysql/arguments.rs). + +- **[`Statement`](sqlx-core/src/statement.rs)**: Handles prepared statements and their metadata. Examples: [PgStatement](sqlx-core/src/postgres/statement.rs), [MySqlStatement](sqlx-core/src/mysql/statement.rs). + +- **Query execution**: Implement [`Executor`](sqlx-core/src/executor.rs) for your connection type to handle query execution and result streaming. + +### Prerequisites + +Before starting, ensure you have: +1. A working database server/client library to connect to your database +2. Understanding of your database's wire protocol or client API +3. Knowledge of your database's type system and SQL dialect + +### Step 1: Initial Setup and Feature Configuration + +**1.1 Add feature flags to Cargo.toml files:** + +Add your database feature to the main `Cargo.toml`, `sqlx-core/Cargo.toml`, and `sqlx-macros/Cargo.toml`. Follow the pattern used by existing databases like `postgres` or `mysql`. Include any native client library dependencies as optional dependencies. + +**1.2 Create the basic module structure:** +```bash +mkdir -p sqlx-core/src/yourdb +``` + +**1.3 Add the module to `sqlx-core/src/lib.rs` and main `src/lib.rs`** with appropriate feature gates. + +**Test:** `cargo check --features yourdb` + +### Step 2: Database Struct and Core Types + +**2.1 Create your database struct** that will implement the [`Database`](sqlx-core/src/database.rs) trait. Look at [Postgres](sqlx-core/src/postgres/database.rs), [MySQL](sqlx-core/src/mysql/database.rs), or [SQLite](sqlx-core/src/sqlite/database.rs) for examples. + +**2.2 Implement core types:** +- **TypeInfo**: Represents your database's type system. Study existing implementations to understand how to map database types to Rust types. +- **Value and ValueRef**: Handle data storage and retrieval. These work with your database's binary or text protocol. +- **DatabaseError**: Convert your database's native errors to SQLx's error system. + +**Test:** `cargo check --features yourdb` + +### Step 3: Connection Implementation + +**3.1 Implement [`Connection`](sqlx-core/src/connection.rs)** for your database. This handles: +- Connection establishment and URL parsing ([`ConnectOptions`](sqlx-core/src/connection.rs)) +- Connection lifecycle (open, close, ping) +- Basic connection management + +Study the connection implementations in existing drivers, particularly how they handle: +- Network protocols (see [PostgreSQL stream handling](sqlx-core/src/postgres/connection/stream.rs)) +- Authentication (see [MySQL auth](sqlx-core/src/mysql/connection/auth.rs)) +- Connection options parsing (see [PostgreSQL options](sqlx-core/src/postgres/options/mod.rs)) + +**Test:** Basic connection establishment + +### Step 4: Type System Integration + +**4.1 Implement [`Type`](sqlx-core/src/types/mod.rs), [`Encode`](sqlx-core/src/encode.rs), and [`Decode`](sqlx-core/src/decode.rs)** traits for basic Rust types. + +Start with simple types like strings and integers. Look at existing type implementations: +- [PostgreSQL types](sqlx-core/src/postgres/types/) +- [MySQL types](sqlx-core/src/mysql/types/) +- [SQLite types](sqlx-core/src/sqlite/types/) + +Each type needs: +- `Type` implementation to provide type metadata +- `Encode` implementation to convert Rust values to database format +- `Decode` implementation to convert database values to Rust types + +**Test:** Type conversion unit tests + +### Step 5: Query Arguments + +**5.1 Implement [`Arguments`](sqlx-core/src/arguments.rs)** for your database. This handles parameter binding in prepared statements. + +Study how existing databases handle parameter encoding: +- [PostgreSQL arguments](sqlx-core/src/postgres/arguments.rs) (binary protocol) +- [MySQL arguments](sqlx-core/src/mysql/arguments.rs) (binary protocol) +- [SQLite arguments](sqlx-core/src/sqlite/arguments.rs) (uses native SQLite binding) + +**Test:** Parameter binding and encoding + +### Step 6: Query Execution + +**6.1 Implement [`Executor`](sqlx-core/src/executor.rs)** for your connection type. This is where queries are actually sent to the database and results are processed. + +Look at executor implementations: +- [PostgreSQL executor](sqlx-core/src/postgres/connection/executor.rs) +- [MySQL executor](sqlx-core/src/mysql/connection/executor.rs) +- [SQLite executor](sqlx-core/src/sqlite/connection/executor.rs) + +**6.2 Implement Row, Column, and QueryResult types** to handle query results and metadata. + +**Test:** Basic query execution (`SELECT 1`, simple queries) + +### Step 7: Statement Preparation + +**7.1 Implement [`Statement`](sqlx-core/src/statement.rs)** if your database supports prepared statements. + +Study existing statement implementations to understand: +- Statement preparation and caching +- Parameter metadata +- Column metadata + +**Test:** Prepared statement execution with parameters + +### Step 8: Transaction Management + +**8.1 Implement transaction support** by implementing the transaction-related methods in your `Connection` and creating a `TransactionManager`. + +Look at existing transaction implementations: +- [PostgreSQL transactions](sqlx-core/src/postgres/transaction.rs) +- [MySQL transactions](sqlx-core/src/mysql/transaction.rs) + +**Test:** BEGIN, COMMIT, ROLLBACK operations + +### Step 9: Integration with Any Driver + +**9.1 Add your database to the [`Any`](sqlx-core/src/any/) driver** to support runtime database selection. + +This involves: +- Adding your database to [`AnyKind`](sqlx-core/src/any/kind.rs) +- Adding connection type to [`AnyConnectionKind`](sqlx-core/src/any/connection/mod.rs) +- Updating delegation macros in Any implementations +- Adding to other Any components (arguments, values, etc.) + +Study how existing databases are integrated into the Any driver. + +**Test:** Runtime database selection with Any driver + +### Step 10: CI and Testing Infrastructure + +**10.1 Add CI support** by updating `.github/workflows/ci.yml` with: +- Your database service in GitHub Actions +- Test job for your database +- Appropriate environment variables and health checks + +**10.2 Create integration tests** in `tests/yourdb/` following the pattern of existing database tests. + +**10.3 Add testing utilities** by implementing [`TestSupport`](sqlx-core/src/testing/mod.rs) for your database. + +### Step 11: Advanced Features (Optional) + +**11.1 Migration support**: Implement [`MigrateDatabase`](sqlx-core/src/migrate/migrate.rs) if your database supports schema migrations. + +**11.2 Listen/Notify**: If your database supports real-time notifications, implement listener functionality (see [PostgreSQL listener](sqlx-core/src/postgres/listener.rs)). + +**11.3 Additional type support**: Add support for database-specific types, arrays, JSON, etc. + +### Step 12: Documentation and Examples + +**12.1 Add comprehensive documentation** to all public APIs with examples. + +**12.2 Create examples** in `examples/yourdb/` showing common usage patterns. + +**12.3 Update the main README** to include your database in the supported databases list. + +### Testing Strategy + +At each step, create tests that verify: + +1. **Compilation**: `cargo check --features yourdb` +2. **Unit tests**: Test individual components in isolation +3. **Integration tests**: Test database connectivity and operations +4. **Type safety**: Ensure compile-time type checking works +5. **Runtime behavior**: Test actual database operations + +### Implementation Tips + +- **Study existing implementations**: The PostgreSQL, MySQL, and SQLite drivers provide excellent examples of different approaches (network protocols vs embedded databases, binary vs text protocols, etc.). + +- **Start simple**: Begin with basic string queries before adding prepared statements, transactions, and complex types. + +- **Incremental development**: Test each component thoroughly before moving to the next. + +- **Protocol efficiency**: Use binary protocols when available for better performance. + +- **Error handling**: Provide clear error messages and proper error type conversions. + +- **Memory safety**: Pay careful attention to lifetimes, especially in async contexts. + +### Common Patterns + +- **Module organization**: Follow the established pattern of separate modules for connection, types, arguments, etc. +- **Feature gating**: Ensure all database-specific code is behind feature flags. +- **Async patterns**: Use `BoxFuture` for async trait methods and `BoxStream` for result streaming. +- **Protocol handling**: Implement proper buffering and message framing for network protocols. + +This progressive approach ensures you can test and validate each component before moving to the next, making the development process manageable and reducing the likelihood of errors. + ## Communication If you're unsure about your contribution or simply want to ask a question about anything, you can: