root/trunk/grails-app/conf/dbnp/configuration/DatabaseUpgrade.groovy @ 1922

Revision 1922, 15.6 KB (checked in by work@…, 3 years ago)

- removed all default instance configuration in favor of just one default.properties file. Instance configuration should be on the server side, not within the code. The setup wizard will help you generate a configuration file...
- removed obsolete references to searchable
- removed obsolete references to grails melody

  • Property svn:keywords set to Rev Author Date
Line 
1package dbnp.configuration
2
3import dbnp.studycapturing.Study
4import org.codehaus.groovy.grails.commons.ConfigurationHolder
5
6/**
7 * A script to automatically perform database changes
8 *
9 * @Author Jeroen Wesbeek
10 * @Since 20101209
11 *
12 * Revision information:
13 * $Rev$
14 * $Author$
15 * $Date$
16 */
17class DatabaseUpgrade {
18        /**
19         * handle database upgrades
20         *
21         * @param dataSource
22         */
23        public static void handleUpgrades(dataSource) {
24                // get a sql instance
25                groovy.sql.Sql sql = new groovy.sql.Sql(dataSource)
26
27                // get configuration
28                def config = ConfigurationHolder.config
29                def db = config.dataSource.driverClassName
30
31                // execute per-change check and upgrade code
32                changeStudyDescription(sql, db)                                 // r1245 / r1246
33                changeStudyDescriptionToText(sql, db)                   // r1327
34                changeTemplateTextFieldSignatures(sql, db)              // prevent Grails issue, see http://jira.codehaus.org/browse/GRAILS-6754
35                setAssayModuleDefaultValues(sql, db)                    // r1490
36                dropMappingColumnNameConstraint(sql, db)                // r1525
37                makeMappingColumnValueNullable(sql, db)                 // r1525
38                alterStudyAndAssay(sql, db)                                             // r1594
39                fixDateCreatedAndLastUpdated(sql, db)
40                dropAssayModulePlatform(sql, db)                                // r1689
41                makeStudyTitleAndTemplateNamesUnique(sql, db)   // #401, #406
42        }
43
44        /**
45         * execute database change r1245 / r1246 if required
46         * @param sql
47         * @param db
48         */
49        public static void changeStudyDescription(sql, db) {
50                // check if we need to perform this upgrade
51                if (sql.firstRow("SELECT count(*) as total FROM template_field WHERE templatefieldentity='dbnp.studycapturing.Study' AND templatefieldname='Description'").total > 0) {
52                        // grom that we are performing the upgrade
53                        if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: study description".grom()
54
55                        // database upgrade required
56                        try {
57                                // get the template field id
58                                def id = sql.firstRow("SELECT id FROM template_field WHERE templatefieldentity='dbnp.studycapturing.Study' AND templatefieldname='Description'").id
59
60                                // iterate through all obsolete study descriptions
61                                sql.eachRow("SELECT study_id, template_text_fields_elt as description FROM study_template_text_fields WHERE template_text_fields_idx='Description'") { row ->
62                                        // migrate the template description to the study object itself
63                                        // so we don't have to bother with sql injections, etc
64                                        def study = Study.findById(row.study_id)
65                                        study.setFieldValue('description', row.description)
66                                        if (!(study.validate() && study.save())) {
67                                                throw new Exception("could not save study with id ${row.study_id}")
68                                        }
69                                }
70
71                                // delete all obsolete descriptions
72                                sql.execute("DELETE FROM study_template_text_fields WHERE template_text_fields_idx='Description'")
73
74                                // find all template id's where this field is used
75                                sql.eachRow("SELECT DISTINCT template_fields_id, fields_idx FROM template_template_field WHERE template_field_id=${id}") { row ->
76                                        // delete the template_template_field reference
77                                        sql.execute("DELETE FROM template_template_field WHERE template_field_id=${id} AND template_fields_id=${row.template_fields_id}")
78
79                                        // and lower the idx-es of the remaining fields
80                                        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}")
81                                }
82
83                                // and delete the obsolete template field
84                                sql.execute("DELETE FROM template_field WHERE id=${id}")
85                        } catch (Exception e) {
86                                println "changeStudyDescription database upgrade failed: " + e.getMessage()
87                        }
88                }
89        }
90
91        /**
92         * execute database change r1327 if required
93         * @param sql
94         * @param db
95         */
96        public static void changeStudyDescriptionToText(sql, db) {
97                // are we running postgreSQL ?
98                if (db == "org.postgresql.Driver") {
99                        // check if column 'description' in table 'study' is not of type 'text'
100                        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) {
101                                // grom that we are performing the upgrade
102                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: study description to text".grom()
103
104                                // database upgrade required
105                                try {
106                                        // change the datatype of study::description to text
107                                        sql.execute("ALTER TABLE study ALTER COLUMN description TYPE text")
108                                } catch (Exception e) {
109                                        println "changeStudyDescriptionToText database upgrade failed: " + e.getMessage()
110                                }
111                        }
112                }
113        }
114
115        /**
116         * it appears that some TEXT template fields are not of type 'text'
117         * due to an issue in how Grails' GORM works with domain inheritance
118         * (see http://jira.codehaus.org/browse/GRAILS-6754)
119         * @param sql
120         * @param db
121         */
122        public static void changeTemplateTextFieldSignatures(sql, db) {
123                if (db == "org.postgresql.Driver") {
124                        // check if any TEXT template fields are of type 'text'
125                        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';")
126                                { row ->
127                                        if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: ${row.tablename} template_text_fields_string/elt to text".grom()
128                                        try {
129                                                // change the datatype of text fields to text
130                                                sql.execute(sprintf("ALTER TABLE %s ALTER COLUMN template_text_fields_elt TYPE text", row.tablename))
131                                                sql.execute(sprintf("ALTER TABLE %s ALTER COLUMN template_text_fields_string TYPE text", row.tablename))
132
133                                        } catch (Exception e) {
134                                                println "changeTemplateTextFieldSignatures database upgrade failed: " + e.getMessage()
135                                        }
136                                }
137                }
138        }
139
140        /**
141         * The fields 'notify' and 'openInFrame' have been added to AssayModule. However, there
142         * seems to be no method to setting the default values of these fields in the database. They
143         * are set to NULL by default, so all existing fields have 'NULL' set.
144         * This method sets the default values
145         * @param sql
146         * @param db
147         */
148        public static void setAssayModuleDefaultValues(sql, db) {
149                // do we need to perform this upgrade?
150                if ((db == "org.postgresql.Driver" || db == "com.mysql.jdbc.Driver") &&
151                        (sql.firstRow("SELECT * FROM assay_module WHERE notify IS NULL") || sql.firstRow("SELECT * FROM assay_module WHERE open_in_frame IS NULL"))
152                ) {
153                        if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: assay_module default values for boolean fields".grom()
154
155                        try {
156                                sql.execute("UPDATE assay_module SET notify=" + ((db == "org.postgresql.Driver") ? 'FALSE' : '0') + " WHERE notify IS NULL")
157                        } catch (Exception e) {
158                                println "setAssayModuleDefaultValues database upgrade failed, notify field couldn't be set to default value: " + e.getMessage()
159                        }
160
161                        // set open_in_frame?
162                        try {
163                                sql.execute("UPDATE assay_module SET open_in_frame=" + ((db == "org.postgresql.Driver") ? 'TRUE' : '1') + " WHERE open_in_frame IS NULL")
164                        } catch (Exception e) {
165                                // Maybe gdt plugin is not updated yet after revision 109 ?
166                                println "setAssayModuleDefaultValues database upgrade failed, openInFrame field couldn't be set to default value: " + e.getMessage()
167                        }
168                }
169        }
170
171        /**
172         * Drop the unique constraint for the "name" column in the MappingColumn domain
173         *
174         * @param sql
175         * @param db
176         */
177        public static void dropMappingColumnNameConstraint(sql, db) {
178                // are we running postgreSQL ?
179                if (db == "org.postgresql.Driver") {
180                        if (sql.firstRow("SELECT * FROM pg_constraint WHERE contype='mapping_column_name_key'")) {
181                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: mapping column name constraint".grom()
182                                try {
183                                        // Check if constraint still exists
184                                        sql.execute("ALTER TABLE mapping_column DROP CONSTRAINT mapping_column_name_key")
185                                } catch (Exception e) {
186                                        println "dropMappingColumnNameConstraint database upgrade failed, `name` field unique constraint couldn't be dropped: " + e.getMessage()
187                                }
188                        }
189                }
190        }
191
192        /**
193         * the importer requires the value field to be nullable
194         * @param sql
195         * @param db
196         */
197        public static void makeMappingColumnValueNullable(sql, db) {
198                // are we running postgreSQL?
199                if (db == "org.postgresql.Driver") {
200                        // do we need to perform this update?
201                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='mapping_column' AND columns.column_name='value' AND is_nullable='NO'")) {
202                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: making mapping_column::value nullable".grom()
203
204                                try {
205                                        sql.execute("ALTER TABLE mapping_column ALTER COLUMN value DROP NOT NULL")
206                                } catch (Exception e) {
207                                        println "makeMappingColumnValueNullable database upgrade failed: " + e.getMessage()
208                                }
209                        }
210                }
211        }
212
213        /**
214         * The field study.code has been set to be nullable
215         * The field assay.externalAssayId has been removed
216         * @param sql
217         * @param db
218         */
219        public static void alterStudyAndAssay(sql, db) {
220                def updated = false
221
222                // are we running postgreSQL ?
223                if (db == "org.postgresql.Driver") {
224                        // see if table assay contains a column external_assayid
225                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='assay' AND columns.column_name='external_assayid'")) {
226                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: dropping column 'external_assayid' from table 'assay'".grom()
227
228                                try {
229                                        sql.execute("ALTER TABLE assay DROP COLUMN external_assayid")
230                                        updated = true
231                                } catch (Exception e) {
232                                        println "alterStudyAndAssay database upgrade failed, externalAssayId could not be removed from assay: " + e.getMessage()
233                                }
234
235                        }
236
237                        // see if table study contains a column code which is not nullable
238                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='study' AND columns.column_name='code' AND is_nullable='NO'")) {
239                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: dropping column 'code' from table 'study'".grom()
240
241                                try {
242                                        sql.execute("ALTER TABLE study ALTER COLUMN code DROP NOT NULL")
243                                        updated = true
244                                } catch (Exception e) {
245                                        println "alterStudyAndAssay database upgrade failed, study.code could not be set to accept null values: " + e.getMessage()
246                                }
247                        }
248
249                        // Load all studies and save them again. This prevents errors on saving later
250                        if (updated) {
251                                if (String.metaClass.getMetaMethod("grom")) "re-saving studies...".grom()
252
253                                Study.list().each { study ->
254                                        if (String.metaClass.getMetaMethod("grom")) "re-saving study: ${study}".grom()
255                                        study.save()
256                                }
257                        }
258                }
259        }
260
261        /**
262         * make sure all date_created and last_updated columns are NOT nullable, and
263         * set values to now() of they are null
264         * @param sql
265         * @param db
266         */
267        public static void fixDateCreatedAndLastUpdated(sql, db) {
268                // are we running PostgreSQL?
269                if (db == "org.postgresql.Driver") {
270                        // see if we need to modify anything?
271                        sql.eachRow("SELECT table_name,column_name FROM information_schema.columns WHERE column_name IN ('last_updated', 'date_created') AND is_nullable='YES'") { row ->
272                                // grom what we are doing
273                                if (String.metaClass.getMetaMethod("grom")) "fixing nullable for ${row.table_name}:${row.column_name}".grom()
274
275                                // fix database
276                                try {
277                                        // setting all null values to now()
278                                        sql.execute(sprintf("UPDATE %s SET %s=now() WHERE %s IS NULL",row.table_name,row.column_name,row.column_name))
279
280                                        // and alter the table to disallow null values
281                                        sql.execute(sprintf("ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",row.table_name,row.column_name))
282                                } catch (Exception e) {
283                                        println "fixDateCreatedAndLastUpdated database upgrade failed: " + e.getMessage()
284                                }
285                        }
286                }
287        }
288       
289       
290        /**
291         * drops the field platform from assay modules
292         * @param sql
293         * @param db
294         */
295        public static void dropAssayModulePlatform(sql, db) {
296                // are we running postgreSQL?
297                if (db == "org.postgresql.Driver") {
298                        // do we need to perform this update?
299                        if (sql.firstRow("SELECT * FROM information_schema.columns WHERE columns.table_name='assay_module' AND columns.column_name='platform'")) {
300                                if (String.metaClass.getMetaMethod("grom")) "performing database upgrade: removing assayModule platform".grom()
301
302                                try {
303                                        sql.execute("ALTER TABLE assay_module DROP COLUMN platform")
304                                } catch (Exception e) {
305                                        println "dropAssayModulePlatform database upgrade failed: " + e.getMessage()
306                                }
307                        }
308                }
309        }
310
311        /**
312         * Make sure the study title, template name and template field names are unique
313         * @param sql
314         * @param db
315         */
316        public static void makeStudyTitleAndTemplateNamesUnique(sql, db) {
317                def titleCount,title,newTitle,templateFieldTypeCount,templateFieldUnitCount
318                def grom = false
319
320                // are we running postgreSQL?
321                if (db == "org.postgresql.Driver") {
322                        // yes, find all duplicate study titles
323                        sql.eachRow("   SELECT DISTINCT a.title, 'study' as tablename, '' as entity FROM study a WHERE (SELECT count(*) FROM study b WHERE b.title=a.title) > 1\
324                                                        UNION SELECT DISTINCT d.name as title, 'template' as tablename, d.entity FROM template d WHERE (SELECT count(*) FROM template c WHERE d.name=c.name AND d.entity=c.entity) > 1\
325                                                        UNION SELECT DISTINCT e.templatefieldname as title, 'template_field' as tablename, e.templatefieldentity as entity FROM template_field e WHERE (SELECT count(*) FROM template_field f WHERE e.templatefieldname=f.templatefieldname AND e.templatefieldentity=f.templatefieldentity) > 1\
326                                                        ") { row ->
327
328                                // grom what we are doing
329                                if (String.metaClass.getMetaMethod("grom") && !grom) {
330                                        "making study titles, template names and template_field names unique".grom()
331                                        grom = true
332                                }
333
334                                // set work variables
335                                titleCount      = 1
336                                title           = row.title.replace("'","\'")
337
338                                // check what we are updating
339                                switch (row.tablename) {
340                                        case "study":
341                                                // update study titles
342                                                sql.eachRow(sprintf("SELECT id FROM study WHERE title='%s'", title)) { studyRow ->
343                                                        newTitle = title + ((titleCount>1) ? " - ${titleCount}" : "")
344                                                        sql.execute(sprintf("UPDATE study SET title='%s' WHERE id=%d",newTitle,studyRow.id))
345                                                        titleCount++
346                                                }
347                                                break
348                                        case "template":
349                                                // update template names
350                                                sql.eachRow(sprintf("SELECT id FROM template WHERE name='%s' AND entity='%s'", title, row.entity)) { templateRow ->
351                                                        newTitle = title + ((titleCount>1) ? " - ${titleCount}" : "")
352                                                        sql.execute(sprintf("UPDATE template SET name='%s' WHERE id=%d",newTitle,templateRow.id))
353                                                        titleCount++
354                                                }
355                                                break
356                                        case "template_field":
357                                                templateFieldTypeCount = [:]
358                                                templateFieldUnitCount = [:]
359
360                                                // update template_field names
361                                                sql.eachRow(sprintf("SELECT id,templatefieldunit as unit,templatefieldtype as type FROM template_field WHERE templatefieldname='%s' AND templatefieldentity='%s'", title, row.entity)) { templateFieldRow ->
362                                                        if (templateFieldRow.unit) {
363                                                                templateFieldUnitCount[ templateFieldRow.unit ] = (templateFieldUnitCount[ templateFieldRow.unit ]) ? templateFieldUnitCount[ templateFieldRow.unit ]+1 : 1
364                                                                newTitle = "${title} (${templateFieldRow.unit})" + ((templateFieldUnitCount[ templateFieldRow.unit ]>1) ? " - ${templateFieldUnitCount[ templateFieldRow.unit ]}" : "")
365                                                        } else {
366                                                                templateFieldTypeCount[ templateFieldRow.type ] = (templateFieldTypeCount[ templateFieldRow.type ]) ? templateFieldTypeCount[ templateFieldRow.type ]+1 : 1
367                                                                newTitle = "${title} (${templateFieldRow.type})" + ((templateFieldTypeCount[ templateFieldRow.type ]>1) ? " - ${templateFieldTypeCount[ templateFieldRow.type ]}" : "")
368                                                                titleCount++
369                                                        }
370                                                        sql.execute(sprintf("UPDATE template_field SET templatefieldname='%s' WHERE id=%d",newTitle,templateFieldRow.id))
371                                                }
372                                                break
373                                        default:
374                                                // this shouldn't happen
375                                                break
376                                }
377                        }
378                }
379        }
380}
Note: See TracBrowser for help on using the browser.