<commit_before>// 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 <EMAIL> (<NAME>)
*/


goog.provide('ydn.db.crud.DbOperator');
goog.require('goog.debug.Logger');
goog.require('ydn.db');
goog.require('ydn.db.ISyncOperator');
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}
 * @implements {ydn.db.ISyncOperator}
 * @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.debug.Logger} logger.
 */
ydn.db.crud.DbOperator.prototype.logger =
    goog.debug.Logger.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.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)) {
    this.logger.warning('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');
    this.logger.finer('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 TypeError('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);
    }

    this.logger.finer('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();
    this.logger.finer('getByKey: ' + k_store_name + ':' + kid);
    req = this.tx_thread.request(ydn.db.Request.Method.GET, [k_store_name]);
    store.hook(req, arguments);
    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');
    this.logger.finer('getById: ' + store_name + ':' + id);
    req = this.tx_thread.request(ydn.db.Request.Method.GET, [store_name]);
    store.hook(req, arguments);
    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;
};


/**
 *
 * @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
    var index_name = arg1;
    if (goog.DEBUG) {
      var msg = ydn.db.KeyRange.validate(/** @type {KeyRangeJson} */ (arg2));
      if (msg) {
        throw new ydn.debug.error.ArgumentException('invalid key range: ' +
            arg2 + ' ' + msg);
      }
    }
    range = ydn.db.KeyRange.parseIDBKeyRange(
        /** @type {KeyRangeJson} */ (arg2));

    if (goog.isNumber(arg3)) {
      limit = arg3;
    } else if (!goog.isDef(arg3)) {
      limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
    } else {
      throw new ydn.debug.error.ArgumentException('limit must be a number');
    }
    if (goog.isNumber(arg4)) {
      offset = arg4;
    } else if (!goog.isDef(arg4)) {
      offset = 0;
    } else {
      throw new ydn.debug.error.ArgumentException('offset must be a number');
    }
    if (goog.isDef(arg5)) {
      if (goog.isBoolean(arg5)) {
        reverse = arg5;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'reverse must be a boolean');
      }
    }
    if (goog.isDef(arg6)) {
      if (goog.isBoolean(arg6)) {
        unique = arg6;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'unique must be a boolean');
      }
    }
    this.logger.finer('keysByIndexKeyRange: ' + store_name);
    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);
  } else {
    if (goog.isObject(arg1)) {
      if (goog.DEBUG) {
        var msg = ydn.db.KeyRange.validate(arg1);
        if (msg) {
          throw new ydn.debug.error.ArgumentException('invalid key range: ' +
              ydn.json.toShortString(arg1) + ' ' + msg);
        }
      }
      range = ydn.db.KeyRange.parseIDBKeyRange(arg1);
    } else {
      if (goog.DEBUG && goog.isDefAndNotNull(arg1)) {
        throw new TypeError('invalid key range: ' +
            ydn.json.toShortString(arg1) + ' of type ' + typeof arg1);
      }
      range = null;
    }
    if (goog.isNumber(arg2)) {
      limit = arg2;
    } else if (!goog.isDef(arg2)) {
      limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
    } else {
      throw new ydn.debug.error.ArgumentException('limit must be a number');
    }
    if (goog.isNumber(arg3)) {
      offset = arg3;
    } else if (!goog.isDef(arg3)) {
      offset = 0;
    } else {
      throw new ydn.debug.error.ArgumentException('offset must be a number');
    }
    if (goog.isDef(arg4)) {
      if (goog.isBoolean(arg4)) {
        reverse = arg4;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'reverse must be a boolean');
      }
    }
    this.logger.finer('keysByKeyRange: ' + store_name);
    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;
};


/**
 * @inheritDoc
 */
