Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change.recreate support (Changing primary key) #88

Open
acicali opened this issue Apr 21, 2015 · 11 comments
Open

change.recreate support (Changing primary key) #88

acicali opened this issue Apr 21, 2015 · 11 comments
Labels

Comments

@acicali
Copy link
Collaborator

acicali commented Apr 21, 2015

My application has the need to change a primary key on a particular table. Can you recommend a concise way of achieving this?

Thanks David!

@acicali
Copy link
Collaborator Author

acicali commented Apr 22, 2015

Using IndexedDB natively does work, but took me some time to figure out. In case someone else stumbles on this issue, the code below will work. David, please comment if this code displays poor practice or anything that may break in a future Dexie change.

(function(){

    var database = new Dexie('Testing');


    // Original table whose primary key
    // will eventually need to change
    database
        .version(1)
        .stores({
            things: '&uuid'
        });



    // Version change whose sole purpose is
    // to delete the table. One could create
    // a temporary table upon this version
    // change to store "things" if that's
    // important to you.
    database
        .version(2)
        .upgrade(function(transaction){
            transaction.idbtrans.db.deleteObjectStore('things');
        });



    // Let Dexie take over and recreate the
    // table. This would also be a good place
    // to populate the table with the "things"
    // that you saved, then you can delete the
    // temporary table.
    database
        .version(3)
        .stores({
            things: '++id'
        });



    database.open();
    database.on('error', function(error){
        console.log('Oh no! - ' + error);
    });

})();

@acicali acicali closed this as completed Apr 22, 2015
@dfahlander
Copy link
Collaborator

Ok, good that you solved it! My intention has originally been that this should just work by changing the primkey. The issue has been to keep existing data while doing this. Possible to achieve by copying data to another store before deleting it, but there's a bug in IE that makes deleteObjectStore() fail if having read from it.

I'll reopen this issue as a reminder to add a unit test for this use case.
Thanks a lot,
David

@mateuscb
Copy link

I had the same problem. Thanks to the code above I was able to get it working. However, in my case, I had to add the scope method in the second version, and explicitly set the store to null. Failure in donig so, resulted in a Failed to open db: UpgradeError: Not yet support for changing primary key. Does it make sense to call set things: null?

    database
        .version(2)
        .stores({
            things: null
        })
        .upgrade(function(transaction){
            transaction.idbtrans.db.deleteObjectStore('things');
        });

@sechel
Copy link

sechel commented Mar 7, 2017

Moin, I tried and migrated some data using this strategy and came across a strange bug/behaviour. Basically if i set a table null it is already gone in the previous upgrade steps. I'm using Dexie version 2.0.0-beta.10

  let db = new Dexie('TestDB');
  db
    .version(1)
    .stores({
      table1: '++id'
    });
  db
    .version(2)
    .stores({
      table1: null,
      table1_tmp: '++id'
    })
    .upgrade(async t => {
      let objects = await t.db.table1.toArray();
      // table1_tmp is undefined if it is set to null in the next version?!
      await t.db.table1_tmp.bulkPut(objects);
    });
  db
    .version(3)
    .stores({
      table1: '++id2',
      // setting table1_tmp to null here affects the previous upgrade step?!
      table1_tmp: null
    })
    .upgrade(async t => {
      // move objects back to table1
    });

@dfahlander
Copy link
Collaborator

Thanks for pointing out. @chrahunt is working on a rewrite of the update process that will do it step-by-step. I am hoping to get it in to 2.0 release, but can't promise that.

@sechel
Copy link

sechel commented Mar 9, 2017

I am hoping to get it in to 2.0 release, but can't promise that.

That'd be great. I'm currently debugging the runUpgraders/updateTablesAndIndexesmethods to find a quick fix but the whole process is a little involved. The global tables array doesn't seem to match the objectStoreNames of the IDBTransaction in the upgrade routine of version 2. Any suggestions?

@sechel
Copy link

sechel commented Mar 9, 2017

Ok, I gave up the debugging. Here is the workaround involving native IndexedDB access:

  let db = new Dexie('TestDB');
  db
    .version(1)
    .stores({
      table1: '++id'
    });
  db
    .version(2)
    .stores({
      table1: null,
      table1_tmp: '++id'
    })
    .upgrade(async t => {
      let objects = await t.table1.toArray();
      let tmpStore = t.idbtrans.objectStore('table1_tmp');
      objects.forEach(o => tmpStore.put(o));
    });
  db
    .version(3)
    .stores({
      table1: '++id2',
      table1_tmp: null
    })
    .upgrade(async t => {
      let objects = await new Promise<any[]>((resolve, reject) => {
        let request = t.idbtrans.objectStore('table1_tmp').getAll();
        request.onsuccess = () => resolve(request.result);
        request.onerror = () => reject(request.error);
      });
      objects.forEach(o => { o.id2 = o.id; delete o.id; });
      await t.table1.bulkPut(objects);
    });

@timbru31
Copy link
Contributor

timbru31 commented Sep 11, 2017

Is there any update on this issue? Sadly the mentioned workaround are not working for me either (always receiving the UpgradeError: Not yet support for changing primary key error) when making a two step/versions upgrade and trying to null the table. Using Dexie v2.0.0-rc.1

My bad, I've had two tables that needed an update to the primary key. Making a two step upgrade with nulling the schema, it worked for me as well!

@sim642
Copy link
Contributor

sim642 commented Jun 29, 2018

@sechel Thanks for the workaround, had to put it into a project right now at least. One remark about the workaround: request event properties are all lowercase (not camelCase), i.e. onsuccess and onerror.

@sechel
Copy link

sechel commented Jun 29, 2018

@sim642 you are right, I corrected the code. One comment on the use of async here: If you take the workaround literally then it will not work in Firefox, you'll have to use native generator functions or plain promises for that.

@dfahlander
Copy link
Collaborator

The latest 3.0 release has improved upgrading support for this use case

https://github.com/dfahlander/Dexie.js/releases/tag/v3.0.0-alpha.3

(see Improved Database Upgrading (PR #714) under Details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants