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

com.bigdata.sparse.AbstractAtomicRowReadOrWrite 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 Jul 3, 2008
 */

package com.bigdata.sparse;

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

import org.apache.log4j.Logger;

import com.bigdata.btree.IIndex;
import com.bigdata.btree.IRangeQuery;
import com.bigdata.btree.ITuple;
import com.bigdata.btree.ITupleIterator;
import com.bigdata.btree.keys.SuccessorUtil;
import com.bigdata.btree.proc.AbstractIndexProcedure;
import com.bigdata.btree.proc.ISimpleIndexProcedure;
import com.bigdata.util.BytesUtil;

/**
 * Abstract class implements the atomic read operation. However, it does NOT
 * declare itself to be a read-only operation since this class is extended by
 * both {@link AtomicRowRead} and {@link AtomicRowWriteRead}.
 * 
 * @author Bryan Thompson
 */
abstract public class AbstractAtomicRowReadOrWrite extends
        AbstractIndexProcedure implements ISimpleIndexProcedure,
        IRowStoreConstants, Externalizable {

    private static final Logger log = Logger.getLogger(AbstractAtomicRowReadOrWrite.class);
    
//    /**
//     * True iff the {@link #log} level is INFO or less.
//     */
//    protected final static boolean INFO = log.isInfoEnabled();
//    
//    /**
//     * True iff the {@link #log} level is DEBUG or less.
//     */
//    protected final static boolean DEBUG = log.isDebugEnabled();
    
    protected Schema schema;
    protected Object primaryKey;
    protected long fromTime;
    protected long toTime;
    protected INameFilter filter;
    
    /**
     * De-serialization ctor.
     */
    protected AbstractAtomicRowReadOrWrite() {

        super();
        
    }
    
    /**
     * Constructor for an atomic write/read operation.
     * 
     * @param schema
     *            The schema governing the property set.
     * @param primaryKey
     *            The value of the primary key (identifies the logical row to be
     *            read).
     * @param fromTime
     *            The first timestamp for which timestamped property values will
     *            be accepted.
     * @param toTime
     *            The first timestamp for which timestamped property values will
     *            NOT be accepted -or- {@link IRowStoreConstants#CURRENT_ROW} to
     *            accept only the most current binding whose timestamp is GTE
     *            fromTime.
     * @param filter
     *            An optional filter used to restrict the property values that
     *            will be returned.
     */
    protected AbstractAtomicRowReadOrWrite(final Schema schema,
            final Object primaryKey, final long fromTime, final long toTime,
            final INameFilter filter) {

        SparseRowStore.assertArgs(schema, primaryKey, fromTime, toTime);

        this.schema = schema;
        
        this.primaryKey = primaryKey;
        
        this.fromTime = fromTime;
        
        this.toTime = toTime;

        this.filter = filter;
        
    }
    
    /**
     * Atomic read on the index.
     * 
     * @param ndx
     *            The index on which the data are stored.
     * @param schema
     *            The schema governing the row.
     * @param primaryKey
     *            The primary key identifies the logical row of interest.
     * @param fromTime
     *            The first timestamp for which timestamped property values will
     *            be accepted.
     * @param toTime
     *            The first timestamp for which timestamped property values will
     *            NOT be accepted -or- {@link IRowStoreConstants#CURRENT_ROW} to
     *            accept only the most current binding whose timestamp is GTE
     *            fromTime.
     * @param writeTime
     *            The resolved timestamp for an atomic write operation -or- ZERO
     *            (0L) IFF the operation is NOT a write.
     * @param filter
     *            An optional filter used to select the values for property
     *            names accepted by that filter.
     * 
     * @return The logical row for that primary key -or- null iff
     *         there is no data for the primaryKey.
     */
    protected static TPS atomicRead(final IIndex ndx, final Schema schema,
            final Object primaryKey, final long fromTime, final long toTime,
            final long writeTime, final INameFilter filter) {

        final byte[] fromKey = schema.getPrefix(ndx.getIndexMetadata()
                .getKeyBuilder(), primaryKey);

        final TPS tps = atomicRead(ndx, fromKey, schema, fromTime, toTime,
                filter, new TPS(schema, writeTime));

        if (tps == null) {

            if (log.isInfoEnabled())
                log.info("No data for primaryKey: " + primaryKey);

        }
    
        return tps;
    
    }

    /**
     * Alternative form useful when you have the raw key (unsigned byte[])
     * rather than a primary key (application object).
     * 
     * @param tps
     * @param fromKey
     * @param ndx
     * @param schema
     * @param fromTime
     * @param toTime
     * @param filter
     * @param tps
     *            The object into which the timestamped property values will be
     *            read.
     * 
     * @return The {@link TPS} -or- null iff there is no data for
     *         the logical row which satisified the various criteria (the
     *         schema, fromTime, toTime, and filter).
     */
    protected static TPS atomicRead(final IIndex ndx, final byte[] fromKey,
            final Schema schema, final long fromTime, final long toTime,
            final INameFilter filter, final TPS tps) {

        assert ndx != null;

        assert schema != null;
        
        assert fromKey != null;
        
        assert tps != null;

        /*
         * Scan all entries within the fromKey/toKey range populating [tps] as
         * we go.
         */
    
        final byte[] toKey = SuccessorUtil.successor(fromKey.clone());
    
        if (log.isInfoEnabled()) {
            log.info("read: fromKey=" + BytesUtil.toString(fromKey)+"\n"+
                     "read:   toKey=" + BytesUtil.toString(toKey));
        }
    
//        /*
//         * Note: If we are only going to accept the most recent bindings then we
//         * read in reverse order. This allows us to efficiently ignore property
//         * values for which we already have a binding in our TPS.
//         */
//        
//        final boolean reverseScan = toTime == CURRENT_ROW; 
//        
//        if (reverseScan) {
//
//            if(INFO)
//                log.info("reverseScan: fromTime=" + fromTime);
//            
//        }
//        
//        final int flags = IRangeQuery.DEFAULT | IRangeQuery.READONLY
//                | (reverseScan ? IRangeQuery.REVERSE : 0);

        final int flags = IRangeQuery.DEFAULT | IRangeQuery.READONLY;
        
//        /*
//         * Used during reverse scans when the [toTime] is [CURRENT_ROW] to track
//         * which properties have bound values, including when a "deleted" entry
//         * is observed for a property.
//         */
//
//        final Set bound = (reverseScan ? new HashSet()
//                : null);
        
        // iterator scanning tuples encoding timestamped property values.
        final ITupleIterator itr = ndx.rangeIterator(fromKey, toKey,
                0/* capacity */, flags, null/* filter */);
    
        // #of entries scanned for that primary key.
        int nscanned = 0;
        
        while(itr.hasNext()) {
            
            final ITuple tuple = itr.next();
            
            final byte[] key = tuple.getKey();
            
            nscanned++;
            
            // Decode the key so that we can get the column name.
            final KeyDecoder keyDecoder = new KeyDecoder(key);
    
            // The column name.
            final String col = keyDecoder.getColumnName();
    
            if (filter != null && !filter.accept(col)) {
    
                // Skip property names that have been filtered out.
    
                if (log.isDebugEnabled()) {
    
                    log.debug("Skipping property: name=" + col + " (filtered)");
                    
                }
    
                continue;
    
            }
    
//            if (toTime == CURRENT_ROW) {
//
//                /*
//                 * This relies on the fact that we traverse the tuples in reverse
//                 * index order when we only want to collect the current bindings.
//                 * Therefore we simply ignore any property value if we already have
//                 * a binding for that property. [bound] is used to quickly detect
//                 * property values for which we have already collected a binding,
//                 * even if that binding was a "deleted" property marker.
//                 */
//
//                if (bound.contains(col)) {
//
//                    if (DEBUG) {
//
//                        log.debug("Skipping property: name=" + col
//                                + " (already bound)");
//
//                    }
//
//                    continue;
//
//                }
//
//                bound.add(col);
//
//            }

            /*
             * Skip column values whose timestamp lies outside of the specified
             * half-open range.
             */
           
            final long columnValueTimestamp = keyDecoder.getTimestamp();
            
            if (columnValueTimestamp < fromTime) {

                if (log.isDebugEnabled()) {

                    log.debug("Ignoring earlier revision: col=" + col
                            + ", fromTime=" + fromTime + ", timestamp="
                            + columnValueTimestamp);

                }

                continue;

            }

            if (toTime != CURRENT_ROW && columnValueTimestamp >= toTime) {

                if (log.isDebugEnabled()) {

                    log.debug("Ignoring later revision: col=" + col
                            + ", toTime=" + toTime + ", timestamp="
                            + columnValueTimestamp);

                }

                continue;

            }

            /*
             * Decode the value. A [null] indicates a deleted property value.
             */

            final byte[] val = tuple.getValue();

            final Object v = ValueType.decode(val);

            /*
             * Add this timestamped property value to the collection.
             */

            tps.set(col, columnValueTimestamp, v);

            if (log.isInfoEnabled())
                log.info("Accept: name=" + col + ", timestamp="
                        + columnValueTimestamp + ", value=" + v);
    
        }
    
        if (nscanned == 0) {
            
            /*
             * Return null iff there are no column values for that primary key.
             * 
             * Note: this is a stronger criteria than none being matched.
             */
            
            return null;
    
        }
    
        if (toTime == CURRENT_ROW) {

            return tps.currentRow();
            
        }
        
        return tps;
        
    }

    /**
     * Return the current binding for the named property.
     * 
     * @param schema
     *            The schema.
     * @param primaryKey
     *            The primary key.
     * @param name
     *            The property name.
     * 
     * @return The current binding -or- null iff there is no
     *         current binding.
     * 
     * @todo this can be optimized by including the encoded column name in the
     *       generated [fromKey] and [toKey] so that we scan less data from the
     *       index and by using a reverse traversal iterator to read the most
     *       recent value in the key range first. This is especially important
     *       if timeseries data are being stored.
     */
    protected static ITPV getCurrentValue(final IIndex ndx,
            final Schema schema, final Object primaryKey, final String name) {

        final TPS tps = atomicRead(ndx, schema, primaryKey, MIN_TIMESTAMP,
                CURRENT_ROW, 0L/* writeTime */, new SingleColumnFilter(name));

        if (tps == null) {

            // never bound.
            return null;
            
        }
        
        final ITPV tpv = tps.get(name);

        if(tpv.getValue() == null) {
            
            // deleted property value.
            return null;
            
        }
        
        return tpv;

    }

    /**
     * The initial version.
     */
    private static final transient byte VERSION0 = 0;

    /**
     * The current version.
     */
    private static final transient byte VERSION = VERSION0;

    @Override
    public void readExternal(final ObjectInput in) throws IOException,
            ClassNotFoundException {
        
        final byte version = in.readByte();

        switch (version) {
        case VERSION0:
            break;
        default:
            throw new UnsupportedOperationException("Unknown version: "
                    + version);
        }

        schema = (Schema) in.readObject();
        
        primaryKey = in.readObject();
        
        fromTime = in.readLong();

        toTime = in.readLong();
        
        filter = (INameFilter) in.readObject();
        
    }

    @Override
    public void writeExternal(final ObjectOutput out) throws IOException {

        out.writeByte(VERSION);

        out.writeObject(schema);
        
        out.writeObject(primaryKey);
        
        out.writeLong(fromTime);
        
        out.writeLong(toTime);
        
        out.writeObject(filter);
        
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy