Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


The official SurrealDB Driver for refinery


     

SurrealDB Refinery

A Refinery driver for SurrealDB, enabling database schema migrations using SurrealQL.

The API is not yet stable. Expect breaking changes before 1.0.0.

Installation

[dependencies]
surrealdb-refinery = "0.2"
surrealdb = "3.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

refinery's own types (Runner, Migration, Report, Target) are re-exported, so there is no need to depend on refinery or refinery-core directly.

The minimum supported Rust version is 1.92.

Features

protocol-ws and protocol-http are enabled by default, which is what connect("ws://…") and connect("http://…") need. Embedded storage engines are off by default, because a migration driver only needs the client API and kv-rocksdb in particular forces a C++ RocksDB build on every consumer:

# for an embedded database, or for mem:// in tests
surrealdb-refinery = { version = "0.2", features = ["kv-mem"] }

Available: protocol-ws, protocol-http, kv-mem, kv-rocksdb, kv-surrealkv.

Usage

Create migration files in a migrations/ directory:

migrations/V1__create_users.surql

DEFINE TABLE users SCHEMAFULL;
DEFINE FIELD name ON users TYPE string;
DEFINE FIELD email ON users TYPE string;
DEFINE INDEX email_idx ON users FIELDS email UNIQUE;

Run them from your application:

use surrealdb_refinery::{MigrationConnection, Runner, load_migrations};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Discover `V{version}__{name}.surql` files on disk, ordered by version.
    let migrations = load_migrations("migrations")?;

    let db = surrealdb::engine::any::connect("mem://").await?;
    // A namespace and database must be selected before migrating.
    db.use_ns("myapp").await?;
    db.use_db("myapp").await?;

    let mut connection = MigrationConnection(&db);
    let report = Runner::new(&migrations).run_async(&mut connection).await?;

    for migration in report.applied_migrations() {
        println!("applied V{} {}", migration.version(), migration.name());
    }

    Ok(())
}

MigrationConnection borrows the Surreal handle, so pass &db. It is generic over the connection type, so a local Surreal<Db> or a remote Surreal<Client> works as well as Surreal<Any>. Do not change the session's namespace or database while a run is in progress — the run holds no session of its own and would continue against the new target.

Migration files

Migration files follow the pattern V{version}__{description}.surql:

  • V1__initial_schema.surql
  • V2__add_users_table.surql
  • V3__add_indexes.surql

The .sql extension is also accepted. A file carrying either extension whose name does not match the pattern is an error, not a warning — a silently skipped migration is how a database ends up half-migrated.

Migrations must not wrap themselves in BEGIN / COMMIT. The driver runs each migration in a transaction of its own, and SurrealDB refuses a nested BEGIN with "Cannot BEGIN a transaction within a transaction". The batch is cancelled, so nothing is applied — but the migration will not run until the BEGIN and COMMIT lines are removed.

How migrations are applied

Each migration body and the history row recording it are executed in a single SurrealDB transaction. A migration therefore cannot be applied without being recorded, and if any statement fails the transaction is cancelled and nothing from that migration persists — including DDL, which SurrealDB rolls back too.

Applied migrations are recorded in refinery_schema_history, which can be renamed with Runner::set_migration_table_name. A UNIQUE index on version makes double-application an error rather than a duplicate row, which is what stops two concurrent runners from both applying the same migration.

The table name must be a bare SurrealDB identifier: ASCII letters, digits and underscores, not starting with a digit. refinery interpolates it into its own INSERT unquoted, so a name needing quotes is rejected rather than silently mishandled.

Notes and limitations

  • Async only. There is no blocking Migrate implementation, just AsyncMigrate.
  • Migrations are read at runtime, not embedded at compile time. The directory must exist wherever the binary runs. refinery's embed_migrations! only discovers .sql and .rs files, which is why this crate does its own discovery.
  • The surrealdb client crate is BUSL-1.1 licensed, so that applies to your dependency graph even though this driver is Apache-2.0.

License

Licensed under the Apache License, Version 2.0.

About

Refinery driver for SurrealDB

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages