source: trunk/grails-app/conf/DatabaseUpgrade.groovy @ 1657

Last change on this file since 1657 was 1657, checked in by work@…, 9 years ago
  • resolved issue #372
  • Property svn:keywords set to Rev Author Date
File size: 11.4 KB
Line 
1import groovy.sql.Sql
2import dbnp.studycapturing.Study
3import org.codehaus.groovy.grails.commons.ConfigurationHolder
4
5/**
6 * A script to automatically perform database changes
7 *
8 * @Author Jeroen Wesbeek
9 * @Since 20101209
10 *
11 * Revision information:
12 * $Rev: 1657 $
13 * $Author: work@osx.eu $
14 * $Date: 2011-03-22 13:03:03 +0000 (di, 22 mrt 2011) $
15 */
16class DatabaseUpgrade {
17        /**
18         * handle database upgrades
19         *
20         * @param dataSource
21         */
22        public static void handleUpgrades(dataSource) {
23                // get a sql instance
24                groovy.sql.Sql sql = new groovy.sql.Sql(dataSource)
25
26                // get configuration
27                def config = ConfigurationHolder.config
28                def db = config.dataSource.driverClassName
29
30                // execute per-change check and upgrade code
31                changeStudyDescription(sql, db)                         // r1245 / r1246
32                changeStudyDescriptionToText(sql, db)           // r1327
33                changeTemplateTextFieldSignatures(sql, db)      // prevent Grails issue, see http://jira.codehaus.org/browse/GRAILS-6754
34                setAssayModuleDefaultValues(sql, db)            // r1490
35                dropMappingColumnNameConstraint(sql, db)        // r1525
36                makeMappingColumnValueNullable(sql, db)         // r1525
37                alterStudyAndAssay(sql, db)                                     // r1594
38                fixDateCreatedAndLastUpdated(sql, db)
39        }
40
41        /**
42         * execute database change r1245 / r1246 if required
43         * @param sql
44         * @param db
45         */
46        public static void changeStudyDescription(sql, db) {
47                // check if we need to perform this upgrade
48                if (sql.firstRow("SELECT count(*) as total FROM template_field WHERE templatefieldentity='dbnp.studycapturing.Study' AND templatefieldname='Description'").total > 0) {
49                        // grom that we are performing the upgrade
50                        if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: study description".grom()
51
52                        // database upgrade required
53                        try {
54                                // get the template field id
55                                def id = sql.firstRow("SELECT id FROM template_field WHERE templatefieldentity='dbnp.studycapturing.Study' AND templatefieldname='Description'").id
56
57                                // iterate through all obsolete study descriptions
58                                sql.eachRow("SELECT study_id, template_text_fields_elt as description FROM study_template_text_fields WHERE template_text_fields_idx='Description'") { row ->
59                                        // migrate the template description to the study object itself
60                                        // so we don't have to bother with sql injections, etc
61                                        def study = Study.findById(row.study_id)
62                                        study.setFieldValue('description', row.description)
63                                        if (!(study.validate() && study.save())) {
64                                                throw new Exception("could not save study with id ${row.study_id}")
65                                        }
66                                }
67
68                                // delete all obsolete descriptions
69                                sql.execute("DELETE FROM study_template_text_fields WHERE template_text_fields_idx='Description'")
70
71                                // find all template id's where this field is used
72                                sql.eachRow("SELECT DISTINCT template_fields_id, fields_idx FROM template_template_field WHERE template_field_id=${id}") { row ->
73                                        // delete the template_template_field reference
74                                        sql.execute("DELETE FROM template_template_field WHERE template_field_id=${id} AND template_fields_id=${row.template_fields_id}")
75
76                                        // and lower the idx-es of the remaining fields
77                                        sql.execute("UPDATE template_template_field SET fields_idx=fields_idx-1 WHERE fields_idx>${row.fields_idx} AND template_fields_id=${row.template_fields_id}")
78                                }
79
80                                // and delete the obsolete template field
81                                sql.execute("DELETE FROM template_field WHERE id=${id}")
82                        } catch (Exception e) {
83                                println "changeStudyDescription database upgrade failed: " + e.getMessage()
84                        }
85                }
86        }
87
88        /**
89         * execute database change r1327 if required
90         * @param sql
91         * @param db
92         */
93        public static void changeStudyDescriptionToText(sql, db) {
94                // are we running postgreSQL ?
95                if (db == "org.postgresql.Driver") {
96                        // check if column 'description' in table 'study' is not of type 'text'
97                        if (sql.firstRow("SELECT count(*) as total FROM information_schema.columns WHERE columns.table_schema::text = 'public'::text AND columns.table_name='study' AND column_name='description' AND data_type != 'text'").total > 0) {
98                                // grom that we are performing the upgrade
99                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: study description to text".grom()
100
101                                // database upgrade required
102                                try {
103                                        // change the datatype of study::description to text
104                                        sql.execute("ALTER TABLE study ALTER COLUMN description TYPE text")
105                                } catch (Exception e) {
106                                        println "changeStudyDescriptionToText database upgrade failed: " + e.getMessage()
107                                }
108                        }
109                }
110        }
111
112        /**
113         * it appears that some TEXT template fields are not of type 'text'
114         * due to an issue in how Grails' GORM works with domain inheritance
115         * (see http://jira.codehaus.org/browse/GRAILS-6754)
116         * @param sql
117         * @param db
118         */
119        public static void changeTemplateTextFieldSignatures(sql, db) {
120                if (db == "org.postgresql.Driver") {
121                        // check if any TEXT template fields are of type 'text'
122                        sql.eachRow("SELECT columns.table_name as tablename FROM information_schema.columns WHERE columns.table_schema::text = 'public'::text AND column_name='template_text_fields_elt' AND data_type != 'text';")
123                                { row ->
124                                        if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: ${row.tablename} template_text_fields_string/elt to text".grom()
125                                        try {
126                                                // change the datatype of text fields to text
127                                                sql.execute(sprintf("ALTER TABLE %s ALTER COLUMN template_text_fields_elt TYPE text", row.tablename))
128                                                sql.execute(sprintf("ALTER TABLE %s ALTER COLUMN template_text_fields_string TYPE text", row.tablename))
129
130                                        } catch (Exception e) {
131                                                println "changeTemplateTextFieldSignatures database upgrade failed: " + e.getMessage()
132                                        }
133                                }
134                }
135        }
136
137        /**
138         * The fields 'notify' and 'openInFrame' have been added to AssayModule. However, there
139         * seems to be no method to setting the default values of these fields in the database. They
140         * are set to NULL by default, so all existing fields have 'NULL' set.
141         * This method sets the default values
142         * @param sql
143         * @param db
144         */
145        public static void setAssayModuleDefaultValues(sql, db) {
146                // do we need to perform this upgrade?
147                if ((db == "org.postgresql.Driver" || db == "com.mysql.jdbc.Driver") &&
148                        (sql.firstRow("SELECT * FROM assay_module WHERE notify IS NULL") || sql.firstRow("SELECT * FROM assay_module WHERE open_in_frame IS NULL"))
149                ) {
150                        if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: assay_module default values for boolean fields".grom()
151
152                        try {
153                                sql.execute("UPDATE assay_module SET notify=" + ((db == "org.postgresql.Driver") ? 'FALSE' : '0') + " WHERE notify IS NULL")
154                        } catch (Exception e) {
155                                println "setAssayModuleDefaultValues database upgrade failed, notify field couldn't be set to default value: " + e.getMessage()
156                        }
157
158                        // set open_in_frame?
159                        try {
160                                sql.execute("UPDATE assay_module SET open_in_frame=" + ((db == "org.postgresql.Driver") ? 'TRUE' : '1') + " WHERE open_in_frame IS NULL")
161                        } catch (Exception e) {
162                                // Maybe gdt plugin is not updated yet after revision 109 ?
163                                println "setAssayModuleDefaultValues database upgrade failed, openInFrame field couldn't be set to default value: " + e.getMessage()
164                        }
165                }
166        }
167
168        /**
169         * Drop the unique constraint for the "name" column in the MappingColumn domain
170         *
171         * @param sql
172         * @param db
173         */
174        public static void dropMappingColumnNameConstraint(sql, db) {
175                // are we running postgreSQL ?
176                if (db == "org.postgresql.Driver") {
177                        if (sql.firstRow("SELECT * FROM pg_constraint WHERE contype='mapping_column_name_key'")) {
178                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: mapping column name constraint".grom()
179                                try {
180                                        // Check if constraint still exists
181                                        sql.execute("ALTER TABLE mapping_column DROP CONSTRAINT mapping_column_name_key")
182                                } catch (Exception e) {
183                                        println "dropMappingColumnNameConstraint database upgrade failed, `name` field unique constraint couldn't be dropped: " + e.getMessage()
184                                }
185                        }
186                }
187        }
188
189        /**
190         * the importer requires the value field to be nullable
191         * @param sql
192         * @param db
193         */
194        public static void makeMappingColumnValueNullable(sql, db) {
195                // are we running postgreSQL?
196                if (db == "org.postgresql.Driver") {
197                        // do we need to perform this update?
198                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='mapping_column' AND columns.column_name='value' AND is_nullable='NO'")) {
199                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: making mapping_column::value nullable".grom()
200
201                                try {
202                                        sql.execute("ALTER TABLE mapping_column ALTER COLUMN value DROP NOT NULL")
203                                } catch (Exception e) {
204                                        println "makeMappingColumnValueNullable database upgrade failed: " + e.getMessage()
205                                }
206                        }
207                }
208        }
209
210        /**
211         * The field study.code has been set to be nullable
212         * The field assay.externalAssayId has been removed
213         * @param sql
214         * @param db
215         */
216        public static void alterStudyAndAssay(sql, db) {
217                def updated = false
218
219                // are we running postgreSQL ?
220                if (db == "org.postgresql.Driver") {
221                        // see if table assay contains a column external_assayid
222                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='assay' AND columns.column_name='external_assayid'")) {
223                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: dropping column 'external_assayid' from table 'assay'".grom()
224
225                                try {
226                                        sql.execute("ALTER TABLE assay DROP COLUMN external_assayid")
227                                        updated = true
228                                } catch (Exception e) {
229                                        println "alterStudyAndAssay database upgrade failed, externalAssayId could not be removed from assay: " + e.getMessage()
230                                }
231
232                        }
233
234                        // see if table study contains a column code which is not nullable
235                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='study' AND columns.column_name='code' AND is_nullable='NO'")) {
236                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: dropping column 'code' from table 'study'".grom()
237
238                                try {
239                                        sql.execute("ALTER TABLE study ALTER COLUMN code DROP NOT NULL")
240                                        updated = true
241                                } catch (Exception e) {
242                                        println "alterStudyAndAssay database upgrade failed, study.code could not be set to accept null values: " + e.getMessage()
243                                }
244                        }
245
246                        // Load all studies and save them again. This prevents errors on saving later
247                        if (updated) {
248                                if (String.metaClass.getMetaMethod("grom")) "re-saving studies...".grom()
249
250                                Study.list().each { study ->
251                                        if (String.metaClass.getMetaMethod("grom")) "re-saving study: ${study}".grom()
252                                        study.save()
253                                }
254                        }
255                }
256        }
257
258        /**
259         * make sure all date_created and last_updated columns are NOT nullable, and
260         * set values to now() of they are null
261         * @param sql
262         * @param db
263         */
264        public static void fixDateCreatedAndLastUpdated(sql, db) {
265                // are we running PostgreSQL?
266                if (db == "org.postgresql.Driver") {
267                        // see if we need to modify anything?
268                        sql.eachRow("SELECT table_name,column_name FROM information_schema.columns WHERE column_name IN ('last_updated', 'date_created') AND is_nullable='YES'") { row ->
269                                // grom what we are doing
270                                if (String.metaClass.getMetaMethod("grom")) "fixing nullable for ${row.table_name}:${row.column_name}".grom()
271
272                                // fix database
273                                try {
274                                        // setting all null values to now()
275                                        sql.execute(sprintf("UPDATE %s SET %s=now() WHERE %s IS NULL",row.table_name,row.column_name,row.column_name))
276
277                                        // and alter the table to disallow null values
278                                        sql.execute(sprintf("ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",row.table_name,row.column_name))
279                                } catch (Exception e) {
280                                        println "fixDateCreatedAndLastUpdated database upgrade failed: " + e.getMessage()
281                                }
282                        }
283                }
284        }
285}
Note: See TracBrowser for help on using the repository browser.