ydn.db.crud.DbOperator.prototype.values = function(arg0, arg1, arg2, arg3, arg4,
                                                   arg5) {

  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;

  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;
      this.logger.finer('listByIds: ' + store_name + ' ' +
          ids.length + ' ids');
      req = this.tx_thread.request(ydn.db.Request.Method.VALUES_IDS,
          [store_name]);
      store.hook(req, arguments);
      req.addTxback(function() {
        this.getCrudExecutor().listByIds(req, store_name, ids);
      }, this);
    } else if (goog.isString(arg1)) { // index name
      var index_name = arg1;
      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} */ (arg2));
        if (msg) {
          throw new ydn.debug.error.ArgumentException('invalid key range: ' +
              arg2 + ' ' + msg);
        }
      }
      var range = ydn.db.KeyRange.parseIDBKeyRange(
          /** @type {KeyRangeJson} */ (arg2));
      if (!goog.isDef(arg3)) {
        limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
      } else if (goog.isNumber(arg3)) {
        limit = arg3;
      } else {
        throw new ydn.debug.error.ArgumentException('limit must be a number.');
      }
      if (!goog.isDef(arg4)) {
        offset = 0;
      } else if (goog.isNumber(arg4)) {
        offset = arg4;
      } else {
        throw new ydn.debug.error.ArgumentException('offset must be a number.');
      }
      if (goog.isBoolean(arg5)) {
        reverse = arg5;
      } else if (goog.isDef(arg5)) {
        throw new ydn.debug.error.ArgumentException(
            'reverse must be a boolean, but ' + arg5);
      }
      this.logger.finer('listByIndexKeyRange: ' + store_name + ':' +
          index_name);
      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, ydn.db.base.QueryMethod.LIST_VALUE,
            store_name, index_name, range, limit, offset, reverse, false);
      }, this);
    } else {
      var range = null;
      if (goog.isObject(arg1)) {
        if (goog.DEBUG) {
          var msg = ydn.db.KeyRange.validate(arg1);
          if (msg) {
            throw new ydn.debug.error.ArgumentException('invalid key range: ' +
                arg1 + ' ' + msg);
          }
        }
        range = ydn.db.KeyRange.parseIDBKeyRange(arg1);
      } else if (goog.DEBUG && goog.isDefAndNotNull(arg1)) {
        throw new TypeError('expect key range object, but found "' +
            ydn.json.toShortString(arg1) + '" of type ' + typeof arg1);
      }
      if (!goog.isDef(arg2)) {
        limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
      } else if (goog.isNumber(arg2)) {
        limit = arg2;
      } else {
        throw new ydn.debug.error.ArgumentException('limit must be a number, ' +
            'but ' + arg2 + ' is ' + typeof arg2);
      }
      if (!goog.isDef(arg3)) {
        offset = 0;
      } else if (goog.isNumber(arg3)) {
        offset = arg3;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'offset must be a number, ' + 'but ' + arg3 + ' is ' + typeof arg3);<commit_msg>list method signature change to comfrom with keys and values methods<commit_after>// 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 <EMAIL> (<NAME>)
*/


goog.provide('ydn.db.crud.DbOperator');
goog.require('goog.debug.Logger');
goog.require('ydn.db');
goog.require('ydn.db.ISyncOperator');
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}
 * @implements {ydn.db.ISyncOperator}
 * @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.debug.Logger} logger.
 */
ydn.db.crud.DbOperator.prototype.logger =
    goog.debug.Logger.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.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)) {
    this.logger.warning('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');
    this.logger.finer('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 TypeError('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);
    }

    this.logger.finer('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();
    this.logger.finer('getByKey: ' + k_store_name + ':' + kid);
    req = this.tx_thread.request(ydn.db.Request.Method.GET, [k_store_name]);
    store.hook(req, arguments);
    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');
    this.logger.finer('getById: ' + store_name + ':' + id);
    req = this.tx_thread.request(ydn.db.Request.Method.GET, [store_name]);
    store.hook(req, arguments);
    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;
};


/**
 *
 * @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
    var index_name = arg1;
    if (goog.DEBUG) {
      var msg = ydn.db.KeyRange.validate(/** @type {KeyRangeJson} */ (arg2));
      if (msg) {
        throw new ydn.debug.error.ArgumentException('invalid key range: ' +
            arg2 + ' ' + msg);
      }
    }
    range = ydn.db.KeyRange.parseIDBKeyRange(
        /** @type {KeyRangeJson} */ (arg2));

    if (goog.isNumber(arg3)) {
      limit = arg3;
    } else if (!goog.isDef(arg3)) {
      limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
    } else {
      throw new ydn.debug.error.ArgumentException('limit must be a number');
    }
    if (goog.isNumber(arg4)) {
      offset = arg4;
    } else if (!goog.isDef(arg4)) {
      offset = 0;
    } else {
      throw new ydn.debug.error.ArgumentException('offset must be a number');
    }
    if (goog.isDef(arg5)) {
      if (goog.isBoolean(arg5)) {
        reverse = arg5;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'reverse must be a boolean');
      }
    }
    if (goog.isDef(arg6)) {
      if (goog.isBoolean(arg6)) {
        unique = arg6;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'unique must be a boolean');
      }
    }
    this.logger.finer('keysByIndexKeyRange: ' + store_name);
    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);
  } else {
    if (goog.isObject(arg1)) {
      if (goog.DEBUG) {
        var msg = ydn.db.KeyRange.validate(arg1);
        if (msg) {
          throw new ydn.debug.error.ArgumentException('invalid key range: ' +
              ydn.json.toShortString(arg1) + ' ' + msg);
        }
      }
      range = ydn.db.KeyRange.parseIDBKeyRange(arg1);
    } else {
      if (goog.DEBUG && goog.isDefAndNotNull(arg1)) {
        throw new TypeError('invalid key range: ' +
            ydn.json.toShortString(arg1) + ' of type ' + typeof arg1);
      }
      range = null;
    }
    if (goog.isNumber(arg2)) {
      limit = arg2;
    } else if (!goog.isDef(arg2)) {
      limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
    } else {
      throw new ydn.debug.error.ArgumentException('limit must be a number');
    }
    if (goog.isNumber(arg3)) {
      offset = arg3;
    } else if (!goog.isDef(arg3)) {
      offset = 0;
    } else {
      throw new ydn.debug.error.ArgumentException('offset must be a number');
    }
    if (goog.isDef(arg4)) {
      if (goog.isBoolean(arg4)) {
        reverse = arg4;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'reverse must be a boolean');
      }
    }
    this.logger.finer('keysByKeyRange: ' + store_name);
    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;
};


