Room Database Migration doesnt properly handle ALTER TABLE migration












18
















Java.lang.IllegalStateException



Migration didn't properly handle
user(therealandroid.github.com.roomcore.java.User).



Expected:



TableInfo{name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, age=Column{name='age',
type='INTEGER', notNull=true, primaryKeyPosition=0},
id=Column{name='id', type='INTEGER', notNull=true,
primaryKeyPosition=1}}, foreignKeys=} Found:



Found



TableInfo{ name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, id=Column{name='id',
type='INTEGER', notNull=true, primaryKeyPosition=1},
age=Column{name='age', type='INTEGER', notNull=false,
primaryKeyPosition=0}}, foreignKeys=}




I'm trying to perform a simple migration, I have a class called Userand it have two columns ID (primary key) and NAME TEXT and then I populate database with two users data, then I add the column AGE in the object User and in the Migration constant I add an alter table to add this new column and lastly I replace version of the database 1 to 2.



Here is the code



User.class



@Entity(tableName = "user")
public class User {

@PrimaryKey
private int id;

@ColumnInfo(name = "name")
private String name;

@ColumnInfo(name = "age")
private int age;


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}


Database class



@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
abstract UserDao userDao();
}


Migration code



public static Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
}
};


and it call



Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
.addMigrations(MIGRATION_1_2)
.allowMainThreadQueries()
.build();


Before change the object adding AGE and performing the migration I add two register and it works.



After performing the migration, I just tried to add a new User as bellow:



  User user = new User();
user.setName("JoooJ");
user.setId(3);
user.setAge(18);

List<User> userList = new ArrayList<>();
userList.add(user);
App.database(this).userDao().insertAll(userList); // The crash happens here


Other informations:



Android Studio 3 and I didn't tested in the actual.



Dependencies:



compile "android.arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "android.arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3


Can someone help me please, I realy don't know what im doing wrong or if it is a bug.










share|improve this question

























  • A bit of a shot in the dark, but perhaps try "ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0" (0 could be whatever you consider suitable).

    – MikeT
    Sep 22 '17 at 22:33






  • 1





    Room expects the column order to match the field order. It would appear that the results of the ALTER TABLE are resulting in a different order.

    – CommonsWare
    Sep 23 '17 at 11:36











  • Check bellow answer. It has complete description: stackoverflow.com/a/51245898/3073945

    – Md. Sajedul Karim
    Jul 9 '18 at 12:49
















18
















Java.lang.IllegalStateException



Migration didn't properly handle
user(therealandroid.github.com.roomcore.java.User).



Expected:



TableInfo{name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, age=Column{name='age',
type='INTEGER', notNull=true, primaryKeyPosition=0},
id=Column{name='id', type='INTEGER', notNull=true,
primaryKeyPosition=1}}, foreignKeys=} Found:



Found



TableInfo{ name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, id=Column{name='id',
type='INTEGER', notNull=true, primaryKeyPosition=1},
age=Column{name='age', type='INTEGER', notNull=false,
primaryKeyPosition=0}}, foreignKeys=}




I'm trying to perform a simple migration, I have a class called Userand it have two columns ID (primary key) and NAME TEXT and then I populate database with two users data, then I add the column AGE in the object User and in the Migration constant I add an alter table to add this new column and lastly I replace version of the database 1 to 2.



Here is the code



User.class



@Entity(tableName = "user")
public class User {

@PrimaryKey
private int id;

@ColumnInfo(name = "name")
private String name;

@ColumnInfo(name = "age")
private int age;


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}


Database class



@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
abstract UserDao userDao();
}


Migration code



public static Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
}
};


and it call



Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
.addMigrations(MIGRATION_1_2)
.allowMainThreadQueries()
.build();


Before change the object adding AGE and performing the migration I add two register and it works.



After performing the migration, I just tried to add a new User as bellow:



  User user = new User();
user.setName("JoooJ");
user.setId(3);
user.setAge(18);

List<User> userList = new ArrayList<>();
userList.add(user);
App.database(this).userDao().insertAll(userList); // The crash happens here


Other informations:



Android Studio 3 and I didn't tested in the actual.



Dependencies:



compile "android.arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "android.arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3


Can someone help me please, I realy don't know what im doing wrong or if it is a bug.










