Package org.openrdf.sail.memory

Source Code of org.openrdf.sail.memory.MemoryStoreConnection$MemEvaluationStatistics$MemCardinalityCalculator

/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
* Copyright James Leigh (c) 2006.
*
* Licensed under the Aduna BSD-style license.
*/

package org.openrdf.sail.memory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import info.aduna.concurrent.locks.Lock;

import org.openrdf.OpenRDFUtil;
import org.openrdf.cursor.CollectionCursor;
import org.openrdf.cursor.Cursor;
import org.openrdf.model.Namespace;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.query.BindingSet;
import org.openrdf.query.algebra.QueryModel;
import org.openrdf.query.algebra.StatementPattern;
import org.openrdf.query.algebra.Var;
import org.openrdf.query.algebra.evaluation.TripleSource;
import org.openrdf.query.algebra.evaluation.cursors.LockingCursor;
import org.openrdf.query.algebra.evaluation.impl.BindingAssigner;
import org.openrdf.query.algebra.evaluation.impl.CompareOptimizer;
import org.openrdf.query.algebra.evaluation.impl.ConjunctiveConstraintSplitter;
import org.openrdf.query.algebra.evaluation.impl.ConstantOptimizer;
import org.openrdf.query.algebra.evaluation.impl.DisjunctiveConstraintOptimizer;
import org.openrdf.query.algebra.evaluation.impl.EvaluationStatistics;
import org.openrdf.query.algebra.evaluation.impl.EvaluationStrategyImpl;
import org.openrdf.query.algebra.evaluation.impl.FilterOptimizer;
import org.openrdf.query.algebra.evaluation.impl.QueryJoinOptimizer;
import org.openrdf.query.algebra.evaluation.impl.QueryModelPruner;
import org.openrdf.query.algebra.evaluation.impl.SameTermFilterOptimizer;
import org.openrdf.query.algebra.evaluation.util.QueryOptimizerList;
import org.openrdf.query.impl.EmptyBindingSet;
import org.openrdf.sail.SailReadOnlyException;
import org.openrdf.sail.helpers.NotifyingSailConnectionBase;
import org.openrdf.sail.inferencer.InferencerConnection;
import org.openrdf.sail.memory.model.MemResource;
import org.openrdf.sail.memory.model.MemStatement;
import org.openrdf.sail.memory.model.MemStatementCursor;
import org.openrdf.sail.memory.model.MemStatementList;
import org.openrdf.sail.memory.model.MemURI;
import org.openrdf.sail.memory.model.MemValue;
import org.openrdf.sail.memory.model.MemValueFactory;
import org.openrdf.sail.memory.model.ReadMode;
import org.openrdf.store.Isolation;
import org.openrdf.store.StoreException;

/**
* Implementation of a Sail Connection for memory stores.
*
* @author Arjohn Kampman
* @author jeen
*/
public class MemoryStoreConnection extends NotifyingSailConnectionBase implements InferencerConnection {

  /*-----------*
   * Variables *
   *-----------*/

  protected final MemoryStore store;

  protected final MemValueFactory vf;

  /**
   * The exclusive transaction lock held by this connection during
   * transactions.
   */
  private volatile Lock txnLock;

  /**
   * A statement list read lock held by this connection during transactions.
   * Keeping this lock prevents statements from being removed from the main
   * statement list during transactions.
   */
  private volatile Lock txnStLock;

  /*--------------*
   * Constructors *
   *--------------*/

  protected MemoryStoreConnection(MemoryStore store) {
    this.store = store;
    this.vf = store.createValueFactory();

    // Set default isolation level (serializable since we don't allow
    // concurrent transactions yet)
    try {
      setTransactionIsolation(Isolation.SERIALIZABLE);
    }
    catch (StoreException e) {
      throw new RuntimeException("unexpected exception", e);
    }
  }

  /*---------*
   * Methods *
   *---------*/

  public MemValueFactory getValueFactory() {
    return vf;
  }

