Package com.hp.hpl.squirrelrdf.rdb

Source Code of com.hp.hpl.squirrelrdf.rdb.ProcessedPattern

/*
* (c) Copyright 2006 Hewlett-Packard Development Company, LP
* All rights reserved.
* [See end of file]
*/

package com.hp.hpl.squirrelrdf.rdb;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.query.core.Binding;
import com.hp.hpl.jena.query.engine1.ExecutionContext;
import com.hp.hpl.squirrelrdf.querymap.QueryMapEngine;
import com.hp.hpl.squirrelrdf.querymap.exceptions.InconsistentException;

/**
* ProcessedPattern
*
* A triple pattern after processing. There is one pattern per database.
*
* @author Damian Steer <pldms@mac.com>
*/
public class ProcessedPattern implements Comparable, QueryMapEngine
{
  final static Log log = LogFactory.getLog(ProcessedPattern.class);
 
  private Database db;

  private List<ProcessedTriple> triples;

  private boolean tied;

  private Set<String> variables;

  private Map<Node, Table> subjToTable;
 
  /**
   * Create a processed pattern associated with a database
   *
   * @param db
   *            The database this pattern is concerned with
   */
  public ProcessedPattern(Database db)
  {
    this.db = db;
    this.triples = new ArrayList<ProcessedTriple>();
    this.tied = false;
    this.subjToTable = new HashMap<Node, Table>();
    this.variables = new HashSet<String>();
  }

  public Database getDatabase()
  {
    return db;
  }

  public Set<String> getVariables()
  {
    return variables;
  }

  /**
   * Add a triple to this pattern.
   *
   * @param subject
   *            The subject
   * @param col
   *            The column corresponding to the predicate
   * @param object
   *            The object
   */
  public void add(Node subject, Col col, Node object)
  {
    if (!db.equals(col.getDatabase()))
    {
      log.error("Mismatching column added");
      return;
    }
    ProcessedTriple triple = new ProcessedTriple(subject, col, object);
    triples.add(triple);

    // Does this pattern give a value for any column?
    if (!object.isVariable() || !subject.isVariable())
      this.tied = true;
    else
      variables.add(object.getName());
    subjToTable.put(subject, col.getTable());
  }
 
  /**
   * Add a subject (with corresponding table) -- used for type queries
   *
   * @param subject
   *          The subject
   * @param table
   *          The table (class) of this subject
   */
  public void add(Node subject, Table table)
  {
    subjToTable.put(subject, table);
  }
 
  /**
   * Get the subjects in this pattern.
   *
   * @return The subjects
   */
  public Set<Node> getSubjects()
  {
    return subjToTable.keySet();
  }
 
  /**
   * Get table for subject
   *
   * @param subject The subject node
   *
   * @return The corresponding table
   */
  public Table getTableForSubject(Node subject)
  {
    return subjToTable.get(subject);
  }

  /**
   * @return True if this pattern gives values for any objects
   */
  public boolean tied()
  {
    return this.tied;
  }

  /**
   * Patterns which give values for keys > patterns which give any values >
   * other patterns
   *
   * @param o
   * @return
   */
  public int compareTo(Object o)
  {
    ProcessedPattern other = (ProcessedPattern) o;

    if (this.tied() && !other.tied())
      return 1;
    if (!this.tied() && other.tied())
      return -1;
    return 0;
  }

  /**
   * Transform this query to an SQL query
   *
   * @param binding Existing binding
   * @param context Execution context
   *
   * @return An SQL query corresponding to this pattern
   * @throws InconsistentException
   */
  public String toSql(Binding binding, ExecutionContext context) throws InconsistentException
 
    Collection<String> select = new ArrayList<String>();
    Collection<String> where = new ArrayList<String>();
    Collection<String> from = new HashSet<String>();
   
    // This is for object vars appearing more than once
    Map<Node, String> objToCol = new HashMap<Node, String>();
   
    for (Node subject: getSubjects())
    {
      Table table = getTableForSubject(subject);
     
      from.add(table.getName() + " AS " + name(subject));
     
     
      // Prebind subj
      Node subj = null;
      if (subject.isConcrete() || binding.contains(subject.getName())) // subj given, or bound?
        subj = subject.isConcrete() ? subject : binding.get(subject.getName());
     
      if (table.hasPrimaryKeys()) // deal with primary keys
      {
        // Get primary key values
        for (Col col: getTableForSubject(subject).getPrimaryCols())
        {
          select.add(name(subject) + "." + col.getName() + " AS " +
              name(subject) + "$" + col.getName() + "$prim");
        }
       
        // Subject given (concrete) or bound -- find that row again
        if (subj != null)
        {
          if (!table.isValidSubject(subj))
          {
            log.warn("Table: " + table.getName());
            throw new InconsistentException("Invalid subject for table: " + subj);
          }
          Map<String, Object> attvals = table.nodeToKeys(subj);
         
          for (String col: attvals.keySet())
          {
            Object val = attvals.get(col);
            where.add(name(subject) + "." + col + " = '" + val + "'");
          }
        }
      }
      else if (subj != null)
      {
        throw new InconsistentException("Concrete subject given for table with no primary keys: " + subj);
      }
    }
   
