@Override
  public Changes changes(long since, int limit) {
    Preconditions.checkState(this.isOpen(), "Database is closed");
    Preconditions.checkArgument(limit > 0, "Limit must be positive number");
    since = since >= 0 ? since : 0;

    String[] args = {Long.toString(since), Long.toString(since + limit)};
    Cursor cursor = null;
    try {
      Long lastSequence = since;
      List<Long> ids = new ArrayList<Long>();
      cursor = this.sqlDb.rawQuery(SQL_CHANGE_IDS_SINCE_LIMIT, args);
      while (cursor.moveToNext()) {
        ids.add(cursor.getLong(0));
        lastSequence = Math.max(lastSequence, cursor.getLong(1));
      }
      List<DocumentRevision> results = this.getDocumentsWithInternalIds(ids);
      if (results.size() != ids.size()) {
        throw new IllegalStateException(
            "The number of document does not match number of ids, "
                + "something must be wrong here.");
      }

      return new Changes(lastSequence, results);
    } catch (SQLException e) {
      throw new IllegalStateException(
          "Error querying all changes since: " + since + ", limit: " + limit, e);
    } finally {
      DatabaseUtils.closeCursorQuietly(cursor);
    }
  }
  static BasicDocumentRevision getFullRevisionFromCurrentCursor(Cursor cursor) {
    String docId = cursor.getString(0);
    long internalId = cursor.getLong(1);
    String revId = cursor.getString(2);
    long sequence = cursor.getLong(3);
    byte[] json = cursor.getBlob(4);
    boolean current = cursor.getInt(5) > 0;
    boolean deleted = cursor.getInt(6) > 0;

    long parent = -1L;
    if (cursor.columnType(7) == Cursor.FIELD_TYPE_INTEGER) {
      parent = cursor.getLong(7);
    } else if (cursor.columnType(7) == Cursor.FIELD_TYPE_NULL) {
    } else {
      throw new IllegalArgumentException("Unexpected type: " + cursor.columnType(7));
    }

    DocumentRevisionBuilder builder =
        new DocumentRevisionBuilder()
            .setDocId(docId)
            .setRevId(revId)
            .setBody(BasicDocumentBody.bodyWith(json))
            .setDeleted(deleted)
            .setSequence(sequence)
            .setInternalId(internalId)
            .setCurrnet(current)
            .setParent(parent);

    return builder.buildBasicDBObject();
  }
 @Override
 public long getDocNumericId(String docId) {
   Preconditions.checkState(this.isOpen(), "Database is closed");
   Preconditions.checkArgument(
       !Strings.isNullOrEmpty(docId), "Input document id can not be empty");
   Cursor cursor = null;
   try {
     cursor = this.sqlDb.rawQuery("SELECT doc_id FROM docs WHERE docid = ?", new String[] {docId});
     if (cursor.moveToFirst()) {
       return cursor.getLong(0);
     }
   } catch (SQLException e) {
     Log.e(LOG_TAG, "Error getting doc numeric id", e);
   } finally {
     DatabaseUtils.closeCursorQuietly(cursor);
   }
   return -1;
 }
 @Override
 public long getLastSequence() {
   Preconditions.checkState(this.isOpen(), "Database is closed");
   String sql = "SELECT MAX(sequence) FROM revs";
   Cursor cursor = null;
   long result = 0;
   try {
     cursor = this.sqlDb.rawQuery(sql, null);
     if (cursor.moveToFirst()) {
       if (cursor.columnType(0) == Cursor.FIELD_TYPE_INTEGER) {
         result = cursor.getLong(0);
       } else if (cursor.columnType(0) == Cursor.FIELD_TYPE_NULL) {
         result = SEQUENCE_NUMBER_START;
       } else {
         throw new IllegalStateException("SQLite return an unexpected value.");
       }
     }
   } catch (SQLException e) {
     Log.e(LOG_TAG, "Error getting last sequence", e);
   } finally {
     DatabaseUtils.closeCursorQuietly(cursor);
   }
   return result;
 }