content: // Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
  //  This source code is licensed under both the GPLv2 (found in the
  //  COPYING file in the root directory) and Apache 2.0 License
  //  (found in the LICENSE.Apache file in the root directory).
  
  package org.rocksdb;
  
  /**
   * Similar to {@link org.rocksdb.WriteBatch} but with a binary searchable
   * index built for all the keys inserted.
   *
   * Calling put, merge, remove or putLogData calls the same function
   * as with {@link org.rocksdb.WriteBatch} whilst also building an index.
   *
   * A user can call {@link org.rocksdb.WriteBatchWithIndex#newIterator()} to
   * create an iterator over the write batch or
   * {@link org.rocksdb.WriteBatchWithIndex#newIteratorWithBase(org.rocksdb.RocksIterator)}
   * to get an iterator for the database with Read-Your-Own-Writes like capability
   */
  public class WriteBatchWithIndex extends AbstractWriteBatch {
    /**
     * Creates a WriteBatchWithIndex where no bytes
     * are reserved up-front, bytewise comparison is
     * used for fallback key comparisons,
     * and duplicate keys operations are retained
     */
    public WriteBatchWithIndex() {
      super(newWriteBatchWithIndex());
    }
  
  
    /**
     * Creates a WriteBatchWithIndex where no bytes
     * are reserved up-front, bytewise comparison is
     * used for fallback key comparisons, and duplicate key
     * assignment is determined by the constructor argument
     *
     * @param overwriteKey if true, overwrite the key in the index when
     *   inserting a duplicate key, in this way an iterator will never
     *   show two entries with the same key.
     */
    public WriteBatchWithIndex(final boolean overwriteKey) {
      super(newWriteBatchWithIndex(overwriteKey));
    }
  
    /**
     * Creates a WriteBatchWithIndex
     *
     * @param fallbackIndexComparator We fallback to this comparator
     *  to compare keys within a column family if we cannot determine
     *  the column family and so look up it's comparator.
     *
     * @param reservedBytes reserved bytes in underlying WriteBatch
     *
     * @param overwriteKey if true, overwrite the key in the index when
     *   inserting a duplicate key, in this way an iterator will never
     *   show two entries with the same key.
     */
    public WriteBatchWithIndex(
        final AbstractComparator<? extends AbstractSlice<?>>
            fallbackIndexComparator, final int reservedBytes,
        final boolean overwriteKey) {
      super(newWriteBatchWithIndex(fallbackIndexComparator.getNativeHandle(),
          reservedBytes, overwriteKey));
    }
  
    /**
     * Create an iterator of a column family. User can call
     * {@link org.rocksdb.RocksIteratorInterface#seek(byte[])} to
     * search to the next entry of or after a key. Keys will be iterated in the
     * order given by index_comparator. For multiple updates on the same key,
     * each update will be returned as a separate entry, in the order of update
     * time.
     *
     * @param columnFamilyHandle The column family to iterate over
     * @return An iterator for the Write Batch contents, restricted to the column
     * family
     */
    public WBWIRocksIterator newIterator(
        final ColumnFamilyHandle columnFamilyHandle) {
      return new WBWIRocksIterator(this, iterator1(nativeHandle_,
              columnFamilyHandle.nativeHandle_));
    }
  
    /**
     * Create an iterator of the default column family. User can call
     * {@link org.rocksdb.RocksIteratorInterface#seek(byte[])} to
     * search to the next entry of or after a key. Keys will be iterated in the
     * order given by index_comparator. For multiple updates on the same key,
     * each update will be returned as a separate entry, in the order of update
     * time.
     *
     * @return An iterator for the Write Batch contents
     */
    public WBWIRocksIterator newIterator() {
      return new WBWIRocksIterator(this, iterator0(nativeHandle_));
    }
  
    /**
     * Provides Read-Your-Own-Writes like functionality by
     * creating a new Iterator that will use {@link org.rocksdb.WBWIRocksIterator}
     * as a delta and baseIterator as a base
     *
     * @param columnFamilyHandle The column family to iterate over
     * @param baseIterator The base iterator,
     *   e.g. {@link org.rocksdb.RocksDB#newIterator()}
     * @return An iterator which shows a view comprised of both the database
     * point-in-time from baseIterator and modifications made in this write batch.
     */
    public RocksIterator newIteratorWithBase(
        final ColumnFamilyHandle columnFamilyHandle,
        final RocksIterator baseIterator) {
      RocksIterator iterator = new RocksIterator(
          baseIterator.parent_,
          iteratorWithBase(nativeHandle_,
                  columnFamilyHandle.nativeHandle_,
                  baseIterator.nativeHandle_));
      //when the iterator is deleted it will also delete the baseIterator
      baseIterator.disOwnNativeHandle();
      return iterator;
    }
  
    /**
     * Provides Read-Your-Own-Writes like functionality by
     * creating a new Iterator that will use {@link org.rocksdb.WBWIRocksIterator}
     * as a delta and baseIterator as a base. Operates on the default column
     * family.
     *
     * @param baseIterator The base iterator,
     *   e.g. {@link org.rocksdb.RocksDB#newIterator()}
     * @return An iterator which shows a view comprised of both the database
     * point-in-timefrom baseIterator and modifications made in this write batch.
     */
    public RocksIterator newIteratorWithBase(final RocksIterator baseIterator) {
      return newIteratorWithBase(baseIterator.parent_.getDefaultColumnFamily(),
          baseIterator);
    }
  
    /**
     * Similar to {@link RocksDB#get(ColumnFamilyHandle, byte[])} but will only
     * read the key from this batch.
     *
     * @param columnFamilyHandle The column family to retrieve the value from
     * @param options The database options to use
     * @param key The key to read the value for
     *
     * @return a byte array storing the value associated with the input key if
     *     any. null if it does not find the specified key.
     *
     * @throws RocksDBException if the batch does not have enough data to resolve
     * Merge operations, MergeInProgress status may be returned.
     */
    public byte[] getFromBatch(final ColumnFamilyHandle columnFamilyHandle,
        final DBOptions options, final byte[] key) throws RocksDBException {
      return getFromBatch(nativeHandle_, options.nativeHandle_,
          key, key.length, columnFamilyHandle.nativeHandle_);
    }
  
    /**
     * Similar to {@link RocksDB#get(byte[])} but will only
     * read the key from this batch.
     *
     * @param options The database options to use
     * @param key The key to read the value for
     *
     * @return a byte array storing the value associated with the input key if
     *     any. null if it does not find the specified key.
     *
     * @throws RocksDBException if the batch does not have enough data to resolve
     * Merge operations, MergeInProgress status may be returned.
     */
    public byte[] getFromBatch(final DBOptions options, final byte[] key)
        throws RocksDBException {
      return getFromBatch(nativeHandle_, options.nativeHandle_, key, key.length);
    }
  
    /**
     * Similar to {@link RocksDB#get(ColumnFamilyHandle, byte[])} but will also
     * read writes from this batch.
     *
     * This function will query both this batch and the DB and then merge
     * the results using the DB's merge operator (if the batch contains any
     * merge requests).
     *
     * Setting {@link ReadOptions#setSnapshot(long, long)} will affect what is
     * read from the DB but will NOT change which keys are read from the batch
     * (the keys in this batch do not yet belong to any snapshot and will be
     * fetched regardless).
     *
     * @param db The Rocks database
     * @param columnFamilyHandle The column family to retrieve the value from
     * @param options The read options to use
     * @param key The key to read the value for
     *
     * @return a byte array storing the value associated with the input key if
     *     any. null if it does not find the specified key.
     *
     * @throws RocksDBException if the value for the key cannot be read
     */
    public byte[] getFromBatchAndDB(final RocksDB db, final ColumnFamilyHandle columnFamilyHandle,
        final ReadOptions options, final byte[] key) throws RocksDBException {
      return getFromBatchAndDB(nativeHandle_, db.nativeHandle_,
          options.nativeHandle_, key, key.length,
          columnFamilyHandle.nativeHandle_);
    }
  
    /**
     * Similar to {@link RocksDB#get(byte[])} but will also
     * read writes from this batch.
     *
     * This function will query both this batch and the DB and then merge
     * the results using the DB's merge operator (if the batch contains any
     * merge requests).
     *
     * Setting {@link ReadOptions#setSnapshot(long, long)} will affect what is
     * read from the DB but will NOT change which keys are read from the batch
     * (the keys in this batch do not yet belong to any snapshot and will be
     * fetched regardless).
     *
     * @param db The Rocks database
     * @param options The read options to use
     * @param key The key to read the value for
     *
     * @return a byte array storing the value associated with the input key if
     *     any. null if it does not find the specified key.
     *
     * @throws RocksDBException if the value for the key cannot be read
     */
    public byte[] getFromBatchAndDB(final RocksDB db, final ReadOptions options,
        final byte[] key) throws RocksDBException {
      return getFromBatchAndDB(nativeHandle_, db.nativeHandle_,
          options.nativeHandle_, key, key.length);
    }
  
    @Override protected final native void disposeInternal(final long handle);
    @Override final native int count0(final long handle);
    @Override final native void put(final long handle, final byte[] key,
        final int keyLen, final byte[] value, final int valueLen);
    @Override final native void put(final long handle, final byte[] key,
        final int keyLen, final byte[] value, final int valueLen,
        final long cfHandle);
    @Override final native void merge(final long handle, final byte[] key,
        final int keyLen, final byte[] value, final int valueLen);
    @Override final native void merge(final long handle, final byte[] key,
        final int keyLen, final byte[] value, final int valueLen,
        final long cfHandle);
    @Override final native void remove(final long handle, final byte[] key,
        final int keyLen);
    @Override final native void remove(final long handle, final byte[] key,
        final int keyLen, final long cfHandle);
    @Override
    final native void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen,
        final byte[] endKey, final int endKeyLen);
    @Override
    final native void deleteRange(final long handle, final byte[] beginKey, final int beginKeyLen,
        final byte[] endKey, final int endKeyLen, final long cfHandle);
    @Override final native void putLogData(final long handle, final byte[] blob,
        final int blobLen);
    @Override final native void clear0(final long handle);
    @Override final native void setSavePoint0(final long handle);
    @Override final native void rollbackToSavePoint0(final long handle);
  
    private native static long newWriteBatchWithIndex();
    private native static long newWriteBatchWithIndex(final boolean overwriteKey);
    private native static long newWriteBatchWithIndex(
        final long fallbackIndexComparatorHandle, final int reservedBytes,
        final boolean overwriteKey);
    private native long iterator0(final long handle);
    private native long iterator1(final long handle, final long cfHandle);
    private native long iteratorWithBase(final long handle,
        final long baseIteratorHandle, final long cfHandle);
    private native byte[] getFromBatch(final long handle, final long optHandle,
        final byte[] key, final int keyLen);
    private native byte[] getFromBatch(final long handle, final long optHandle,
        final byte[] key, final int keyLen, final long cfHandle);
    private native byte[] getFromBatchAndDB(final long handle,
        final long dbHandle,  final long readOptHandle, final byte[] key,
        final int keyLen);
    private native byte[] getFromBatchAndDB(final long handle,
        final long dbHandle, final long readOptHandle, final byte[] key,
        final int keyLen, final long cfHandle);
  }
  