share|improve this question

























  • A bit of a shot in the dark, but perhaps try "ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0" (0 could be whatever you consider suitable).

    – MikeT
    Sep 22 '17 at 22:33






  • 1





    Room expects the column order to match the field order. It would appear that the results of the ALTER TABLE are resulting in a different order.

    – CommonsWare
    Sep 23 '17 at 11:36











  • Check bellow answer. It has complete description: stackoverflow.com/a/51245898/3073945

    – Md. Sajedul Karim
    Jul 9 '18 at 12:49














18












18








18


8







Java.lang.IllegalStateException



Migration didn't properly handle
user(therealandroid.github.com.roomcore.java.User).



Expected:



TableInfo{name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, age=Column{name='age',
type='INTEGER', notNull=true, primaryKeyPosition=0},
id=Column{name='id', type='INTEGER', notNull=true,
primaryKeyPosition=1}}, foreignKeys=} Found:



Found



TableInfo{ name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, id=Column{name='id',
type='INTEGER', notNull=true, primaryKeyPosition=1},
age=Column{name='age', type='INTEGER', notNull=false,
primaryKeyPosition=0}}, foreignKeys=}




I'm trying to perform a simple migration, I have a class called Userand it have two columns ID (primary key) and NAME TEXT and then I populate database with two users data, then I add the column AGE in the object User and in the Migration constant I add an alter table to add this new column and lastly I replace version of the database 1 to 2.



Here is the code



User.class



@Entity(tableName = "user")
public class User {

@PrimaryKey
private int id;

@ColumnInfo(name = "name")
private String name;

@ColumnInfo(name = "age")
private int age;


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}


Database class



@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
abstract UserDao userDao();
}


Migration code



public static Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
}
};


and it call



Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
.addMigrations(MIGRATION_1_2)
.allowMainThreadQueries()
.build();


Before change the object adding AGE and performing the migration I add two register and it works.



After performing the migration, I just tried to add a new User as bellow:



  User user = new User();
user.setName("JoooJ");
user.setId(3);
user.setAge(18);

List<User> userList = new ArrayList<>();
userList.add(user);
App.database(this).userDao().insertAll(userList); // The crash happens here


Other informations:



Android Studio 3 and I didn't tested in the actual.



Dependencies:



compile "android.arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "android.arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3


Can someone help me please, I realy don't know what im doing wrong or if it is a bug.










share|improve this question

















Java.lang.IllegalStateException



Migration didn't properly handle
user(therealandroid.github.com.roomcore.java.User).



Expected:



TableInfo{name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, age=Column{name='age',
type='INTEGER', notNull=true, primaryKeyPosition=0},
id=Column{name='id', type='INTEGER', notNull=true,
primaryKeyPosition=1}}, foreignKeys=} Found:



Found



TableInfo{ name='user', columns={name=Column{name='name', type='TEXT',
notNull=false, primaryKeyPosition=0}, id=Column{name='id',
type='INTEGER', notNull=true, primaryKeyPosition=1},
age=Column{name='age', type='INTEGER', notNull=false,
primaryKeyPosition=0}}, foreignKeys=}




I'm trying to perform a simple migration, I have a class called Userand it have two columns ID (primary key) and NAME TEXT and then I populate database with two users data, then I add the column AGE in the object User and in the Migration constant I add an alter table to add this new column and lastly I replace version of the database 1 to 2.



Here is the code



User.class



@Entity(tableName = "user")
public class User {

@PrimaryKey
private int id;

@ColumnInfo(name = "name")
private String name;

@ColumnInfo(name = "age")
private int age;


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}


Database class



@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
abstract UserDao userDao();
}


Migration code



public static Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
}
};


and it call



Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
.addMigrations(MIGRATION_1_2)
.allowMainThreadQueries()
.build();


Before change the object adding AGE and performing the migration I add two register and it works.



After performing the migration, I just tried to add a new User as bellow:



  User user = new User();
user.setName("JoooJ");
user.setId(3);
user.setAge(18);

List<User> userList = new ArrayList<>();
userList.add(user);
App.database(this).userDao().insertAll(userList); // The crash happens here


Other informations:



Android Studio 3 and I didn't tested in the actual.



Dependencies:



compile "android.arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "android.arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3


Can someone help me please, I realy don't know what im doing wrong or if it is a bug.