  public Cursor<? extends BindingSet> evaluate(QueryModel query, BindingSet bindings, boolean includeInferred)
    throws StoreException
  {
    logger.trace("Incoming query model:\n{}", query);

    // Clone the tuple expression to allow for more aggresive optimizations
    query = query.clone();

    Lock stLock = store.getStatementsReadLock();

    try {
      int snapshot = store.getCurrentSnapshot();
      ReadMode readMode = ReadMode.COMMITTED;

      if (!isAutoCommit()) {
        snapshot++;
        readMode = ReadMode.TRANSACTION;
      }

      TripleSource tripleSource = new MemTripleSource(includeInferred, snapshot, readMode);
      EvaluationStrategyImpl strategy = new EvaluationStrategyImpl(tripleSource, query);

      QueryOptimizerList optimizerList = new QueryOptimizerList();
      optimizerList.add(new BindingAssigner());
      optimizerList.add(new ConstantOptimizer(strategy));
      optimizerList.add(new CompareOptimizer());
      optimizerList.add(new ConjunctiveConstraintSplitter());
      optimizerList.add(new DisjunctiveConstraintOptimizer());
      optimizerList.add(new SameTermFilterOptimizer());
      optimizerList.add(new QueryModelPruner());
      optimizerList.add(new QueryJoinOptimizer(new MemEvaluationStatistics()));
      optimizerList.add(new FilterOptimizer());

      optimizerList.optimize(query, bindings);

      logger.trace("Optimized query model:\n{}", query);

      Cursor<BindingSet> iter;
      iter = strategy.evaluate(query, EmptyBindingSet.getInstance());
      iter = new LockingCursor<BindingSet>(stLock, iter);
      return iter;
    }
    catch (StoreException e) {
      stLock.release();
      throw e;
    }
    catch (RuntimeException e) {
      stLock.release();
      throw e;
    }
  }

  public Cursor<? extends Resource> getContextIDs()
    throws StoreException
  {
    // Note: we can't do this in a streaming fashion due to concurrency
    // issues; iterating over the set of URIs or bnodes while another thread
    // adds statements with new resources would result in
    // ConcurrentModificationException's (issue SES-544).

    // Create a list of all resources that are used as contexts
    ArrayList<MemResource> contextIDs = new ArrayList<MemResource>(32);

    Lock stLock = store.getStatementsReadLock();

    try {
      final int snapshot = isAutoCommit() ? store.getCurrentSnapshot() : store.getCurrentSnapshot() + 1;
      final ReadMode readMode = isAutoCommit() ? ReadMode.COMMITTED : ReadMode.TRANSACTION;

      synchronized (vf.getURIFactory()) {
        for (MemResource memResource : vf.getMemURIs()) {
          if (isContextResource(memResource, snapshot, readMode)) {
            contextIDs.add(memResource);
          }
        }
      }
      synchronized (vf.getBNodeFactory()) {
        for (MemResource memResource : vf.getMemBNodes()) {
          if (isContextResource(memResource, snapshot, readMode)) {
            contextIDs.add(memResource);
          }
        }
      }
    }
    finally {
      stLock.release();
    }

    return new CollectionCursor<MemResource>(contextIDs);
  }

  private boolean isContextResource(MemResource memResource, int snapshot, ReadMode readMode)
    throws StoreException
  {
    MemStatementList contextStatements = memResource.getContextStatementList();

    // Filter resources that are not used as context identifier
    if (contextStatements.size() == 0) {
      return false;
    }

    // Filter more thoroughly by considering snapshot and read-mode parameters
    MemStatementCursor iter = new MemStatementCursor(contextStatements, null, null, null, false, snapshot,
        readMode);
    try {
      return iter.next() != null;
    }
    finally {
      iter.close();
    }
  }

  public Cursor<? extends Statement> getStatements(Resource subj, URI pred, Value obj,
      boolean includeInferred, Resource... contexts)
    throws StoreException
  {
    Lock stLock = store.getStatementsReadLock();

    try {
      int snapshot = store.getCurrentSnapshot();
      ReadMode readMode = ReadMode.COMMITTED;

      if (!isAutoCommit()) {
        snapshot++;
        readMode = ReadMode.TRANSACTION;
      }

      return new LockingCursor<MemStatement>(stLock, store.createStatementIterator(subj, pred, obj,
          !includeInferred, snapshot, readMode, vf, contexts));
    }
    catch (RuntimeException e) {
      stLock.release();
      throw e;
    }
  }

  public long size(Resource subj, URI pred, Value obj, boolean includeInferred, Resource... contexts)
    throws StoreException
  {
    Lock stLock = store.getStatementsReadLock();

    try {
      Cursor<? extends Statement> iter = getStatements(subj, pred, obj, includeInferred, contexts);

      try {
        long size = 0L;

        while (iter.next() != null) {
          size++;
        }

        return size;
      }
      finally {
        iter.close();
      }
    }
    finally {
      stLock.release();
    }
  }

