query-interface.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. var Utils = require("../../utils")
  2. /**
  3. Returns an object that treats SQLite's inabilities to do certain queries.
  4. @class QueryInterface
  5. @static
  6. */
  7. var QueryInterface = module.exports = {
  8. /**
  9. A wrapper that fixes SQLite's inability to remove columns from existing tables.
  10. It will create a backup of the table, drop the table afterwards and create a
  11. new table with the same name but without the obsolete column.
  12. @method removeColumn
  13. @for QueryInterface
  14. @param {String} tableName The name of the table.
  15. @param {String} attributeName The name of the attribute that we want to remove.
  16. @param {CustomEventEmitter} emitter The EventEmitter from outside.
  17. @param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
  18. @since 1.6.0
  19. */
  20. removeColumn: function(tableName, attributeName, emitter, queryAndEmit) {
  21. this.describeTable(tableName).complete(function(err, fields) {
  22. if (err) {
  23. emitter.emit('error', err)
  24. } else {
  25. delete fields[attributeName]
  26. var sql = this.QueryGenerator.removeColumnQuery(tableName, fields)
  27. , subQueries = sql.split(';').filter(function(q) { return q !== '' })
  28. QueryInterface.execMultiQuery.call(this, subQueries, 'removeColumn', emitter, queryAndEmit)
  29. }
  30. }.bind(this))
  31. },
  32. /**
  33. A wrapper that fixes SQLite's inability to change columns from existing tables.
  34. It will create a backup of the table, drop the table afterwards and create a
  35. new table with the same name but with a modified version of the respective column.
  36. @method changeColumn
  37. @for QueryInterface
  38. @param {String} tableName The name of the table.
  39. @param {Object} attributes An object with the attribute's name as key and it's options as value object.
  40. @param {CustomEventEmitter} emitter The EventEmitter from outside.
  41. @param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
  42. @since 1.6.0
  43. */
  44. changeColumn: function(tableName, attributes, emitter, queryAndEmit) {
  45. var attributeName = Utils._.keys(attributes)[0]
  46. this.describeTable(tableName).complete(function(err, fields) {
  47. if (err) {
  48. emitter.emit('error', err)
  49. } else {
  50. fields[attributeName] = attributes[attributeName]
  51. var sql = this.QueryGenerator.removeColumnQuery(tableName, fields)
  52. , subQueries = sql.split(';').filter(function(q) { return q !== '' })
  53. QueryInterface.execMultiQuery.call(this, subQueries, 'changeColumn', emitter, queryAndEmit)
  54. }
  55. }.bind(this))
  56. },
  57. /**
  58. A wrapper that fixes SQLite's inability to rename columns from existing tables.
  59. It will create a backup of the table, drop the table afterwards and create a
  60. new table with the same name but with a renamed version of the respective column.
  61. @method renameColumn
  62. @for QueryInterface
  63. @param {String} tableName The name of the table.
  64. @param {String} attrNameBefore The name of the attribute before it was renamed.
  65. @param {String} attrNameAfter The name of the attribute after it was renamed.
  66. @param {CustomEventEmitter} emitter The EventEmitter from outside.
  67. @param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
  68. @since 1.6.0
  69. */
  70. renameColumn: function(tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit) {
  71. this.describeTable(tableName).complete(function(err, fields) {
  72. if (err) {
  73. emitter.emit('error', err)
  74. } else {
  75. fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore])
  76. delete fields[attrNameBefore]
  77. var sql = this.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields)
  78. , subQueries = sql.split(';').filter(function(q) { return q !== '' })
  79. QueryInterface.execMultiQuery.call(this, subQueries, 'renameColumn', emitter, queryAndEmit)
  80. }
  81. }.bind(this))
  82. },
  83. execMultiQuery: function(queries, methodName, emitter, queryAndEmit) {
  84. var chainer = new Utils.QueryChainer()
  85. queries.splice(0, queries.length - 1).forEach(function(query) {
  86. chainer.add(this.sequelize, 'query', [query + ";", null, { raw: true }])
  87. }.bind(this))
  88. chainer
  89. .runSerially()
  90. .complete(function(err) {
  91. if (err) {
  92. emitter.emit(methodName, err)
  93. emitter.emit('error', err)
  94. } else {
  95. queryAndEmit.call(this, queries.splice(queries.length - 1)[0], methodName, {}, emitter)
  96. }
  97. }.bind(this))
  98. },
  99. dropAllTables: function(options) {
  100. var self = this
  101. if (!options) {
  102. options = {}
  103. }
  104. var skip = options.skip || [];
  105. return new Utils.CustomEventEmitter(function(dropAllTablesEmitter) {
  106. var events = []
  107. , chainer = new Utils.QueryChainer()
  108. , onError = function(err) {
  109. self.emit('dropAllTables', err)
  110. dropAllTablesEmitter.emit('error', err)
  111. }
  112. self
  113. .showAllTables()
  114. .error(onError)
  115. .success(function(tableNames) {
  116. self
  117. .sequelize
  118. .query('PRAGMA foreign_keys;')
  119. .proxy(dropAllTablesEmitter, { events: ['sql'] })
  120. .error(onError)
  121. .success(function(result) {
  122. var foreignKeysAreEnabled = result.foreign_keys === 1
  123. if (foreignKeysAreEnabled) {
  124. var queries = []
  125. queries.push('PRAGMA foreign_keys = OFF')
  126. tableNames.forEach(function(tableName) {
  127. // if tableName is not in the Array of tables names then dont drop it
  128. if (skip.indexOf(tableName) === -1) {
  129. queries.push(self.QueryGenerator.dropTableQuery(tableName).replace(';', ''))
  130. }
  131. })
  132. queries.push('PRAGMA foreign_keys = ON')
  133. QueryInterface.execMultiQuery.call(self, queries, 'dropAllTables', dropAllTablesEmitter, self.queryAndEmit)
  134. } else {
  135. // add the table removal query to the chainer
  136. tableNames.forEach(function(tableName) {
  137. chainer.add(self, 'dropTable', [ tableName, { cascade: true } ])
  138. })
  139. chainer
  140. .runSerially()
  141. .proxy(dropAllTablesEmitter, { events: ['sql'] })
  142. .error(onError)
  143. .success(function() {
  144. self.emit('dropAllTables', null)
  145. dropAllTablesEmitter.emit('success', null)
  146. })
  147. }
  148. })
  149. })
  150. }).run()
  151. }
  152. }