ydn.db
Version:
Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.
1,482 lines (1,348 loc) • 62.7 kB
JavaScript
// Copyright 2012 YDN Authors. All Rights Reserved.
//
// 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 Provide atomic CRUD database operations on a transaction queue.
*
* @author kyawtun@yathit.com (Kyaw Tun)
*/
goog.provide('ydn.db.crud.DbOperator');
goog.require('goog.log');
goog.require('goog.userAgent');
goog.require('ydn.db');
goog.require('ydn.db.Key');
goog.require('ydn.db.Request');
goog.require('ydn.db.crud.IOperator');
goog.require('ydn.db.tr.AtomicSerial');
goog.require('ydn.db.tr.DbOperator');
goog.require('ydn.db.tr.Thread');
goog.require('ydn.debug.error.ArgumentException');
goog.require('ydn.debug.error.NotSupportedException');
/**
* Construct storage to execute CRUD database operations.
*
* Execution database operation is atomic, if a new transaction require,
* otherwise existing transaction is used and the operation become part of
* the existing transaction. A new transaction is required if the transaction
* is not active or locked. Active transaction can be locked by using
* mutex.
*
* @param {!ydn.db.crud.Storage} storage base storage object.
* @param {!ydn.db.schema.Database} schema schema.
* @param {ydn.db.tr.Thread} tx_thread
* @param {ydn.db.tr.Thread} sync_thread
* @implements {ydn.db.crud.IOperator}
* @constructor
* @extends {ydn.db.tr.DbOperator}
* @struct
*/
ydn.db.crud.DbOperator = function(storage, schema, tx_thread, sync_thread) {
goog.base(this, storage, schema, tx_thread, sync_thread);
};
goog.inherits(ydn.db.crud.DbOperator, ydn.db.tr.DbOperator);
/**
* @protected
* @type {goog.log.Logger} logger.
*/
ydn.db.crud.DbOperator.prototype.logger =
goog.log.getLogger('ydn.db.crud.DbOperator');
/**
* @return {ydn.db.crud.req.IRequestExecutor} executor.
*/
ydn.db.crud.DbOperator.prototype.getCrudExecutor = function() {
return /** @type {ydn.db.crud.req.IRequestExecutor} */ (this.getExecutor());
};
/**
*
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.countByIndex = function(store_name, index_name,
index_key_range, unique) {
var req;
var me = this;
/**
* @type {!Array.<string>}
*/
var store_names;
/**
* @type {IDBKeyRange}
*/
var key_range;
var store = this.schema.getStore(store_name);
if (!store) {
throw new ydn.debug.error.ArgumentException('store name "' + store_name +
'" not found.');
}
if (goog.DEBUG && goog.isDef(unique) && !goog.isBoolean(unique)) {
throw new ydn.debug.error.ArgumentException('unique value "' + unique +
'" must be boolean, but found ' + typeof unique + '.');
}
store_names = [store_name];
if (goog.isObject(index_key_range)) {
if (goog.DEBUG) {
var msg1 = ydn.db.KeyRange.validate(index_key_range);
if (msg1) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
ydn.json.toShortString(index_key_range) + ' ' + msg1);
}
}
key_range = ydn.db.KeyRange.parseIDBKeyRange(index_key_range);
} else {
if (goog.DEBUG && goog.isDefAndNotNull(index_key_range)) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
ydn.json.toShortString(index_key_range) +
' of type ' + typeof index_key_range);
}
key_range = null;
}
goog.log.finer(this.logger, 'countIndexKeyRange: ' + store_name + ' ' +
(index_name ? index_name : '') + ydn.json.stringify(key_range));
req = this.tx_thread.request(ydn.db.Request.Method.COUNT, store_names);
store.hook(req, arguments);
req.addTxback(function(tx) {
this.getCrudExecutor().countKeyRange(req, store_names[0], key_range,
index_name, !!unique);
}, this);
return req;
};
/**
*
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.count = function(store_name, index_or_keyrange,
index_key_range, unique) {
var req;
var me = this;
/**
* @type {!Array.<string>}
*/
var store_names;
/**
* @type {string}
*/
var index_name;
/**
* @type {IDBKeyRange}
*/
var key_range;
if (!goog.isDefAndNotNull(store_name)) {
goog.log.warning(this.logger, 'count method requires store name(s)');
var stores = this.schema.getStoreNames();
req = this.tx_thread.request(ydn.db.Request.Method.COUNT, stores);
req.await(function(cnt, is_error, cb) {
if (is_error) {
cb(cnt, true);
return;
}
var total = 0;
for (var i = 0; i < cnt.length; i++) {
total += cnt[i];
}
cb(total, false);
}, this);
req.addTxback(function() {
//console.log('counting');
this.getCrudExecutor().countStores(req, store_names);
}, this);
} else if (goog.isArray(store_name)) {
if (goog.isDef(index_key_range) || goog.isDef(index_or_keyrange)) {
throw new ydn.debug.error.ArgumentException('too many arguments.');
}
store_names = store_name;
for (var i = 0; i < store_names.length; i++) {
if (!this.schema.hasStore(store_names[i])) {
throw new ydn.debug.error.ArgumentException('store name "' +
store_names[i] + '" at ' + i + ' not found.');
}
}
//console.log('waiting to count');
goog.log.finer(this.logger, 'countStores: ' + ydn.json.stringify(store_names));
req = this.tx_thread.request(ydn.db.Request.Method.COUNT, store_names);
req.addTxback(function() {
//console.log('counting');
this.getCrudExecutor().countStores(req, store_names);
}, this);
} else if (goog.isString(store_name)) {
var store = this.schema.getStore(store_name);
if (!store) {
throw new ydn.debug.error.ArgumentException('store name "' + store_name +
'" not found.');
}
if (goog.DEBUG && goog.isDef(unique) && !goog.isBoolean(unique)) {
throw new ydn.debug.error.ArgumentException('unique value "' + unique +
'" must be boolean, but found ' + typeof unique + '.');
}
store_names = [store_name];
if (goog.isString(index_or_keyrange)) {
// index key range count.
index_name = index_or_keyrange;
if (goog.isObject(index_key_range)) {
if (goog.DEBUG) {
var msg1 = ydn.db.KeyRange.validate(index_key_range);
if (msg1) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
ydn.json.toShortString(index_key_range) + ' ' + msg1);
}
}
key_range = ydn.db.KeyRange.parseIDBKeyRange(index_key_range);
} else {
if (goog.DEBUG && goog.isDefAndNotNull(index_key_range)) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
ydn.json.toShortString(index_key_range) +
' of type ' + typeof index_key_range);
}
key_range = null;
}
} else if (goog.isObject(index_or_keyrange) ||
!goog.isDefAndNotNull(index_or_keyrange)) {
if (goog.isObject(index_or_keyrange)) {
if (goog.DEBUG) {
var msg = ydn.db.KeyRange.validate(index_or_keyrange);
if (msg) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
ydn.json.toShortString(index_or_keyrange) + ' ' + msg);
}
}
key_range = ydn.db.KeyRange.parseIDBKeyRange(index_or_keyrange);
} else {
if (goog.isDefAndNotNull(index_or_keyrange)) {
throw new ydn.debug.error.ArgumentException('key range must be ' +
' an object but found ' +
ydn.json.toShortString(index_or_keyrange) + ' of type ' +
typeof index_or_keyrange);
}
key_range = null;
}
} else {
throw new ydn.debug.error.ArgumentException('invalid second argument ' +
'for count "' + ydn.json.toShortString(index_key_range) +
'" of type ' + typeof index_or_keyrange);
}
goog.log.finer(this.logger, 'countKeyRange: ' + store_name + ' ' +
(index_name ? index_name : '') + ydn.json.stringify(key_range));
req = this.tx_thread.request(ydn.db.Request.Method.COUNT, store_names);
store.hook(req, arguments);
req.addTxback(function(tx) {
this.getCrudExecutor().countKeyRange(req, store_names[0], key_range,
index_name, !!unique);
}, this);
} else {
throw new ydn.debug.error.ArgumentException(
'Invalid store name or store names.');
}
return req;
};
/**
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.get = function(arg1, arg2) {
var me = this;
var req;
if (arg1 instanceof ydn.db.Key) {
/**
* @type {ydn.db.Key}
*/
var k = arg1;
var k_store_name = k.getStoreName();
var store = this.schema.getStore(k_store_name);
if (!store) {
if (this.schema.isAutoSchema()) {
if (this.getStorage().isReady()) {
return ydn.db.Request.succeed(ydn.db.Request.Method.GET, undefined);
} else {
req = new ydn.db.Request(ydn.db.Request.Method.GET);
this.getStorage().onReady(function() {
me.get(arg1, arg2).addCallbacks(function(x) {
req.callback(x);
}, function(e) {
req.errback(e);
});
});
return req;
}
} else {
throw new ydn.debug.error.ArgumentException('Store: ' +
k_store_name + ' not found.');
}
}
var kid = k.getId();
goog.log.finer(this.logger, 'getByKey: ' + k_store_name + ':' + kid);
req = this.tx_thread.request(ydn.db.Request.Method.GET_BY_KEY,
[k_store_name]);
store.hook(req, arguments, undefined, this);
req.addTxback(function() {
this.getCrudExecutor().getById(req, k_store_name, kid);
}, this);
} else if (goog.isString(arg1) && goog.isDef(arg2)) {
var store_name = arg1;
var store = this.schema.getStore(store_name);
if (!store) {
if (this.schema.isAutoSchema()) {
if (this.getStorage().isReady()) {
return ydn.db.Request.succeed(ydn.db.Request.Method.GET, undefined);
} else {
req = new ydn.db.Request(ydn.db.Request.Method.GET);
this.getStorage().onReady(function() {
me.get(arg1, arg2).addCallbacks(function(x) {
req.callback(x);
}, function(e) {
req.errback(e);
});
});
return req;
}
} else {
throw new ydn.debug.error.ArgumentException('Store name "' +
store_name + '" not found.');
}
}
var id = arg2;
goog.asserts.assert(ydn.db.Key.isValidKey(id), 'key ' + id + ' of type ' +
(typeof id) + ' is not a valid key');
goog.log.finer(this.logger, 'getById: ' + store_name + ':' + id);
req = this.tx_thread.request(ydn.db.Request.Method.GET, [store_name]);
store.hook(req, arguments, undefined, this);
req.addTxback(function() {
this.getCrudExecutor().getById(req, store_name, /** @type {IDBKey} */ (id));
}, this);
} else {
throw new ydn.debug.error.ArgumentException(
'get require valid input arguments.');
}
return req;
};
/**
* Value by key range.
* @param {string} store_name
* @param {ydn.db.KeyRange|IDBKeyRange=} opt_kr
* @param {number=} opt_limit
* @param {number=} opt_offset
* @param {boolean=} opt_reverse
* @return {!ydn.db.Request.<!Array.<Object>>}
*/
ydn.db.crud.DbOperator.prototype.keysByKeyRange = function(store_name, opt_kr,
opt_limit, opt_offset, opt_reverse) {
var store = this.schema.getStore(store_name);
var limit, offset, reverse;
var range = null;
if (goog.isObject(opt_kr)) {
if (goog.DEBUG) {
var msg = ydn.db.KeyRange.validate(opt_kr);
if (msg) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
opt_kr + ' ' + msg);
}
}
range = ydn.db.KeyRange.parseIDBKeyRange(opt_kr);
} else if (goog.DEBUG && goog.isDefAndNotNull(opt_kr)) {
throw new ydn.debug.error.ArgumentException('expect key range object,' +
' but found "' + ydn.json.toShortString(opt_kr) + '" of type ' + typeof opt_kr);
}
if (!goog.isDef(opt_limit)) {
limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
} else if (goog.isNumber(opt_limit)) {
limit = opt_limit;
} else {
throw new ydn.debug.error.ArgumentException('limit must be a number, ' +
'but ' + opt_limit + ' is ' + typeof opt_limit);
}
if (!goog.isDef(opt_offset)) {
offset = 0;
} else if (goog.isNumber(opt_offset)) {
offset = opt_offset;
} else {
throw new ydn.debug.error.ArgumentException(
'offset must be a number, ' + 'but ' + opt_offset + ' is ' +
typeof opt_offset);
}
if (goog.isDef(opt_reverse)) {
if (goog.isBoolean(opt_reverse)) {
reverse = opt_reverse;
} else {
throw new ydn.debug.error.ArgumentException('reverse must be a ' +
'boolean, but ' + opt_reverse + ' is ' + typeof opt_reverse);
}
}
goog.log.finer(this.logger, 'keysByKeyRange: ' + store_name);
var req = this.tx_thread.request(ydn.db.Request.Method.KEYS, [store_name]);
store.hook(req, arguments);
req.addTxback(function() {
this.getCrudExecutor().list(req, ydn.db.base.QueryMethod.LIST_PRIMARY_KEY,
store_name, null, range, limit, offset, reverse, false);
}, this);
return req;
};
/**
* Value by key range.
* @param {string} store_name
* @param {string} index_name
* @param {ydn.db.KeyRange|IDBKeyRange|KeyRangeJson=} opt_kr
* @param {number=} opt_limit
* @param {number=} opt_offset
* @param {boolean=} opt_reverse
* @param {boolean=} opt_unique
* @return {!ydn.db.Request<!Array<IDBKey>>}
*/
ydn.db.crud.DbOperator.prototype.keysByIndex = function(store_name,
index_name, opt_kr, opt_limit, opt_offset, opt_reverse, opt_unique) {
var range, limit, offset, reverse, unique;
if (goog.DEBUG) {
var msg = ydn.db.KeyRange.validate(/** @type {KeyRangeJson} */ (opt_kr));
if (msg) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
opt_kr + ' ' + msg);
}
}
var store = this.schema.getStore(store_name);
range = ydn.db.KeyRange.parseIDBKeyRange(
/** @type {KeyRangeJson} */ (opt_kr));
if (goog.isNumber(opt_limit)) {
limit = opt_limit;
} else if (!goog.isDef(opt_limit)) {
limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
} else {
throw new ydn.debug.error.ArgumentException('limit must be a number');
}
if (goog.isNumber(opt_offset)) {
offset = opt_offset;
} else if (!goog.isDef(opt_offset)) {
offset = 0;
} else {
throw new ydn.debug.error.ArgumentException('offset must be a number');
}
if (goog.isDef(opt_reverse)) {
if (goog.isBoolean(opt_reverse)) {
reverse = opt_reverse;
} else {
throw new ydn.debug.error.ArgumentException(
'reverse must be a boolean');
}
}
if (goog.isDef(opt_unique)) {
if (goog.isBoolean(opt_unique)) {
unique = opt_unique;
} else {
throw new ydn.debug.error.ArgumentException(
'unique must be a boolean');
}
}
goog.log.finer(this.logger, 'keysByIndex: ' + store_name);
var req = this.tx_thread.request(ydn.db.Request.Method.KEYS_INDEX,
[store_name]);
store.hook(req, arguments);
req.addTxback(function() {
this.getCrudExecutor().list(req, ydn.db.base.QueryMethod.LIST_PRIMARY_KEY,
store_name, index_name, range, limit, offset, reverse, unique);
}, this);
return req;
};
/**
*
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.keys = function(opt_store_name, arg1,
arg2, arg3, arg4, arg5, arg6) {
var me = this;
/**
* @type {number}
*/
var limit;
/**
* @type {number}
*/
var offset;
/**
* @type {ydn.db.IDBKeyRange}
*/
var range = null;
/**
* @type {boolean}
*/
var reverse = false;
/**
* @type {boolean}
*/
var unique = false;
/**
*
* @type {string}
*/
var store_name = /** @type {string} */ (opt_store_name);
var store = this.schema.getStore(store_name);
if (goog.DEBUG) {
if (!goog.isString(store_name)) {
throw new ydn.debug.error.ArgumentException(
'store name must be a string, ' +
'but ' + store_name + ' of type ' + typeof store_name + ' is not.');
}
if (!this.schema.isAutoSchema()) {
if (!store) {
throw new ydn.debug.error.ArgumentException('store name "' +
store_name + '" not found.');
}
if (goog.isString(arg1)) {
var index = store.getIndex(arg1);
if (!index) {
throw new ydn.debug.error.ArgumentException('index "' + arg1 +
'" not found in store "' + store_name + '".');
}
}
}
}
if (this.schema.isAutoSchema() && !store) {
return ydn.db.Request.succeed(ydn.db.Request.Method.KEYS, []);
}
var req;
if (goog.isString(arg1)) { // index key range
req = this.keysByIndex(store_name,
/** @type {string} */ (arg1),
/** @type {ydn.db.KeyRange|IDBKeyRange} */ (arg2),
/** @type {number|undefined} */ (arg3),
/** @type {number|undefined} */ (arg4),
/** @type {boolean|undefined} */ (arg5),
/** @type {boolean|undefined} */ (arg6));
} else {
req = this.keysByKeyRange(store_name,
/** @type {ydn.db.KeyRange|IDBKeyRange} */ (arg1),
/** @type {number|undefined} */ (arg2), arg3,
/** @type {boolean|undefined} */ (arg4));
}
return req;
};
/**
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.values = function(arg0, arg1, arg2, arg3, arg4,
arg5, arg6) {
var me = this;
var req;
var method = ydn.db.Request.Method.NONE;
/**
* @type {number}
*/
var limit;
/**
* @type {number}
*/
var offset;
/**
* @type {boolean}
*/
var reverse = false;
/**
* @type {boolean}
*/
var unique = false;
if (goog.isString(arg0)) {
var store_name = arg0;
var store = this.schema.getStore(store_name);
if (!store) {
if (this.schema.isAutoSchema()) {
if (this.getStorage().isReady()) {
return ydn.db.Request.succeed(ydn.db.Request.Method.VALUES, []);
} else {
req = new ydn.db.Request(ydn.db.Request.Method.VALUES);
this.getStorage().onReady(function() {
me.values(arg0, arg1, arg2, arg3, arg4,
arg5).addCallbacks(function(x) {
req.callback(x);
}, function(e) {
req.errback(e);
});
});
return req;
}
} else {
throw new ydn.db.NotFoundError(store_name);
}
}
if (goog.isArray(arg1)) {
if (goog.DEBUG && (goog.isDef(arg2) || goog.isDef(arg3))) {
throw new ydn.debug.error.ArgumentException('too many input arguments');
}
var ids = arg1;
goog.log.finer(this.logger, 'listByIds: ' + store_name + ' ' +
ids.length + ' ids');
req = this.tx_thread.request(ydn.db.Request.Method.VALUES_IDS,
[store_name]);
store.hook(req, arguments, undefined, this);
req.addTxback(function() {
this.getCrudExecutor().listByIds(req, store_name, ids);
}, this);
} else if (goog.isString(arg1)) { // index name
req = this.valuesByIndex(store_name, arg1,
/** @type {ydn.db.KeyRange|IDBKeyRange} */ (arg2), arg3,
/** @type {number|undefined} */ (arg4), arg5, arg6);
} else {
req = this.valuesByKeyRange(store_name,
/** @type {ydn.db.KeyRange|IDBKeyRange} */ (arg1),
/** @type {number|undefined} */ (arg2), arg3,
/** @type {boolean|undefined} */ (arg4));
}
} else if (goog.isArray(arg0)) {
if (arg0[0] instanceof ydn.db.Key) {
var store_names = [];
/**
* @type {!Array.<!ydn.db.Key>}
*/
var keys = /** @type {!Array.<!ydn.db.Key>} */ (arg0);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var i_store_name = key.getStoreName();
if (!this.schema.hasStore(i_store_name)) {
if (this.schema.isAutoSchema()) {
var fail_array = [];
// I think more efficient than: fail_array.length = keys.length;
fail_array[keys.length - 1] = undefined;
return ydn.db.Request.succeed(ydn.db.Request.Method.GET,
fail_array);
} else {
throw new ydn.debug.error.ArgumentException('Store: ' +
i_store_name + ' not found.');
}
}
if (!goog.array.contains(store_names, i_store_name)) {
store_names.push(i_store_name);
}
}
goog.log.finer(this.logger, 'listByKeys: ' + ydn.json.stringify(store_names) +
' ' + keys.length + ' keys');
req = this.tx_thread.request(ydn.db.Request.Method.VALUES_KEYS,
store_names);
req.addTxback(function() {
this.getCrudExecutor().listByKeys(req, keys);
}, this);
} else {
throw new ydn.debug.error.ArgumentException('first argument' +
'must be array of ydn.db.Key, but ' + arg0[0] + ' of ' +
typeof arg0[0] + ' found.');
}
} else {
throw new ydn.debug.error.ArgumentException('first argument ' + arg0 +
' is invalid.');
}
return req;
};
/**
* Value by key range.
* @param {string} store_name
* @param {ydn.db.KeyRange|IDBKeyRange=} opt_kr
* @param {number=} opt_limit
* @param {number=} opt_offset
* @param {boolean=} opt_reverse
* @return {!ydn.db.Request.<!Array.<Object>>}
*/
ydn.db.crud.DbOperator.prototype.valuesByKeyRange = function(store_name, opt_kr,
opt_limit, opt_offset, opt_reverse) {
var store = this.schema.getStore(store_name);
var limit, offset, reverse;
var range = null;
if (goog.isObject(opt_kr)) {
if (goog.DEBUG) {
var msg = ydn.db.KeyRange.validate(opt_kr);
if (msg) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
opt_kr + ' ' + msg);
}
}
range = ydn.db.KeyRange.parseIDBKeyRange(opt_kr);
} else if (goog.DEBUG && goog.isDefAndNotNull(opt_kr)) {
throw new ydn.debug.error.ArgumentException('expect key range object,' +
' but found "' + ydn.json.toShortString(opt_kr) + '" of type ' + typeof opt_kr);
}
if (!goog.isDef(opt_limit)) {
limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
} else if (goog.isNumber(opt_limit)) {
limit = opt_limit;
} else {
throw new ydn.debug.error.ArgumentException('limit must be a number, ' +
'but ' + opt_limit + ' is ' + typeof opt_limit);
}
if (!goog.isDef(opt_offset)) {
offset = 0;
} else if (goog.isNumber(opt_offset)) {
offset = opt_offset;
} else {
throw new ydn.debug.error.ArgumentException(
'offset must be a number, ' + 'but ' + opt_offset + ' is ' +
typeof opt_offset);
}
if (goog.isDef(opt_reverse)) {
if (goog.isBoolean(opt_reverse)) {
reverse = opt_reverse;
} else {
throw new ydn.debug.error.ArgumentException('reverse must be a ' +
'boolean, but ' + opt_reverse + ' is ' + typeof opt_reverse);
}
}
goog.log.finer(this.logger, (range ? 'listByKeyRange: ' : 'listByStore: ') +
store_name);
var method = ydn.db.Request.Method.VALUES;
var req = this.tx_thread.request(method, [store_name]);
store.hook(req, arguments);
req.addTxback(function() {
this.getCrudExecutor().list(req, ydn.db.base.QueryMethod.LIST_VALUE,
store_name, null, range, limit, offset, reverse, false);
}, this);
return req;
};
/**
* Value by key range.
* @param {string} store_name
* @param {string} index_name index name.
* @param {ydn.db.KeyRange|IDBKeyRange|KeyRangeJson=} opt_kr
* @param {number=} opt_limit
* @param {number=} opt_offset
* @param {boolean=} opt_reverse
* @param {boolean=} opt_unique
* @return {!ydn.db.Request.<!Array.<Object>>}
*/
ydn.db.crud.DbOperator.prototype.valuesByIndex = function(store_name,
index_name, opt_kr, opt_limit, opt_offset, opt_reverse, opt_unique) {
var store = this.schema.getStore(store_name);
var limit, offset, reverse, unique;
if (goog.DEBUG) {
if (!store.hasIndex(index_name)) {
throw new ydn.debug.error.ArgumentException('index "' +
index_name + '" not found in store "' + store_name + '"');
}
var msg = ydn.db.KeyRange.validate(/** @type {KeyRangeJson} */ (opt_kr));
if (msg) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
opt_kr + ' ' + msg);
}
}
var range = ydn.db.KeyRange.parseIDBKeyRange(
/** @type {KeyRangeJson} */ (opt_kr));
if (!goog.isDef(opt_limit)) {
limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
} else if (goog.isNumber(opt_limit)) {
limit = opt_limit;
} else {
throw new ydn.debug.error.ArgumentException('limit must be a number.');
}
if (!goog.isDef(opt_offset)) {
offset = 0;
} else if (goog.isNumber(opt_offset)) {
offset = opt_offset;
} else {
throw new ydn.debug.error.ArgumentException('offset must be a number.');
}
if (goog.isBoolean(opt_reverse)) {
reverse = opt_reverse;
} else if (goog.isDef(opt_reverse)) {
throw new ydn.debug.error.ArgumentException('reverse must be a boolean,' +
' but ' + opt_reverse);
}
if (goog.isDef(opt_unique)) {
if (goog.isBoolean(opt_unique)) {
unique = opt_unique;
} else {
throw new ydn.debug.error.ArgumentException(
'unique must be a boolean');
}
}
goog.log.finer(this.logger, 'listByIndexKeyRange: ' + store_name + ':' +
index_name);
var method = ydn.db.Request.Method.VALUES_INDEX;
var req = this.tx_thread.request(method, [store_name]);
store.hook(req, arguments);
req.addTxback(function() {
this.getCrudExecutor().list(req, ydn.db.base.QueryMethod.LIST_VALUE,
store_name, index_name, range, limit, offset, reverse, unique);
}, this);
return req;
};
/**
* List
* @param {ydn.db.base.QueryMethod} type
* @param {string} store_name
* @param {string=} opt_index
* @param {ydn.db.KeyRange|ydn.db.IDBKeyRange=} opt_key_range
* @param {number=} opt_limit
* @param {number=} opt_offset
* @param {boolean=} opt_reverse
* @param {boolean=} opt_unique
* @param {Array.<IDBKey|undefined>=} opt_pos last cursor position.
* @return {!ydn.db.Request}
*/
ydn.db.crud.DbOperator.prototype.list = function(type, store_name, opt_index,
opt_key_range, opt_limit, opt_offset, opt_reverse, opt_unique, opt_pos) {
var store = this.schema.getStore(store_name);
if (!store) {
if (this.schema.isAutoSchema()) {
return ydn.db.Request.succeed(ydn.db.Request.Method.GET, []);
} else {
throw new ydn.db.NotFoundError(store_name);
}
}
var me = this;
var req;
var method = ydn.db.Request.Method.NONE;
if (goog.DEBUG) {
if (opt_index && !store.hasIndex(opt_index)) {
throw new ydn.debug.error.ArgumentException('index "' +
opt_index + '" not found in store "' + store_name + '"');
}
var msg = ydn.db.KeyRange.validate(opt_key_range);
if (msg) {
throw new ydn.debug.error.ArgumentException('invalid key range: ' +
opt_key_range + ' ' + msg);
}
}
var range = ydn.db.KeyRange.parseIDBKeyRange(opt_key_range);
var limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
if (goog.isNumber(opt_limit)) {
limit = opt_limit;
} else if (goog.isDefAndNotNull(opt_limit)) {
throw new ydn.debug.error.ArgumentException('limit must be a number but "' +
opt_limit + '" of type ' + typeof opt_limit + ' found.');
}
var offset = 0;
if (goog.isNumber(opt_offset)) {
offset = opt_offset;
} else if (goog.isDefAndNotNull(opt_offset)) {
throw new ydn.debug.error.ArgumentException('offset must be a number but' +
' "' + opt_offset + '" of type ' + typeof opt_offset + ' found.');
}
var reverse = false;
if (goog.isBoolean(opt_reverse)) {
reverse = opt_reverse;
} else if (goog.isDefAndNotNull(opt_reverse)) {
throw new ydn.debug.error.ArgumentException('reverse must be a boolean ' +
'but "' + opt_reverse + '" of type ' + typeof opt_reverse + ' found.');
}
var unique = false;
if (goog.isBoolean(opt_unique)) {
unique = opt_unique;
} else if (goog.isDefAndNotNull(opt_unique)) {
throw new ydn.debug.error.ArgumentException('unique must be a boolean but' +
' "' + opt_unique + '" of type ' + typeof opt_unique + ' found.');
}
if (offset && !!opt_pos && goog.isDef(opt_pos[0])) {
throw new ydn.debug.error.ArgumentException('offset must not given when ' +
'initial cursor position is defined');
}
goog.log.finer(this.logger, type + ': ' + store_name + ':' + opt_index);
method = ydn.db.Request.Method.VALUES_INDEX;
req = this.tx_thread.request(method, [store_name]);
// store.hook(req, arguments);
req.addTxback(function() {
this.getCrudExecutor().list(req, type, store_name,
opt_index || null, range, limit, offset, reverse, unique, opt_pos);
}, this);
return req;
};
/**
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.add = function(store_name_or_schema, value,
opt_keys) {
if (goog.isArray(value)) {
return this.addAll(/** @type {string} */(store_name_or_schema),
/** @type {!Array<!Object>} */(value),
/** @type {!Array<IDBKey>} */(opt_keys));
}
var store = this.getStore(store_name_or_schema);
var store_name = store.getName();
var req;
// https://developer.mozilla.org/en-US/docs/IndexedDB/IDBObjectStore#put
if ((goog.isString(store.keyPath)) && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
throw new ydn.debug.error.ArgumentException(
'key must not be provided while the store uses in-line key.');
//} else if (store.autoIncrement && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
// throw new ydn.debug.error.ArgumentException('key must not be provided ' +
// 'while autoIncrement is true.');
} else if (!store.usedInlineKey() && !store.autoIncrement &&
!goog.isDef(opt_keys)) {
// The object store uses out-of-line keys and has no key generator, and no
// key parameter was provided.
throw new ydn.debug.error.ArgumentException(
'out-of-line key must be provided for store: ' + store_name);
}
if (goog.isArray(value)) {
var objs = value;
var keys = /** @type {!Array.<(number|string)>|undefined} */ (opt_keys);
//console.log('waiting to putObjects');
goog.log.finer(this.logger, 'addObjects: ' + store_name + ' ' + objs.length +
' objects');
for (var i = 0; i < objs.length; i++) {
store.generateIndex(objs[i]);
}
req = this.tx_thread.request(ydn.db.Request.Method.ADDS,
[store_name], ydn.db.base.TransactionMode.READ_WRITE);
req.addTxback(function() {
//console.log('putObjects');
this.getCrudExecutor().insertObjects(req, false, false, store_name, objs,
keys);
}, this);
if (store.dispatch_events) {
req.addCallback(function(keys) {
var event = new ydn.db.events.StoreEvent(ydn.db.events.Types.CREATED,
this.getStorage(), store_name, keys, objs);
this.getStorage().dispatchDbEvent(event);
}, this);
}
} else if (goog.isObject(value)) {
var obj = value;
var key = /** @type {number|string|undefined} */ (opt_keys);
var label = 'store: ' + store_name + ' key: ' + store.extractKey(obj, key);
goog.log.finer(this.logger, 'addObject: ' + label);
store.generateIndex(obj);
req = this.tx_thread.request(ydn.db.Request.Method.ADD,
[store_name], ydn.db.base.TransactionMode.READ_WRITE);
req.addTxback(function() {
this.getCrudExecutor().insertObjects(req, false, true, store_name, [obj],
[key]);
}, this);
if (store.dispatch_events) {
req.addCallback(function(key) {
var event = new ydn.db.events.RecordEvent(ydn.db.events.Types.CREATED,
this.getStorage(), store.getName(), key, obj);
this.getStorage().dispatchDbEvent(event);
}, this);
}
} else {
throw new ydn.debug.error.ArgumentException('record must be an object or ' +
'array list of objects, but ' + value + ' of type ' + typeof value +
' found.');
}
return req;
};
/**
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.addAll = function(store_name_or_schema, value,
opt_keys) {
var store = this.getStore(store_name_or_schema);
var store_name = store.getName();
var req;
// https://developer.mozilla.org/en-US/docs/IndexedDB/IDBObjectStore#put
if ((goog.isString(store.keyPath)) && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
throw new ydn.debug.error.ArgumentException(
'key must not be provided while the store uses in-line key.');
//} else if (store.autoIncrement && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
// throw new ydn.debug.error.ArgumentException('key must not be provided ' +
// 'while autoIncrement is true.');
} else if (!store.usedInlineKey() && !store.autoIncrement &&
!goog.isDef(opt_keys)) {
// The object store uses out-of-line keys and has no key generator, and no
// key parameter was provided.
throw new ydn.debug.error.ArgumentException(
'out-of-line key must be provided for store: ' + store_name);
}
if (goog.isArray(value)) {
var objs = value;
var keys = /** @type {!Array.<(number|string)>|undefined} */ (opt_keys);
//console.log('waiting to putObjects');
goog.log.finer(this.logger, 'addObjects: ' + store_name + ' ' + objs.length +
' objects');
for (var i = 0; i < objs.length; i++) {
store.generateIndex(objs[i]);
}
req = this.tx_thread.request(ydn.db.Request.Method.ADDS,
[store_name], ydn.db.base.TransactionMode.READ_WRITE);
req.addTxback(function() {
//console.log('putObjects');
this.getCrudExecutor().insertObjects(req, false, false, store_name, objs,
keys);
}, this);
if (store.dispatch_events) {
req.addCallback(function(keys) {
var event = new ydn.db.events.StoreEvent(ydn.db.events.Types.CREATED,
this.getStorage(), store.getName(), keys, objs);
this.getStorage().dispatchDbEvent(event);
}, this);
}
} else {
throw new ydn.debug.error.ArgumentException('record must be an ' +
'array list of objects, but ' + value + ' of type ' + typeof value +
' found.');
}
return req;
};
/**
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.put = function(arg1, value, opt_keys) {
var req;
var me = this;
if (arg1 instanceof ydn.db.Key) {
/**
* @type {!ydn.db.Key}
*/
var k = arg1;
var k_s_name = k.getStoreName();
var k_store = this.schema.getStore(k_s_name);
if (!k_store) {
throw new ydn.debug.error.ArgumentException('store "' + k_s_name +
'" not found.');
}
if (k_store.usedInlineKey()) {
var v_k = k_store.extractKey(value);
if (goog.isDefAndNotNull(v_k)) {
if (ydn.db.cmp(v_k, k.getId()) != 0) {
throw new ydn.debug.error.ArgumentException('Inline key must be ' +
k + ' but ' + v_k + ' found.');
}
} else {
k_store.setKeyValue(value, k.getId());
}
return this.put(k_s_name, value);
} else {
return this.put(k_s_name, value, k.getId());
}
} else if (goog.isArray(arg1)) { // array of keys
if (goog.DEBUG && goog.isDef(opt_keys)) {
throw new ydn.debug.error.ArgumentException('too many arguments');
}
var db_keys = /** @type {!Array.<!ydn.db.Key>} */ (arg1);
if (goog.DEBUG && !goog.isDef(value)) {
throw new ydn.debug.error.ArgumentException('record values required');
}
goog.asserts.assertArray(value, 'record values must also be in an array');
var values = /** @type {!Array} */ (value);
goog.asserts.assert(db_keys.length === values.length, 'number of keys ' +
'and number of object must be same, but found ' + db_keys.length +
' vs. ' + values.length);
var store_names = [];
for (var i = 0, n = db_keys.length; i < n; i++) {
var s_name = db_keys[i].getStoreName();
if (goog.array.indexOf(store_names, s_name) == -1) {
store_names.push(s_name);
}
var store = this.schema.getStore(s_name);
if (!store) {
throw new ydn.debug.error.ArgumentException('store "' + s_name +
'" not found.');
}
if (store.usedInlineKey()) {
store.setKeyValue(values[i], db_keys[i].getId());
}
}
goog.log.finer(this.logger, 'putByKeys: to ' + ydn.json.stringify(store_names) + ' ' +
values.length + ' objects');
for (var i = 0; i < values.length; i++) {
store.generateIndex(values[i]);
}
req = this.tx_thread.request(ydn.db.Request.Method.PUT_KEYS, store_names,
ydn.db.base.TransactionMode.READ_WRITE);
store.hook(req, arguments);
req.addTxback(function() {
me.getCrudExecutor().putByKeys(req, values, db_keys);
}, this);
} else if (goog.isString(arg1) || goog.isObject(arg1)) {
var store = this.getStore(arg1);
var st_name = store.getName();
// https://developer.mozilla.org/en-US/docs/IndexedDB/IDBObjectStore#put
if (store.usedInlineKey() && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
throw new ydn.debug.error.ArgumentException(
'key must not be provided while the store uses in-line key.');
//} else if (store.autoIncrement && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
// throw new ydn.debug.error.ArgumentException('key must not be provided' +
// ' while autoIncrement is true.');
} else if (!store.usedInlineKey() && !store.autoIncrement &&
!goog.isDef(opt_keys)) {
// The object store uses out-of-line keys and has no key generator, and no
// key parameter was provided.
throw new ydn.debug.error.ArgumentException(
'out-of-line key must be provided for store: ' + st_name);
}
if (goog.isArray(value)) {
var objs = value;
var keys = /** @type {!Array.<(number|string)>|undefined} */ (opt_keys);
goog.log.finer(this.logger, 'putObjects: ' + st_name + ' ' +
objs.length + ' objects');
for (var i = 0; i < objs.length; i++) {
store.generateIndex(objs[i]);
}
req = this.tx_thread.request(ydn.db.Request.Method.PUTS,
[st_name], ydn.db.base.TransactionMode.READ_WRITE);
store.hook(req, arguments);
req.addTxback(function() {
//console.log('putObjects');
this.getCrudExecutor().insertObjects(req, true, false, st_name, objs,
keys);
}, this);
if (store.dispatch_events) {
req.addCallback(function(keys) {
var event = new ydn.db.events.StoreEvent(ydn.db.events.Types.UPDATED,
this.getStorage(), st_name, keys, objs);
this.getStorage().dispatchDbEvent(event);
}, this);
}
} else if (goog.isObject(value)) {
var obj = value;
var key = /** @type {number|string|undefined} */ (opt_keys);
if (goog.DEBUG) {
if (goog.isDef(key)) {
goog.asserts.assert(ydn.db.Key.isValidKey(key), key +
' of type ' + (typeof key) + ' is invalid key for ' +
ydn.json.toShortString(obj));
} else if (!store.isAutoIncrement() && store.usedInlineKey()) {
goog.asserts.assert(ydn.db.Key.isValidKey(store.extractKey(obj)),
'in-line key on ' + store.getKeyPath() + ' must provided in ' +
ydn.json.toShortString(obj));
}
}
goog.log.finer(this.logger, 'putObject: ' + st_name + ' ' + (goog.isDef(key) ? key : '(without-key)'));
// note File is also instanceof Blob
var is_blob = (goog.isDef(goog.global['Blob']) && obj instanceof Blob) &&
// check for using blob store
store.isFixed() && !store.usedInlineKey() &&
store.countIndex() == 0 &&
// only webkit need to encode blob into dataURL.
goog.userAgent.WEBKIT;
if (is_blob) {
// we cannot invoke request to thread, because encoding is async,
// we must wait encoding is ready before starting transaction.
// TODO: this will cause transaction could not be reused.
req = new ydn.db.Request(ydn.db.Request.Method.PUT);
var fr = new FileReader();
fr.onload = function(e) {
var value = e.target.result;
var rq = me.tx_thread.request(ydn.db.Request.Method.PUT,
[st_name], ydn.db.base.TransactionMode.READ_WRITE);
store.hook(rq, [st_name, obj, key]);
rq.addTxback(function() {
me.getCrudExecutor().insertObjects(rq, true, true, st_name, [value],
[key]);
}, this);
rq.addCallbacks(function(x) {
req.callback(x);
}, function(e) {
req.errback(e);
});
};
fr.onerror = function(e) {
req.errback(e);
};
fr.onabort = function(e) {
req.errback(e);
};
fr.readAsDataURL(/** @type {!Blob} */ (obj));
} else {
store.generateIndex(obj);
req = this.tx_thread.request(ydn.db.Request.Method.PUT,
[st_name], ydn.db.base.TransactionMode.READ_WRITE);
var args = [st_name, obj, key];
store.hook(req, args);
req.addTxback(function() {
// Note: here we are reading from arguments array, so that if
// hook manipulate the value, we get updated value.
// encryption hook manipulate both key and value.
var keys = goog.isDef(key) ? [args[2]] : undefined;
me.getCrudExecutor().insertObjects(req, true, true, st_name,
[args[1]], keys);
}, this);
}
if (store.dispatch_events) {
req.addCallback(function(key) {
var event = new ydn.db.events.RecordEvent(ydn.db.events.Types.UPDATED,
this.getStorage(), st_name, key, obj);
this.getStorage().dispatchDbEvent(event);
}, this);
}
} else {
throw new ydn.debug.error.ArgumentException('put record value must be ' +
'Object or array of Objects');
}
} else {
throw new ydn.debug.error.ArgumentException('the first argument of put ' +
'must be store name, store schema or array of keys.');
}
return req;
};
/**
* @inheritDoc
*/
ydn.db.crud.DbOperator.prototype.putAll = function (arg1, value, opt_keys) {
var req;
var me = this;
var store = this.getStore(arg1);
var st_name = store.getName();
// https://developer.mozilla.org/en-US/docs/IndexedDB/IDBObjectStore#put
if (store.usedInlineKey() && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
throw new ydn.debug.error.ArgumentException(
'key must not be provided while the store uses in-line key.');
//} else if (store.autoIncrement && goog.isDef(opt_keys)) {
// The object store uses in-line keys or has a key generator, and a key
// parameter was provided.
// throw new ydn.debug.error.ArgumentException('key must not be provided' +
// ' while autoIncrement is true.');
} else if (!store.usedInlineKey() && !store.autoIncrement && !goog.isDef(opt_keys)) {
// The object store uses out-of-line keys and has no key generator, and no
// key parameter was provided.
throw new ydn.debug.error.ArgumentException(
'out-of-line key must be provided for store: ' + st_name);
}
var objs = value;
var keys = /** @type {!Array.<(number|string)>|undefined} */ (opt_keys);
goog.log.finer(this.logger, 'putObjects: ' + st_name + ' ' +
objs.length + ' objects');
for (var i = 0; i < objs.length; i++) {
store.generateIndex(objs[i]);
}
req = this.tx_thread.request(ydn.db.Request.Method.PUTS,
[st_name], ydn.db.base.TransactionMode.READ_WRITE);
store.hook(req, arguments);
req.addTxback(function () {
//console.log('putObjects');
this.getCrudExecutor().insertObjects(req, true, false, st_name, objs,
keys);
}, this);
if (store.dispatch_events) {
req.addCallback(function (keys) {
var event = new ydn.db.events.StoreEvent(ydn.db.events.Types.UPDATED,
this.getStorage(), st_name, keys, objs);
this.getStorage().dispatchDbEvent(event);
}, this);
}
return req;
};
/**
* Dump object into the database. Use only by synchronization process when
* updating from server.
* This is friendly module use only.
* @param {string|!Array.<!ydn.db.Key>} store_name store name.
* @param {!Array.<Object>} objs objects.
* @param {!Array.<!IDBKey>=} opt_keys keys.
* @param {boolean=} opt_use_main_thread default is background thread.
* @param {number=} opt_hook_idx hook index to ignore.
* @return {!goog.async.Deferred} df return no result.
*/
ydn.db.crud.DbOperator.prototype.dumpInternal = function(store_name, objs,
opt_keys, opt_use_main_thread, opt_hook_idx) {
var thread = opt_use_main_thread ? this.tx_thread : this.sync_thread;
var store_names, db_keys;
if (goog.isString(store_name)) {
var store = this.schema.getStore(store_name);
if (goog.DEBUG) {
if (store) {
if (!store.usedInlineKey() && !store.isAutoIncrement() &&
!goog.isDefAndNotNull(opt_keys)) {
throw new ydn.debug.error.ArgumentException(
'key required for store "' + store_name + '"');
}
} else {
throw new ydn.db.NotFoundError(store_name);
}
}
for (var i = 0; i < objs.length; i++) {
store.generateIndex(objs[i]);
}
store_names = [store_name];
} else {
goog.asserts.assertArray(store_name, 'store name ' + store_name + ' +' +
' must be an array or string, but ' + (typeof store_name));
db_keys = store_name;
store_names = [];
for (var i = 0, n = db_keys.length; i < n; i++) {
var s_name = db_keys[i].getStoreName();
var store = this.schema.getStore(s_name);
if (goog.array.indexOf(store_names, s_name) == -1) {
store_names.push(s_name);
}
if (goog.DEBUG && !store) {
throw new ydn.db.NotFoundError(s_name);
}
store.generateIndex(objs[i]);
}
}
var req;
if (goog.isString(store_name)) {
var s_n = store_name;
var store = this.schema.getStore(s_n);
req = thread.request(ydn.db.Request.Method.PUTS,
store_names, ydn.db.base.TransactionMode.READ_WRITE);
if (opt_hook_idx) {
store.hook(req, [s_n, objs, opt_keys], opt_hook_idx);
}
req.addTxback(function() {
this.getCrudExecutor().insertObjects(req, true, false, s_n, objs,
opt_keys);
}, this);
} else {
req = thread.request(ydn.db.Request.Method.PUT_KEYS,
store_names, ydn.db.base.TransactionMode.READ_WRITE);
if (opt_hook_idx) {
for (var i = 0; i < store_names.length; i++) {
var store = this.schema.getStore(store_names[i]);
store.hook(req, [objs, db_keys], opt_hook_idx);
}
}
req.addTxback(function() {
this.getCrudExecutor().putByKeys(req, objs, db_keys);
}, this);
}
return req;
};
/**
* Remove record by keys.
* @param {!Array.<!ydn.db.Key>} keys keys.
* @return {!ydn.db.Request} df.
*/
ydn.db.crud.DbOperator.prototype.removeInternalByKeys = function(keys) {
var store_names = [];
for (var i = 0, n = keys.length; i < n; i++) {
var s_name = keys[i].getStoreName();
if (goog.array.indexOf(store_names, s_name) == -1) {
store_names.push(s_name);
}
if (goog.DEBUG && !this.schema.hasStore(s_name)) {
throw new ydn.db.NotFoundError(s_name);
}
}
var me = this;
var df = this.sync_thread.request(ydn.db.Request.Method.REMOVE_KEYS,
store_names, ydn.db.base.TransactionMode.READ_WRITE);
df.addTxback(function() {
this.getCrudExecutor().removeByKeys(df, keys);
}, this);
return df;
};
/**
* Remove record by keys.
* @param {string} store_name store_name.
* @param {IDBKeyRange=} opt_kr key range.
* @return {!ydn.db.Request} df.
*/
ydn.db.crud.DbOperator.prototype.removeInternal = function(store_name, opt_kr) {
var df = this.sync_thread.request(ydn.db.Request.Method.REMOVE, [store_name],
ydn.db.base.TransactionMode.READ_WRITE);
df.addTxback(function() {
this.getCrudExecutor().removeByKeyRange(df, store_name, opt_kr || null);
}, this);
return df;
};
/**
* List records from the database. Use only by synchronization process when
* updating from server.
* This is friendly module use only.
* @param {string} store_name store name.
* @param {?string} index_name index name.
* @param {IDBKeyRange|ydn.db.KeyRange} key_range