You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
7.7 KiB

/**
* This module provides a promise interface to the sqlite3 database module.
*/
const sqlite = require('sqlite3');
const pkg = require('./package.json'); // for sqlite3 version number
//-----------------------------------------------------------------------------
// The Database class
//-----------------------------------------------------------------------------
class Database {
static get OPEN_READONLY() {
return sqlite.OPEN_READONLY;
}
static get OPEN_READWRITE() {
return sqlite.OPEN_READWRITE;
}
static get OPEN_CREATE() {
return sqlite.OPEN_CREATE;
}
static get SQLITE3_VERSION() {
return pkg.dependencies.sqlite3.substring(1);
}
static open(filename, mode) {
let db = new Database();
return db.open(filename, mode);
}
open(filename, mode) {
if (typeof mode === 'undefined') {
mode = Database.OPEN_READWRITE | Database.OPEN_CREATE;
} else if (typeof mode !== 'number') {
throw new TypeError('Database.open: mode is not a number');
}
return new Promise((resolve, reject) => {
if (this.db) {
return reject(new Error('Database.open: database is already open'));
}
let db = new sqlite.Database(filename, mode, (err) => {
if (err) {
reject(err);
} else {
this.db = db;
this.filename = filename;
resolve(this);
}
});
});
}
on(evt, cb) {
return this.db.on(evt, cb);
}
close(fn) {
if (!this.db) {
return Promise.reject(new Error('Database.close: database is not open'));
}
if (fn) {
return fn(this)
.then((result) => {
return this.close().then((_) => {
return result;
});
})
.catch((err) => {
return this.close().then((_) => {
return Promise.reject(err);
});
});
}
return new Promise((resolve, reject) => {
this.db.close((err) => {
if (err) {
reject(err);
} else {
this.db = null;
resolve(this);
}
});
});
}
run(...args) {
return new Promise((resolve, reject) => {
if (!this.db) {
return reject(new Error('Database.run: database is not open'));
}
// Need a real function because 'this' is used.
let callback = function (err) {
if (err) {
reject(err);
} else {
resolve({
lastID: this.lastID,
changes: this.changes,
});
}
};
args.push(callback);
this.db.run.apply(this.db, args);
});
}
get(...args) {
return new Promise((resolve, reject) => {
if (!this.db) {
return reject(new Error('Database.get: database is not open'));
}
let callback = (err, row) => {
if (err) {
reject(err);
} else {
resolve(row);
}
};
args.push(callback);
this.db.get.apply(this.db, args);
});
}
all(...args) {
return new Promise((resolve, reject) => {
if (!this.db) {
return reject(new Error('Database.all: database is not open'));
}
let callback = (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
};
args.push(callback);
this.db.all.apply(this.db, args);
});
}
each(...args) {
if (args.length === 0 || typeof args[args.length - 1] !== 'function') {
throw TypeError('Database.each: last arg is not a function');
}
return new Promise((resolve, reject) => {
if (!this.db) {
return reject(new Error('Database.each: database is not open'));
}
let callback = (err, nrows) => {
if (err) {
reject(err);
} else {
resolve(nrows);
}
};
args.push(callback);
this.db.each.apply(this.db, args);
});
}
exec(sql) {
return new Promise((resolve, reject) => {
if (!this.db) {
return reject(new Error('Database.exec: database is not open'));
}
this.db.exec(sql, (err) => {
if (err) {
reject(err);
} else {
resolve(this);
}
});
});
}
transaction(fn) {
return this.exec('BEGIN TRANSACTION').then((_) => {
return fn(this)
.then((result) => {
return this.exec('END TRANSACTION').then((_) => {
return result;
});
})
.catch((err) => {
return this.exec('ROLLBACK TRANSACTION').then((_) => {
return Promise.reject(err);
});
});
});
}
prepare(...args) {
return new Promise((resolve, reject) => {
if (!this.db) {
return reject(new Error('Database.prepare: database is not open'));
}
let statement;
let callback = (err) => {
if (err) {
reject(err);
} else {
resolve(new Statement(statement));
}
};
args.push(callback);
statement = this.db.prepare.apply(this.db, args);
});
}
}
//-----------------------------------------------------------------------------
// The Statement class
//-----------------------------------------------------------------------------
class Statement {
constructor(statement) {
if (!(statement instanceof sqlite.Statement)) {
throw new TypeError(`Statement: 'statement' is not a statement instance`);
}
this.statement = statement;
}
bind(...args) {
return new Promise((resolve, reject) => {
let callback = (err) => {
if (err) {
reject(err);
} else {
resolve(this);
}
};
args.push(callback);
this.statement.bind.apply(this.statement, args);
});
}
reset() {
return new Promise((resolve, reject) => {
this.statement.reset((_) => {
resolve(this);
});
});
}
finalize() {
return new Promise((resolve, reject) => {
this.statement.finalize((err) => {
if (err) {
reject(err);
} else {
resolve(); // can't use it anymore
}
});
});
}
run(...args) {
return new Promise((resolve, reject) => {
// Need a real function because 'this' is used.
let callback = function (err) {
if (err) {
reject(err);
} else {
resolve({
lastID: this.lastID,
changes: this.changes,
});
}
};
args.push(callback);
this.statement.run.apply(this.statement, args);
});
}
get(...args) {
return new Promise((resolve, reject) => {
let callback = (err, row) => {
if (err) {
reject(err);
} else {
resolve(row);
}
};
args.push(callback);
this.statement.get.apply(this.statement, args);
});
}
all(...args) {
return new Promise((resolve, reject) => {
let callback = (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
};
args.push(callback);
this.statement.all.apply(this.statement, args);
});
}
each(...args) {
if (args.length === 0 || typeof args[args.length - 1] !== 'function') {
throw TypeError('Statement.each: last arg is not a function');
}
return new Promise((resolve, reject) => {
let callback = (err, nrows) => {
if (err) {
reject(err);
} else {
resolve(nrows);
}
};
args.push(callback);
this.statement.each.apply(this.statement, args);
});
}
}
//-----------------------------------------------------------------------------
// Module Exports
//-----------------------------------------------------------------------------
module.exports = Database;