For the past several months, I’ve been working on a mobile application that uses SQLite as a local database (using Ionic React and TypeORM). I have no previous production-level experience with SQLite. One thing that can be a little jarring is that the process of handling schema changes seems more convoluted than other popular databases.
I wanted to add timestamps with default values of “now” to a table, something like:
await queryRunner.query(`
ALTER TABLE cats ADD COLUMN createdAt TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP;
`);
await queryRunner.query(`
ALTER TABLE cats ADD COLUMN updatedAt TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP;
`);
But that led to an error with the following message when the migration ran:
Query: in selectSQL cursor Cannot add a column with non-constant default
After finding this StackOverflow response to a related question, I added a migration that generally followed Making Other Kinds Of Table Schema Changes in the Alter Table SQLite documentation. Something like:
/*
* 1. Create new table
* 2. Copy data from old table to new table
* 3. Drop old table
* 4. Rename new table to old table name
* */
export class AddCatsTimestamps1908773635544
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
// 1. Create new table
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS temp_cats(
id INTEGER PRIMARY KEY NOT NULL,
name TEXT,
createdAt TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedAt TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
masterId INTEGER,
CONSTRAINT "FK_Cats_Master" FOREIGN KEY ("masterId") REFERENCES "masters" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
);
`);
// 2. Copy data from old table to new table
await queryRunner.query(`
INSERT INTO temp_cats (id, name, masterId)
SELECT id, name, masterId
FROM cats;
`);
// 3. Drop old table
await queryRunner.query(`
DROP TABLE cats;
`);
// 4. Rename new table to old table name
await queryRunner.query(`
ALTER TABLE temp_cats RENAME TO cats;
`);
}
I was reluctant to add a migration like this. It felt like I was polluting the codebase. Ultimately, however, the logic is simple to follow, and it was potentially good to recollect the few schema alterations I had made previously into one statement for the table.