const path = require('path'); const sqlite3 = require('./sqlite3-binding.js'); const EventEmitter = require('events').EventEmitter; module.exports = exports = sqlite3; function normalizeMethod (fn) { return function (sql) { let errBack; const args = Array.prototype.slice.call(arguments, 1); if (typeof args[args.length - 1] === 'function') { const callback = args[args.length - 1]; errBack = function(err) { if (err) { callback(err); } }; } const statement = new Statement(this, sql, errBack); return fn.call(this, statement, args); }; } function inherits(target, source) { for (const k in source.prototype) target.prototype[k] = source.prototype[k]; } sqlite3.cached = { Database: function(file, a, b) { if (file === '' || file === ':memory:') { // Don't cache special databases. return new Database(file, a, b); } let db; file = path.resolve(file); if (!sqlite3.cached.objects[file]) { db = sqlite3.cached.objects[file] = new Database(file, a, b); } else { // Make sure the callback is called. db = sqlite3.cached.objects[file]; const callback = (typeof a === 'number') ? b : a; if (typeof callback === 'function') { function cb() { callback.call(db, null); } if (db.open) process.nextTick(cb); else db.once('open', cb); } } return db; }, objects: {} }; const Database = sqlite3.Database; const Statement = sqlite3.Statement; const Backup = sqlite3.Backup; inherits(Database, EventEmitter); inherits(Statement, EventEmitter); inherits(Backup, EventEmitter); // Database#prepare(sql, [bind1, bind2, ...], [callback]) Database.prototype.prepare = normalizeMethod(function(statement, params) { return params.length ? statement.bind.apply(statement, params) : statement; }); // Database#run(sql, [bind1, bind2, ...], [callback]) Database.prototype.run = normalizeMethod(function(statement, params) { statement.run.apply(statement, params).finalize(); return this; }); // Database#get(sql, [bind1, bind2, ...], [callback]) Database.prototype.get = normalizeMethod(function(statement, params) { statement.get.apply(statement, params).finalize(); return this; }); // Database#all(sql, [bind1, bind2, ...], [callback]) Database.prototype.all = normalizeMethod(function(statement, params) { statement.all.apply(statement, params).finalize(); return this; }); // Database#each(sql, [bind1, bind2, ...], [callback], [complete]) Database.prototype.each = normalizeMethod(function(statement, params) { statement.each.apply(statement, params).finalize(); return this; }); Database.prototype.map = normalizeMethod(function(statement, params) { statement.map.apply(statement, params).finalize(); return this; }); // Database#backup(filename, [callback]) // Database#backup(filename, destName, sourceName, filenameIsDest, [callback]) Database.prototype.backup = function() { let backup; if (arguments.length <= 2) { // By default, we write the main database out to the main database of the named file. // This is the most likely use of the backup api. backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]); } else { // Otherwise, give the user full control over the sqlite3_backup_init arguments. backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]); } // Per the sqlite docs, exclude the following errors as non-fatal by default. backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED]; return backup; }; Statement.prototype.map = function() { const params = Array.prototype.slice.call(arguments); const callback = params.pop(); params.push(function(err, rows) { if (err) return callback(err); const result = {}; if (rows.length) { const keys = Object.keys(rows[0]); const key = keys[0]; if (keys.length > 2) { // Value is an object for (let i = 0; i < rows.length; i++) { result[rows[i][key]] = rows[i]; } } else { const value = keys[1]; // Value is a plain value for (let i = 0; i < rows.length; i++) { result[rows[i][key]] = rows[i][value]; } } } callback(err, result); }); return this.all.apply(this, params); }; let isVerbose = false; const supportedEvents = [ 'trace', 'profile', 'change' ]; Database.prototype.addListener = Database.prototype.on = function(type) { const val = EventEmitter.prototype.addListener.apply(this, arguments); if (supportedEvents.indexOf(type) >= 0) { this.configure(type, true); } return val; }; Database.prototype.removeListener = function(type) { const val = EventEmitter.prototype.removeListener.apply(this, arguments); if (supportedEvents.indexOf(type) >= 0 && !this._events[type]) { this.configure(type, false); } return val; }; Database.prototype.removeAllListeners = function(type) { const val = EventEmitter.prototype.removeAllListeners.apply(this, arguments); if (supportedEvents.indexOf(type) >= 0) { this.configure(type, false); } return val; }; // Save the stack trace over EIO callbacks. sqlite3.verbose = function() { if (!isVerbose) { const trace = require('./trace'); [ 'prepare', 'get', 'run', 'all', 'each', 'map', 'close', 'exec' ].forEach(function (name) { trace.extendTrace(Database.prototype, name); }); [ 'bind', 'get', 'run', 'all', 'each', 'map', 'reset', 'finalize', ].forEach(function (name) { trace.extendTrace(Statement.prototype, name); }); isVerbose = true; } return sqlite3; };