UNPKG

ydn.db

Version:

Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.

255 lines (216 loc) 8.22 kB
// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Base database service provider. * * @author kyawtun@yathit.com (Kyaw Tun) */ goog.provide('ydn.db.tr.Storage'); goog.require('ydn.db.con.Storage'); goog.require('ydn.db.tr.AtomicParallel'); goog.require('ydn.db.tr.AtomicSerial'); goog.require('ydn.db.tr.DbOperator'); goog.require('ydn.db.tr.Parallel'); goog.require('ydn.db.tr.Serial'); /** * Create storage providing method to run in transaction. * * @param {string=} opt_dbname database name. * @param {!ydn.db.schema.Database|DatabaseSchema=} opt_schema database schema * or its configuration in JSON format. If not provided, default empty schema * is used. * schema used in chronical order. * @param {!StorageOptions=} opt_options options. * @extends {ydn.db.con.Storage} * @constructor */ ydn.db.tr.Storage = function(opt_dbname, opt_schema, opt_options) { goog.base(this, opt_dbname, opt_schema, opt_options); this.ptx_no = 0; var is_serial = true; var req_type = ydn.db.tr.Thread.Policy.SINGLE; if (opt_options) { if (goog.isDef(opt_options.isSerial)) { is_serial = !!opt_options.isSerial; } if (opt_options.policy) { req_type = /** @type {ydn.db.tr.Thread.Policy} */ (opt_options.policy); } } var tx_thread = this.newTxQueue(req_type, is_serial); /** * here we must define sync thread first, so that it is ready when * executing main thread. * This sync thread is used internally. * todo: this should belong to db_operator, not storage instance. * @final * @type {!ydn.db.tr.Thread} */ this.sync_thread = this.newTxQueue(ydn.db.tr.Thread.Policy.ATOMIC, false); /** * main thread. * @final * @type {!ydn.db.tr.DbOperator} */ this.db_operator = this.newOperator(tx_thread, this.sync_thread); }; goog.inherits(ydn.db.tr.Storage, ydn.db.con.Storage); /** * @type {ydn.db.tr.Thread} * @protected */ ydn.db.tr.Storage.prototype.sync_thread; /** * @type {ydn.db.tr.DbOperator} * @protected */ ydn.db.tr.Storage.prototype.db_operator; /** * * @type {number} * @protected */ ydn.db.tr.Storage.prototype.ptx_no = 0; /** * Create a new db operator. * @param {ydn.db.tr.Thread.Policy=} opt_policy thread policy. Default is * SINGLE. * @param {boolean=} opt_is_serial serial request. * @param {!Array.<string>=} opt_store_names store names for tx scope. * @param {ydn.db.base.StandardTransactionMode=} opt_mode tx mode. * @param {number=} opt_max_tx limit number of transaction. * @param {boolean=} opt_no_sync no synchronization. * @return {ydn.db.tr.DbOperator} db operator. * @final */ ydn.db.tr.Storage.prototype.branch = function(opt_policy, opt_is_serial, opt_store_names, opt_mode, opt_max_tx, opt_no_sync) { opt_policy = opt_policy || ydn.db.tr.Thread.Policy.SINGLE; var mode; if (opt_mode == ydn.db.base.StandardTransactionMode.READ_ONLY) { mode = ydn.db.base.TransactionMode.READ_ONLY; } else if (opt_mode == ydn.db.base.StandardTransactionMode.READ_WRITE) { mode = ydn.db.base.TransactionMode.READ_WRITE; } var tx_thread = this.newTxQueue(opt_policy, opt_is_serial, opt_store_names, mode, opt_max_tx); var sync_thread = opt_no_sync ? null : this.sync_thread; return this.newOperator(tx_thread, sync_thread); }; /** * @param {!ydn.db.tr.Thread} tx_thread transaction thread. * @param {ydn.db.tr.Thread} sync_thread thread for synchronization. * @return {!ydn.db.tr.DbOperator} the db operator. * @protected */ ydn.db.tr.Storage.prototype.newOperator = function(tx_thread, sync_thread) { return new ydn.db.tr.DbOperator(this, this.schema, tx_thread, sync_thread); }; /** * Create a new thread queue. * @param {ydn.db.tr.Thread.Policy} request_type thread policy. * @param {boolean=} opt_is_serial serial request. * @param {!Array.<string>=} opt_store_names store names as scope. * @param {ydn.db.base.TransactionMode=} opt_mode mode as scope. * @param {number=} opt_max_tx limit number of transaction. * @return {!ydn.db.tr.Thread} new transactional storage. */ ydn.db.tr.Storage.prototype.newTxQueue = function(request_type, opt_is_serial, opt_store_names, opt_mode, opt_max_tx) { if (opt_is_serial) { if (request_type == ydn.db.tr.Thread.Policy.MULTI || request_type == ydn.db.tr.Thread.Policy.REPEAT || request_type == ydn.db.tr.Thread.Policy.ALL || request_type == ydn.db.tr.Thread.Policy.SINGLE) { return new ydn.db.tr.Serial(this, this.ptx_no++, request_type, opt_store_names, opt_mode, opt_max_tx); } else if (request_type == ydn.db.tr.Thread.Policy.ATOMIC) { return new ydn.db.tr.AtomicSerial(this, this.ptx_no++); } else { throw new ydn.debug.error.ArgumentException('Invalid requestType "' + request_type + '"'); } } else { if (request_type == ydn.db.tr.Thread.Policy.MULTI || request_type == ydn.db.tr.Thread.Policy.REPEAT || request_type == ydn.db.tr.Thread.Policy.ALL || request_type == ydn.db.tr.Thread.Policy.SINGLE) { return new ydn.db.tr.Parallel(this, this.ptx_no++, request_type, opt_store_names, opt_mode, opt_max_tx); } else if (request_type == ydn.db.tr.Thread.Policy.ATOMIC) { return new ydn.db.tr.AtomicParallel(this, this.ptx_no++); } else { throw new ydn.debug.error.ArgumentException('Invalid requestType "' + request_type + '"'); } } }; /** * Run a new transaction. * @param {function(!ydn.db.tr.DbOperator)} trFn function that invoke in the * transaction. * @param {Array.<string>=} opt_store_names list of store name involved in the * transaction. Default to all store names. * @param {ydn.db.base.StandardTransactionMode=} opt_mode mode, default to * 'readonly'. * @return {!ydn.db.Request} * @final */ ydn.db.tr.Storage.prototype.run = function(trFn, opt_store_names, opt_mode) { if (goog.DEBUG && arguments.length > 3) { throw new ydn.debug.error.ArgumentException('too many input arguments, ' + 'run accept not more than 3 input arguments, but ' + arguments.length + ' found.'); } this.ptx_no++; var store_names = opt_store_names || this.schema.getStoreNames(); // NOTE: ydn.db.base.TransactionMode can be number (as in old definition). var mode = ydn.db.base.TransactionMode.READ_ONLY; if (opt_mode) { if (opt_mode == ydn.db.base.StandardTransactionMode.READ_WRITE) { mode = ydn.db.base.TransactionMode.READ_WRITE; } else if (opt_mode != ydn.db.base.StandardTransactionMode.READ_ONLY) { throw new ydn.debug.error.ArgumentException('Invalid transaction mode "' + opt_mode + '"'); } } var tx_thread = this.newTxQueue(ydn.db.tr.Thread.Policy.ALL, false, store_names, mode, 1); var db_operator = this.newOperator(tx_thread, this.sync_thread); var req = new ydn.db.Request(ydn.db.Request.Method.RUN); /** * @param {ydn.db.base.TxEventTypes} type * @param {*} e */ var onComplete = function(type, e) { req.removeTx(); var success = type === ydn.db.base.TxEventTypes.COMPLETE; req.setDbValue(tx_thread.getTxNo(), !success); }; var me = this; tx_thread.processTx(function(tx) { goog.log.finest(me.logger, 'executing run in transaction on ' + tx_thread); req.setTx(tx, tx_thread.getLabel() + 'R0'); // Request 0 trFn(db_operator); }, store_names, mode, onComplete); return req; }; /** * Current transaction count. Useful for debugging or performance analysis * purpose only. * @return {number} transaction number. */ ydn.db.tr.Storage.prototype.getTxNo = function() { return this.db_operator ? this.db_operator.getTxNo() : NaN; };