UNPKG

websql-configurable

Version:

WebSQL Database API, implemented for Node using sqlite3

143 lines (128 loc) 4.36 kB
import noop from 'noop-fn'; import Queue from 'tiny-queue'; import immediate from 'immediate'; import WebSQLResultSet from './WebSQLResultSet.js'; function errorUnhandled() { return true; // a non-truthy return indicates error was handled } // WebSQL has some bizarre behavior regarding insertId/rowsAffected. To try // to match the observed behavior of Chrome/Safari as much as possible, we // sniff the SQL message to try to massage the returned insertId/rowsAffected. // This helps us pass the tests, although it's error-prone and should // probably be revised. function massageSQLResult(sql, insertId, rowsAffected, rows) { if (/^\s*UPDATE\b/i.test(sql)) { // insertId is always undefined for "UPDATE" statements insertId = void 0; } else if (/^\s*CREATE\s+TABLE\b/i.test(sql)) { // WebSQL always returns an insertId of 0 for "CREATE TABLE" statements insertId = 0; rowsAffected = 0; } else if (/^\s*DROP\s+TABLE\b/i.test(sql)) { // WebSQL always returns insertId=undefined and rowsAffected=0 // for "DROP TABLE" statements. Go figure. insertId = void 0; rowsAffected = 0; } else if (!/^\s*INSERT\b/i.test(sql)) { // for all non-inserts (deletes, etc.) insertId is always undefined // ¯\_(ツ)_/¯ insertId = void 0; } return new WebSQLResultSet(insertId, rowsAffected, rows); } function SQLTask(sql, args, sqlCallback, sqlErrorCallback) { this.sql = sql; this.args = args; this.sqlCallback = sqlCallback; this.sqlErrorCallback = sqlErrorCallback; } function runBatch(self, batch) { function onDone() { self._running = false; runAllSql(self); } var readOnly = self._websqlDatabase._currentTask.readOnly; self._websqlDatabase._db.exec(batch, readOnly, function (err, results) { /* istanbul ignore next */ if (err) { self._error = err; return onDone(); } for (var i = 0; i < results.length; i++) { var res = results[i]; var batchTask = batch[i]; if (res.error) { if (batchTask.sqlErrorCallback(self, res.error)) { // user didn't handle the error self._error = res.error; return onDone(); } } else { try { batchTask.sqlCallback(self, massageSQLResult( batch[i].sql, res.insertId, res.rowsAffected, res.rows)); } catch (err) { self._error = err; runAllSql(self); } } } onDone(); }); } function runAllSql(self) { if (self._running || self._complete) { return; } if (self._error || !self._sqlQueue.length) { self._complete = true; return self._websqlDatabase._onTransactionComplete(self._error); } self._running = true; var batch = []; var task; while ((task = self._sqlQueue.shift())) { batch.push(task); } runBatch(self, batch); } function executeSql(self, sql, args, sqlCallback, sqlErrorCallback, executeDelay) { self._sqlQueue.push(new SQLTask(sql, args, sqlCallback, sqlErrorCallback)); if (self._runningTimeout) { return; } self._runningTimeout = true; executeDelay(function () { self._runningTimeout = false; runAllSql(self); }); } function WebSQLTransaction(websqlDatabase, executeDelay) { this._websqlDatabase = websqlDatabase; this._error = null; this._complete = false; this._runningTimeout = false; this._executeDelay = executeDelay || immediate; this._sqlQueue = new Queue(); if (!websqlDatabase._currentTask.readOnly) { // Since we serialize all access to the database, there is no need to // run read-only tasks in a transaction. This is a perf boost. this._sqlQueue.push(new SQLTask('BEGIN;', [], noop, noop)); } } /** * @param {string} sql * @param {ObjectArray} args * @param {SQLStatementCallback} sqlCallback * @param {SQLStatementErrorCallback} sqlErrorCallback */ WebSQLTransaction.prototype.executeSql = function (sql, args, sqlCallback, sqlErrorCallback) { args = Array.isArray(args) ? args : []; sqlCallback = typeof sqlCallback === 'function' ? sqlCallback : noop; sqlErrorCallback = typeof sqlErrorCallback === 'function' ? sqlErrorCallback : errorUnhandled; executeSql(this, sql, args, sqlCallback, sqlErrorCallback, this._executeDelay); }; WebSQLTransaction.prototype._checkDone = function () { runAllSql(this); }; export default WebSQLTransaction;