/**
 * @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;
      this.logger.finer('listByIds: ' + store_name + ' ' +
          ids.length + ' ids');
      req = this.tx_thread.request(ydn.db.Request.Method.VALUES_IDS,
          [store_name]);
      store.hook(req, arguments);
      req.addTxback(function() {
        this.getCrudExecutor().listByIds(req, store_name, ids);
      }, this);
    } else if (goog.isString(arg1)) { // index name
      var index_name = arg1;
      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} */ (arg2));
        if (msg) {
          throw new ydn.debug.error.ArgumentException('invalid key range: ' +
              arg2 + ' ' + msg);
        }
      }
      var range = ydn.db.KeyRange.parseIDBKeyRange(
          /** @type {KeyRangeJson} */ (arg2));
      if (!goog.isDef(arg3)) {
        limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
      } else if (goog.isNumber(arg3)) {
        limit = arg3;
      } else {
        throw new ydn.debug.error.ArgumentException('limit must be a number.');
      }
      if (!goog.isDef(arg4)) {
        offset = 0;
      } else if (goog.isNumber(arg4)) {
        offset = arg4;
      } else {
        throw new ydn.debug.error.ArgumentException('offset must be a number.');
      }
      if (goog.isBoolean(arg5)) {
        reverse = arg5;
      } else if (goog.isDef(arg5)) {
        throw new ydn.debug.error.ArgumentException(
            'reverse must be a boolean, but ' + arg5);
      }
      if (goog.isDef(arg6)) {
        if (goog.isBoolean(arg6)) {
          unique = arg6;
        } else {
          throw new ydn.debug.error.ArgumentException(
              'unique must be a boolean');
        }
      }
      this.logger.finer('listByIndexKeyRange: ' + store_name + ':' +
          index_name);
      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, ydn.db.base.QueryMethod.LIST_VALUE,
            store_name, index_name, range, limit, offset, reverse, unique);
      }, this);
    } else {
      var range = null;
      if (goog.isObject(arg1)) {
        if (goog.DEBUG) {
          var msg = ydn.db.KeyRange.validate(arg1);
          if (msg) {
            throw new ydn.debug.error.ArgumentException('invalid key range: ' +
                arg1 + ' ' + msg);
          }
        }
        range = ydn.db.KeyRange.parseIDBKeyRange(arg1);
      } else if (goog.DEBUG && goog.isDefAndNotNull(arg1)) {
        throw new TypeError('expect key range object, but found "' +
            ydn.json.toShortString(arg1) + '" of type ' + typeof arg1);
      }
      if (!goog.isDef(arg2)) {
        limit = ydn.db.base.DEFAULT_RESULT_LIMIT;
      } else if (goog.isNumber(arg2)) {
        limit = arg2;
      } else {
        throw new ydn.debug.error.ArgumentException('limit must be a number, ' +
            'but ' + arg2 + ' is ' + typeof arg2);
      }
      if (!goog.isDef(arg3)) {
        offset = 0;
      } else if (goog.isNumber(arg3)) {
        offset = arg3;
      } else {
        throw new ydn.debug.error.ArgumentException(
            'offset must be a number, ' + 'but ' + arg3 + ' is ' + typeof arg3);
{'fraction_non_alphanumeric': 0.1457022787189058, 'fraction_numerical': 0.006525540967346193, 'mean_word_length': 2.267269316049804, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4304101', 'n_tokens_mistral': 12823, 'n_tokens_neox': 12547, 'n_words': 2502}