2013-09-13 13:50:41 -04:00
|
|
|
// SQLite3
|
2013-09-08 15:57:32 -04:00
|
|
|
// -------
|
|
|
|
|
|
|
|
// Other dependencies, including the `sqlite3` library,
|
|
|
|
// which needs to be added as a dependency to the project
|
|
|
|
// using this database.
|
2013-11-25 02:00:12 -05:00
|
|
|
var _ = require('lodash');
|
2013-09-13 13:50:41 -04:00
|
|
|
var sqlite3 = require('sqlite3');
|
2013-09-08 15:57:32 -04:00
|
|
|
|
|
|
|
// All other local project modules needed in this scope.
|
2013-09-11 23:36:55 -04:00
|
|
|
var ServerBase = require('./base').ServerBase;
|
2013-09-08 15:57:32 -04:00
|
|
|
var Builder = require('../../lib/builder').Builder;
|
2013-09-12 02:09:29 -04:00
|
|
|
var Transaction = require('../../lib/transaction').Transaction;
|
2013-09-04 17:32:32 -04:00
|
|
|
var SchemaInterface = require('../../lib/schemainterface').SchemaInterface;
|
2013-09-13 13:50:41 -04:00
|
|
|
var Helpers = require('../../lib/helpers').Helpers;
|
2013-10-27 22:34:58 -04:00
|
|
|
var Promise = require('../../lib/promise').Promise;
|
2013-09-03 23:02:23 -04:00
|
|
|
|
2013-09-17 06:54:26 -04:00
|
|
|
var grammar = require('./sqlite3/grammar').grammar;
|
|
|
|
var schemaGrammar = require('./sqlite3/schemagrammar').schemaGrammar;
|
|
|
|
|
2013-09-08 15:57:32 -04:00
|
|
|
// Constructor for the SQLite3Client.
|
2013-09-11 23:36:55 -04:00
|
|
|
var SQLite3Client = exports.Client = ServerBase.extend({
|
2013-09-03 23:02:23 -04:00
|
|
|
|
2013-09-11 23:36:55 -04:00
|
|
|
dialect: 'sqlite3',
|
2013-09-03 23:02:23 -04:00
|
|
|
|
2013-09-17 06:54:26 -04:00
|
|
|
// Attach the appropriate grammar definitions onto the current client.
|
|
|
|
attachGrammars: function() {
|
|
|
|
this.grammar = grammar;
|
|
|
|
this.schemaGrammar = schemaGrammar;
|
|
|
|
},
|
|
|
|
|
2013-09-13 17:20:40 -04:00
|
|
|
// Runs the query on the specified connection, providing the bindings
|
|
|
|
// and any other necessary prep work.
|
2013-09-12 00:26:33 -04:00
|
|
|
runQuery: function(connection, sql, bindings, builder) {
|
2013-09-13 13:27:00 -04:00
|
|
|
if (!connection) throw new Error('No database connection exists for the query');
|
2013-09-12 02:09:29 -04:00
|
|
|
if (sql === '__rename_column__') {
|
|
|
|
return this.ddl(connection, sql, bindings, builder);
|
2013-09-04 17:32:32 -04:00
|
|
|
}
|
2013-09-12 00:26:33 -04:00
|
|
|
var method = (builder.type === 'insert' ||
|
|
|
|
builder.type === 'update' || builder.type === 'delete') ? 'run' : 'all';
|
|
|
|
// Call the querystring and then release the client
|
2013-10-27 22:34:58 -04:00
|
|
|
var dfd = Promise.pending();
|
2013-09-12 00:26:33 -04:00
|
|
|
connection[method](sql, bindings, function(err, resp) {
|
|
|
|
if (err) return dfd.reject(err);
|
|
|
|
// We need the context here, because it has the "this.lastID" or "this.changes"
|
2013-10-27 22:34:58 -04:00
|
|
|
return dfd.fulfill([resp, this]);
|
2013-09-12 00:26:33 -04:00
|
|
|
});
|
|
|
|
return dfd.promise;
|
2013-09-03 23:02:23 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
poolDefaults: {
|
|
|
|
max: 1,
|
|
|
|
min: 1,
|
|
|
|
destroy: function(client) { client.close(); }
|
|
|
|
},
|
|
|
|
|
2013-09-12 02:09:29 -04:00
|
|
|
ddl: function(connection, sql, bindings, builder) {
|
|
|
|
var client = this;
|
2013-10-27 22:34:58 -04:00
|
|
|
return Promise.promisify(connection.run, connection)('begin transaction;').then(function() {
|
2013-09-12 02:09:29 -04:00
|
|
|
var transaction = new Transaction({client: client});
|
|
|
|
var containerObj = transaction.getContainerObject(connection);
|
|
|
|
return transaction.initiateDeferred(function(trx) {
|
|
|
|
client.alterSchema.call(client, builder, trx);
|
|
|
|
})(containerObj);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-09-11 23:36:55 -04:00
|
|
|
getRawConnection: function() {
|
2013-10-27 22:34:58 -04:00
|
|
|
var dfd = Promise.pending();
|
2013-09-11 23:36:55 -04:00
|
|
|
var db = new sqlite3.Database(this.connectionSettings.filename, function(err) {
|
|
|
|
if (err) return dfd.reject(err);
|
2013-10-27 22:34:58 -04:00
|
|
|
dfd.fulfill(db);
|
2013-09-03 23:02:23 -04:00
|
|
|
});
|
2013-09-11 23:36:55 -04:00
|
|
|
return dfd.promise;
|
2013-09-03 23:02:23 -04:00
|
|
|
},
|
|
|
|
|
2013-09-13 16:50:51 -04:00
|
|
|
// Used to explicitly close a connection, called internally by the pool
|
|
|
|
// when a connection times out or the pool is shutdown.
|
|
|
|
destroyRawConnection: function(connection) {
|
|
|
|
connection.close();
|
2013-09-03 23:02:23 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
// Begins a transaction statement on the instance,
|
|
|
|
// resolving with the connection of the current transaction.
|
2013-09-08 15:57:32 -04:00
|
|
|
startTransaction: function(connection) {
|
|
|
|
return this.getConnection().tap(function(connection) {
|
2013-10-27 22:34:58 -04:00
|
|
|
return Promise.promisify(connection.run, connection)('begin transaction;');
|
2013-09-03 23:02:23 -04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-09-12 00:26:33 -04:00
|
|
|
// Finishes the transaction statement on the instance.
|
|
|
|
finishTransaction: function(type, transaction, msg) {
|
|
|
|
var client = this;
|
|
|
|
var dfd = transaction.dfd;
|
2013-10-27 22:34:58 -04:00
|
|
|
return Promise
|
|
|
|
.promisify(transaction.connection.run, transaction.connection)(type + ';')
|
|
|
|
.then(function(resp) {
|
|
|
|
if (type === 'commit') dfd.fulfill(msg || resp);
|
|
|
|
if (type === 'rollback') dfd.reject(msg || resp);
|
|
|
|
}, function (err) {
|
|
|
|
dfd.reject(err);
|
|
|
|
}).ensure(function() {
|
|
|
|
return client.releaseConnection(transaction.connection).tap(function() {
|
|
|
|
transaction.connection = null;
|
|
|
|
});
|
2013-09-12 00:26:33 -04:00
|
|
|
});
|
2013-09-04 17:32:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
// This needs to be refactored... badly.
|
|
|
|
alterSchema: function(builder, trx) {
|
|
|
|
var currentCol, command;
|
2013-10-27 22:34:58 -04:00
|
|
|
var connection = trx.connection;
|
|
|
|
var query = Promise.promisify(connection.all, connection);
|
2013-09-04 17:32:32 -04:00
|
|
|
|
2013-10-27 22:34:58 -04:00
|
|
|
return Promise.all([
|
|
|
|
query('PRAGMA table_info(' + builder.table + ')', []),
|
|
|
|
query('SELECT name, sql FROM sqlite_master WHERE type="table" AND name="' + builder.table + '"', [])
|
2013-09-04 17:32:32 -04:00
|
|
|
])
|
|
|
|
.tap(function(resp) {
|
|
|
|
var pragma = resp[0];
|
|
|
|
var sql = resp[1][0];
|
|
|
|
command = builder.commands[builder.currentIndex];
|
|
|
|
if (!(currentCol = _.findWhere(pragma, {name: command.from}))) {
|
|
|
|
throw new Error('The column ' + command.from + ' is not in the current table');
|
|
|
|
}
|
2013-10-27 22:34:58 -04:00
|
|
|
return query('ALTER TABLE ' + sql.name + ' RENAME TO __migrate__' + sql.name);
|
2013-09-04 17:32:32 -04:00
|
|
|
}).spread(function(pragma, sql) {
|
|
|
|
sql = sql[0];
|
|
|
|
var currentColumn = '"' + command.from + '" ' + currentCol.type;
|
|
|
|
var newColumn = '"' + command.to + '" ' + currentCol.type;
|
|
|
|
if (sql.sql.indexOf(currentColumn) === -1) {
|
|
|
|
return trx.reject('Unable to find the column to change');
|
|
|
|
}
|
2013-10-27 22:34:58 -04:00
|
|
|
return Promise.all([
|
|
|
|
query(sql.sql.replace(currentColumn, newColumn)),
|
|
|
|
query('SELECT * FROM "__migrate__' + sql.name + '"'),
|
2013-09-04 17:32:32 -04:00
|
|
|
]);
|
|
|
|
}).spread(function(createTable, selected) {
|
2013-09-12 02:09:29 -04:00
|
|
|
var qb = new Builder(builder.knex).transacting(trx);
|
2013-09-04 17:32:32 -04:00
|
|
|
qb.table = builder.table;
|
2013-10-27 22:34:58 -04:00
|
|
|
return Promise.all([
|
2013-09-04 17:32:32 -04:00
|
|
|
qb.insert(_.map(selected, function(row) {
|
|
|
|
row[command.to] = row[command.from];
|
|
|
|
return _.omit(row, command.from);
|
|
|
|
})),
|
2013-10-27 22:34:58 -04:00
|
|
|
query('DROP TABLE "__migrate__' + builder.table + '"')
|
2013-09-04 17:32:32 -04:00
|
|
|
]);
|
|
|
|
}).then(trx.commit, trx.rollback);
|
2013-09-03 23:02:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
});
|