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
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;
|