Kamil Grosicki
Kamil Grosicki

Reputation: 163

How to properly add indices in database migration for Room?

I have a problem migrating my Room database. In updated database I have to change one field from integer to double value. I read its not as easy as it sounds and in order to do that I have to create new temporary table with this changed property, copy all values from previous table, delete old one and finally rename the temporary table.

My entity has 2 indices, which are causing problems. Here is my current best solution that is not passing auto generated room migration validation.

oceny is the name of the original table, oceny_temp is the temportary name. Even though i am adding those indices in explicitly with create index the validation is still not passing as it says that in expected table has 2 indices and in found there is 0.

static final Migration MIGRATION_25_26 = new Migration(25,26) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {

        database.execSQL("CREATE TABLE IF NOT EXISTS oceny_temp" +
                        "(`idOceny` TEXT NOT NULL, " +
                        "`idUcznia` TEXT, " +
                        "`idPrzedmiotu` TEXT, " +
                        "`semestr` INTEGER NOT NULL, " +
                        "`typOceny` INTEGER NOT NULL, " +
                        "`wartosc` TEXT, " +
                        "`wartoscDoSredniej` REAL, " +
                        "`czyProponowana` INTEGER NOT NULL, " +
                        "`kategoria` TEXT, " +
                        "`waga` REAL NOT NULL, " +
                        "`maxPunktow` REAL NOT NULL, " +
                        "`odczytana` INTEGER NOT NULL, " +
                        "`dataWystawienia` TEXT, " +
                        "`wystawiajacy` TEXT, " +
                        "`typOczekiwania` INTEGER NOT NULL, " +
                        "`wersjaRekordu` TEXT, " +
                        "`rekordUsuniety` INTEGER NOT NULL, " +
                        "PRIMARY KEY(`idOceny`), " +
                        "FOREIGN KEY(`idUcznia`) REFERENCES `uczniowie`(`idUcznia`) ON UPDATE NO ACTION ON DELETE CASCADE )");

        database.execSQL("CREATE INDEX IF NOT EXISTS index_oceny_idPrzedmiotu ON oceny_temp (idPrzedmiotu)");
        database.execSQL("CREATE INDEX IF NOT EXISTS index_oceny_idUcznia ON oceny_temp (idUcznia)");


        // Copy the data
        database.execSQL(
                "INSERT INTO oceny_temp (idOceny, idUcznia, idPrzedmiotu, semestr, typOceny, wartosc, wartoscDoSredniej, czyProponowana, kategoria, waga, maxPunktow, odczytana, dataWystawienia, wystawiajacy, typOczekiwania, wersjaRekordu, rekordUsuniety) " +
                                "SELECT idOceny, idUcznia, idPrzedmiotu, semestr, typOceny, wartosc, wartoscDoSredniej, czyProponowana, kategoria, waga, maxPunktow, odczytana, dataWystawienia, wystawiajacy, typOczekiwania, wersjaRekordu, rekordUsuniety FROM oceny");

        // Remove the old table
        database.execSQL("DROP TABLE oceny");

        // Change the table name to the correct one
        database.execSQL("ALTER TABLE oceny_temp RENAME TO oceny");
    }
};

The error message:

Migration didn't properly handle 

oceny(de.wolterskluwer.idziennik.model.grades.Grade).
 Expected:
TableInfo{name='oceny', columns={dataWystawienia=Column{name='dataWystawienia', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, typOczekiwania=Column{name='typOczekiwania', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, idPrzedmiotu=Column{name='idPrzedmiotu', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, kategoria=Column{name='kategoria', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, rekordUsuniety=Column{name='rekordUsuniety', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, wystawiajacy=Column{name='wystawiajacy', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, wersjaRekordu=Column{name='wersjaRekordu', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, maxPunktow=Column{name='maxPunktow', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, czyProponowana=Column{name='czyProponowana', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, typOceny=Column{name='typOceny', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, semestr=Column{name='semestr', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, waga=Column{name='waga', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, odczytana=Column{name='odczytana', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, idUcznia=Column{name='idUcznia', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, idOceny=Column{name='idOceny', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1}, wartosc=Column{name='wartosc', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, wartoscDoSredniej=Column{name='wartoscDoSredniej', type='REAL', affinity='4', notNull=false, primaryKeyPosition=0}}, foreignKeys=[ForeignKey{referenceTable='uczniowie', onDelete='CASCADE', onUpdate='NO ACTION', columnNames=[idUcznia], referenceColumnNames=[idUcznia]}], indices=[Index{name='index_oceny_idPrzedmiotu', unique=false, columns=[idPrzedmiotu]}, Index{name='index_oceny_idUcznia', unique=false, columns=[idUcznia]}]}
 Found:
TableInfo{name='oceny', columns={dataWystawienia=Column{name='dataWystawienia', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, typOczekiwania=Column{name='typOczekiwania', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, idPrzedmiotu=Column{name='idPrzedmiotu', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, kategoria=Column{name='kategoria', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, rekordUsuniety=Column{name='rekordUsuniety', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, wystawiajacy=Column{name='wystawiajacy', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, wersjaRekordu=Column{name='wersjaRekordu', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, maxPunktow=Column{name='maxPunktow', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, czyProponowana=Column{name='czyProponowana', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, typOceny=Column{name='typOceny', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, semestr=Column{name='semestr', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, waga=Column{name='waga', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, odczytana=Column{name='odczytana', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}, idUcznia=Column{name='idUcznia', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, idOceny=Column{name='idOceny', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1}, wartosc=Column{name='wartosc', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, wartoscDoSredniej=Column{name='wartoscDoSredniej', type='REAL', affinity='4', notNull=false, primaryKeyPosition=0}}, foreignKeys=[ForeignKey{referenceTable='uczniowie', onDelete='CASCADE', onUpdate='NO ACTION', columnNames=[idUcznia], referenceColumnNames=[idUcznia]}], indices=[]}

Additional info: i added IF NOT EXISTS to the create index, because it was crashing saying that the index already exists, which doesn't make sense to me

Upvotes: 6

Views: 2358

Answers (2)

Sharp
Sharp

Reputation: 1399

The problem is that you're sending a "CREATE INDEX IF NOT EXISTS" command, but the index with the same name already exists, therefore no new index is created.

You can solve this by either dropping the existing index before trying to create the new one, or by simply moving the create index command at the end of the flow.

Here's the full code which should work:

static final Migration MIGRATION_25_26 = new Migration(25,26) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {

        database.execSQL("CREATE TABLE IF NOT EXISTS oceny_temp" +
                        "(`idOceny` TEXT NOT NULL, " +
                        "`idUcznia` TEXT, " +
                        "`idPrzedmiotu` TEXT, " +
                        "`semestr` INTEGER NOT NULL, " +
                        "`typOceny` INTEGER NOT NULL, " +
                        "`wartosc` TEXT, " +
                        "`wartoscDoSredniej` REAL, " +
                        "`czyProponowana` INTEGER NOT NULL, " +
                        "`kategoria` TEXT, " +
                        "`waga` REAL NOT NULL, " +
                        "`maxPunktow` REAL NOT NULL, " +
                        "`odczytana` INTEGER NOT NULL, " +
                        "`dataWystawienia` TEXT, " +
                        "`wystawiajacy` TEXT, " +
                        "`typOczekiwania` INTEGER NOT NULL, " +
                        "`wersjaRekordu` TEXT, " +
                        "`rekordUsuniety` INTEGER NOT NULL, " +
                        "PRIMARY KEY(`idOceny`), " +
                        "FOREIGN KEY(`idUcznia`) REFERENCES `uczniowie`(`idUcznia`) ON UPDATE NO ACTION ON DELETE CASCADE )");


        // Copy the data
        database.execSQL(
                "INSERT INTO oceny_temp (idOceny, idUcznia, idPrzedmiotu, semestr, typOceny, wartosc, wartoscDoSredniej, czyProponowana, kategoria, waga, maxPunktow, odczytana, dataWystawienia, wystawiajacy, typOczekiwania, wersjaRekordu, rekordUsuniety) " +
                                "SELECT idOceny, idUcznia, idPrzedmiotu, semestr, typOceny, wartosc, wartoscDoSredniej, czyProponowana, kategoria, waga, maxPunktow, odczytana, dataWystawienia, wystawiajacy, typOczekiwania, wersjaRekordu, rekordUsuniety FROM oceny");

        // Remove the old table
        database.execSQL("DROP TABLE oceny");

        // Change the table name to the correct one
        database.execSQL("ALTER TABLE oceny_temp RENAME TO oceny");

        // Recreate the indices
        database.execSQL("CREATE INDEX IF NOT EXISTS index_oceny_idPrzedmiotu ON oceny (idPrzedmiotu)");
        database.execSQL("CREATE INDEX IF NOT EXISTS index_oceny_idUcznia ON oceny (idUcznia)");
    }
};

Note that, since we already renamed the table at the end of the flow, we create the index upon that migrated table.

Upvotes: 3

Njuacha Hubert
Njuacha Hubert

Reputation: 566

It seems like some how the indices are lost when the table is renamed. So I resolved this issue by adding one more line after renaming my temporal table to the correct one. This creates the indices on the renamed table. For your case it would be like this ...

    // Change the table name to the correct one
    database.execSQL("ALTER TABLE oceny_temp RENAME TO oceny");

    // Create the indices on the table
    database.execSQL("CREATE INDEX IF NOT EXISTS index_oceny_idPrzedmiotu ON oceny (idPrzedmiotu)");
    database.execSQL("CREATE INDEX IF NOT EXISTS index_oceny_idUcznia ON oceny (idUcznia)");

    

Upvotes: 5

Related Questions