question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Figure out what to do about migrations, if anything.

See original GitHub issue

Working:

  • You can currently define inserts, updates, and deletes in a file and use them for migrations.

Annoying:

  • Table creation generates a bunch of nonsense you might not need if the table is deleted/altered by later migrations
  • Table creation is restricted to one per file

Missing:

  • Detection of breaking changes in the schema and warning about it
  • Generation of migrations automatically when you use refactoring tools on the create table statements?

As we discussed, we could go as far as generating the onUpgrade switch automatically. We’d probably want to create something like .sqm files (for migration) which relax the one-table-per-file restriction and don’t actually generate anything for them.

Lots to think about since this probably won’t happen for a while.

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Reactions:13
  • Comments:6

github_iconTop GitHub Comments

4reactions
AlecStrongcommented, Feb 12, 2016

You can already omit the create table from .sq files and have a generated interface with only string constants

1reaction
gotevcommented, Feb 23, 2017

With the current SQLDelight 0.5.1 version, to perform a table migration (which involves adding and/or removing columns or other changes) I’m using the following procedure.

Let’s say this is the initial version (v1) of my table, defined in Billing.sq:

CREATE TABLE billing (
    _id INTEGER NOT NULL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    company_name TEXT,
    address TEXT,
    zip_code TEXT,
    city TEXT,
    vat TEXT,
    country TEXT,
    number_of_users INTEGER,
    FOREIGN KEY (user_id) REFERENCES user(_id) ON DELETE CASCADE
);

get_by_user_id:
SELECT *
FROM billing
WHERE user_id = ?;

This is the corresponding Java model (the code is also using AutoValue, Retrolambda, Timber, SQLBrite, and RxLifecycle):

@AutoValue
public abstract class Billing implements BillingModel {
    private static final Factory<Billing> FACTORY =
                                new Factory<>(AutoValue_Billing::new);

    @UiThread
    public static Observable<Billing> getByUserId(BriteDatabase db,
                                                  Activity activity,
                                                  long userId) {

        Timber.d("Get billing for user with ID %d", userId);

        return getObservable(db, FACTORY.get_by_user_id(userId),
                             FACTORY.get_by_user_idMapper())
                .compose(RxLifecycleAndroid.bindActivity(activity.lifecycle()))
                .observeOn(AndroidSchedulers.mainThread());
    }

    @UiThread
    private static <T> Observable<T> getObservable(BriteDatabase db,
                                                   SqlDelightStatement stmt,
                                                   final RowMapper<T> mapper) {
        return db.createQuery(stmt.tables, stmt.statement, stmt.args)
                 .mapToOne(mapper::map);
    }
}

Create the v2 Billing_v2.sq file and copy the content of Billing.sq. Replace the occurrences of billing with billing_v2. Perform the changes you need on the table by adding or removing columns and by writing an import_from_v1 SQL statement to import the old data into the new table:

CREATE TABLE billing_v2 (
    _id INTEGER NOT NULL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    company_name TEXT,
    billing_type INTEGER,
    address TEXT,
    zip_code TEXT,
    city TEXT,
    vat TEXT,
    country TEXT,
    FOREIGN KEY (user_id) REFERENCES user(_id) ON DELETE CASCADE
);

get_by_user_id:
SELECT *
FROM billing_v2
WHERE user_id = ?;

import_from_v1:
INSERT INTO billing_v2(_id, user_id, company_name, address, zip_code,
                       city, vat, country)
SELECT _id, user_id, company_name, address, zip_code, city, vat, country
FROM billing;

Then modify the Java model by replacing BillingModel with BillingModel_v2 plus some little changes, if needed:

@AutoValue
public abstract class Billing implements BillingModel_v2 {
    private static final Factory<Billing> FACTORY =
                                new Factory<>(AutoValue_Billing::new);

    @UiThread
    public static Observable<Billing> getByUserId(BriteDatabase db,
                                                  Activity activity,
                                                  long userId) {

        Timber.d("Get billing for user with ID %d", userId);

        return getObservable(db, FACTORY.get_by_user_id(userId),
                             FACTORY.get_by_user_idMapper())
                .compose(RxLifecycleAndroid.bindActivity(activity.lifecycle()))
                .observeOn(AndroidSchedulers.mainThread());
    }

    @UiThread
    private static <T> Observable<T> getObservable(BriteDatabase db,
                                                   SqlDelightStatement stmt,
                                                   final RowMapper<T> mapper) {
        return db.createQuery(stmt.tables, stmt.statement, stmt.args)
                 .mapToOne(mapper::map);
    }
}

This will be the database migration:

public void onMigration(SQLiteDatabase db) {

    db.beginTransaction();

    try {
        db.execSQL(Billing_v2Model.CREATE_TABLE);
        db.execSQL(Billing_v2Model.MIGRATE_FROM_V1);
        db.execSQL("DROP TABLE " + BillingModel.TABLE_NAME);

        db.setTransactionSuccessful();

    } finally {
        db.endTransaction();
    }
}

At this point, the migration is over.

In this way you perform few changes in the Java code and you can use SQLDelight mapping without workarounds. However, this approach is error-prone as it requires a lot of manual operations and has drawbacks. I’m going to list a few.

The old Billing.sq file still gets processed by SQLDelight and produces the interface, even if it will not be used anymore. You can be in trouble if you have auto incrementing primary keys or if other tables are referencing the old table’s primary key, which requires additional and very careful manual work. SQLite has also many limitations, for example you can’t remove a column in an existing table or change its type.

At the moment, I can’t think of a better way to do this.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to “solve” migration: A practical guide
Beyond border control, countries can approach migration from a holistic point of view which seeks to take advantage of its potential to boost...
Read more >
Ten Things You Can Do to Support Immigrants and Refugees
2) Speak up and speak out. Talk to friends and family, and to members of your civic and religious groups about immigrants and...
Read more >
3 ways governments can solve migration crises
1. Address the drivers of involuntary migration and create more legal avenues of migration. Currently, migrants arriving in Malta or Lampedusa, ...
Read more >
10 things you should know about migration and refugees | NRC
10 things you should know about migration and refugees · #1: What is migration? Migration is the movement of individuals or groups of...
Read more >
We must learn to live with migration - World Bank Blogs
So, the strategy to address migration must change from wishing for a zero-migration scenario to learning to live with migrants .
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found