any-db-sqlite3
Version:
The SQLite3 adapter for any-db
164 lines (137 loc) • 3.92 kB
JavaScript
'use strict'
var EventEmitter = require('events').EventEmitter
var sqlite3 = require('sqlite3')
var inherits = require('inherits')
var Readable = require('stream').Readable
var adapter = exports
adapter.name = 'sqlite3'
adapter.verbose = sqlite3.verbose
adapter.createQuery = function(text, values, callback) {
if (text instanceof SQLite3Query) return text
return new SQLite3Query(text, values, callback)
}
adapter.createConnection = function(opts, callback) {
var filename
if (opts.host) {
filename = opts.host + (opts.database || '')
} else {
filename = opts.database
}
if (!filename || filename == '/:memory') filename = ':memory:'
var mode = Object.keys(opts)
.map(function(k) {
return (k == k.toUpperCase() && sqlite3[k]) | 0
})
.reduce(function(mode, flag) {
return mode | flag
}, 0)
if (!mode) mode = sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE
var db = new sqlite3.Database(filename, mode)
db.serialize()
return new SQLite3Connection(db, callback)
}
inherits(SQLite3Connection, EventEmitter)
function SQLite3Connection(db, callback) {
EventEmitter.call(this)
this._db = db
var self = this
this._db.on('open', function() {
self.emit('open')
})
this._db.on('close', function() {
self.emit('end')
})
this._db.on('error', function(err) {
self.emit('error', err)
})
if (callback) {
self.once('error', callback)
self.once('open', function handleOpenEvent() {
self.removeListener('error', callback)
callback(null, self)
})
}
}
SQLite3Connection.prototype.adapter = adapter
SQLite3Connection.prototype.query = function(text, values, callback) {
var query = adapter.createQuery(text, values, callback)
this.emit('query', query)
if (query.text.match(/^\s*(insert|update|replace)\s+/i)) {
runQuery(this._db, query)
} else {
eachQuery(this._db, query)
}
return query
}
function runQuery(db, query) {
db.run(query.text, query.values, function(err) {
query.complete(err, this ? this.changes : 0, this ? this.lastID : -1)
})
}
function eachQuery(db, query) {
db.each(query.text, query.values, onRow, function(err, count) {
query.complete(err, count)
})
function onRow(err, row) {
query.onRow(err, row)
}
}
SQLite3Connection.prototype.end = function(callback) {
if (callback) this.on('end', callback)
this._db.close()
}
inherits(SQLite3Query, Readable)
function SQLite3Query(text, values, callback) {
Readable.call(this, { objectMode: true })
this.text = text
this._fields = null
this._result = { rows: [] }
this._errored = false
if (typeof values == 'function') {
callback = values
values = []
}
this.values = values || []
if ((this.callback = callback)) {
this.on('error', function(error) {
this.callback(error)
})
this.on('data', function(row) {
this._result.rows.push(row)
})
}
}
SQLite3Query.prototype._read = function() {}
SQLite3Query.prototype.onRow = function(err, row) {
if (this._errored) return
this._gotData = true
if (err) {
this._errored = true
this.emit('close')
this.emit('error', err)
return
}
if (!this._result.fields) {
this._result.fields = Object.keys(row).map(function(name) {
return { name: name }
})
this.emit('fields', this._result.fields)
}
this.push(row)
}
SQLite3Query.prototype.complete = function(err, count, lastId, affectedRows) {
this.push(null)
if (this._errored) return // we've emitted an error from the row callback
if (!err && !this._gotData) this.emit('fields', [])
this.emit('close')
if (err) return this.emit('error', err)
this._result.rowCount = count
this._result.lastInsertId = lastId
if (affectedRows) {
this._result.affectedRows = affectedRows
this._result.changedRows = affectedRows
}
if (this.callback) {
this.callback(null, this._result)
}
}