-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
242 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
'use strict'; | ||
|
||
var _ = require('lodash'); | ||
var BluebirdPromise = require('bluebird'); | ||
var Adapter = require('./base'); | ||
var sqlite3 = require('sqlite3'); | ||
|
||
BluebirdPromise.promisifyAll(sqlite3.Database.prototype); | ||
|
||
var like = Adapter.Translator.like, | ||
contains = Adapter.Translator.contains, | ||
startsWith = Adapter.Translator.startsWith, | ||
endsWith = Adapter.Translator.endsWith, | ||
regex = Adapter.Translator.regex, | ||
wrapValue = Adapter.Translator.wrapValue; | ||
|
||
var returning = require('./mixins/returning'), | ||
EmbedPseudoReturn = returning.EmbedPseudoReturn, | ||
ExtractPseudoReturn = returning.ExtractPseudoReturn; | ||
|
||
/** | ||
* SQLite3 Adapter | ||
* | ||
* Documentation forthcoming. | ||
* | ||
* @since 1.0 | ||
* @public | ||
* @constructor | ||
* @extends Adapter | ||
*/ | ||
var SQLite3Adapter = Adapter.extend(/** @lends SQLite3Adapter# */ { | ||
|
||
init: function() { | ||
this._super.apply(this, arguments); | ||
this._connections = {}; | ||
this._connectionID = 0; | ||
this._database = undefined; | ||
this._databasePromise = undefined; | ||
}, | ||
|
||
/** | ||
* Get or create the a database for this adapter. | ||
* | ||
* @method | ||
* @private | ||
*/ | ||
_resolveDatabase: BluebirdPromise.method(function() { | ||
if (this._databasePromise) { return this._databasePromise; } | ||
|
||
var filename = this._connection.filename; | ||
var mode = this._connection.mode; | ||
var promise = new BluebirdPromise(function(resolve, reject) { | ||
var db = new sqlite3.Database(filename, mode) | ||
.on('open', function() { resolve(db); }) | ||
.on('error', reject); | ||
}) | ||
.bind(this).tap(function(db) { | ||
this._database = db; | ||
}); | ||
|
||
return (this._databasePromise = promise); | ||
}), | ||
|
||
/** | ||
* Disconnect this adapter's database & setup to allow another one to be | ||
* created. | ||
* | ||
* @method | ||
* @private | ||
*/ | ||
_disconnectDatabase: BluebirdPromise.method(function() { | ||
var result = this._database.closeAsync(); | ||
this._database = undefined; | ||
this._databasePromise = undefined; | ||
return result; | ||
}), | ||
|
||
/** | ||
* Connect for SQLite3Adapter. | ||
* | ||
* @method | ||
* @protected | ||
* @see {Adapter#_connect} | ||
*/ | ||
_connect: BluebirdPromise.method(function() { | ||
var id = (this._connectionID += 1); | ||
var result = this._connections[id] = { id: id }; | ||
return this._resolveDatabase().bind(this).then(function(db) { | ||
return _.merge(result, { db: db }); | ||
}); | ||
}), | ||
|
||
/** | ||
* Disconnect for SQLite3Adapter. | ||
* | ||
* @method | ||
* @protected | ||
* @see {Adapter#_disconnect} | ||
*/ | ||
_disconnect: BluebirdPromise.method(function(connection) { | ||
delete this._connections[connection.id]; | ||
return _.size(this._connections) === 0 && | ||
this._disconnectDatabase(); | ||
}), | ||
|
||
/** | ||
* Execute for SQLite3Adapter. | ||
* | ||
* @method | ||
* @private | ||
* @see {Adapter#_execute} | ||
*/ | ||
_execute: BluebirdPromise.method(function(connection, sql, args, id) { | ||
return BluebirdPromise.bind({}) | ||
.then(function() { | ||
return new BluebirdPromise(function(resolve, reject) { | ||
var method = id.enabled ? 'run' : 'all'; | ||
connection.db[method](sql, args, function(err, result) { | ||
if (err) { reject(err); } | ||
else { resolve([result, this]); } | ||
}); | ||
}); | ||
}) | ||
.spread(function(result, details) { | ||
if (details.lastID) { id(details.lastID); } | ||
return { | ||
rows: result || [], | ||
fields: _.keys(_.reduce(result, _.extend, {})).sort() | ||
}; | ||
}); | ||
}) | ||
|
||
}); | ||
|
||
SQLite3Adapter.reopenClass(/** @lends SQLite3Adapter */ { | ||
|
||
Phrasing: Adapter.Phrasing.extend(), | ||
Translator: Adapter.Translator.extend({ | ||
predicates: function(p) { | ||
this._super.apply(this, arguments); | ||
|
||
var likeFormat = '%s LIKE %s ESCAPE \'\\\''; | ||
p('iexact', likeFormat); | ||
p('contains', likeFormat, wrapValue(like, contains)); | ||
p('icontains', likeFormat, wrapValue(like, contains)); | ||
p('startsWith', likeFormat, wrapValue(like, startsWith)); | ||
p('istartsWith', likeFormat, wrapValue(like, startsWith)); | ||
p('endsWith', likeFormat, wrapValue(like, endsWith)); | ||
p('iendsWith', likeFormat, wrapValue(like, endsWith)); | ||
p('regex', '%s REGEXP %s', wrapValue(regex)); | ||
p('iregex', '%s REGEXP \'(?i)\' || %s', wrapValue(regex)); | ||
}, | ||
|
||
type: function(type/*, options*/) { | ||
// TODO: handle more types & options | ||
var result; | ||
if (type === 'serial') { result = 'integer primary key autoincrement'; } | ||
else { result = this._super.apply(this, arguments); } | ||
return result; | ||
} | ||
}, { __name__: 'SQLite3Translator' }) | ||
}); | ||
|
||
SQLite3Adapter.Phrasing.reopen(EmbedPseudoReturn); | ||
SQLite3Adapter.reopen(ExtractPseudoReturn); | ||
|
||
module.exports = SQLite3Adapter.reopenClass({ __name__: 'SQLite3Adapter' }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
'use strict'; | ||
|
||
if (!/^(1|true)$/i.test(process.env.TEST_SQLITE || '1')) { return; } | ||
|
||
var _ = require('lodash'); | ||
var expect = require('chai').expect; | ||
var Database = require('../../../lib/db/database'); | ||
var BluebirdPromise = require('bluebird'); | ||
|
||
var shared = require('./shared_behaviors'); | ||
var returning = require('../../../lib/db/adapters/mixins/returning'); | ||
var PseudoReturn = returning.PseudoReturn; | ||
|
||
var db, connection = { | ||
adapter: 'sqlite3', | ||
filename: '' | ||
}; | ||
|
||
var resetSequence = BluebirdPromise.method(function(/*table*/) { | ||
// no need to reset | ||
}); | ||
|
||
describe('SQLite3', function() { | ||
before(function() { db = this.db = Database.create(connection); }); | ||
before(function() { this.resetSequence = resetSequence; }); | ||
after(function(done) { db.disconnect().then(done, done); }); | ||
|
||
it('executes raw sql', function(done) { | ||
var returnId = PseudoReturn.create('id'); | ||
var queries = [ | ||
['CREATE TABLE azul_raw_sql_test ' + | ||
'(id integer primary key autoincrement, name varchar(255))'], | ||
['INSERT INTO azul_raw_sql_test (name) VALUES (\'Azul\')', [returnId]], | ||
['SELECT * FROM azul_raw_sql_test'], | ||
['DROP TABLE azul_raw_sql_test'] | ||
]; | ||
BluebirdPromise.reduce(queries, function(array, info) { | ||
var query = info[0], args = info[1] || []; | ||
return db._adapter.execute(query, args).then(function(result) { | ||
return array.concat([result]); | ||
}); | ||
}, []) | ||
.spread(function(result1, result2, result3, result4) { | ||
expect(result1).to.eql({ rows: [], fields: [] }); | ||
expect(result2).to.eql({ | ||
rows: [{ id: 1 }], fields: ['id'] }); | ||
expect(result3).to.eql({ | ||
rows: [{ id: 1, name: 'Azul' }], | ||
fields: ['id', 'name'] }); | ||
expect(result4).to.eql({ rows: [], fields: [] }); | ||
}) | ||
.done(done, done); | ||
}); | ||
|
||
it('receives rows from raw sql', function(done) { | ||
var query = 'SELECT CAST(? AS INTEGER) AS number'; | ||
var args = ['1']; | ||
db._adapter.execute(query, args) | ||
.then(function(result) { | ||
expect(result.rows).to.eql([{ number: 1 }]); | ||
}) | ||
.done(done, done); | ||
}); | ||
|
||
// run all shared examples | ||
_.each(shared, function(fn, name) { | ||
if (fn.length !== 0) { | ||
throw new Error('Cannot execute shared example: ' + name); | ||
} | ||
fn(); | ||
}); | ||
}); |