All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.btree.proc.BatchRemove Maven / Gradle / Ivy

/**

 Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

 Contact:
 SYSTAP, LLC DBA Blazegraph
 2501 Calvert ST NW #106
 Washington, DC 20008
 [email protected]

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; version 2 of the License.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*
 * Created on Feb 12, 2007
 */

package com.bigdata.btree.proc;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import com.bigdata.btree.Errors;
import com.bigdata.btree.IIndex;
import com.bigdata.btree.raba.IRaba;
import com.bigdata.btree.raba.codec.IRabaCoder;
import com.bigdata.util.BytesUtil;

/**
 * Batch removal of one or more tuples, optionally returning their existing
 * values, the #of tuples that were deleted, or a mask indicating which tuples
 * were deleted (polymorphic return type).
 * 
 * @author Bryan Thompson
 */
public class BatchRemove extends AbstractKeyArrayIndexProcedure implements
        IParallelizableIndexProcedure {

    /**
     * 
     */
    private static final long serialVersionUID = -5332443478547654844L;

    private boolean assertFound;
    private ReturnWhatEnum returnWhat;

    /**
     * True iff the procedure will verify that each supplied key was in fact
     * found in the index.
     */
    public boolean getAssertFound() {

        return assertFound;

    }

    /**
     * True iff the old values stored under the keys will be returned by
     * {@link #apply(IIndex)}.
     */
    public boolean getReturnOldValues() {

        return returnWhat == ReturnWhatEnum.OldValues;

    }

    @Override
    public final boolean isReadOnly() {
        
        return false;
        
    }

    /**
     * What to return.
     * 
     * @author Bryan
     *         Thompson
     */
    private static enum ReturnWhatEnum {
      
        /**
         * Return the #of tuples that were deleted.
         */
        MutationCount(0),
        
        /**
         * Return the old value for each tuple.
         */
        OldValues(1),
        
        /**
         * Return a {@link ResultBitBuffer}, which is basically a bit mask
         * indicating which of the caller's tuples were deleted.
         */
        BitMask(2);
        
        private final int w;
        
        private ReturnWhatEnum(final int w) {
            this.w = w;
        }

        public int getValue() {
            return w;
        }
        
        public static ReturnWhatEnum valueOf(final int w) {
            switch (w) {
            case 0:
                return MutationCount;
            case 1:
                return OldValues;
            case 2:
                return BitMask;
            default:
                throw new IllegalArgumentException();
            }
        }
        
    };
    
    /**
     * Factory for {@link BatchRemove} procedures.
     * 
     * @author Bryan Thompson
     */
    public static class BatchRemoveConstructor extends
            AbstractKeyArrayIndexProcedureConstructor {

        /**
         * Singleton requests the return of the values that were removed from
         * the index by the operation.
         */
        public static final BatchRemoveConstructor RETURN_OLD_VALUES = new BatchRemoveConstructor(
				false/* assertFound */, ReturnWhatEnum.OldValues);

        /**
         * Singleton does NOT request the return of the values that were removed
         * from the index by the operation. Instead, only the #of deleted tuples
         * is return (the mutationCount).
         */
        public static final BatchRemoveConstructor RETURN_MUTATION_COUNT = new BatchRemoveConstructor(
				false/* assertFound */, ReturnWhatEnum.MutationCount);

        /**
         * Singleton requests the return of a {@link ResultBitBuffer} providing
         * a bit mask of the tuples which were removed from the index by this
         * operation (that is, those tuples which were pre-existing in the index
         * in a non-deleted state).
         */
        public static final BatchRemoveConstructor RETURN_BIT_MASK = new BatchRemoveConstructor(
				false/* assertFound */, ReturnWhatEnum.BitMask);

        /**
         * Singleton does NOT request the return of the values that were removed
         * from the index by the operation but asserts that each key was in fact
         * present in the index.
         */
		public static final BatchRemoveConstructor ASSERT_FOUND_RETURN_NO_VALUES = new BatchRemoveConstructor(
				true/* assertFound */, ReturnWhatEnum.BitMask);

        private final boolean assertFound;
        private final ReturnWhatEnum returnWhat;

        /**
         * Values ARE NOT sent.
         */
        @Override
        public final boolean sendValues() {
            
            return false;
            
        }

        private BatchRemoveConstructor(final boolean assertFound,
                final ReturnWhatEnum returnWhat) {

            this.assertFound = assertFound;
            
            this.returnWhat = returnWhat;

        }

        @Override
        public BatchRemove newInstance(final IRabaCoder keySer, final IRabaCoder valSer,
                final int fromIndex, final int toIndex, final byte[][] keys, final byte[][] vals) {

			if (vals != null)
				throw new IllegalArgumentException(Errors.ERR_VALS_NOT_NULL);

            return new BatchRemove(keySer, valSer, fromIndex, toIndex, keys,
                    assertFound, returnWhat);

        }

    }

    /**
     * De-serialization ctor.
     * 
     */
    public BatchRemove() {

    }

    /**
     * Batch remove operation.
     * 
     * @param keys
     *            A series of keys paired to values. Each key is an variable
     *            length unsigned byte[]. The keys MUST be presented in sorted
     *            order.
     * @param returnOldValues
     *            When true the old values for those keys will be
     *            returned by {@link #apply(IIndex)}.
     * 
     * @see BatchRemoveConstructor
     */
    protected BatchRemove(final IRabaCoder keySer, final IRabaCoder valSer,
            final int fromIndex, final int toIndex, final byte[][] keys, final boolean assertFound,
            final ReturnWhatEnum returnWhat) {

        super(keySer, valSer, fromIndex, toIndex, keys, null/* vals */);

        this.assertFound = assertFound;

        this.returnWhat = returnWhat;

        if (returnWhat == null)
            throw new IllegalArgumentException();
        
    }

    /**
     * Applies the operation.
     * 
     * @param ndx
     * 
     * @return The old values as a {@link ResultBuffer} iff they were requested.
     * 
     * @throws AssertionError
     *             if {@link #getAssertFound()} is true and a
     *             given key was not found in the index.
     */
    @Override
    public Object applyOnce(final IIndex ndx, final IRaba keys, final IRaba vals) {

        final int n = keys.size();

        final boolean returnOldValues = getReturnOldValues();
        
        final byte[][] ret = returnOldValues ? new byte[n][] : null;

        final boolean[] modified = returnWhat == ReturnWhatEnum.BitMask ? new boolean[n]
                : null;
        
        int i = 0, mutationCount = 0;

        while (i < n) {

            final byte[] key = keys.get(i);
            
            if (!returnOldValues && ndx.contains(key)) {

                // Track a mutation counter.
                mutationCount++;
                
                if (modified != null) {

                    modified[i] = true;

                }
                
            }

            final byte[] oldval = ndx.remove(key);

            if(assertFound) {
                
                if (oldval == null) {

                    throw new AssertionError("No entry: "
                            + BytesUtil.toString(key));
                    
                }
                
            }
            
            if (returnOldValues) {

                ret[i] = oldval;

            }

            i++;

        }

        switch (returnWhat) {
        
        case MutationCount:
            
            return Long.valueOf(mutationCount);
        
        case OldValues:
        
            return new ResultBuffer(n, ret, ndx.getIndexMetadata()
                    .getTupleSerializer().getLeafValuesCoder());
        
        case BitMask:
            
            return new ResultBitBuffer(n, modified, mutationCount);
        
        default:
            throw new AssertionError();
        
        }
        
    }

    /**
	 * Returns an appropriate aggregator depending on {@link #returnWhat}.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	protected IResultHandler newAggregator() {

        switch (returnWhat) {
        
        case MutationCount:
            
			return (IResultHandler) new LongAggregator();

        case OldValues:
        
    		return (IResultHandler) new ResultBufferHandler(getKeys().size(), getValuesCoder());
    		
        case BitMask:
            
			return (IResultHandler) new ResultBitBufferHandler(getKeys().size());
        
        default:
            throw new AssertionError();
        
        }

	}

    @Override
    protected void readMetadata(final ObjectInput in) throws IOException,
            ClassNotFoundException {

        super.readMetadata(in);

        assertFound = in.readBoolean();

        returnWhat = ReturnWhatEnum.valueOf(in.readByte());

    }

    @Override
    protected void writeMetadata(final ObjectOutput out) throws IOException {

        super.writeMetadata(out);

        out.writeBoolean(assertFound);

        out.writeByte((byte) returnWhat.getValue());

    }

}