  public Cursor<? extends Namespace> getNamespaces()
    throws StoreException
  {
    return new CollectionCursor<Namespace>(store.getNamespaceStore());
  }

  public String getNamespace(String prefix)
    throws StoreException
  {
    return store.getNamespaceStore().getNamespace(prefix);
  }

  @Override
  public void begin()
    throws StoreException
  {
    if (!store.isWritable()) {
      throw new SailReadOnlyException("Unable to start transaction: data file is locked or read-only");
    }

    txnStLock = store.getStatementsReadLock();

    // Prevent concurrent transactions by acquiring an exclusive txn lock
    txnLock = store.getTransactionLock();
    store.startTransaction();
    super.begin();
  }

  @Override
  public void commit()
    throws StoreException
  {
    store.commit();
    txnLock.release();
    txnStLock.release();
    super.commit();
  }

  @Override
  public void rollback()
    throws StoreException
  {
    try {
      store.rollback();
      super.rollback();
    }
    finally {
      txnLock.release();
      txnStLock.release();
    }
  }

  public void addStatement(Resource subj, URI pred, Value obj, Resource... contexts)
    throws StoreException
  {
    addStatementInternal(subj, pred, obj, true, contexts);
  }

  public boolean addInferredStatement(Resource subj, URI pred, Value obj, Resource... contexts)
    throws StoreException
  {
    return addStatementInternal(subj, pred, obj, false, contexts);
  }

  /**
   * Adds the specified statement to this MemoryStore.
   *
   * @throws StoreException
   */
  private boolean addStatementInternal(Resource subj, URI pred, Value obj, boolean explicit,
      Resource... contexts)
    throws StoreException
  {
    assert txnStLock.isActive();
    assert txnLock.isActive();

    Statement st = null;

    if (contexts != null && contexts.length == 0) {
      st = store.addStatement(subj, pred, obj, null, explicit, vf);
      if (st != null) {
        notifyStatementAdded(st);
      }
    }
    else {
      for (Resource context : OpenRDFUtil.notNull(contexts)) {
        st = store.addStatement(subj, pred, obj, context, explicit, vf);
        if (st != null) {
          notifyStatementAdded(st);
        }
      }
    }

    // FIXME: this return type is invalid in case multiple contexts were
    // specified
    return st != null;
  }

  public void removeStatements(Resource subj, URI pred, Value obj, Resource... contexts)
    throws StoreException
  {
    removeStatementsInternal(subj, pred, obj, true, contexts);
  }

  public boolean removeInferredStatements(Resource subj, URI pred, Value obj, Resource... contexts)
    throws StoreException
  {
    return removeStatementsInternal(subj, pred, obj, false, contexts);
  }

  public void flushUpdates() {
    // no-op; changes are reported as soon as they come in
  }

  /**
   * Removes the statements that match the specified pattern of subject,
   * predicate, object and context.
   *
   * @param subj
   *        The subject for the pattern, or <tt>null</tt> for a wildcard.
   * @param pred
   *        The predicate for the pattern, or <tt>null</tt> for a wildcard.
   * @param obj
   *        The object for the pattern, or <tt>null</tt> for a wildcard.
   * @param explicit
   *        Flag indicating whether explicit or inferred statements should be
   *        removed; <tt>true</tt> removes explicit statements that match the
   *        pattern, <tt>false</tt> removes inferred statements that match the
   *        pattern.
   * @throws StoreException
   */
  private boolean removeStatementsInternal(Resource subj, URI pred, Value obj, boolean explicit,
      Resource... contexts)
    throws StoreException
  {
    Cursor<MemStatement> stIter = store.createStatementIterator(subj, pred, obj, explicit,
        store.getCurrentSnapshot() + 1, ReadMode.TRANSACTION, vf, contexts);

    return removeIteratorStatements(stIter, explicit);
  }

  protected boolean removeIteratorStatements(Cursor<MemStatement> stIter, boolean explicit)
    throws StoreException
  {
    boolean statementsRemoved = false;

    try {
      MemStatement st;
      while ((st = stIter.next()) != null) {

        if (store.removeStatement(st, explicit)) {
          statementsRemoved = true;
          notifyStatementRemoved(st);
        }
      }
    }
    finally {
      stIter.close();
    }

    return statementsRemoved;

  }

  public void setNamespace(String prefix, String name)
    throws StoreException
  {
    // FIXME: changes to namespace prefixes not isolated in transactions yet
    try {
      store.getNamespaceStore().setNamespace(prefix, name);
    }
    catch (IllegalArgumentException e) {
      throw new StoreException(e.getMessage());
    }
  }

  public void removeNamespace(String prefix)
    throws StoreException
  {
    // FIXME: changes to namespace prefixes not isolated in transactions yet
    store.getNamespaceStore().removeNamespace(prefix);
  }

  public void clearNamespaces()
    throws StoreException
  {
    // FIXME: changes to namespace prefixes not isolated in transactions yet
    store.getNamespaceStore().clear();
  }

  /*-----------------------------*
   * Inner class MemTripleSource *
   *-----------------------------*/

  /**
   * Implementation of the TripleSource interface from the Sail Query Model
   */
  protected class MemTripleSource implements TripleSource {

    protected final int snapshot;

    protected final ReadMode readMode;

    protected final boolean includeInferred;

    public MemTripleSource(boolean includeInferred, int snapshot, ReadMode readMode) {
      this.includeInferred = includeInferred;
      this.snapshot = snapshot;
      this.readMode = readMode;
    }

    public Cursor<MemStatement> getStatements(Resource subj, URI pred, Value obj, Resource... contexts) {
      return store.createStatementIterator(subj, pred, obj, !includeInferred, snapshot, readMode, vf,
          contexts);
    }

    public MemValueFactory getValueFactory() {
      return vf;
    }
  } // end inner class MemTripleSource

  /*-------------------------------------*
   * Inner class MemEvaluationStatistics *
   *-------------------------------------*/

  /**
   * Uses the MemoryStore's statement sizes to give cost estimates based on the
   * size of the expected results. This process could be improved with
   * repository statistics about size and distribution of statements.
   *
   * @author Arjohn Kampman
   * @author James Leigh
   */
  protected class MemEvaluationStatistics extends EvaluationStatistics {

    @Override
    protected CardinalityCalculator createCardinalityCalculator() {
      return new MemCardinalityCalculator();
    }

    protected class MemCardinalityCalculator extends CardinalityCalculator {

      @Override
      public double getCardinality(StatementPattern sp) {
        Resource subj = (Resource)getConstantValue(sp.getSubjectVar());
        URI pred = (URI)getConstantValue(sp.getPredicateVar());
        Value obj = getConstantValue(sp.getObjectVar());
        Resource context = (Resource)getConstantValue(sp.getContextVar());

        // Perform look-ups for value-equivalents of the specified values
        MemResource memSubj = vf.getMemResource(subj);
        MemURI memPred = vf.getMemURI(pred);
        MemValue memObj = vf.getMemValue(obj);
        MemResource memContext = vf.getMemResource(context);

        if (subj != null && memSubj == null || pred != null && memPred == null || obj != null
            && memObj == null || context != null && memContext == null)
        {
          // non-existent subject, predicate, object or context
          return 0.0;
        }

        // Search for the smallest list that can be used by the iterator
        List<Integer> listSizes = new ArrayList<Integer>(4);
        if (memSubj != null) {
          listSizes.add(memSubj.getSubjectStatementCount());
        }
        if (memPred != null) {
          listSizes.add(memPred.getPredicateStatementCount());
        }
        if (memObj != null) {
          listSizes.add(memObj.getObjectStatementCount());
        }
        if (memContext != null) {
          listSizes.add(memContext.getContextStatementCount());
        }

        double cardinality;

        if (listSizes.isEmpty()) {
          // all wildcards
          cardinality = store.size();
        }
        else {
          cardinality = Collections.min(listSizes);

          // List<Var> vars = getVariables(sp);
          // int constantVarCount = countConstantVars(vars);
          //
          // // Subtract 1 from var count as this was used for the list
          // size
          // double unboundVarFactor = (double)(vars.size() -
          // constantVarCount) / (vars.size() - 1);
          //
          // cardinality = Math.pow(cardinality, unboundVarFactor);
        }

        return cardinality;
      }

      protected Value getConstantValue(Var var) {
        if (var != null) {
          return var.getValue();
        }

        return null;
      }
    }
  } // end inner class MemCardinalityCalculator
}
TOP

Related Classes of org.openrdf.sail.memory.MemoryStoreConnection$MemEvaluationStatistics$MemCardinalityCalculator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.