Home > Mobile >  Kotlin, Pre-packaged database has an invalid schema: Column order is wrong?
Kotlin, Pre-packaged database has an invalid schema: Column order is wrong?

Time:04-04

I exported a copy of the database from the emulator and moved the test database to an external file. This has been working well for sometime, however, today something changed and this error appeared.

Pre-packaged database has an invalid schema: tableLinkUserToPassword

Expected:

TableInfo 
{ 
    name='tableLinkUserToPassword', 
    columns = { 
                  userId = Column { name='userId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null' },
                  password = Column { name='password', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null' }
    }, 
    foreignKeys = [ForeignKey 
                   {
                       referenceTable='tableUser', onDelete='CASCADE', onUpdate='NO ACTION', columnNames=[userId], referenceColumnNames=[userId]
                   }
                  ], 
    indices = [Index { name='index_tableLinkUserToPassword_password', unique=false, columns=[password], orders=[ASC] }, 
               Index { name='index_tableLinkUserToPassword_userId', unique=false, columns=[userId], orders=[ASC]}
              ]
}

Found:

    TableInfo{name='tableLinkUserToPassword', columns={password=Column{name='password', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, userId=Column{name='userId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[ForeignKey{referenceTable='tableUser', onDelete='CASCADE', onUpdate='NO ACTION', columnNames=[userId], referenceColumnNames=[userId]}], indices=[Index{name='index_tableLinkUserToPassword_userId', unique=false, columns=[userId], orders=[ASC]}, Index{name='index_tableLinkUserToPassword_password', unique=false, columns=[password], orders=[ASC]}]}

The UserId and the Password columns are switched in order. And notNull=false seems not to match.

I have DB Browser but how would I change the order or columns?

Entity:

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey

@Entity(
    tableName = "tableLinkUserToPassword",
    foreignKeys = [
        ForeignKey(
            entity = EntityUser::class,
            parentColumns = ["userId"],
            childColumns = ["userId"],
            onDelete = ForeignKey.CASCADE
        )
    ]
)

data class EntityLinkUserToPassword(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(index = true)
    val userId: Int,
    val password: String,
    )

Thanks for any assistance

CodePudding user response:

The order in which the columns appear is not an issue, it's the order in which they are extracted (I think). It is the reported values that matter.

However, what can be seen, as an example, is that the password column EXPECTED (what is extracted from the @Entity annotated class) has the NOT NULL constraint (i.e. it must not be null) as per notNull=true, whilst it found that there is no NOT NULL constraint coded for the password column in the FOUND (i.e. the pre-packaged database) as per notNull=false.

So you either have to change the EntityLinkUserToPassword class to allow null, or change the pre-packaged database to have NOT NULL coded on the password column.

e.g. val password: String?,

You need to check ALL columns for discrepancies between the found and expected.

P.S. a second index on the userId column is a waste and inefficient. The PrimaryKey is an index. So there is no need for the @ColumnInfo annotation.

However, again there is another discrepancy the second index on the pre-packaged database is on the password column. So you should have the @ColumnInfo annotation moved to apply to the password val/column e.g. I believe that you want :-

data class EntityLinkUserToPassword(
    @PrimaryKey(autoGenerate = true)
    val userId: Int,
    @ColumnInfo(index = true)
    val password: String?,
    )
  • Note the above is based upon observation, the suggested code has not been tested and is not necessarily full comprehensive, so may contain omissions and or errors.

I have DB Broswer but how would I chnage the order or columns?

You would/could :-

  1. Rename the EntityLinkUserToPassword table e.g. ALTER TABLE EntityLinkUserToPassword RENAME TO renamed_EntityLinkUserToPassword;
  2. Use UPDATE renamed_EntityLinkUserToPassword SET password = 'a suitable default value' WHERE password IS NULL;
    1. This so that you don't get NOT NULL constraint conflicts when copying the data.
  3. Create the new table with the correct schema (see later)
  4. Use INSERT INTO EntityLinkUserToPassword SELECT * FROM renamed_EntityLinkUserToPassword ORDER BY userId ASC;
  5. DROP TABLE IF EXISTS renamed_EntityLinkUserToPassword'

Getting the correct schema

  1. With the classes annotated with @Entity coded as required AND defined in the entities parameter of the @Database annotation, compile the project.
  2. using the Android View in Android Studio look at the Java(generated) for a class the same name as the @Database annotated class but suffixed with _Impl.
  3. Look for the createAllTables method. The SQL for the creation of the tables is hard coded. Copy it and this will be the EXACT SQL.
  • Related