Skip to content

Latest commit

 

History

History
252 lines (159 loc) · 12.1 KB

File metadata and controls

252 lines (159 loc) · 12.1 KB

How to contribute

So, you've decided to contribute, that's great!

You can use this document to figure out how and where to start.

Getting started

  • 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.

Making changes

  • Fork the repository on GitHub.
  • Create a branch on your fork.
    • You can usually base it on the main branch.
    • Make sure not to commit directly to main.
  • 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.

What to work on

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:

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:

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:

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 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

Step 3: Connection Implementation

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:

Test: Basic connection establishment

Step 4: Type System Integration

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:

  • 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 for your database. This handles parameter binding in prepared statements.

Study how existing databases handle parameter encoding:

Test: Parameter binding and encoding

Step 6: Query Execution

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)

Step 7: Statement Preparation

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

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:

Test: BEGIN, COMMIT, ROLLBACK operations

Step 9: Integration with Any Driver

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

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 for your database.

Step 11: Advanced Features (Optional)

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.

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: