UNPKG

inspired-server

Version:
157 lines (130 loc) 4.7 kB
Q = require 'q' class Migration constructor: (@classes) -> @db = new App.DB @queries = { tables: [] dropConstraints: [] dropFields: [] fields: [] uniques: [] constraints: [] } promises = []; for table, entityClass of @classes promises.push @prepareTable(table, entityClass) Q.all(promises) .then => @runQueries() .catch (e) -> console.log '[ERROR]', e runQuery: (sql) -> console.log '[MIGRATION]', sql @db.adapter().query sql, [] handleError: (sql, e) -> switch e.code when '42P07' then #nothing when '42710' then #nothing else console.log '[ERROR]', sql, e runQueries: -> deferred = Q.defer() deferred.resolve() for table, queries of @queries for sql in queries when sql? and sql isnt '' deferred.promise .then @runQuery.bind(@, sql) .catch @handleError.bind(@, sql) prepareTable: (table, entityClass) -> sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='#{table}'" @db.adapter().query(sql, []).then @parseTable.bind(@, table, entityClass) parseTable: (table, entityClass, result) -> instance = new entityClass fields = instance.fields() data = { fields: { inCode: {}, inDB: {}, onlyInCode: {}, onlyInDB: {}, inBoth: {} } constraints: { inCode: {}, inDB: {}, onlyInCode: {}, onlyInDB: {}, inBoth: {} } } for name, field of fields data.fields.inCode[name] = field data.constraints.inCode[name] = field if field.isForeignKey() ### Index the result set by column name. The input is numerically keyed but the output uses the column name as the key. ### data.fields.inDB = result.rows.reduce (rows, row) -> rows[row.column_name] = row rows , {} ### @todo Refactor ### for name, row of data.fields.inDB when not data.fields.inCode[name]? data.fields.onlyInDB[name] = row for name, field of data.fields.inCode when not data.fields.inDB[name]? data.fields.onlyInCode[name] = field for name, field of data.fields.inCode when data.fields.inDB[name]? data.fields.inBoth[name] = field for name, row of data.constraints.inDB when not data.constraints.inCode[name]? data.constraints.onlyInDB[name] = row for name, field of data.constraints.inCode when not data.constraints.inDB[name]? data.constraints.onlyInCode[name] = field # Make sure the table exists or create it @queries.tables.push "CREATE TABLE IF NOT EXISTS \"#{table}\" ()" # DROP constraints for column, row of data.constraints.onlyInDB @queries.dropConstraints.push "ALTER TABLE \"#{table}\" DROP CONSTRAINT #{table}_#{column}_unique" @queries.dropConstraints.push "ALTER TABLE \"#{table}\" DROP CONSTRAINT #{table}_#{column}_fk" # DROP fields for column, row of data.fields.onlyInDB @queries.dropFields.push "ALTER TABLE \"#{table}\" DROP IF EXISTS \"#{column}\" CASCADE" # ADD fields for column, field of data.fields.onlyInCode if field.isForeignKey() and field.multiple() # if field.unique() # OneToMany # else # ManyToMany ### @todo Handle ManyToMany ### else @fieldHelper table, column, field # ALTER fields for column, field of data.fields.inBoth @fieldHelper table, column, field, data.fields.inDB[column] # ADD constraints for column, field of data.constraints.onlyInCode if field.multiple() # if field.unique() # OneToMany # else # ManyToMany ### @todo Handle ManyToMany ### else @fieldUniqueHelper table, column if field.unique() # OneToOne constraintName = "#{table}_#{column}_fk" @queries.constraints.push "ALTER TABLE \"#{table}\" ADD CONSTRAINT #{constraintName} FOREIGN KEY (#{column}) REFERENCES \"#{field.references()}\" (\"uuid\")" fieldHelper: (table, column, field, row) -> sql = '' sqlLength = if field.length() then "(#{field.length()})" else '' if row? typeChange = (field.type() isnt row.udt_name) lengthChange = (field.length() isnt row.character_maximum_length) defaultChange = (field.default() isnt row.column_default) sql += " TYPE #{field.type()}#{sqlLength}" if typeChange or lengthChange sql += " SET DEFAULT #{field.default()}" if defaultChange else sql += " #{field.type()}#{sqlLength}" sql += " DEFAULT #{field.default()}" operation = if row? then "ALTER" else "ADD" sql = "ALTER TABLE \"#{table}\" #{operation} \"#{column}\"" + sql if sql @queries.fields.push sql if sql if field.unique() @fieldUniqueHelper table, column fieldUniqueHelper: (table, column) -> constraintName = "#{table}_#{column}_unique" @queries.uniques.push "ALTER TABLE \"#{table}\" ADD CONSTRAINT #{constraintName} UNIQUE (#{column})" module.exports = Migration