Skip to content

Conversation

@luislavena
Copy link
Owner

@luislavena luislavena commented Oct 11, 2025

Drift was previously limited to SQLite3, which meant it couldn't be used by others who needed PostgreSQL or MySQL for their applications.

This PR adds support for multiple databases by introducing a Drift::Dialect interface that encapsulates the SQL syntax differences between SQLite3, PostgreSQL, and MySQL. Now all three databases work seamlessly with both the library API and the CLI.

Drop support for `DB::Connection` for `db` in Migrator in order to
simplify the types used by it.

This helps to reduce the confusion on what to pass to Migrator but also
helps with type resolution as `DB::Database | DB::Connection` was
presenting some issues when dealing with DB::Database and DB::Connection
method overload.
Abstract away SQL-specific elements from Migrator logic into
Drift::Dialect-derived implementations.

Do this to prepare for other dialects/drivers (MySQL, PostgreSQL, etc).
Perform sorting and filtering on SQLite side to avoid allocating that
response and performing the filtering in Crystal side (which is way less
optimized that SQLite VM)

- Before: 2.25k ops/sec (445µs per operation, 253kB memory)
- After: 24.65k ops/sec (40.57µs per operation, 12.2kB memory)

Benchmark script:
* https://gist.github.com/luislavena/460de5a9cb1e8a4085de14aa6644c64a
@luislavena luislavena force-pushed the refactor-sql-dialects branch from 95e01c8 to 7a74960 Compare October 13, 2025 00:04
@github-actions
Copy link

Preview Binaries - Build #1

Built from commit 7a74960 by @luislavena

Downloads:

Note: These artifacts will expire in 90 days. View build logs

As we prepare Drift to be DB-agnostic, move out the SQLite3 dependency
to be only used during development.
Almost direct translation from SQLite3 implementation with adjustments
for table and arguments placeholders
Introduces DIALECTS hash to support testing against multiple
database dialects (SQLite3 and PostgreSQL). Each dialect can
be skipped via environment variable.

Also adds cleanup_tables helper to drop test tables between
PostgreSQL test runs to ensure clean state.
Introduces a macro to run parameterized tests across multiple
database dialects. The macro generates test blocks for each
configured dialect (SQLite3 and PostgreSQL), providing a clean
database connection via a Proc. PostgreSQL connections trigger
automatic cleanup of test tables before each test.

The macro is defined but not yet used, ensuring no behavior
changes to the existing test suite.
Modified ready_migrator to require db parameter instead of using a
default value. Updated prepared_migrator to accept db parameter and
removed internal database creation. This prepares the helpers for
parameterized dialect testing.

Tests now fail with compilation errors as expected. These will be
resolved in subsequent tasks.
Tests for #prepared? and #prepare! methods now run against both
SQLite3 and PostgreSQL dialects using the for_each_dialect macro.

Each test properly manages database connections using dialect_db.call
and closes connections after assertions complete.
Updates all prepared_migrator calls to pass the required db parameter
and adds proper db.close cleanup to prevent resource leaks. This
change is part of the multi-dialect testing refactoring.
Wrap #applied?, #applied_ids, #apply_plan, #apply(id), and
#apply(ids) test blocks in for_each_dialect macro to run against
both SQLite3 and PostgreSQL databases.
Wrap #rollback(id) and #rollback(ids) test blocks in
for_each_dialect macro to run against both SQLite3 and
PostgreSQL databases.
Updates the macro to properly evaluate SKIP_POSTGRESQL environment
variable at compile time using env(). Removes skip flag from DIALECTS
configuration hash as it's now handled directly in the macro.

PostgreSQL tests now run but fail due to incomplete Dialect
implementation (all methods return TODO/empty values).
Slightly adjust used queries inside specs to avoid having to write
different queries per-dialect.
Updates documentation to reflect the new database support/compatibility.
Adds crystal-lang/crystal-mysql to support MySQL dialect
implementation.
Adds MySQL 8.0 service for local development and testing.
Configures health checks and persistent volume storage.
Adds MySQL dialect with INFORMATION_SCHEMA-based schema checking,
question mark parameter placeholders, and BIGINT column types.
Adds MySQL to DIALECTS configuration with opt-out via
SKIP_MYSQL environment variable. Updates helper functions
to handle MySQL-specific SQL syntax.

Also fixes MySQL Docker image to 8.0 with native password
authentication for compatibility with crystal-mysql driver,
and corrects dialect detection for MySql:: namespace.
Tests that MySQL connections are properly detected and
return Drift::Dialect::MySQL instance.
Configures MySQL 8.0 container in GitHub Actions workflow
to run full test suite against all supported databases.
Requires mysql driver so CLI can connect to MySQL databases.
Updates supported databases list, adds MySQL usage example,
and documents testing with multiple databases.
Use MySQL 5.7 instead of 8.0 in CI as crystal-mysql does not support
modern authentication plugin and fails regular one.
@luislavena luislavena force-pushed the refactor-sql-dialects branch from 7a74960 to 0495b73 Compare October 13, 2025 00:08
@github-actions
Copy link

Preview Binaries - Build #2

Built from commit 0495b73 by @luislavena

Downloads:

Note: These artifacts will expire in 90 days. View build logs

@luislavena
Copy link
Owner Author

luislavena commented Oct 13, 2025

Hello @watzon, took a bit but explored adding PostgreSQL support to drift:

image

😆

You can check out this branch or download one of the pre-compiled binaries from the build artifacts.

Let me know what do you think.

Cheers.
❤️ ❤️ ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants