/**
   * Gets a parsed query for the passed in parameters. If the ParsedQuery requested has already been
   * created on this app node, it will retrieve it from a cache instead of recreating it. Use this
   * method instead of parseQuery if a cached copy is acceptable (almost always).
   *
   * @param db Database that the query will run against
   * @param toParse Query to be parsed.
   * @param session Database session.
   * @return a PreparedStatement to use.
   */
  public static ParsedQuery getParsedQuery(String db, Query toParse, Session session)
      throws FieldNotIndexedException {

    if (db == null || db.trim().equals("")) {
      throw new IllegalArgumentException("Query must be populated.");
    }
    if (toParse == null) {
      throw new IllegalArgumentException("Query cannot be null.");
    }
    final String key = db + ":" + toParse.getTable() + ":" + toParse.getWhere();
    // StopWatch pull = new StopWatch();
    // pull.start();
    Cache c = CacheFactory.getCache("parsedQuery");
    //        synchronized (CacheSynchronizer.getLockingObject(key, ParsedQuery.class))
    //        {
    Element e = c.get(key);
    // pull.stop();
    // logger.debug("Time to pull a parsed query from cache: " + pull.getTime());
    if (e == null) {
      logger.debug("Creating new ParsedQuery for: " + key);
      // StopWatch sw = new StopWatch();
      // sw.start();
      e = new Element(key, parseQuery(db, toParse, session));
      c.put(e);
      // sw.stop();
      // logger.debug("Time to create a new parsed query: " + sw.getTime());
    } else {
      logger.trace("Pulling ParsedQuery from Cache: " + e.getObjectValue().toString());
    }
    return (ParsedQuery) e.getObjectValue();
    // }
  }
 /**
  * Parses a query to determine if it is valid and determine the information we actually need to
  * perform the query.
  *
  * @param db Database that the query will run against
  * @param toParse Query to be parsed.
  * @param session Database session.
  * @return A ParsedQuery object for the query.
  * @throws FieldNotIndexedException
  */
 public static ParsedQuery parseQuery(String db, Query toParse, Session session)
     throws FieldNotIndexedException {
   // let's parse the where clause so we know what we are actually searching for
   WhereClause where = new WhereClause(toParse.getWhere());
   // determine if the query is valid; in other words is it searching on valid getFields that we
   // have indexed
   List<String> fieldsToQueryOn = where.getFields();
   IndexRepository indexRepo = new IndexRepositoryImpl(session);
   List<Index> indices = indexRepo.readAllCached(new Identifier(db, toParse.getTable()));
   Index indexToUse = null;
   for (Index index : indices) {
     //            if (index.isActive())//only use active indexes
     //            {
     if (Utils.equalLists(index.getFieldsValues(), fieldsToQueryOn)) {
       indexToUse = index; // we have a perfect match; the index matches the query exactly
       break;
     }
     //            }
   }
   if (indexToUse
       == null) { // whoops, no perfect match, let try for a partial match (ie, the index has more
     // getFields than the query)
     // querying on non-primary getFields will lead to us being unable to determine which bucket to
     // search
     for (Index index : indices) {
       //                if (index.isActive())//only use active indexes
       //                {
       // make a copy of the fieldsToQueryOn so we don't mutate the orginal
       ArrayList<String> fieldsToQueryOnCopy = new ArrayList<>(fieldsToQueryOn);
       ArrayList<String> indexFields =
           new ArrayList<>(index.getFieldsValues()); // make a copy here too
       fieldsToQueryOnCopy.removeAll(
           indexFields); // we remove all the getFields we have, from the getFields we want
       // if there are not any getFields left in getFields we want
       if (fieldsToQueryOnCopy.isEmpty()
           && fieldsToQueryOn.contains(
               indexFields.get(
                   0))) { // second clause in this statement is what ensure we have a primary
         // index; see TODO above.
         // we have an index that will work (even though we have extra getFields in it)
         indexToUse = index;
         break;
       }
       //                }
     }
   }
   if (indexToUse == null) {
     throw new FieldNotIndexedException(fieldsToQueryOn);
   }
   ParsedQuery toReturn = new ParsedQuery(toParse, where, indexToUse);
   return toReturn;
 }