android sqlite android-room






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 2 '18 at 15:50









W.K.S

5,78275898




5,78275898










asked Sep 22 '17 at 19:34









diogojmediogojme

1,2801220




1,2801220













  • A bit of a shot in the dark, but perhaps try "ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0" (0 could be whatever you consider suitable).

    – MikeT
    Sep 22 '17 at 22:33






  • 1





    Room expects the column order to match the field order. It would appear that the results of the ALTER TABLE are resulting in a different order.

    – CommonsWare
    Sep 23 '17 at 11:36











  • Check bellow answer. It has complete description: stackoverflow.com/a/51245898/3073945

    – Md. Sajedul Karim
    Jul 9 '18 at 12:49



















  • A bit of a shot in the dark, but perhaps try "ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0" (0 could be whatever you consider suitable).

    – MikeT
    Sep 22 '17 at 22:33






  • 1





    Room expects the column order to match the field order. It would appear that the results of the ALTER TABLE are resulting in a different order.

    – CommonsWare
    Sep 23 '17 at 11:36











  • Check bellow answer. It has complete description: stackoverflow.com/a/51245898/3073945

    – Md. Sajedul Karim
    Jul 9 '18 at 12:49

















A bit of a shot in the dark, but perhaps try "ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0" (0 could be whatever you consider suitable).

– MikeT
Sep 22 '17 at 22:33





A bit of a shot in the dark, but perhaps try "ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0" (0 could be whatever you consider suitable).

– MikeT
Sep 22 '17 at 22:33




1




1





Room expects the column order to match the field order. It would appear that the results of the ALTER TABLE are resulting in a different order.

– CommonsWare
Sep 23 '17 at 11:36





Room expects the column order to match the field order. It would appear that the results of the ALTER TABLE are resulting in a different order.

– CommonsWare
Sep 23 '17 at 11:36













Check bellow answer. It has complete description: stackoverflow.com/a/51245898/3073945

– Md. Sajedul Karim
Jul 9 '18 at 12:49





Check bellow answer. It has complete description: stackoverflow.com/a/51245898/3073945

– Md. Sajedul Karim
Jul 9 '18 at 12:49












5 Answers
5






active

oldest

votes


















34














The error message is hard to parse, but there's a difference:




TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} Found:




Found




TableInfo{ name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}




Age is nullable but Room expected it to be not null.



Change your migration to:



database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");




Since this exception explanation is VERY difficult to parse, I have created a small script that does the diff for you.



Example:



mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}"


Result:



expected/found diff






share|improve this answer





















  • 1





    Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

    – diogojme
    Sep 25 '17 at 17:43











  • Try your migrations on a dummy database and see if the query does what you expect it to do.

    – Benoit Duffez
    Sep 25 '17 at 18:40











  • For sure I will, thanks for the feedback.

    – diogojme
    Sep 25 '17 at 20:36











  • This doesnt work for me although it should..

    – Ali Kazi
    May 21 '18 at 1:54






  • 1





    I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

    – Raphael C
    May 31 '18 at 14:02



















7














I too wrote a small JS script which you can find https://hrankit.github.io/RoomSQLiteDifferenceFinder/



The process is pretty Simple.




  1. Input the Expected error log in Expected column which is the Left One.


  2. Input the Found error log in Found column which is the Right One.


  3. Press Go. button. The error logs get converted to JSON.


  4. Press Compare button and Voila, you have the difference you need.



This plugin finds out the difference in the two Expected and Found dump from the Android Studio Logcat.



Checkout the image of comparison here








share|improve this answer





















  • 2





    Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

    – Adriaan
    Sep 1 '18 at 11:53











  • @HRankit Just Awesome, your script solved my problem.

    – Ritesh Adulkar
    Jan 9 at 15:32



















4














None of the answers are correct in any of the links. After much experiments, found a way for it. The ALTER query needs to be written in the following way to make it work:



database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")


However, the Integer DEFAULT value can be anything.



If you want to add String type column, add in the following manner:



database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")


This works like a charm.






share|improve this answer





















  • 1





    The only correct answer

    – kulikovman
    Mar 8 at 9:03











  • @kulikovman Thanks !!

    – Prabhtej Singh
    Mar 8 at 10:03



















2














I faced this issue today, I just changed int fields to Integer in Entities. As int cannot be null but Integer objects could be null.