    for (Iterator i = triples.iterator(); i.hasNext();)
    {
      ProcessedTriple triple = (ProcessedTriple) i.next();
      triple.addSelect(select, objToCol);
      triple.addWhere(where, objToCol, binding);
    }
    StringBuffer query = new StringBuffer();
    if (select.isEmpty()) // No select. Can happen with ASK, for example
      select.add("*");
     
    query.append("SELECT ");
    query.append(join(" , ", select));
    query.append("\n");
    if (from.isEmpty())
    {
      log.warn("FROM is empty!");
    }
    query.append("FROM ");
    query.append(join(" , ", from));
    query.append("\n");
    if (!where.isEmpty())
    {
      query.append("WHERE\n");
      query.append(join(" AND\n", where));
    }
    return query.toString();
  }

  private StringBuffer join(String sep, Collection<String> coll)
  {
    StringBuffer joined = new StringBuffer();
    boolean rest = false;
    for (Iterator i = coll.iterator(); i.hasNext();)
    {
      if (rest)
        joined.append(sep);
      joined.append(i.next());
      rest = true;
    }
    return joined;
  }
 
  /**
   * Execute this query given an existing binding and a context
   *
   * @param binding Existing binding
   * @param context The execution context
   * @return Iterator over results for continuing binding
   */
  public Iterator<Map<String, Node>> execute(Binding binding, ExecutionContext context)
  {
    try
    {
      String sqlQuery = toSql(binding, context);
      log.debug("Query: [" + db.getName() + "]\n" + sqlQuery + "\n");
      Connection connection = db.getConnection();
      Statement sm = connection.createStatement();
      ResultSet results = sm.executeQuery(sqlQuery);
     
      return new ResultSetIterator(results, this, context);
    }
    catch (SQLException e)
    {
      log.error("Problem executing SQL query", e);
    }
    catch (InconsistentException e)
    {
      log.warn("Failure due to inconsistency: " + e.getMessage());
    }
    catch (ClassNotFoundException e)
    {
      log.error("Driver not found?", e);
    }
   
    List<Map<String, Node>> l = Collections.emptyList();
    return l.iterator();
  }
 
  /* *
   * Give an alias for a subject node.
   * Easy for variables, tricky for concrete (but only used in this class not outside).
   *
   * @param node A (subject) node we want a useful name for.
   */
  private String name(Node node)
  {
    if (node.isVariable()) return node.getName();
    return subjToTable.get(node).getName() + "$" + node.hashCode();
  }


/**
* A processed triple. Has some idea about turning itself into SQL.
*/
class ProcessedTriple
{
  private Node subject;

  private Col col;

  private Node object;

  public ProcessedTriple(Node subject, Col col, Node object)
  {
    this.subject = subject;
    this.col = col;
    this.object = object;
  }

  public Node getSubject()
  {
    return subject;
  }

  public Col getCol()
  {
    return col;
  }

  public Node getObject()
  {
    return object;
  }

  public void addSelect(Collection<String> select, Map<Node, String> objToCol)
  {
    if (object.isConcrete())
      return;
    // Already SELECTed, so skip...
    if (objToCol.containsKey(object))
      return;
    String selectBit = name(subject) + "." + col.getName() + " AS "
        + object.getName();
    select.add(selectBit);
    objToCol.put(object, name(subject) + "." + col.getName());
  }

  public void addWhere(Collection<String> where, Map<Node, String> objToCol, Binding binding)
  {
    Node theObject = object;
   
    if (object.isVariable() && binding.contains(object.getName())) // Check for existing value
      theObject = binding.get(object.getName());
     
    String thing = name(subject) + "." + col.getName();
    // Phew. This is a test for whether an (object) variable is already
    // bound
    // by another expression in this query.
    if (theObject.isVariable() && objToCol.containsKey(theObject)
        && !thing.equals(objToCol.get(theObject)))
    {
      String otherThing = objToCol.get(theObject);
      String identCond = name(subject) + "." + col.getName() + "="
          + otherThing;
      where.add(identCond);
    }

    // No NULLs -- stick with RDB dream
    if (theObject.isVariable())
    {
      String notNullCond = name(subject) + "." + col.getName() + " IS NOT NULL";
      where.add(notNullCond);
      return;
    }
    else
    // Constraint on value
    {
      String whereBit = name(subject) + "." + col.getName() + "='"
          + theObject.getLiteral().getValue() + "'";
      where.add(whereBit);
    }
  }
}

}

/*
* (c) Copyright 2006 Hewlett-Packard Development Company, LP All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The name of the author may not
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 
TOP

Related Classes of com.hp.hpl.squirrelrdf.rdb.ProcessedPattern

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.