ddl.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _omit2 = require('lodash/omit');
  4. var _omit3 = _interopRequireDefault(_omit2);
  5. var _map2 = require('lodash/map');
  6. var _map3 = _interopRequireDefault(_map2);
  7. var _identity2 = require('lodash/identity');
  8. var _identity3 = _interopRequireDefault(_identity2);
  9. var _find2 = require('lodash/find');
  10. var _find3 = _interopRequireDefault(_find2);
  11. var _uniqueId2 = require('lodash/uniqueId');
  12. var _uniqueId3 = _interopRequireDefault(_uniqueId2);
  13. var _assign2 = require('lodash/assign');
  14. var _assign3 = _interopRequireDefault(_assign2);
  15. var _bluebird = require('bluebird');
  16. var _bluebird2 = _interopRequireDefault(_bluebird);
  17. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  18. // So altering the schema in SQLite3 is a major pain.
  19. // We have our own object to deal with the renaming and altering the types
  20. // for sqlite3 things.
  21. function SQLite3_DDL(client, tableCompiler, pragma, connection) {
  22. this.client = client;
  23. this.tableCompiler = tableCompiler;
  24. this.pragma = pragma;
  25. this.tableName = this.tableCompiler.tableNameRaw;
  26. this.alteredName = (0, _uniqueId3.default)('_knex_temp_alter');
  27. this.connection = connection;
  28. }
  29. // SQLite3_DDL
  30. //
  31. // All of the SQLite3 specific DDL helpers for renaming/dropping
  32. // columns and changing datatypes.
  33. // -------
  34. (0, _assign3.default)(SQLite3_DDL.prototype, {
  35. getColumn: _bluebird2.default.method(function (column) {
  36. var currentCol = (0, _find3.default)(this.pragma, { name: column });
  37. if (!currentCol) throw new Error('The column ' + column + ' is not in the ' + this.tableName + ' table');
  38. return currentCol;
  39. }),
  40. getTableSql: function getTableSql() {
  41. return this.trx.raw('SELECT name, sql FROM sqlite_master WHERE type="table" AND name="' + this.tableName + '"');
  42. },
  43. renameTable: _bluebird2.default.method(function () {
  44. return this.trx.raw('ALTER TABLE "' + this.tableName + '" RENAME TO "' + this.alteredName + '"');
  45. }),
  46. dropOriginal: function dropOriginal() {
  47. return this.trx.raw('DROP TABLE "' + this.tableName + '"');
  48. },
  49. dropTempTable: function dropTempTable() {
  50. return this.trx.raw('DROP TABLE "' + this.alteredName + '"');
  51. },
  52. copyData: function copyData() {
  53. return this.trx.raw('SELECT * FROM "' + this.tableName + '"').bind(this).then(this.insertChunked(20, this.alteredName));
  54. },
  55. reinsertData: function reinsertData(iterator) {
  56. return function () {
  57. return this.trx.raw('SELECT * FROM "' + this.alteredName + '"').bind(this).then(this.insertChunked(20, this.tableName, iterator));
  58. };
  59. },
  60. insertChunked: function insertChunked(amount, target, iterator) {
  61. iterator = iterator || _identity3.default;
  62. return function (result) {
  63. var batch = [];
  64. var ddl = this;
  65. return _bluebird2.default.reduce(result, function (memo, row) {
  66. memo++;
  67. batch.push(row);
  68. if (memo % 20 === 0 || memo === result.length) {
  69. return ddl.trx.queryBuilder().table(target).insert((0, _map3.default)(batch, iterator)).then(function () {
  70. batch = [];
  71. }).thenReturn(memo);
  72. }
  73. return memo;
  74. }, 0);
  75. };
  76. },
  77. createTempTable: function createTempTable(createTable) {
  78. return function () {
  79. return this.trx.raw(createTable.sql.replace(this.tableName, this.alteredName));
  80. };
  81. },
  82. _doReplace: function _doReplace(sql, from, to) {
  83. var matched = sql.match(/^CREATE TABLE (\S+) \((.*)\)/);
  84. var tableName = matched[1];
  85. var defs = matched[2];
  86. if (!defs) {
  87. throw new Error('No column definitions in this statement!');
  88. }
  89. var parens = 0,
  90. args = [],
  91. ptr = 0;
  92. var i = 0;
  93. var x = defs.length;
  94. for (i = 0; i < x; i++) {
  95. switch (defs[i]) {
  96. case '(':
  97. parens++;
  98. break;
  99. case ')':
  100. parens--;
  101. break;
  102. case ',':
  103. if (parens === 0) {
  104. args.push(defs.slice(ptr, i));
  105. ptr = i + 1;
  106. }
  107. break;
  108. case ' ':
  109. if (ptr === i) {
  110. ptr = i + 1;
  111. }
  112. break;
  113. }
  114. }
  115. args.push(defs.slice(ptr, i));
  116. args = args.map(function (item) {
  117. var split = item.split(' ');
  118. if (split[0] === from) {
  119. // column definition
  120. if (to) {
  121. split[0] = to;
  122. return split.join(' ');
  123. }
  124. return ''; // for deletions
  125. }
  126. // skip constraint name
  127. var idx = /constraint/i.test(split[0]) ? 2 : 0;
  128. // primary key and unique constraints have one or more
  129. // columns from this table listed between (); replace
  130. // one if it matches
  131. if (/primary|unique/i.test(split[idx])) {
  132. return item.replace(/\(.*\)/, function (columns) {
  133. return columns.replace(from, to);
  134. });
  135. }
  136. // foreign keys have one or more columns from this table
  137. // listed between (); replace one if it matches
  138. // foreign keys also have a 'references' clause
  139. // which may reference THIS table; if it does, replace
  140. // column references in that too!
  141. if (/foreign/.test(split[idx])) {
  142. split = item.split(/ references /i);
  143. // the quoted column names save us from having to do anything
  144. // other than a straight replace here
  145. split[0] = split[0].replace(from, to);
  146. if (split[1].slice(0, tableName.length) === tableName) {
  147. split[1] = split[1].replace(/\(.*\)/, function (columns) {
  148. return columns.replace(from, to);
  149. });
  150. }
  151. return split.join(' references ');
  152. }
  153. return item;
  154. });
  155. return sql.replace(/\(.*\)/, function () {
  156. return '(' + args.join(', ') + ')';
  157. }).replace(/,\s*([,)])/, '$1');
  158. },
  159. // Boy, this is quite a method.
  160. renameColumn: _bluebird2.default.method(function (from, to) {
  161. var _this = this;
  162. return this.client.transaction(function (trx) {
  163. _this.trx = trx;
  164. return _this.getColumn(from).bind(_this).then(_this.getTableSql).then(function (sql) {
  165. var a = this.client.wrapIdentifier(from);
  166. var b = this.client.wrapIdentifier(to);
  167. var createTable = sql[0];
  168. var newSql = this._doReplace(createTable.sql, a, b);
  169. if (sql === newSql) {
  170. throw new Error('Unable to find the column to change');
  171. }
  172. return _bluebird2.default.bind(this).then(this.createTempTable(createTable)).then(this.copyData).then(this.dropOriginal).then(function () {
  173. return this.trx.raw(newSql);
  174. }).then(this.reinsertData(function (row) {
  175. row[to] = row[from];
  176. return (0, _omit3.default)(row, from);
  177. })).then(this.dropTempTable);
  178. });
  179. }, { connection: this.connection });
  180. }),
  181. dropColumn: _bluebird2.default.method(function (column) {
  182. var _this2 = this;
  183. return this.client.transaction(function (trx) {
  184. _this2.trx = trx;
  185. return _this2.getColumn(column).bind(_this2).then(_this2.getTableSql).then(function (sql) {
  186. var createTable = sql[0];
  187. var a = this.client.wrapIdentifier(column);
  188. var newSql = this._doReplace(createTable.sql, a, '');
  189. if (sql === newSql) {
  190. throw new Error('Unable to find the column to change');
  191. }
  192. return _bluebird2.default.bind(this).then(this.createTempTable(createTable)).then(this.copyData).then(this.dropOriginal).then(function () {
  193. return this.trx.raw(newSql);
  194. }).then(this.reinsertData(function (row) {
  195. return (0, _omit3.default)(row, column);
  196. })).then(this.dropTempTable);
  197. });
  198. }, { connection: this.connection });
  199. })
  200. });
  201. exports.default = SQLite3_DDL;
  202. module.exports = exports['default'];