share|improve this answer































    1














    If you are getting notNull differences, you can simply mark your class field with @NonNull annotation, or change your sql with ALTER TABLE. But if you are getting column type differences, such as expected: TYPE=TEXT, then found TYPE='' (COLLATE NOCASE), or expected INTEGER, found INT, then the only solution is to drop and recreate your table. Sqlite does not allow changing column types.



    Use INTEGER in Sqlite instead of INT and mark your Java entity with @ColumnInfo(collate = NOCASE) (if you use NOCASE in Sqlite).



    Take a look at the json file under appschemas to get the sql for the expected queries.



    static final Migration MIGRATION_2_3= new Migration(2, 3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {

    database.execSQL("DROP TABLE IF EXISTS table_tmp");

    database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

    database.execSQL("insert into table_tmp (`id`, `name` , ...");

    database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

    database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

    database.execSQL("DROP TABLE IF EXISTS table");

    database.execSQL("alter table table_tmp rename to table");

    }
    };





    share|improve this answer

























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46372036%2froom-database-migration-doesnt-properly-handle-alter-table-migration%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      34














      The error message is hard to parse, but there's a difference:




      TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} Found:




      Found




      TableInfo{ name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}




      Age is nullable but Room expected it to be not null.



      Change your migration to:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");




      Since this exception explanation is VERY difficult to parse, I have created a small script that does the diff for you.



      Example:



      mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}"


      Result:



      expected/found diff






      share|improve this answer





















      • 1





        Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

        – diogojme
        Sep 25 '17 at 17:43











      • Try your migrations on a dummy database and see if the query does what you expect it to do.

        – Benoit Duffez
        Sep 25 '17 at 18:40











      • For sure I will, thanks for the feedback.

        – diogojme
        Sep 25 '17 at 20:36











      • This doesnt work for me although it should..

        – Ali Kazi
        May 21 '18 at 1:54






      • 1





        I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

        – Raphael C
        May 31 '18 at 14:02
















      34














      The error message is hard to parse, but there's a difference:




      TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} Found:




      Found




      TableInfo{ name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}




      Age is nullable but Room expected it to be not null.



      Change your migration to:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");




      Since this exception explanation is VERY difficult to parse, I have created a small script that does the diff for you.



      Example:



      mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}"


      Result:



      expected/found diff






      share|improve this answer





















      • 1





        Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

        – diogojme
        Sep 25 '17 at 17:43











      • Try your migrations on a dummy database and see if the query does what you expect it to do.

        – Benoit Duffez
        Sep 25 '17 at 18:40











      • For sure I will, thanks for the feedback.

        – diogojme
        Sep 25 '17 at 20:36











      • This doesnt work for me although it should..

        – Ali Kazi
        May 21 '18 at 1:54






      • 1





        I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

        – Raphael C
        May 31 '18 at 14:02














      34












      34








      34







      The error message is hard to parse, but there's a difference:




      TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} Found:




      Found




      TableInfo{ name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}




      Age is nullable but Room expected it to be not null.



      Change your migration to:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");




      Since this exception explanation is VERY difficult to parse, I have created a small script that does the diff for you.



      Example:



      mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}"


      Result:



      expected/found diff






      share|improve this answer















      The error message is hard to parse, but there's a difference:




      TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} Found:




      Found




      TableInfo{ name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}




      Age is nullable but Room expected it to be not null.



      Change your migration to:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");




      Since this exception explanation is VERY difficult to parse, I have created a small script that does the diff for you.



      Example:



      mig "java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=}"


      Result:



      expected/found diff







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Mar 2 '18 at 13:24

























      answered Sep 23 '17 at 12:45









      Benoit DuffezBenoit Duffez

      5,854957102




      5,854957102








      • 1





        Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

        – diogojme
        Sep 25 '17 at 17:43











      • Try your migrations on a dummy database and see if the query does what you expect it to do.

        – Benoit Duffez
        Sep 25 '17 at 18:40











      • For sure I will, thanks for the feedback.

        – diogojme
        Sep 25 '17 at 20:36











      • This doesnt work for me although it should..

        – Ali Kazi
        May 21 '18 at 1:54






      • 1





        I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

        – Raphael C
        May 31 '18 at 14:02














      • 1





        Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

        – diogojme
        Sep 25 '17 at 17:43











      • Try your migrations on a dummy database and see if the query does what you expect it to do.

        – Benoit Duffez
        Sep 25 '17 at 18:40











      • For sure I will, thanks for the feedback.

        – diogojme
        Sep 25 '17 at 20:36











      • This doesnt work for me although it should..

        – Ali Kazi
        May 21 '18 at 1:54






      • 1





        I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

        – Raphael C
        May 31 '18 at 14:02








      1




      1





      Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

      – diogojme
      Sep 25 '17 at 17:43





      Works like a charm, thanks for helping. And now I understand, when you perform migrations, you must specify the DEFAULT value for the new column to be populated.

      – diogojme
      Sep 25 '17 at 17:43













      Try your migrations on a dummy database and see if the query does what you expect it to do.

      – Benoit Duffez
      Sep 25 '17 at 18:40





      Try your migrations on a dummy database and see if the query does what you expect it to do.

      – Benoit Duffez
      Sep 25 '17 at 18:40













      For sure I will, thanks for the feedback.

      – diogojme
      Sep 25 '17 at 20:36





      For sure I will, thanks for the feedback.

      – diogojme
      Sep 25 '17 at 20:36













      This doesnt work for me although it should..

      – Ali Kazi
      May 21 '18 at 1:54





      This doesnt work for me although it should..

      – Ali Kazi
      May 21 '18 at 1:54




      1




      1





      I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

      – Raphael C
      May 31 '18 at 14:02





      I also found something interesting: if you created indices in your @Entity annotation in your model, you need to also do it in SQL in your migration script, for example CREATE INDEX index_History_nodeId` ON History (nodeId)` and if you don't you'll get a Migration didn't properly handle

      – Raphael C
      May 31 '18 at 14:02













      7














      I too wrote a small JS script which you can find https://hrankit.github.io/RoomSQLiteDifferenceFinder/



      The process is pretty Simple.




      1. Input the Expected error log in Expected column which is the Left One.


      2. Input the Found error log in Found column which is the Right One.


      3. Press Go. button. The error logs get converted to JSON.


      4. Press Compare button and Voila, you have the difference you need.



      This plugin finds out the difference in the two Expected and Found dump from the Android Studio Logcat.



      Checkout the image of comparison here








      share|improve this answer





















      • 2





        Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

        – Adriaan
        Sep 1 '18 at 11:53











      • @HRankit Just Awesome, your script solved my problem.

        – Ritesh Adulkar
        Jan 9 at 15:32
















      7














      I too wrote a small JS script which you can find https://hrankit.github.io/RoomSQLiteDifferenceFinder/



      The process is pretty Simple.




      1. Input the Expected error log in Expected column which is the Left One.


      2. Input the Found error log in Found column which is the Right One.


      3. Press Go. button. The error logs get converted to JSON.


      4. Press Compare button and Voila, you have the difference you need.



      This plugin finds out the difference in the two Expected and Found dump from the Android Studio Logcat.



      Checkout the image of comparison here








      share|improve this answer





















      • 2





        Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

        – Adriaan
        Sep 1 '18 at 11:53











      • @HRankit Just Awesome, your script solved my problem.

        – Ritesh Adulkar
        Jan 9 at 15:32














      7












      7








      7







      I too wrote a small JS script which you can find https://hrankit.github.io/RoomSQLiteDifferenceFinder/



      The process is pretty Simple.




      1. Input the Expected error log in Expected column which is the Left One.


      2. Input the Found error log in Found column which is the Right One.


      3. Press Go. button. The error logs get converted to JSON.


      4. Press Compare button and Voila, you have the difference you need.



      This plugin finds out the difference in the two Expected and Found dump from the Android Studio Logcat.



      Checkout the image of comparison here








      share|improve this answer















      I too wrote a small JS script which you can find https://hrankit.github.io/RoomSQLiteDifferenceFinder/



      The process is pretty Simple.




      1. Input the Expected error log in Expected column which is the Left One.


      2. Input the Found error log in Found column which is the Right One.


      3. Press Go. button. The error logs get converted to JSON.


      4. Press Compare button and Voila, you have the difference you need.



      This plugin finds out the difference in the two Expected and Found dump from the Android Studio Logcat.



      Checkout the image of comparison here









      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 2 '18 at 5:02









      eyllanesc

      84.5k103562




      84.5k103562










      answered Sep 1 '18 at 11:50









      HRankitHRankit

      7122




      7122








      • 2





        Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

        – Adriaan
        Sep 1 '18 at 11:53











      • @HRankit Just Awesome, your script solved my problem.

        – Ritesh Adulkar
        Jan 9 at 15:32














      • 2





        Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

        – Adriaan
        Sep 1 '18 at 11:53











      • @HRankit Just Awesome, your script solved my problem.

        – Ritesh Adulkar
        Jan 9 at 15:32








      2




      2





      Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

      – Adriaan
      Sep 1 '18 at 11:53





      Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

      – Adriaan
      Sep 1 '18 at 11:53













      @HRankit Just Awesome, your script solved my problem.

      – Ritesh Adulkar
      Jan 9 at 15:32





      @HRankit Just Awesome, your script solved my problem.

      – Ritesh Adulkar
      Jan 9 at 15:32











      4














      None of the answers are correct in any of the links. After much experiments, found a way for it. The ALTER query needs to be written in the following way to make it work:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")


      However, the Integer DEFAULT value can be anything.



      If you want to add String type column, add in the following manner:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")


      This works like a charm.






      share|improve this answer





















      • 1





        The only correct answer

        – kulikovman
        Mar 8 at 9:03











      • @kulikovman Thanks !!

        – Prabhtej Singh
        Mar 8 at 10:03
















      4














      None of the answers are correct in any of the links. After much experiments, found a way for it. The ALTER query needs to be written in the following way to make it work:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")


      However, the Integer DEFAULT value can be anything.



      If you want to add String type column, add in the following manner:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")


      This works like a charm.






      share|improve this answer





















      • 1





        The only correct answer

        – kulikovman
        Mar 8 at 9:03











      • @kulikovman Thanks !!

        – Prabhtej Singh
        Mar 8 at 10:03














      4












      4








      4







      None of the answers are correct in any of the links. After much experiments, found a way for it. The ALTER query needs to be written in the following way to make it work:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")


      However, the Integer DEFAULT value can be anything.



      If you want to add String type column, add in the following manner:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")


      This works like a charm.






      share|improve this answer















      None of the answers are correct in any of the links. After much experiments, found a way for it. The ALTER query needs to be written in the following way to make it work:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")


      However, the Integer DEFAULT value can be anything.



      If you want to add String type column, add in the following manner:



      database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")


      This works like a charm.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 16 '18 at 8:43









      quant

      1,60711527




      1,60711527










      answered Nov 16 '18 at 7:07









      Prabhtej SinghPrabhtej Singh

      585




      585








      • 1





        The only correct answer

        – kulikovman
        Mar 8 at 9:03











      • @kulikovman Thanks !!

        – Prabhtej Singh
        Mar 8 at 10:03














      • 1





        The only correct answer

        – kulikovman
        Mar 8 at 9:03











      • @kulikovman Thanks !!

        – Prabhtej Singh
        Mar 8 at 10:03








      1




      1





      The only correct answer

      – kulikovman
      Mar 8 at 9:03





      The only correct answer

      – kulikovman
      Mar 8 at 9:03













      @kulikovman Thanks !!

      – Prabhtej Singh
      Mar 8 at 10:03





      @kulikovman Thanks !!

      – Prabhtej Singh
      Mar 8 at 10:03











      2














      I faced this issue today, I just changed int fields to Integer in Entities. As int cannot be null but Integer objects could be null.






      share|improve this answer




























        2














        I faced this issue today, I just changed int fields to Integer in Entities. As int cannot be null but Integer objects could be null.






        share|improve this answer


























          2












          2








          2







          I faced this issue today, I just changed int fields to Integer in Entities. As int cannot be null but Integer objects could be null.






          share|improve this answer













          I faced this issue today, I just changed int fields to Integer in Entities. As int cannot be null but Integer objects could be null.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Dec 14 '18 at 15:20









          Zaid MirzaZaid Mirza

          1,71211125




          1,71211125























              1














              If you are getting notNull differences, you can simply mark your class field with @NonNull annotation, or change your sql with ALTER TABLE. But if you are getting column type differences, such as expected: TYPE=TEXT, then found TYPE='' (COLLATE NOCASE), or expected INTEGER, found INT, then the only solution is to drop and recreate your table. Sqlite does not allow changing column types.



              Use INTEGER in Sqlite instead of INT and mark your Java entity with @ColumnInfo(collate = NOCASE) (if you use NOCASE in Sqlite).



              Take a look at the json file under appschemas to get the sql for the expected queries.



              static final Migration MIGRATION_2_3= new Migration(2, 3) {
              @Override
              public void migrate(SupportSQLiteDatabase database) {

              database.execSQL("DROP TABLE IF EXISTS table_tmp");

              database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

              database.execSQL("insert into table_tmp (`id`, `name` , ...");

              database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

              database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

              database.execSQL("DROP TABLE IF EXISTS table");

              database.execSQL("alter table table_tmp rename to table");

              }
              };





              share|improve this answer






























                1














                If you are getting notNull differences, you can simply mark your class field with @NonNull annotation, or change your sql with ALTER TABLE. But if you are getting column type differences, such as expected: TYPE=TEXT, then found TYPE='' (COLLATE NOCASE), or expected INTEGER, found INT, then the only solution is to drop and recreate your table. Sqlite does not allow changing column types.



                Use INTEGER in Sqlite instead of INT and mark your Java entity with @ColumnInfo(collate = NOCASE) (if you use NOCASE in Sqlite).



                Take a look at the json file under appschemas to get the sql for the expected queries.



                static final Migration MIGRATION_2_3= new Migration(2, 3) {
                @Override
                public void migrate(SupportSQLiteDatabase database) {

                database.execSQL("DROP TABLE IF EXISTS table_tmp");

                database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

                database.execSQL("insert into table_tmp (`id`, `name` , ...");

                database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

                database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

                database.execSQL("DROP TABLE IF EXISTS table");

                database.execSQL("alter table table_tmp rename to table");

                }
                };





                share|improve this answer




























                  1












                  1








                  1







                  If you are getting notNull differences, you can simply mark your class field with @NonNull annotation, or change your sql with ALTER TABLE. But if you are getting column type differences, such as expected: TYPE=TEXT, then found TYPE='' (COLLATE NOCASE), or expected INTEGER, found INT, then the only solution is to drop and recreate your table. Sqlite does not allow changing column types.



                  Use INTEGER in Sqlite instead of INT and mark your Java entity with @ColumnInfo(collate = NOCASE) (if you use NOCASE in Sqlite).



                  Take a look at the json file under appschemas to get the sql for the expected queries.



                  static final Migration MIGRATION_2_3= new Migration(2, 3) {
                  @Override
                  public void migrate(SupportSQLiteDatabase database) {

                  database.execSQL("DROP TABLE IF EXISTS table_tmp");

                  database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

                  database.execSQL("insert into table_tmp (`id`, `name` , ...");

                  database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

                  database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

                  database.execSQL("DROP TABLE IF EXISTS table");

                  database.execSQL("alter table table_tmp rename to table");

                  }
                  };





                  share|improve this answer















                  If you are getting notNull differences, you can simply mark your class field with @NonNull annotation, or change your sql with ALTER TABLE. But if you are getting column type differences, such as expected: TYPE=TEXT, then found TYPE='' (COLLATE NOCASE), or expected INTEGER, found INT, then the only solution is to drop and recreate your table. Sqlite does not allow changing column types.



                  Use INTEGER in Sqlite instead of INT and mark your Java entity with @ColumnInfo(collate = NOCASE) (if you use NOCASE in Sqlite).



                  Take a look at the json file under appschemas to get the sql for the expected queries.



                  static final Migration MIGRATION_2_3= new Migration(2, 3) {
                  @Override
                  public void migrate(SupportSQLiteDatabase database) {

                  database.execSQL("DROP TABLE IF EXISTS table_tmp");

                  database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

                  database.execSQL("insert into table_tmp (`id`, `name` , ...");

                  database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

                  database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

                  database.execSQL("DROP TABLE IF EXISTS table");

                  database.execSQL("alter table table_tmp rename to table");

                  }
                  };






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Feb 19 at 14:49

























                  answered Nov 2 '18 at 4:16









                  live-lovelive-love

                  17.9k108987




                  17.9k108987






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46372036%2froom-database-migration-doesnt-properly-handle-alter-table-migration%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Bressuire

                      Vorschmack

                      Quarantine