Skip to content

LucaCappelletti94/sqlite-wasm-uuid-rs

Repository files navigation

sqlite-wasm-uuid-rs

Crates.io Docs.rs CI License

Rust SQLite-WASM extension for UUIDv4 (Random) & UUIDv7 (Time-ordered) generation. Powered by the uuid crate and built for sqlite-wasm-rs. The crate is no_std and compiles to wasm32-unknown-unknown.

SQL Functions

  • uuid(): Returns a new random Version 4 UUID as a 36-character string.
  • uuid_str(X): Parses X (blob or text) and returns a canonical 36-char string.
  • uuid_blob(X): Converts X to a 16-byte blob, or generates a new one if no X.
  • uuid7(): Returns a new Version 7 UUID as a 36-character string.
  • uuid7_blob(): Returns a new Version 7 UUID as a 16-byte BLOB. If called with 1 argument, converts the input UUID (TEXT or BLOB format) to a 16-byte BLOB.

For instance, you can now set the DEFAULT value of a TEXT column to uuid() and of a BLOB column to uuid_blob() to have UUIDs automatically generated upon insertion.

CREATE TABLE so_many_uuids (
    id_text TEXT PRIMARY KEY DEFAULT (uuid()),
    id_blob BLOB PRIMARY KEY DEFAULT (uuid_blob()),
    idv7_text TEXT DEFAULT (uuid7()),
    idv7_blob BLOB DEFAULT (uuid7_blob())
);

Usage

Add the dependency to your Cargo.toml:

[dependencies]
sqlite-wasm-uuid-rs = "0.1"

Then, depending on which library you are using to interface with SQLite-WASM, register the extension and use the functions as shown below. Please be mindful that the following examples are not executed as part of the CI tests because the different libraries have different sqlite dependencies which would conflict with each other. Instead, complete working examples are provided in the test-rusqlite and test-diesel directories.

Rusqlite

use rusqlite::Connection;

// Register the extension (unsafe because it affects global SQLite state)
unsafe {
    sqlite_wasm_uuid_rs::register().expect("failed to register");
}

let conn = Connection::open_in_memory().unwrap();

// Generate a random UUIDv4 string
let uuid_str: String = conn.query_row("SELECT uuid()", [], |r| r.get(0)).unwrap();

// Generate a random UUIDv4 blob
let uuid_blob: Vec<u8> = conn.query_row("SELECT uuid_blob()", [], |r| r.get(0)).unwrap();

See test-rusqlite for a complete CI-tested example.

Diesel

Do note that if you are using diesel you can avoid using this extension altogether by simply using declare_sql_function to map the Rust functions you need. Nevertheless, if you want to use the extension, here's how to do it:

// Register the extension
unsafe {
    sqlite_wasm_uuid_rs::register().expect("failed to register");
}

// Use raw SQL or `sql_query`
diesel::sql_query("SELECT uuid()").execute(&mut conn)?;

// Or use the functions in your schema definitions (requires custom SQL types)
// See test-diesel for the full boilerplate setup.

See test-diesel for a complete CI-tested example.

Here instead for completeness is how you could implement the UUID functions yourself in Diesel without using this extension:

#[declare_sql_function]
extern "SQL" {
    /// Generates a UUID v4
    fn uuidv4() -> Binary;
    /// Generates a UUID v7
    fn uuidv7() -> Binary;
}

/// Returns a UUID v4 as a byte vector
fn uuidv4_impl() -> Vec<u8> {
    Uuid::new_v4().as_bytes().to_vec()
}

/// Returns a UUID v7 as a byte vector, using the UTC timestamp.
fn uuidv7_impl() -> Vec<u8> {
    let utc_now = chrono::Utc::now();
    let seconds = utc_now.timestamp() as u64;
    let subsec_nanos = utc_now.timestamp_subsec_nanos();
    let counter = 0;
    let usable_counter_bits = 12;
    let timestamp =
        uuid::Timestamp::from_unix_time(seconds, subsec_nanos, counter, usable_counter_bits);
    Uuid::new_v7(timestamp).as_bytes().to_vec()
}

fn establish_connection() -> SqliteConnection {
    let connection =
        SqliteConnection::establish(":memory:").expect("Error connecting to in-memory SQLite");
    uuidv4_utils::register_impl(&connection, uuidv4_impl).expect("Failed to register uuidv4");
    uuidv7_utils::register_impl(&connection, uuidv7_impl).expect("Failed to register uuidv7");
    connection
}

Testing

To run the tests (including the usage examples which are mirrored in the test suite), use wasm-pack:

# Run tests in Headless Firefox
wasm-pack test --firefox --headless

# Or in Headless Chrome
wasm-pack test --chrome --headless

Note: Standard cargo test does not work for this crate as it targets wasm32-unknown-unknown and requires a browser environment provided by wasm-pack.

About

Extension for `sqlite-wasm-rs` adding support for `uuid4` and `uuid7`

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages