So, you've decided to contribute, that's great!
You can use this document to figure out how and where to start.
- Make sure you have a GitHub account.
- Take a look at existing issues.
- If you need to create an issue:
- Make sure to clearly describe it.
- Including steps to reproduce when it is a bug.
- Include the version of SQLx used.
- Include the database driver and version.
- Include the database version.
- Fork the repository on GitHub.
- Create a branch on your fork.
- You can usually base it on the
mainbranch. - Make sure not to commit directly to
main.
- You can usually base it on the
- Make commits of logical and atomic units.
- Make sure you have added the necessary tests for your changes.
- Push your changes to a topic branch in your fork of the repository.
- Submit a pull request to the original repository.
We try to mark issues with a suggested level of experience (in Rust/SQL/SQLx). Where possible we try to spell out how to go about implementing the feature.
To start with, check out:
- Issues labeled as "good first issue".
- Issues labeled as "Easy".
Additionally, it's always good to work on improving/adding examples and documentation.
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.
SQLx uses a trait-based architecture where each database implements a set of core traits:
-
Database: 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: Handles database connections and basic operations like connecting, closing, pinging, and transaction management. See examples: PostgreSQL, MySQL, SQLite. -
Row: Represents a single row from a query result, providing access to column data by index or name. Examples: PgRow, MySqlRow. -
Column: Provides metadata about columns (name, type, etc.). Examples: PgColumn, MySqlColumn. -
ValueandValueRef: Handle owned and borrowed values from the database. Examples: PgValue, MySqlValue. -
TypeInfo: Provides information about database types for the type system. Examples: PgTypeInfo, MySqlTypeInfo. -
Arguments: Handles query parameter binding and encoding. Examples: PgArguments, MySqlArguments. -
Statement: Handles prepared statements and their metadata. Examples: PgStatement, MySqlStatement. -
Query execution: Implement
Executorfor your connection type to handle query execution and result streaming.
Before starting, ensure you have:
- A working database server/client library to connect to your database
- Understanding of your database's wire protocol or client API
- Knowledge of your database's type system and SQL dialect
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:
mkdir -p sqlx-core/src/yourdb1.3 Add the module to sqlx-core/src/lib.rs and main src/lib.rs with appropriate feature gates.
Test: cargo check --features yourdb
2.1 Create your database struct that will implement the Database trait. Look at Postgres, MySQL, or SQLite 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
3.1 Implement Connection for your database. This handles:
- Connection establishment and URL parsing (
ConnectOptions) - 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)
- Authentication (see MySQL auth)
- Connection options parsing (see PostgreSQL options)
Test: Basic connection establishment
4.1 Implement Type, Encode, and Decode traits for basic Rust types.
Start with simple types like strings and integers. Look at existing type implementations:
Each type needs:
Typeimplementation to provide type metadataEncodeimplementation to convert Rust values to database formatDecodeimplementation to convert database values to Rust types
Test: Type conversion unit tests
5.1 Implement Arguments for your database. This handles parameter binding in prepared statements.
Study how existing databases handle parameter encoding:
- PostgreSQL arguments (binary protocol)
- MySQL arguments (binary protocol)
- SQLite arguments (uses native SQLite binding)
Test: Parameter binding and encoding
6.1 Implement Executor for your connection type. This is where queries are actually sent to the database and results are processed.
Look at executor implementations:
6.2 Implement Row, Column, and QueryResult types to handle query results and metadata.
Test: Basic query execution (SELECT 1, simple queries)
7.1 Implement Statement 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
8.1 Implement transaction support by implementing the transaction-related methods in your Connection and creating a TransactionManager.
Look at existing transaction implementations:
Test: BEGIN, COMMIT, ROLLBACK operations
9.1 Add your database to the Any driver to support runtime database selection.
This involves:
- Adding your database to
AnyKind - Adding connection type to
AnyConnectionKind - 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
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 for your database.
11.1 Migration support: Implement MigrateDatabase if your database supports schema migrations.
11.2 Listen/Notify: If your database supports real-time notifications, implement listener functionality (see PostgreSQL listener).
11.3 Additional type support: Add support for database-specific types, arrays, JSON, etc.
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.
At each step, create tests that verify:
- Compilation:
cargo check --features yourdb - Unit tests: Test individual components in isolation
- Integration tests: Test database connectivity and operations
- Type safety: Ensure compile-time type checking works
- Runtime behavior: Test actual database operations
-
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.
- 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
BoxFuturefor async trait methods andBoxStreamfor 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.
If you're unsure about your contribution or simply want to ask a question about anything, you can:
- Visit the SQLx Discord server
- Discuss something directly in the Github issue.