Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fix changelogs so that they run against empty database.

The changelogs weren't working against an empty database for various reasons,
so they needed fixing. This turned out to be quite involved since you can't
just modify changesets that have already been run against a production DB.

After much jumping through hoops, this is the result.
  • Loading branch information...
commit fe757d64b5bb096f02a076e414ca997f6807c973 1 parent 5c60599
@pledbrook pledbrook authored
View
6 migrations/changelog-1.1.1.groovy
@@ -28,6 +28,12 @@ databaseChangeLog = {
}
}
+ // This changeset is required if the changelog is executed against an InnoDB database.
+ // Without it, later changesets fail due to problems creating associated foreign keys.
+ changeSet(author: "pledbrook", id: "FixPluginLicenseTableType") {
+ sql('ALTER TABLE plugin_license ENGINE=MyISAM')
+ }
+
changeSet(author: "pledbrook (generated)", id: "1320944813799-3") {
addColumn(tableName: "plugin") {
column(name: "organization", type: "varchar(255)")
View
6 migrations/changelog-1.1.groovy
@@ -8,6 +8,12 @@ databaseChangeLog = {
}
}
+ // This changeset is required if the changelog is executed against an InnoDB database.
+ // Without it, later changesets fail due to problems creating associated foreign keys.
+ changeSet(author: "pledbrook", id: "FixRolePermissionsTableType") {
+ sql('ALTER TABLE role_permissions ENGINE=MyISAM')
+ }
+
changeSet(author: "pledbrook (generated)", id: "1320427106545-1") {
createTable(tableName: "bi_images") {
column(autoIncrement: "true", name: "id", type: "bigint") {
View
55 migrations/changelog.groovy
@@ -28,8 +28,61 @@ databaseChangeLog = {
include file: "changelog-1.1.2.groovy"
include file: "changelog-1.2.groovy"
include file: "changelog-1.2.2.groovy"
- include file: "changelog-1.2.18.groovy"
+
+ // See end of the file for why this is here...
include file: "changelog-1.3.groovy"
+ include file: "changelog-1.2.18.groovy"
+
include file: "changelog-2.0.groovy"
}
}
+
+/*
+ A Story of Woe
+
+ For anyone dealing with these database migrations, I think an explanation is in order. You
+ will find all sorts of bizarre tricks hidden in the pre-2.0 changelogs and it behoves you
+ to understand why they are there.
+
+ The first thing you need to know is that the early production database for grails.org was
+ not managed via database migration. Just Hibernate's 'update' auto-ddl setting. Of course,
+ it was later discovered that not all, and in fact surprisingly few, refactorings are supported
+ by Hibernate's update. That's when Autobase was introduced.
+
+ Autobase was designed to work in conjunction with Hibernate's update support, providing
+ the extra refactorings that were needed. Unfortunately, you couldn't just take the app and
+ run it against an empty database because Hibernate 'update' would just use the latest GORM
+ model and the Autobase refactorings would fail.
+
+ When the Database Migration plugin came on the scene, it seemed it would solve all our
+ problems. And it would have done. If we hadn't made more mistakes. So init-changelog.groovy
+ was created so that the initial schema could be created in an empty database with all the
+ subsequent refactorings working. And it did work. And we were happy. Until we decided to
+ finally switch the MySQL database from MyISAM to InnoDB. Uh-oh.
+
+ The switch highlighted some problems with existing refactorings, which meant that they
+ wouldn't work against an empty database. And if you started with an empty database in a
+ MySQL server set to InnoDB by default, even more things stopped working. Unfortuantely,
+ most of the changesets had already been run against the production database so changing
+ their IDs or modifying them simply didn't work.
+
+ To add to the fun and games, a development changelog, changelog-1.3.groovy, got executed
+ against the production database - not clever. But when changelog-1.2.18.groovy was created,
+ the assumption was that it had already run. And of course the migrations worked against
+ the existing database. But they didn't work against an empty one. So the former had to
+ be moved before the latter, even though it's number is higher. Such is life.
+
+ Going back to the InnoDB changes, there are several changesets that create indexes, foreign
+ keys, etc. just so that subsequent changesets that remove those things actually work. Of
+ course, those changesets haven't run against the production database so all those indexes
+ (and tables!) will be created again. Fortunately, we can mark all those changesets as
+ having already been run, and this currently only affects two databases that we control.
+
+ Going forward from here, everything should become much saner as the changesets required
+ to get an empty database up to the latest version are now in sync with the production
+ servers.
+
+ If there is one lesson to learn from this story it's that you should start using a database
+ refactoring tool _as soon as you hit production_! And switch of Hibernate's 'update' at
+ the same time. If we'd done that, none of this sorry story would have happened.
+*/
View
71 migrations/pre-fixes-changelog.groovy
@@ -72,4 +72,75 @@ databaseChangeLog = {
column name: "title"
}
}
+
+ // All these changes are required to get subsequent refactorings to work on an empty
+ // InnoDB database. Trust me. See the parent changelog for more information on why
+ // this is necessary.
+ changeSet(id: "CreatesForDropping", author: "pledbrook") {
+ sql('ALTER TABLE content ENGINE=MyISAM')
+ sql('ALTER TABLE download_file ENGINE=MyISAM')
+ sql('ALTER TABLE mirror ENGINE=MyISAM')
+ sql('ALTER TABLE plugin ENGINE=MyISAM')
+ sql('ALTER TABLE rating ENGINE=MyISAM')
+ sql('ALTER TABLE rating_link ENGINE=MyISAM')
+ sql('ALTER TABLE screencast ENGINE=MyISAM')
+ sql('ALTER TABLE screencast_mirror ENGINE=MyISAM')
+ sql('ALTER TABLE user_info ENGINE=MyISAM')
+ sql('ALTER TABLE user_role ENGINE=MyISAM')
+
+ createTable(tableName: "test_domain") { column name: "id", type: "BIGINT(20)" }
+ createTable(tableName: "test_rater") { column name: "id", type: "BIGINT(20)" }
+
+ createIndex(tableName: "content", indexName: 'FK38B73479BC488994') { column name: "title" }
+ createIndex(tableName: "plugin", indexName: 'FKC5476F339B7AA533') { column name: "id" }
+ createIndex(tableName: "plugin", indexName: 'FKC5476F33DDE70EC0') { column name: "name" }
+ createIndex(tableName: "plugin", indexName: 'FKC5476F3317DC9031') { column name: "title" }
+ createIndex(tableName: "plugin", indexName: 'FKC5476F33B0CAFAD7') { column name: "grails_version" }
+ createIndex(tableName: 'rating_link', indexName: 'FK1827315C45884E64') { column name: 'rating_id' }
+ createIndex(tableName: 'screencast_mirror', indexName: 'FK326C9CD3A67C8B4A') { column(name: 'screencast_id') }
+
+ addForeignKeyConstraint(constraintName: 'FK38B73479839030FB',
+ baseTableName: 'content', baseColumnNames: 'current_id',
+ referencedTableName: 'content', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FK38B734797B9748B6',
+ baseTableName: 'content', baseColumnNames: 'author_id',
+ referencedTableName: 'user', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FKBFFD6BBF550BF0EF',
+ baseTableName: 'mirror', baseColumnNames: 'file_id',
+ referencedTableName: 'download_file', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FKC5476F33FA44DC5E',
+ baseTableName: 'plugin', baseColumnNames: 'screenshots_id',
+ referencedTableName: 'content', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FKC5476F33B7D872D1',
+ baseTableName: 'plugin', baseColumnNames: 'installation_id',
+ referencedTableName: 'content', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FKC5476F33343A5DCF',
+ baseTableName: 'plugin', baseColumnNames: 'description_id',
+ referencedTableName: 'content', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FKC5476F33CD28C875',
+ baseTableName: 'plugin', baseColumnNames: 'faq_id',
+ referencedTableName: 'content', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FK1437D8A21ADE5676',
+ baseTableName: 'user_info', baseColumnNames: 'user_id',
+ referencedTableName: 'user', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FK143BF46A16D0D038',
+ baseTableName: 'user_role', baseColumnNames: 'user_roles_id',
+ referencedTableName: 'user', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FK143BF46A75B39296',
+ baseTableName: 'user_role', baseColumnNames: 'role_id',
+ referencedTableName: 'role', referencedColumnNames: 'id')
+
+ addForeignKeyConstraint(constraintName: 'FK1827315C45884E64',
+ baseTableName: 'rating_link', baseColumnNames: 'rating_id',
+ referencedTableName: 'rating', referencedColumnNames: 'id')
+ }
}
Please sign in to comment.
Something went wrong with that request. Please try again.