'use strict'; exports.__esModule = true; var _omit2 = require('lodash/omit'); var _omit3 = _interopRequireDefault(_omit2); var _map2 = require('lodash/map'); var _map3 = _interopRequireDefault(_map2); var _identity2 = require('lodash/identity'); var _identity3 = _interopRequireDefault(_identity2); var _find2 = require('lodash/find'); var _find3 = _interopRequireDefault(_find2); var _uniqueId2 = require('lodash/uniqueId'); var _uniqueId3 = _interopRequireDefault(_uniqueId2); var _assign2 = require('lodash/assign'); var _assign3 = _interopRequireDefault(_assign2); var _bluebird = require('bluebird'); var _bluebird2 = _interopRequireDefault(_bluebird); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // So altering the schema in SQLite3 is a major pain. // We have our own object to deal with the renaming and altering the types // for sqlite3 things. function SQLite3_DDL(client, tableCompiler, pragma, connection) { this.client = client; this.tableCompiler = tableCompiler; this.pragma = pragma; this.tableName = this.tableCompiler.tableNameRaw; this.alteredName = (0, _uniqueId3.default)('_knex_temp_alter'); this.connection = connection; } // SQLite3_DDL // // All of the SQLite3 specific DDL helpers for renaming/dropping // columns and changing datatypes. // ------- (0, _assign3.default)(SQLite3_DDL.prototype, { getColumn: _bluebird2.default.method(function (column) { var currentCol = (0, _find3.default)(this.pragma, { name: column }); if (!currentCol) throw new Error('The column ' + column + ' is not in the ' + this.tableName + ' table'); return currentCol; }), getTableSql: function getTableSql() { return this.trx.raw('SELECT name, sql FROM sqlite_master WHERE type="table" AND name="' + this.tableName + '"'); }, renameTable: _bluebird2.default.method(function () { return this.trx.raw('ALTER TABLE "' + this.tableName + '" RENAME TO "' + this.alteredName + '"'); }), dropOriginal: function dropOriginal() { return this.trx.raw('DROP TABLE "' + this.tableName + '"'); }, dropTempTable: function dropTempTable() { return this.trx.raw('DROP TABLE "' + this.alteredName + '"'); }, copyData: function copyData() { return this.trx.raw('SELECT * FROM "' + this.tableName + '"').bind(this).then(this.insertChunked(20, this.alteredName)); }, reinsertData: function reinsertData(iterator) { return function () { return this.trx.raw('SELECT * FROM "' + this.alteredName + '"').bind(this).then(this.insertChunked(20, this.tableName, iterator)); }; }, insertChunked: function insertChunked(amount, target, iterator) { iterator = iterator || _identity3.default; return function (result) { var batch = []; var ddl = this; return _bluebird2.default.reduce(result, function (memo, row) { memo++; batch.push(row); if (memo % 20 === 0 || memo === result.length) { return ddl.trx.queryBuilder().table(target).insert((0, _map3.default)(batch, iterator)).then(function () { batch = []; }).thenReturn(memo); } return memo; }, 0); }; }, createTempTable: function createTempTable(createTable) { return function () { return this.trx.raw(createTable.sql.replace(this.tableName, this.alteredName)); }; }, _doReplace: function _doReplace(sql, from, to) { var matched = sql.match(/^CREATE TABLE (\S+) \((.*)\)/); var tableName = matched[1]; var defs = matched[2]; if (!defs) { throw new Error('No column definitions in this statement!'); } var parens = 0, args = [], ptr = 0; var i = 0; var x = defs.length; for (i = 0; i < x; i++) { switch (defs[i]) { case '(': parens++; break; case ')': parens--; break; case ',': if (parens === 0) { args.push(defs.slice(ptr, i)); ptr = i + 1; } break; case ' ': if (ptr === i) { ptr = i + 1; } break; } } args.push(defs.slice(ptr, i)); args = args.map(function (item) { var split = item.split(' '); if (split[0] === from) { // column definition if (to) { split[0] = to; return split.join(' '); } return ''; // for deletions } // skip constraint name var idx = /constraint/i.test(split[0]) ? 2 : 0; // primary key and unique constraints have one or more // columns from this table listed between (); replace // one if it matches if (/primary|unique/i.test(split[idx])) { return item.replace(/\(.*\)/, function (columns) { return columns.replace(from, to); }); } // foreign keys have one or more columns from this table // listed between (); replace one if it matches // foreign keys also have a 'references' clause // which may reference THIS table; if it does, replace // column references in that too! if (/foreign/.test(split[idx])) { split = item.split(/ references /i); // the quoted column names save us from having to do anything // other than a straight replace here split[0] = split[0].replace(from, to); if (split[1].slice(0, tableName.length) === tableName) { split[1] = split[1].replace(/\(.*\)/, function (columns) { return columns.replace(from, to); }); } return split.join(' references '); } return item; }); return sql.replace(/\(.*\)/, function () { return '(' + args.join(', ') + ')'; }).replace(/,\s*([,)])/, '$1'); }, // Boy, this is quite a method. renameColumn: _bluebird2.default.method(function (from, to) { var _this = this; return this.client.transaction(function (trx) { _this.trx = trx; return _this.getColumn(from).bind(_this).then(_this.getTableSql).then(function (sql) { var a = this.client.wrapIdentifier(from); var b = this.client.wrapIdentifier(to); var createTable = sql[0]; var newSql = this._doReplace(createTable.sql, a, b); if (sql === newSql) { throw new Error('Unable to find the column to change'); } return _bluebird2.default.bind(this).then(this.createTempTable(createTable)).then(this.copyData).then(this.dropOriginal).then(function () { return this.trx.raw(newSql); }).then(this.reinsertData(function (row) { row[to] = row[from]; return (0, _omit3.default)(row, from); })).then(this.dropTempTable); }); }, { connection: this.connection }); }), dropColumn: _bluebird2.default.method(function (column) { var _this2 = this; return this.client.transaction(function (trx) { _this2.trx = trx; return _this2.getColumn(column).bind(_this2).then(_this2.getTableSql).then(function (sql) { var createTable = sql[0]; var a = this.client.wrapIdentifier(column); var newSql = this._doReplace(createTable.sql, a, ''); if (sql === newSql) { throw new Error('Unable to find the column to change'); } return _bluebird2.default.bind(this).then(this.createTempTable(createTable)).then(this.copyData).then(this.dropOriginal).then(function () { return this.trx.raw(newSql); }).then(this.reinsertData(function (row) { return (0, _omit3.default)(row, column); })).then(this.dropTempTable); }); }, { connection: this.connection }); }) }); exports.default = SQLite3_DDL; module.exports = exports['default'];