Skip to content

Active version change transaction is not accessible in OpenDbRequestBuilder::with_on_upgraded_needed #66

@mgoldenberg

Description

@mgoldenberg

Overview

Currently, OpenDbRequestBuilder::with_on_upgrade_needed passes two arguments to the provided callback: a VersionChangeEvent and a Database. Unfortunately, neither of these provides access to the active version change transaction, which means that it is impossible to accomplish certain kinds of tasks.

Note that in previous releases of this crate, the active version change transaction was accessible! (see here).

Example: Adding Index to Existing Object Store During Upgrade

For example, one cannot add an index to an existing object store. This is because in order to get access to the existing object store, the only option is to start a new transaction on the database, which interferes with the active one.

Code

use indexed_db_futures::{
    Build,
    database::Database,
    error::{Error, OpenDbError},
    transaction::TransactionMode,
};

const OBJECT_STORE: &str = "object_store";

pub async fn upgrade(name: &str) -> Result<(), OpenDbError> {
    let current = Database::open(name).await?.version() as u32;

    if current < 2 {
        Database::open(name)
            .with_version(2u32)
            .with_on_upgrade_needed(|_, db: Database| -> Result<(), Error> {
                db.create_object_store(OBJECT_STORE).build()?;
                Ok(())
            })
            .await?
            .close();
    }

    if current < 3 {
        Database::open(name)
            .with_version(3u32)
            .with_on_upgrade_needed(|_, db: Database| -> Result<(), Error> {
                db.transaction(OBJECT_STORE)  // Fails to start this transaction!
                    .with_mode(TransactionMode::Versionchange)
                    .build()?
                    .object_store(OBJECT_STORE)?
                    .create_index("index", "key_path".into())
                    .build()?;
                Ok(())
            })
            .await?
            .close();
    }

    Ok(())
}

Test

To illustrate that the code above fails to run, one can construct the following test.

#[cfg(all(test, target_family = "wasm"))]
mod tests {
    use crate::upgrade;

    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

    #[wasm_bindgen_test::wasm_bindgen_test]
    async fn test_upgrade() {
        upgrade("database").await.expect("should upgrade database");
    }
}

One can then run this test with following command.

wasm-pack test --firefox --headless

And, finally, the error output contains the following information.

Base(DomException(InvalidStateError(DomException { obj: Object { obj: JsValue(InvalidStateError: IDBDatabase.transaction: Can't start a transaction while running an upgrade transaction)}})))

I'm not sure if there is a convenient workaround to accomplish what I'm trying to do here. If not, would it be possible to expose the active version change transaction through the VersionChangeEvent or the Database? I'm happy to try and push a pull request is you think it wouldn't be too involved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions